mirror of
https://github.com/meienberger/runtipi.git
synced 2024-10-04 07:38:21 +03:00
feat(docker-json): make internal port optional, add extra_hosts, network_mode and ulimits (#1631)
This commit is contained in:
parent
7d1b522f1f
commit
2e0a8839e9
@ -12,7 +12,6 @@ describe('getDockerCompose', async () => {
|
||||
const serviceImage2 = faker.system.semver();
|
||||
|
||||
const servicePort1 = faker.number.int({ min: 64, max: 65535 });
|
||||
const servicePort2 = faker.number.int({ min: 64, max: 65535 });
|
||||
|
||||
const fakeEnv = {
|
||||
one: faker.system.semver(),
|
||||
@ -46,7 +45,6 @@ describe('getDockerCompose', async () => {
|
||||
},
|
||||
dependsOn: [serviceName1],
|
||||
image: serviceImage2,
|
||||
internalPort: servicePort2,
|
||||
addPorts: [
|
||||
{ containerPort: 3000, hostPort: 4444, tcp: true },
|
||||
{ containerPort: 3001, hostPort: 4445, udp: true },
|
||||
@ -216,4 +214,106 @@ describe('getDockerCompose', async () => {
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should remove port mappings and network if networkMode is set', async () => {
|
||||
// arrange
|
||||
const serviceName1 = faker.word.noun();
|
||||
const serviceImage1 = faker.system.semver();
|
||||
const servicePort1 = faker.number.int({ min: 64, max: 65535 });
|
||||
|
||||
const services = [
|
||||
{
|
||||
isMain: true,
|
||||
name: serviceName1,
|
||||
image: serviceImage1,
|
||||
internalPort: servicePort1,
|
||||
networkMode: 'host',
|
||||
},
|
||||
] satisfies ServiceInput[];
|
||||
|
||||
// act
|
||||
const result = getDockerCompose(services, {
|
||||
exposed: false,
|
||||
exposedLocal: false,
|
||||
openPort: false,
|
||||
isVisibleOnGuestDashboard: false,
|
||||
});
|
||||
|
||||
// assert
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"services:
|
||||
${serviceName1}:
|
||||
image: ${serviceImage1}
|
||||
container_name: ${serviceName1}
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
labels:
|
||||
generated: true
|
||||
traefik.enable: false
|
||||
traefik.http.middlewares.${serviceName1}-web-redirect.redirectscheme.scheme: https
|
||||
traefik.http.services.${serviceName1}.loadbalancer.server.port: "${servicePort1}"
|
||||
networks:
|
||||
tipi_main_network:
|
||||
name: runtipi_tipi_main_network
|
||||
external: true
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('can add ulimits to service', async () => {
|
||||
// arrange
|
||||
const serviceName1 = faker.word.noun();
|
||||
const serviceImage1 = faker.system.semver();
|
||||
const servicePort1 = faker.number.int({ min: 64, max: 65535 });
|
||||
|
||||
const services = [
|
||||
{
|
||||
isMain: true,
|
||||
name: serviceName1,
|
||||
image: serviceImage1,
|
||||
internalPort: servicePort1,
|
||||
ulimits: {
|
||||
nofile: 1000,
|
||||
nproc: {
|
||||
soft: 1000,
|
||||
hard: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
] satisfies ServiceInput[];
|
||||
|
||||
// act
|
||||
const result = getDockerCompose(services, {
|
||||
exposed: false,
|
||||
exposedLocal: false,
|
||||
openPort: false,
|
||||
isVisibleOnGuestDashboard: false,
|
||||
});
|
||||
|
||||
// assert
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"services:
|
||||
${serviceName1}:
|
||||
image: ${serviceImage1}
|
||||
container_name: ${serviceName1}
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- tipi_main_network
|
||||
ulimits:
|
||||
nproc:
|
||||
soft: 1000
|
||||
hard: 1000
|
||||
nofile: 1000
|
||||
labels:
|
||||
generated: true
|
||||
traefik.enable: false
|
||||
traefik.http.middlewares.${serviceName1}-web-redirect.redirectscheme.scheme: https
|
||||
traefik.http.services.${serviceName1}.loadbalancer.server.port: "${servicePort1}"
|
||||
networks:
|
||||
tipi_main_network:
|
||||
name: runtipi_tipi_main_network
|
||||
external: true
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
@ -17,12 +17,19 @@ const buildService = (params: Service, form: AppEventForm) => {
|
||||
.setCommand(params.command)
|
||||
.setHealthCheck(params.healthCheck)
|
||||
.setDependsOn(params.dependsOn)
|
||||
.addPorts(params.addPorts)
|
||||
.addVolumes(params.volumes)
|
||||
.setRestartPolicy('unless-stopped')
|
||||
.addNetwork('tipi_main_network');
|
||||
.addExtraHosts(params.extraHosts)
|
||||
.addUlimits(params.ulimits)
|
||||
.addPorts(params.addPorts)
|
||||
.addNetwork('tipi_main_network')
|
||||
.setNetworkMode(params.networkMode);
|
||||
|
||||
if (params.isMain) {
|
||||
if (!params.internalPort) {
|
||||
throw new Error('Main service must have an internal port specified');
|
||||
}
|
||||
|
||||
if (form.openPort) {
|
||||
service.addPort({
|
||||
containerPort: params.internalPort,
|
||||
|
@ -1 +1,3 @@
|
||||
export * from './constants';
|
||||
import { APP_DIR, DATA_DIR, APP_DATA_DIR } from './constants';
|
||||
|
||||
export { APP_DIR, DATA_DIR, APP_DATA_DIR };
|
||||
|
@ -10,11 +10,19 @@ const dependsOnSchema = z.union([
|
||||
),
|
||||
]);
|
||||
|
||||
const ulimitsSchema = z.object({
|
||||
nproc: z.number().or(z.object({ soft: z.number(), hard: z.number() })),
|
||||
nofile: z.number().or(z.object({ soft: z.number(), hard: z.number() })),
|
||||
});
|
||||
|
||||
export const serviceSchema = z.object({
|
||||
image: z.string(),
|
||||
name: z.string(),
|
||||
internalPort: z.number(),
|
||||
internalPort: z.number().optional(),
|
||||
isMain: z.boolean().optional(),
|
||||
networkMode: z.string().optional(),
|
||||
extraHosts: z.array(z.string()).optional(),
|
||||
ulimits: ulimitsSchema.optional(),
|
||||
addPorts: z
|
||||
.array(
|
||||
z.object({
|
||||
|
@ -20,6 +20,11 @@ interface HealthCheck {
|
||||
retries: number;
|
||||
}
|
||||
|
||||
interface Ulimits {
|
||||
nproc: number | { soft: number; hard: number };
|
||||
nofile: number | { soft: number; hard: number };
|
||||
}
|
||||
|
||||
export interface BuilderService {
|
||||
image: string;
|
||||
containerName: string;
|
||||
@ -32,6 +37,9 @@ export interface BuilderService {
|
||||
labels?: Record<string, string | boolean>;
|
||||
dependsOn?: DependsOn;
|
||||
networks?: string[];
|
||||
networkMode?: string;
|
||||
extraHosts?: string[];
|
||||
ulimits?: Ulimits;
|
||||
}
|
||||
|
||||
export type BuiltService = ReturnType<typeof ServiceBuilder.prototype.build>;
|
||||
@ -288,6 +296,34 @@ export class ServiceBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
setNetworkMode(networkMode?: string) {
|
||||
if (!networkMode) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.service.networkMode = networkMode;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
addExtraHosts(extraHosts?: string[]) {
|
||||
if (!extraHosts) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.service.extraHosts = extraHosts;
|
||||
return this;
|
||||
}
|
||||
|
||||
addUlimits(ulimits?: Ulimits) {
|
||||
if (!ulimits) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.service.ulimits = ulimits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the service object.
|
||||
* @returns The built service object.
|
||||
@ -306,11 +342,19 @@ export class ServiceBuilder {
|
||||
throw new Error('Service name and image are required');
|
||||
}
|
||||
|
||||
if (this.service.networkMode) {
|
||||
this.service.ports = undefined;
|
||||
this.service.networks = undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
image: this.service.image,
|
||||
container_name: this.service.containerName,
|
||||
restart: this.service.restart,
|
||||
networks: this.service.networks,
|
||||
network_mode: this.service.networkMode,
|
||||
extra_hosts: this.service.extraHosts,
|
||||
ulimits: this.service.ulimits,
|
||||
healthcheck: this.service.healthCheck,
|
||||
environment: this.service.environment,
|
||||
ports: this.service.ports,
|
||||
|
@ -1 +1,3 @@
|
||||
export * from './docker-helpers';
|
||||
import { compose, handleViewAppLogsEvent, handleViewRuntipiLogsEvent } from './docker-helpers';
|
||||
|
||||
export { compose, handleViewAppLogsEvent, handleViewRuntipiLogsEvent };
|
||||
|
Loading…
Reference in New Issue
Block a user