@catladder/cli 0.0.0-fix-migrate-string-cmd-508a469f → 0.0.0-fix-values-as-files-73f39da5
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/README.md +2 -1
- package/dist/apps/cli/commands/general/index.js +17 -9
- package/dist/apps/cli/commands/general/index.js.map +1 -1
- package/dist/apps/cli/commands/general/portForward.js +5 -6
- package/dist/apps/cli/commands/general/portForward.js.map +1 -1
- package/dist/apps/cli/commands/project/commandCloudSqlProxy.js +27 -19
- package/dist/apps/cli/commands/project/commandCloudSqlProxy.js.map +1 -1
- package/dist/apps/cli/commands/project/commandConfigSecrets.d.ts +2 -1
- package/dist/apps/cli/commands/project/commandConfigSecrets.js +175 -110
- package/dist/apps/cli/commands/project/commandConfigSecrets.js.map +1 -1
- package/dist/apps/cli/commands/project/commandDeletePods.js +4 -3
- package/dist/apps/cli/commands/project/commandDeletePods.js.map +1 -1
- package/dist/apps/cli/commands/project/{commandInitGitlab.d.ts → commandInitProject.old.d.ts} +0 -0
- package/dist/apps/cli/commands/project/{commandInitProject.js → commandInitProject.old.js} +1 -1
- package/dist/apps/cli/commands/project/commandInitProject.old.js.map +1 -0
- package/dist/apps/cli/commands/project/{commandInitProject.d.ts → commandSetup.d.ts} +0 -0
- package/dist/apps/cli/commands/project/{commandReloadConfig.js → commandSetup.js} +4 -8
- package/dist/apps/cli/commands/project/commandSetup.js.map +1 -0
- package/dist/apps/cli/commands/project/commandTriggerCronjob.js +2 -2
- package/dist/apps/cli/commands/project/commandTriggerCronjob.js.map +1 -1
- package/dist/apps/cli/commands/project/index.js +3 -6
- package/dist/apps/cli/commands/project/index.js.map +1 -1
- package/dist/apps/cli/commands/project/setup/index.d.ts +2 -0
- package/dist/apps/cli/commands/project/setup/index.js +103 -0
- package/dist/apps/cli/commands/project/setup/index.js.map +1 -0
- package/dist/apps/cli/commands/project/setup/setupAccessTokens.d.ts +2 -0
- package/dist/apps/cli/commands/project/setup/setupAccessTokens.js +115 -0
- package/dist/apps/cli/commands/project/setup/setupAccessTokens.js.map +1 -0
- package/dist/apps/cli/commands/project/setup/setupContext.d.ts +3 -0
- package/dist/apps/cli/commands/project/setup/setupContext.js +74 -0
- package/dist/apps/cli/commands/project/setup/setupContext.js.map +1 -0
- package/dist/apps/cli/commands/project/setup/setupKubernetes.d.ts +3 -0
- package/dist/apps/cli/commands/project/setup/setupKubernetes.js +132 -0
- package/dist/apps/cli/commands/project/setup/setupKubernetes.js.map +1 -0
- package/dist/apps/cli/commands/project/utils/autocompletions.js +1 -1
- package/dist/apps/cli/commands/project/utils/autocompletions.js.map +1 -1
- package/dist/apps/cli/commands/project/utils/ensureNamespace.js +6 -8
- package/dist/apps/cli/commands/project/utils/ensureNamespace.js.map +1 -1
- package/dist/apps/cli/config/writeConfig.js +1 -1
- package/dist/apps/cli/config/writeConfig.js.map +1 -1
- package/dist/apps/cli/verify/migration/fromv2.js +8 -6
- package/dist/apps/cli/verify/migration/fromv2.js.map +1 -1
- package/dist/config/getProjectConfig.d.ts +8 -5
- package/dist/config/getProjectConfig.js +81 -36
- package/dist/config/getProjectConfig.js.map +1 -1
- package/dist/k8sApi/index.d.ts +7 -5
- package/dist/k8sApi/index.js +23 -9
- package/dist/k8sApi/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/cluster.js +0 -2
- package/dist/utils/cluster.js.map +1 -1
- package/dist/utils/gitlab.d.ts +1 -0
- package/dist/utils/gitlab.js +17 -1
- package/dist/utils/gitlab.js.map +1 -1
- package/dist/utils/projects/index.js +7 -8
- package/dist/utils/projects/index.js.map +1 -1
- package/package.json +3 -3
- package/src/apps/cli/commands/general/index.ts +6 -7
- package/src/apps/cli/commands/general/portForward.ts +2 -1
- package/src/apps/cli/commands/project/commandCloudSqlProxy.ts +24 -6
- package/src/apps/cli/commands/project/commandConfigSecrets.ts +139 -128
- package/src/apps/cli/commands/project/commandDeletePods.ts +2 -1
- package/src/apps/cli/commands/project/{commandInitProject.ts → commandInitProject.old.ts} +0 -0
- package/src/apps/cli/commands/project/commandSetup.ts +12 -0
- package/src/apps/cli/commands/project/commandTriggerCronjob.ts +3 -3
- package/src/apps/cli/commands/project/index.ts +4 -6
- package/src/apps/cli/commands/project/setup/index.ts +42 -0
- package/src/apps/cli/commands/project/setup/setupAccessTokens.ts +72 -0
- package/src/apps/cli/commands/project/setup/setupContext.ts +37 -0
- package/src/apps/cli/commands/project/setup/setupKubernetes.ts +112 -0
- package/src/apps/cli/commands/project/utils/autocompletions.ts +2 -2
- package/src/apps/cli/commands/project/utils/ensureNamespace.ts +2 -1
- package/src/apps/cli/config/writeConfig.ts +1 -1
- package/src/apps/cli/verify/migration/fromv2.ts +4 -2
- package/src/config/getProjectConfig.ts +74 -34
- package/src/k8sApi/index.ts +20 -8
- package/src/utils/cluster.ts +0 -2
- package/src/utils/gitlab.ts +11 -0
- package/src/utils/projects/index.ts +3 -1
- package/dist/apps/cli/commands/project/commandInitGitlab.js +0 -136
- package/dist/apps/cli/commands/project/commandInitGitlab.js.map +0 -1
- package/dist/apps/cli/commands/project/commandInitProject.js.map +0 -1
- package/dist/apps/cli/commands/project/commandReloadConfig.d.ts +0 -3
- package/dist/apps/cli/commands/project/commandReloadConfig.js.map +0 -1
- package/dist/utils/formatEnvVars.d.ts +0 -2
- package/dist/utils/formatEnvVars.js +0 -10
- package/dist/utils/formatEnvVars.js.map +0 -1
- package/src/apps/cli/commands/project/commandInitGitlab.ts +0 -248
- package/src/apps/cli/commands/project/commandReloadConfig.ts +0 -12
- package/src/utils/formatEnvVars.ts +0 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Vorpal from "vorpal";
|
|
2
|
-
import
|
|
2
|
+
import { getk8sApi } from "../../../../k8sApi";
|
|
3
3
|
|
|
4
4
|
import { logError } from "../../../../utils/log";
|
|
5
5
|
import {
|
|
@@ -40,6 +40,7 @@ export default async (vorpal: Vorpal) =>
|
|
|
40
40
|
message: "Continue ? 🤔 ",
|
|
41
41
|
});
|
|
42
42
|
this.log("");
|
|
43
|
+
const k8sApi = getk8sApi();
|
|
43
44
|
if (shouldContinue) {
|
|
44
45
|
for (const podName of selectedPodNames) {
|
|
45
46
|
await k8sApi.deleteNamespacedPod(podName, namespace, "true");
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Vorpal from "vorpal";
|
|
2
|
+
import { setupProject } from "./setup";
|
|
3
|
+
|
|
4
|
+
export default async (vorpal: Vorpal) =>
|
|
5
|
+
vorpal
|
|
6
|
+
.command(
|
|
7
|
+
"project-setup",
|
|
8
|
+
"Initializes all environments and creates requires resources, service accounts, etc."
|
|
9
|
+
)
|
|
10
|
+
.action(async function () {
|
|
11
|
+
await setupProject(this);
|
|
12
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { V1Job, V1ObjectMeta } from "@kubernetes/client-node";
|
|
2
2
|
|
|
3
3
|
import Vorpal from "vorpal";
|
|
4
|
-
import {
|
|
4
|
+
import { getk8sApiBatch, getk8sApiBatchBeta } from "../../../../k8sApi";
|
|
5
5
|
import { logError } from "../../../../utils/log";
|
|
6
6
|
import { namespaceAutoCompletion } from "../general/namespaceAutoCompletion";
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ import ensureCluster from "./utils/ensureCluster";
|
|
|
12
12
|
async function triggerCronjob(namespace: string) {
|
|
13
13
|
const {
|
|
14
14
|
body: { items: jobs },
|
|
15
|
-
} = await
|
|
15
|
+
} = await getk8sApiBatchBeta().listNamespacedCronJob(namespace);
|
|
16
16
|
|
|
17
17
|
const jobNames = jobs.map((j) => j.metadata.name);
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@ async function triggerCronjob(namespace: string) {
|
|
|
34
34
|
job.metadata = metadata as V1ObjectMeta;
|
|
35
35
|
job.spec = jobSpec;
|
|
36
36
|
try {
|
|
37
|
-
const result = await
|
|
37
|
+
const result = await getk8sApiBatch().createNamespacedJob(namespace, job);
|
|
38
38
|
|
|
39
39
|
this.log("");
|
|
40
40
|
this.log(`yeah, you got a job, man. 😺 ${result.body.metadata.name}`);
|
|
@@ -8,8 +8,8 @@ import commandEnvVars from "./commandEnvVars";
|
|
|
8
8
|
import commandGetMyTotalWorktime from "./commandGetMyTotalWorktime";
|
|
9
9
|
import commandGetShell from "./commandGetShell";
|
|
10
10
|
import commandGitlabCi from "./commandGitlabCi";
|
|
11
|
-
import
|
|
12
|
-
import commandInitProject from "./commandInitProject";
|
|
11
|
+
import commandSetup from "./commandSetup";
|
|
12
|
+
//import commandInitProject from "./commandInitProject.old";
|
|
13
13
|
import commandListPods from "./commandListPods";
|
|
14
14
|
import commandMigrateHelm3 from "./commandMigrateHelm3";
|
|
15
15
|
import commandNamespace from "./commandNamespace";
|
|
@@ -24,12 +24,10 @@ import commandPortForward from "./commandPortForward";
|
|
|
24
24
|
import commandTriggerCronjob from "./commandTriggerCronjob";
|
|
25
25
|
|
|
26
26
|
import commandOpenGrafanaPod from "./commandOpenGrafanaPod";
|
|
27
|
-
import commandReloadConfig from "./commandReloadConfig";
|
|
28
27
|
|
|
29
28
|
export default async (vorpal: Vorpal) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
commandInitGitlab(vorpal);
|
|
29
|
+
commandSetup(vorpal);
|
|
30
|
+
|
|
33
31
|
commandEnvVars(vorpal);
|
|
34
32
|
|
|
35
33
|
commandNamespace(vorpal);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CommandInstance } from "vorpal";
|
|
2
|
+
import { getAllPipelineContexts } from "../../../../../config/getProjectConfig";
|
|
3
|
+
import { projectConfigSecrets } from "../commandConfigSecrets";
|
|
4
|
+
import { setupAccessTokens } from "./setupAccessTokens";
|
|
5
|
+
import { setupContext } from "./setupContext";
|
|
6
|
+
|
|
7
|
+
export const setupProject = async (instance: CommandInstance) => {
|
|
8
|
+
const allContext = await getAllPipelineContexts();
|
|
9
|
+
|
|
10
|
+
for (const context of allContext) {
|
|
11
|
+
await setupContext(instance, context);
|
|
12
|
+
}
|
|
13
|
+
await setupAccessTokens(instance);
|
|
14
|
+
instance.log("");
|
|
15
|
+
const { configSecrets } = await instance.prompt({
|
|
16
|
+
default: true,
|
|
17
|
+
message: "Before deployments work, you need to config secrets. Do it now?",
|
|
18
|
+
name: "configSecrets",
|
|
19
|
+
type: "confirm",
|
|
20
|
+
});
|
|
21
|
+
instance.log("");
|
|
22
|
+
if (configSecrets) {
|
|
23
|
+
await projectConfigSecrets(instance);
|
|
24
|
+
} else {
|
|
25
|
+
instance.log(
|
|
26
|
+
"👆 don't forget to config secret using `project-config-secrets`"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
instance.log("");
|
|
30
|
+
instance.log("gitlab is ready! 🥂");
|
|
31
|
+
instance.log("\n");
|
|
32
|
+
instance.log("do not forget to make sure that:");
|
|
33
|
+
[
|
|
34
|
+
"you have __health route in place",
|
|
35
|
+
"lint and test are defined",
|
|
36
|
+
"secrets are configured (call project-config-secret)",
|
|
37
|
+
"eat your vegetables",
|
|
38
|
+
"be awesome 🤩",
|
|
39
|
+
].forEach((tip) => instance.log(` - ${tip}`));
|
|
40
|
+
instance.log("\n");
|
|
41
|
+
instance.log("\n");
|
|
42
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import open from "open";
|
|
2
|
+
import { CommandInstance } from "vorpal";
|
|
3
|
+
import { doGitlabRequest, getProjectInfo } from "../../../../../utils/gitlab";
|
|
4
|
+
|
|
5
|
+
export const setupAccessTokens = async (instance: CommandInstance) => {
|
|
6
|
+
const { id: projectId, web_url: projectWebUrl } = await getProjectInfo(
|
|
7
|
+
instance
|
|
8
|
+
);
|
|
9
|
+
try {
|
|
10
|
+
await doGitlabRequest(instance, `projects/${projectId}/variables/GL_TOKEN`);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
if (e.message !== "not found") {
|
|
13
|
+
throw e;
|
|
14
|
+
}
|
|
15
|
+
// not found
|
|
16
|
+
|
|
17
|
+
instance.log(
|
|
18
|
+
"I need add a GL_TOKEN to the project, so that semantic release will work\n"
|
|
19
|
+
);
|
|
20
|
+
instance.log(
|
|
21
|
+
"👉 Please please create a project access token in gitlab and copy its value into clipboard\n\n - name: something like 'semantic-release'\n - expires: leave empty\n - scopes: api, read_repository"
|
|
22
|
+
);
|
|
23
|
+
instance.log("\n");
|
|
24
|
+
|
|
25
|
+
const { understood } = await instance.prompt({
|
|
26
|
+
default: true,
|
|
27
|
+
message: "Understood and open gitlab now? 🤔",
|
|
28
|
+
name: "understood",
|
|
29
|
+
type: "confirm",
|
|
30
|
+
});
|
|
31
|
+
if (!understood) {
|
|
32
|
+
instance.log("continuing anyway...");
|
|
33
|
+
}
|
|
34
|
+
open(`${projectWebUrl}/-/settings/access_tokens`);
|
|
35
|
+
|
|
36
|
+
instance.log("\n");
|
|
37
|
+
|
|
38
|
+
instance.log("Enter your copied token now: ");
|
|
39
|
+
|
|
40
|
+
instance.log("\n");
|
|
41
|
+
const { GL_TOKEN } = await instance.prompt({
|
|
42
|
+
type: "password",
|
|
43
|
+
name: "GL_TOKEN",
|
|
44
|
+
message: "Access Token: ",
|
|
45
|
+
});
|
|
46
|
+
await doGitlabRequest(instance, `projects/${projectId}/variables`, {
|
|
47
|
+
key: "GL_TOKEN",
|
|
48
|
+
value: GL_TOKEN,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const deploy_tokens = await doGitlabRequest(
|
|
53
|
+
instance,
|
|
54
|
+
`projects/${projectId}/deploy_tokens`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
!deploy_tokens.find(
|
|
59
|
+
(v: { name: string }) => v.name === "gitlab-deploy-token"
|
|
60
|
+
)
|
|
61
|
+
) {
|
|
62
|
+
instance.log(
|
|
63
|
+
"I will setup the 'GitLab Deploy Token', so Kubernetes can pull images from this project."
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
await doGitlabRequest(instance, `projects/${projectId}/deploy_tokens`, {
|
|
67
|
+
id: projectId,
|
|
68
|
+
name: "gitlab-deploy-token",
|
|
69
|
+
scopes: ["read_registry"],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Context, isOfDeployType } from "@catladder/pipeline";
|
|
2
|
+
import { CommandInstance } from "vorpal";
|
|
3
|
+
import { setupKubernetes } from "./setupKubernetes";
|
|
4
|
+
|
|
5
|
+
export const setupContext = async (
|
|
6
|
+
instance: CommandInstance,
|
|
7
|
+
context: Context
|
|
8
|
+
) => {
|
|
9
|
+
instance.log("");
|
|
10
|
+
instance.log(
|
|
11
|
+
"=================================================================================="
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
instance.log(
|
|
15
|
+
"🐱 🔧 setting up " +
|
|
16
|
+
context.environment.shortName +
|
|
17
|
+
":" +
|
|
18
|
+
context.componentName +
|
|
19
|
+
"..."
|
|
20
|
+
);
|
|
21
|
+
instance.log("");
|
|
22
|
+
const deployConfig = context.componentConfig.deploy;
|
|
23
|
+
if (isOfDeployType(deployConfig, "kubernetes")) {
|
|
24
|
+
await setupKubernetes(instance, context);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
instance.log("");
|
|
28
|
+
instance.log(
|
|
29
|
+
"✅ " +
|
|
30
|
+
context.environment.shortName +
|
|
31
|
+
":" +
|
|
32
|
+
context.componentName +
|
|
33
|
+
" done!"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
instance.log("");
|
|
37
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Context,
|
|
3
|
+
getFullKubernetesClusterName,
|
|
4
|
+
isOfDeployType,
|
|
5
|
+
} from "@catladder/pipeline";
|
|
6
|
+
import { CommandInstance } from "vorpal";
|
|
7
|
+
import { $ } from "zx";
|
|
8
|
+
import { connectToCluster } from "../../../../../utils/cluster";
|
|
9
|
+
import { upsertAllVariables } from "../../../../../utils/gitlab";
|
|
10
|
+
import ensureNamespace from "../utils/ensureNamespace";
|
|
11
|
+
|
|
12
|
+
export const setupKubernetes = async (
|
|
13
|
+
instance: CommandInstance,
|
|
14
|
+
context: Context
|
|
15
|
+
) => {
|
|
16
|
+
const deployConfig = context.componentConfig.deploy;
|
|
17
|
+
if (!isOfDeployType(deployConfig, "kubernetes")) {
|
|
18
|
+
throw new Error("cannot run setupKubernetes on non-kubernetes deployments");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const fullName = getFullKubernetesClusterName(deployConfig.cluster);
|
|
22
|
+
instance.log(`cluster: ${fullName}`);
|
|
23
|
+
|
|
24
|
+
await connectToCluster(fullName);
|
|
25
|
+
instance.log("");
|
|
26
|
+
instance.log("ensuring namespace ...");
|
|
27
|
+
const namespace = await ensureNamespace(context);
|
|
28
|
+
instance.log("Namespace " + namespace + " created / updated!");
|
|
29
|
+
instance.log("");
|
|
30
|
+
//$.verbose = true;
|
|
31
|
+
|
|
32
|
+
// we name the service account and the role and the role binding with the same name
|
|
33
|
+
// we currently create one per component to better separate them
|
|
34
|
+
instance.log("ensuring service accounts...");
|
|
35
|
+
const serviceAccountName = `cl-${context.componentName}-deploy`;
|
|
36
|
+
const KUBE_URL =
|
|
37
|
+
await $`TERM=dumb kubectl cluster-info | grep -E 'Kubernetes master|Kubernetes control plane' | awk '/http/ {print $NF}'`.then(
|
|
38
|
+
(s) => s.stdout.trim()
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// first upsert service acount in the ns
|
|
42
|
+
try {
|
|
43
|
+
await $`kubectl delete serviceaccount --namespace ${namespace} ${serviceAccountName}`;
|
|
44
|
+
await $`kubectl delete rolebinding --namespace ${namespace} ${serviceAccountName}`;
|
|
45
|
+
await $`kubectl delete role --namespace ${namespace} ${serviceAccountName}`;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
// ignore
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await $`kubectl create serviceaccount --namespace ${namespace} ${serviceAccountName}`;
|
|
51
|
+
|
|
52
|
+
// upsert role in the ns
|
|
53
|
+
|
|
54
|
+
await $`cat <<EOF | kubectl apply -f -
|
|
55
|
+
kind: Role
|
|
56
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
57
|
+
metadata:
|
|
58
|
+
namespace: ${namespace}
|
|
59
|
+
name: ${serviceAccountName}
|
|
60
|
+
rules:
|
|
61
|
+
- apiGroups: ["", "extensions", "apps", "networking.k8s.io", "batch"]
|
|
62
|
+
resources: ["deployments", "replicasets", "statefulsets", "pods", "secrets", "configmaps", "services", "ingresses", "serviceaccounts", "jobs", "cronjobs"]
|
|
63
|
+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # You can also use ["*"]
|
|
64
|
+
---
|
|
65
|
+
kind: RoleBinding
|
|
66
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
67
|
+
metadata:
|
|
68
|
+
name: ${serviceAccountName}
|
|
69
|
+
namespace: ${namespace}
|
|
70
|
+
subjects:
|
|
71
|
+
- kind: ServiceAccount
|
|
72
|
+
name: ${serviceAccountName}
|
|
73
|
+
namespace: ${namespace}
|
|
74
|
+
roleRef:
|
|
75
|
+
kind: Role
|
|
76
|
+
name: ${serviceAccountName}
|
|
77
|
+
apiGroup: rbac.authorization.k8s.io
|
|
78
|
+
EOF
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
// get token name
|
|
82
|
+
const tokenName =
|
|
83
|
+
await $`kubectl get serviceaccount --namespace ${namespace} ${serviceAccountName} -o jsonpath='{.secrets[0].name}'`;
|
|
84
|
+
|
|
85
|
+
const KUBE_CA_PEM =
|
|
86
|
+
await $`kubectl get secret ${tokenName} --namespace ${namespace} -o jsonpath="{['data']['ca\\.crt']}"`.then(
|
|
87
|
+
(c) => c.stdout.trim()
|
|
88
|
+
);
|
|
89
|
+
const KUBE_TOKEN =
|
|
90
|
+
await $`kubectl get secret ${tokenName} --namespace ${namespace} -o jsonpath="{['data']['token']}" | base64 --decode`.then(
|
|
91
|
+
(c) => c.stdout.trim()
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const vars = {
|
|
95
|
+
KUBE_TOKEN,
|
|
96
|
+
KUBE_CA_PEM,
|
|
97
|
+
KUBE_URL,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
instance.log("service accounts created / updated!");
|
|
101
|
+
|
|
102
|
+
instance.log("");
|
|
103
|
+
instance.log("pusing secrets to gitlab...");
|
|
104
|
+
|
|
105
|
+
await upsertAllVariables(
|
|
106
|
+
instance,
|
|
107
|
+
vars,
|
|
108
|
+
context.environment.shortName,
|
|
109
|
+
context.componentName
|
|
110
|
+
);
|
|
111
|
+
instance.log("done!");
|
|
112
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getAllEnvs, getAllEnvsInAllComponents } from "@catladder/pipeline";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
getAllComponentsWithAllEnvsFlat,
|
|
4
4
|
getProjectConfig,
|
|
5
5
|
} from "../../../../../config/getProjectConfig";
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ export const allEnvs = async () => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const envAndComponents = async () => {
|
|
16
|
-
const allEnvAndcomponents = await
|
|
16
|
+
const allEnvAndcomponents = await getAllComponentsWithAllEnvsFlat();
|
|
17
17
|
|
|
18
18
|
return allEnvAndcomponents.reduce<string[]>(
|
|
19
19
|
(acc, { env, componentName }) => [...acc, env + ":" + componentName],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, getKubernetesNamespace } from "@catladder/pipeline";
|
|
2
2
|
import { V1Namespace, V1ObjectMeta } from "@kubernetes/client-node";
|
|
3
|
-
import
|
|
3
|
+
import { getk8sApi } from "../../../../../k8sApi";
|
|
4
4
|
|
|
5
5
|
export default async function (context: Context) {
|
|
6
6
|
const namespace = getKubernetesNamespace(
|
|
@@ -31,6 +31,7 @@ export default async function (context: Context) {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
namespaceBody.metadata = metadata;
|
|
34
|
+
const k8sApi = getk8sApi();
|
|
34
35
|
try {
|
|
35
36
|
await k8sApi.readNamespace(namespace);
|
|
36
37
|
|
|
@@ -45,7 +45,7 @@ export const writeConfig = async (
|
|
|
45
45
|
encoding: "utf-8",
|
|
46
46
|
});
|
|
47
47
|
vorpal.log("adding type @catladder/pipeline....");
|
|
48
|
-
await spawn("yarn add @catladder/pipeline -DW", {
|
|
48
|
+
await spawn("yarn add --non-interactive @catladder/pipeline -DW", {
|
|
49
49
|
shell: true,
|
|
50
50
|
});
|
|
51
51
|
await exec("git add " + file);
|
|
@@ -94,7 +94,7 @@ export const migrateV2 = async (vorpal: Vorpal) => {
|
|
|
94
94
|
const {
|
|
95
95
|
CUSTOMER_NAME,
|
|
96
96
|
APP_NAME,
|
|
97
|
-
COMPONENT_NAME,
|
|
97
|
+
COMPONENT_NAME = "web",
|
|
98
98
|
APP_DIR = ".",
|
|
99
99
|
STAGING_ENABLED,
|
|
100
100
|
} = gitlabCi.variables;
|
|
@@ -147,7 +147,7 @@ export const migrateV2 = async (vorpal: Vorpal) => {
|
|
|
147
147
|
|
|
148
148
|
const startCommand = Array.isArray(baseValues?.application?.command)
|
|
149
149
|
? baseValues?.application?.command.join(" ")
|
|
150
|
-
: baseValues?.application?.command
|
|
150
|
+
: baseValues?.application?.command;
|
|
151
151
|
return {
|
|
152
152
|
vars: env ? transformVars(env) : undefined,
|
|
153
153
|
dir: dir,
|
|
@@ -249,6 +249,8 @@ export const migrateV2 = async (vorpal: Vorpal) => {
|
|
|
249
249
|
this.log("-------------------");
|
|
250
250
|
|
|
251
251
|
this.log("done!");
|
|
252
|
+
this.log("");
|
|
253
|
+
this.log("You can remove the values*.yml files now.");
|
|
252
254
|
|
|
253
255
|
this.log("-------------------");
|
|
254
256
|
} else {
|
|
@@ -3,29 +3,42 @@ import {
|
|
|
3
3
|
getAllEnvs,
|
|
4
4
|
getEnvironment as _getEnvironment,
|
|
5
5
|
createContext,
|
|
6
|
+
getSecretVarName,
|
|
7
|
+
Config,
|
|
6
8
|
} from "@catladder/pipeline";
|
|
7
9
|
|
|
8
10
|
import { CommandInstance } from "vorpal";
|
|
9
|
-
import { getAllVariables } from "../utils/gitlab";
|
|
10
|
-
|
|
11
|
+
import { getAllVariables, getVariableValueByRawName } from "../utils/gitlab";
|
|
12
|
+
|
|
11
13
|
import { getGitRoot } from "../utils/projects";
|
|
12
14
|
import { readYaml } from "../utils/files";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
{
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
import { watch } from "fs";
|
|
16
|
+
|
|
17
|
+
let currentConfig: Config = null;
|
|
18
|
+
|
|
19
|
+
// reload the config on change
|
|
20
|
+
const reloadConfigAndObserve = async () => {
|
|
21
|
+
const gitRoot = await getGitRoot();
|
|
22
|
+
const result = readConfigSync(gitRoot);
|
|
23
|
+
if (!result) {
|
|
24
|
+
// can't do anything, there is no config
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { config, path } = result;
|
|
28
|
+
const watcher = watch(path, () => {
|
|
29
|
+
watcher.close();
|
|
30
|
+
reloadConfigAndObserve();
|
|
31
|
+
});
|
|
32
|
+
currentConfig = config;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const getProjectConfig = async () => {
|
|
36
|
+
if (!currentConfig) {
|
|
37
|
+
// initially
|
|
38
|
+
await reloadConfigAndObserve();
|
|
39
|
+
}
|
|
40
|
+
return currentConfig;
|
|
41
|
+
};
|
|
29
42
|
|
|
30
43
|
export const getGitlabCiFilePath = async () => {
|
|
31
44
|
const gitRoot = await getGitRoot();
|
|
@@ -58,21 +71,37 @@ export const getPipelineContextByChoice = async (
|
|
|
58
71
|
const config = await getProjectConfig();
|
|
59
72
|
return createContext(config, componentName, env);
|
|
60
73
|
};
|
|
61
|
-
export const
|
|
74
|
+
export const getAllComponentsWithAllEnvsFlat = async (): Promise<
|
|
75
|
+
Array<{ env: string; componentName: string }>
|
|
76
|
+
> => {
|
|
62
77
|
const config = await getProjectConfig();
|
|
63
78
|
if (!config) {
|
|
64
79
|
return [];
|
|
65
80
|
}
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
return Object.keys(config.components).flatMap((componentName) =>
|
|
82
|
+
getAllEnvs(config, componentName).map((env) => ({ env, componentName }))
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getAllComponentsWithAllEnvsHierarchical = async (): Promise<{
|
|
87
|
+
[componentName: string]: string[];
|
|
88
|
+
}> => {
|
|
89
|
+
const config = await getProjectConfig();
|
|
90
|
+
if (!config) {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Object.fromEntries(
|
|
95
|
+
Object.keys(config.components).map((componentName) => [
|
|
96
|
+
componentName,
|
|
97
|
+
getAllEnvs(config, componentName),
|
|
98
|
+
])
|
|
70
99
|
);
|
|
71
100
|
};
|
|
72
101
|
|
|
73
102
|
export const getAllPipelineContexts = async () => {
|
|
74
103
|
return Promise.all(
|
|
75
|
-
(await
|
|
104
|
+
(await getAllComponentsWithAllEnvsFlat())
|
|
76
105
|
.filter((c) => c.env !== "local")
|
|
77
106
|
.map(({ env, componentName }) =>
|
|
78
107
|
getPipelineContextByChoice(env, componentName)
|
|
@@ -82,9 +111,20 @@ export const getAllPipelineContexts = async () => {
|
|
|
82
111
|
|
|
83
112
|
export const getEnvironment = async (env: string, componentName: string) => {
|
|
84
113
|
const config = await getProjectConfig();
|
|
114
|
+
|
|
85
115
|
return _getEnvironment(config, componentName, env);
|
|
86
116
|
};
|
|
87
117
|
|
|
118
|
+
export const getGitlabVar = async (
|
|
119
|
+
vorpal: CommandInstance,
|
|
120
|
+
env: string,
|
|
121
|
+
componentName: string,
|
|
122
|
+
variableName: string
|
|
123
|
+
) => {
|
|
124
|
+
const rawVariableName = getSecretVarName(env, componentName, variableName);
|
|
125
|
+
return await getVariableValueByRawName(vorpal, rawVariableName);
|
|
126
|
+
};
|
|
127
|
+
|
|
88
128
|
const resolveSecrets = async (
|
|
89
129
|
vorpal: CommandInstance,
|
|
90
130
|
allEnvVars: Record<string, string>
|
|
@@ -93,16 +133,15 @@ const resolveSecrets = async (
|
|
|
93
133
|
|
|
94
134
|
return Object.fromEntries(
|
|
95
135
|
Object.entries(allEnvVars).map(([key, value]) => {
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
} else {
|
|
104
|
-
return [key, ""];
|
|
136
|
+
const containsSecret = String(value)?.includes?.("$CL_");
|
|
137
|
+
if (containsSecret) {
|
|
138
|
+
for (const variable of allVariablesInGitlab) {
|
|
139
|
+
value = value.replace(
|
|
140
|
+
new RegExp("\\$" + variable.key, "g"),
|
|
141
|
+
variable.value
|
|
142
|
+
);
|
|
105
143
|
}
|
|
144
|
+
return [key, value];
|
|
106
145
|
}
|
|
107
146
|
return [key, value];
|
|
108
147
|
})
|
|
@@ -115,6 +154,7 @@ export const getEnvVars = async (
|
|
|
115
154
|
componentName: string
|
|
116
155
|
) => {
|
|
117
156
|
const envionment = await getEnvironment(env, componentName);
|
|
118
|
-
|
|
157
|
+
// in the pipeline the secrets alreadyy exists and bash will expand them
|
|
158
|
+
// but here we need to manually load them
|
|
119
159
|
return resolveSecrets(vorpal, envionment.envVars);
|
|
120
160
|
};
|
package/src/k8sApi/index.ts
CHANGED
|
@@ -5,17 +5,29 @@ import {
|
|
|
5
5
|
BatchV1beta1Api,
|
|
6
6
|
} from "@kubernetes/client-node";
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
export const reload = () => {
|
|
8
|
+
const getKubeConfig = () => {
|
|
9
|
+
const kc = new KubeConfig();
|
|
11
10
|
kc.loadFromDefault();
|
|
11
|
+
return kc;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* get kubernetes client. avoid reusing the instance when context get changed
|
|
15
|
+
* @returns kuberenetes client
|
|
16
|
+
*/
|
|
17
|
+
export const getk8sApi = () => {
|
|
18
|
+
const kc = getKubeConfig();
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
return kc.makeApiClient(CoreV1Api);
|
|
21
|
+
};
|
|
15
22
|
|
|
16
|
-
const
|
|
23
|
+
export const getk8sApiBatch = () => {
|
|
24
|
+
const kc = getKubeConfig();
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
return kc.makeApiClient(BatchV1Api);
|
|
27
|
+
};
|
|
20
28
|
|
|
21
|
-
export
|
|
29
|
+
export const getk8sApiBatchBeta = () => {
|
|
30
|
+
const kc = getKubeConfig();
|
|
31
|
+
|
|
32
|
+
return kc.makeApiClient(BatchV1beta1Api);
|
|
33
|
+
};
|
package/src/utils/cluster.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { $ } from "zx";
|
|
2
|
-
import { reload } from "../k8sApi";
|
|
3
2
|
|
|
4
3
|
export const getCurrentContext = async () =>
|
|
5
4
|
(await $`kubectl config current-context`).stdout.trim();
|
|
@@ -10,5 +9,4 @@ export const getCurrentConnectedClusterName = async () => {
|
|
|
10
9
|
|
|
11
10
|
export const connectToCluster = async (fullname: string) => {
|
|
12
11
|
await $`kubectl config use-context ${fullname}`;
|
|
13
|
-
reload();
|
|
14
12
|
};
|
package/src/utils/gitlab.ts
CHANGED
|
@@ -73,6 +73,9 @@ export const doGitlabRequest = async <T = any>(
|
|
|
73
73
|
if (result.status >= 200 && result.status < 400) {
|
|
74
74
|
return result.json();
|
|
75
75
|
}
|
|
76
|
+
if (result.status === 404) {
|
|
77
|
+
throw new Error("not found");
|
|
78
|
+
}
|
|
76
79
|
|
|
77
80
|
throw new Error(
|
|
78
81
|
`Could not send request to gitlab api ${path}: ${result.status} "${
|
|
@@ -125,6 +128,14 @@ export const getAllVariables = memoizee(
|
|
|
125
128
|
{ promise: true }
|
|
126
129
|
);
|
|
127
130
|
|
|
131
|
+
export const getVariableValueByRawName = async (
|
|
132
|
+
vorpal: CommandInstance,
|
|
133
|
+
rawName: string
|
|
134
|
+
) => {
|
|
135
|
+
const allVariables = await getAllVariables(vorpal);
|
|
136
|
+
return allVariables.find((v) => v.key === rawName)?.value;
|
|
137
|
+
};
|
|
138
|
+
|
|
128
139
|
const maskableRegex = new RegExp("^[a-zA-Z0-9_+=/@:.~-]{8,}$"); // SEE https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js#L20
|
|
129
140
|
const isMaskable = (value: string): boolean => maskableRegex.test(value);
|
|
130
141
|
|