k8s-deploy-helper 0.1.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/README.md +1 -0
- package/bin/cli.js +18 -0
- package/k8s.config.json +33 -0
- package/package.json +30 -0
- package/src/config/loadConfig.js +15 -0
- package/src/config/validateConfig.js +13 -0
- package/src/docker/buildPush.js +28 -0
- package/src/index.js +27 -0
- package/src/k8s/apply.js +32 -0
- package/src/k8s/deployment.js +50 -0
- package/src/k8s/ingress.js +0 -0
- package/src/k8s/service.js +30 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
## K8s-deploy-helper
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runDeploy } from '../src/index.js';
|
|
4
|
+
|
|
5
|
+
const program = new Command();
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name('k8s-deploy')
|
|
9
|
+
.description('Build and deploy apps to Kubernetes')
|
|
10
|
+
.version('0.1.0');
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.command('deploy')
|
|
14
|
+
.description('Build Docker image and deploy to Kubernetes')
|
|
15
|
+
.option('-c, --config <path>', 'Config file path', 'k8s.config.json')
|
|
16
|
+
.action(runDeploy);
|
|
17
|
+
|
|
18
|
+
program.parse();
|
package/k8s.config.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"appName": "my-app",
|
|
3
|
+
"namespace": "default",
|
|
4
|
+
|
|
5
|
+
"docker": {
|
|
6
|
+
"image": "lucky/my-app",
|
|
7
|
+
"tag": "latest",
|
|
8
|
+
"context": ".",
|
|
9
|
+
"dockerfile": "Dockerfile"
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"kubernetes": {
|
|
13
|
+
"replicas": 2,
|
|
14
|
+
"containerPort": 3000,
|
|
15
|
+
"servicePort": 80,
|
|
16
|
+
"serviceType": "ClusterIP"
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"resources": {
|
|
20
|
+
"limits": {
|
|
21
|
+
"cpu": "500m",
|
|
22
|
+
"memory": "512Mi"
|
|
23
|
+
},
|
|
24
|
+
"requests": {
|
|
25
|
+
"cpu": "250m",
|
|
26
|
+
"memory": "256Mi"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
"env": {
|
|
31
|
+
"NODE_ENV": "production"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "k8s-deploy-helper",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI tool to build, push and deploy applications to Kubernetes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"k8s-deploy": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "node ./bin/cli.js",
|
|
11
|
+
"lint": "eslint .",
|
|
12
|
+
"release": "npm publish"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"kubernetes",
|
|
16
|
+
"devops",
|
|
17
|
+
"docker",
|
|
18
|
+
"ci-cd",
|
|
19
|
+
"deployment",
|
|
20
|
+
"helm"
|
|
21
|
+
],
|
|
22
|
+
"author": "Lokesh Shimpi",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"commander": "^14.0.2",
|
|
26
|
+
"execa": "^9.6.1",
|
|
27
|
+
"fs-extra": "^11.3.3",
|
|
28
|
+
"js-yaml": "^4.1.1"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export function loadConfig(configPath) {
|
|
5
|
+
const absolutePath = path.resolve(process.cwd(), configPath);
|
|
6
|
+
|
|
7
|
+
if (!fs.existsSync(absolutePath)) {
|
|
8
|
+
throw new Error(`Config file not found: ${absolutePath}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const raw = fs.readFileSync(absolutePath, 'utf-8');
|
|
12
|
+
const config = JSON.parse(raw);
|
|
13
|
+
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function validateConfig(config) {
|
|
2
|
+
if (!config.appName) {
|
|
3
|
+
throw new Error('appName is required in config');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (!config.docker?.image) {
|
|
7
|
+
throw new Error('docker.image is required');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!config.kubernetes?.containerPort) {
|
|
11
|
+
throw new Error('kubernetes.containerPort is required');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
export async function buildAndPushImage(config) {
|
|
5
|
+
const { image, tag, context, dockerfile } = config.docker;
|
|
6
|
+
|
|
7
|
+
const dockerfilePath = dockerfile || 'Dockerfile';
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
10
|
+
throw new Error(`Dockerfile not found at path: ${dockerfilePath}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const fullImageName = `${image}:${tag}`;
|
|
14
|
+
|
|
15
|
+
console.log(`\n🐳 Building Docker image: ${fullImageName}`);
|
|
16
|
+
|
|
17
|
+
await execa(
|
|
18
|
+
'docker',
|
|
19
|
+
['build', '-t', fullImageName, '-f', dockerfilePath, context || '.'],
|
|
20
|
+
{ stdio: 'inherit' }
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
console.log(`\n📤 Pushing Docker image: ${fullImageName}`);
|
|
24
|
+
|
|
25
|
+
await execa('docker', ['push', fullImageName], { stdio: 'inherit' });
|
|
26
|
+
|
|
27
|
+
return fullImageName;
|
|
28
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { loadConfig } from './config/loadConfig.js';
|
|
2
|
+
import { validateConfig } from './config/validateConfig.js';
|
|
3
|
+
import { buildAndPushImage } from './docker/buildPush.js';
|
|
4
|
+
import { generateDeployment } from './k8s/deployment.js';
|
|
5
|
+
import { generateService } from './k8s/service.js';
|
|
6
|
+
import { applyKubernetes } from './k8s/apply.js';
|
|
7
|
+
|
|
8
|
+
export async function runDeploy(options) {
|
|
9
|
+
console.log('Loading config...');
|
|
10
|
+
const config = loadConfig(options.config);
|
|
11
|
+
validateConfig(config);
|
|
12
|
+
|
|
13
|
+
console.log(`App: ${config.appName}`);
|
|
14
|
+
|
|
15
|
+
let image = `${config.docker.image}:${config.docker.tag}`;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
image = await buildAndPushImage(config);
|
|
19
|
+
} catch {
|
|
20
|
+
console.warn('\n⚠️ Image push failed — continuing deployment');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
generateDeployment(config, image);
|
|
24
|
+
generateService(config);
|
|
25
|
+
|
|
26
|
+
await applyKubernetes(config);
|
|
27
|
+
}
|
package/src/k8s/apply.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
|
|
3
|
+
export async function applyKubernetes(config) {
|
|
4
|
+
const namespace = config.namespace || 'default';
|
|
5
|
+
|
|
6
|
+
console.log(`\n☸️ Deploying to Kubernetes namespace: ${namespace}`);
|
|
7
|
+
|
|
8
|
+
// Ensure namespace exists (idempotent)
|
|
9
|
+
await execa(
|
|
10
|
+
'kubectl',
|
|
11
|
+
['create', 'namespace', namespace],
|
|
12
|
+
{ stdio: 'ignore' }
|
|
13
|
+
).catch(() => {
|
|
14
|
+
// Namespace already exists — ignore
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
console.log('📦 Applying deployment.yaml');
|
|
18
|
+
await execa(
|
|
19
|
+
'kubectl',
|
|
20
|
+
['apply', '-f', 'deployment.yaml', '-n', namespace],
|
|
21
|
+
{ stdio: 'inherit' }
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
console.log('📦 Applying service.yaml');
|
|
25
|
+
await execa(
|
|
26
|
+
'kubectl',
|
|
27
|
+
['apply', '-f', 'service.yaml', '-n', namespace],
|
|
28
|
+
{ stdio: 'inherit' }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
console.log('\n✅ Kubernetes resources applied successfully');
|
|
32
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
export function generateDeployment(config, image) {
|
|
5
|
+
const deployment = {
|
|
6
|
+
apiVersion: 'apps/v1',
|
|
7
|
+
kind: 'Deployment',
|
|
8
|
+
metadata: {
|
|
9
|
+
name: config.appName,
|
|
10
|
+
namespace: config.namespace || 'default'
|
|
11
|
+
},
|
|
12
|
+
spec: {
|
|
13
|
+
replicas: config.kubernetes.replicas,
|
|
14
|
+
selector: {
|
|
15
|
+
matchLabels: {
|
|
16
|
+
app: config.appName
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
template: {
|
|
20
|
+
metadata: {
|
|
21
|
+
labels: {
|
|
22
|
+
app: config.appName
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
spec: {
|
|
26
|
+
containers: [
|
|
27
|
+
{
|
|
28
|
+
name: config.appName,
|
|
29
|
+
image,
|
|
30
|
+
ports: [
|
|
31
|
+
{
|
|
32
|
+
containerPort: config.kubernetes.containerPort
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
env: Object.entries(config.env || {}).map(
|
|
36
|
+
([key, value]) => ({ name: key, value })
|
|
37
|
+
),
|
|
38
|
+
resources: config.resources
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const yamlContent = yaml.dump(deployment);
|
|
47
|
+
fs.writeFileSync('deployment.yaml', yamlContent);
|
|
48
|
+
|
|
49
|
+
return 'deployment.yaml';
|
|
50
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
export function generateService(config) {
|
|
5
|
+
const service = {
|
|
6
|
+
apiVersion: 'v1',
|
|
7
|
+
kind: 'Service',
|
|
8
|
+
metadata: {
|
|
9
|
+
name: config.appName,
|
|
10
|
+
namespace: config.namespace || 'default'
|
|
11
|
+
},
|
|
12
|
+
spec: {
|
|
13
|
+
type: config.kubernetes.serviceType,
|
|
14
|
+
selector: {
|
|
15
|
+
app: config.appName
|
|
16
|
+
},
|
|
17
|
+
ports: [
|
|
18
|
+
{
|
|
19
|
+
port: config.kubernetes.servicePort,
|
|
20
|
+
targetPort: config.kubernetes.containerPort
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const yamlContent = yaml.dump(service);
|
|
27
|
+
fs.writeFileSync('service.yaml', yamlContent);
|
|
28
|
+
|
|
29
|
+
return 'service.yaml';
|
|
30
|
+
}
|