Loading modules/containers/containers.cpp +4 −3 Changes for modules/containers/containers.cpp: 4 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * list -> {containers, images, jobs, bridges} or * {unavailable} when containerd isn't there * containers: [{name, image, state, pid, started, exitCode, restarts, * error?, network, address?, autostart, restart, memory, tasks}] * error?, network, address?, lease?, autostart, restart, memory, tasks}] * images: [{image, id, digest, size, layers, created, pulled, * architecture, entrypoint?, cmd?, workdir?, user?, env[], ports[], * volumes[], usedBy[]}] Loading @@ -41,7 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * create {container} a container: name, image, entrypoint[], args[], * update {name, container} env[], user, workdir, hostname, boot, * autostart, restart, network, bridge, address, * gateway, dns[], binds[], tmpfs[], readOnly, * gateway, mac, dns[], binds[], tmpfs[], devices[], readOnly, * memoryMax, tasksMax, capabilities[], * dropCapabilities[] (update: name renames) * delete {name} (its files go in the background) Loading Loading @@ -78,7 +78,7 @@ const std::set<std::string> Numbers = {"pid", "started", "exit_code", "restarts" // repeated keys -> JSON list names const std::vector<std::pair<std::string, std::string>> Lists = { {"entrypoint", "entrypoint"}, {"arg", "args"}, {"env", "env"}, {"dns", "dns"}, {"bind", "binds"}, {"tmpfs", "tmpfs"}, {"capability", "capabilities"}, {"drop_capability", "dropCapabilities"}, {"tmpfs", "tmpfs"}, {"device", "devices"}, {"capability", "capabilities"}, {"drop_capability", "dropCapabilities"}, {"port", "ports"}, {"volume", "volumes"}, {"used_by", "usedBy"}}; std::string camel(const std::string &k) { Loading Loading @@ -240,6 +240,7 @@ std::string settingsFields(json_object *c, Fields &f, bool create) { text("bridge", "bridge"); text("address", "address"); text("gateway", "gateway"); text("mac", "mac"); flag("readOnly", "read_only"); number("memoryMax", "memory_max"); number("tasksMax", "tasks_max"); Loading www/modules/containers.js +25 −6 Changes for www/modules/containers.js: 25 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,12 @@ 'Host network (like nspawn without --private-network)': 'Netzwerk des Hosts (wie nspawn ohne --private-network)', 'Only loopback': 'Nur Loopback', 'Own address in a bridge': 'Eigene Adresse in einer Bridge', 'Bridge': 'Bridge', 'Address (CIDR)': 'Adresse (CIDR)', 'Gateway': 'Gateway', 'DNS servers': 'DNS-Server', 'Address from DHCP': 'Adresse per DHCP', 'MAC address': 'MAC-Adresse', 'default: made from the name (stays the same)': 'Vorgabe: aus dem Namen (bleibt gleich)', 'Devices (one per line, e.g. /dev/dri for an Intel GPU)': 'Geräte (eines pro Zeile, z. B. /dev/dri für eine Intel-GPU)', 'The container may use only these devices (and /dev/null & co.); its user gets their groups (render, video).': 'Der Container darf nur diese Geräte benutzen (und /dev/null & Co.); sein Benutzer bekommt ihre Gruppen (render, video).', 'DHCP: {0}': 'DHCP: {0}', 'DHCP': 'DHCP', 'separated by blanks; empty: the host\'s': 'durch Leerzeichen getrennt; leer: die des Hosts', 'No bridge yet: create one under Network.': 'Noch keine Bridge: unter Netzwerk anlegen.', 'The image listens on {0}.': 'Das Image lauscht auf {0}.', Loading Loading @@ -188,14 +194,21 @@ const net = h('select', {name: 'network'}, [['host', t('Host network (like nspawn without --private-network)')], ['none', t('Only loopback')], ['bridge', t('Own address in a bridge')]].map(([v, l]) => h('option', {value: v, selected: c.network === v}, l))); const dhcp = h('input', {type: 'checkbox', name: 'dhcp', checked: c.address === 'dhcp'}); const staticBox = h('div', {class: 'row'}, h('label', {}, t('Address (CIDR)'), h('input', {name: 'address', value: c.address === 'dhcp' ? '' : (c.address || ''), placeholder: '192.168.1.50/24'})), h('label', {}, t('Gateway'), h('input', {name: 'gateway', value: c.gateway || '', placeholder: '192.168.1.1'}))); const bridgeBox = h('div', {}, h('div', {class: 'row'}, h('label', {}, t('Bridge'), h('select', {name: 'bridge'}, bridges.map(b => h('option', {value: b, selected: b === c.bridge}, b)), c.bridge && !bridges.includes(c.bridge) ? h('option', {value: c.bridge, selected: true}, c.bridge) : null)), h('label', {}, t('Address (CIDR)'), h('input', {name: 'address', value: c.address || '', placeholder: '192.168.1.50/24'})), h('label', {}, t('Gateway'), h('input', {name: 'gateway', value: c.gateway || '', placeholder: '192.168.1.1'}))), h('label', {}, t('MAC address'), h('input', {name: 'mac', value: c.mac || '', placeholder: t('default: made from the name (stays the same)')}))), h('label', {class: 'check'}, dhcp, t('Address from DHCP')), staticBox, bridges.length ? null : h('p', {class: 'hint'}, t('No bridge yet: create one under Network.'))); dhcp.addEventListener('change', () => { staticBox.hidden = dhcp.checked; }); staticBox.hidden = dhcp.checked; const portsHint = h('p', {class: 'hint'}); const volumesHint = h('p', {class: 'hint'}); const entry = h('input', {name: 'entrypoint', value: joinWords(c.entrypoint), class: 'mono'}); Loading Loading @@ -246,6 +259,8 @@ h('label', {}, t('Working directory'), workdir), h('label', {}, t('Host name'), h('input', {name: 'hostname', value: c.hostname || '', placeholder: c.name || ''}))), h('label', {}, t('tmpfs directories (one per line)'), area('tmpfs', c.tmpfs, 2)), h('label', {}, t('Devices (one per line, e.g. /dev/dri for an Intel GPU)'), area('devices', c.devices, 2)), h('p', {class: 'hint'}, t('The container may use only these devices (and /dev/null & co.); its user gets their groups (render, video).')), h('div', {class: 'row'}, h('label', {}, t('Memory limit (MiB, 0: none)'), h('input', {name: 'memoryMax', type: 'number', min: 0, value: Math.round((c.memoryMax || 0) / 1048576)})), Loading @@ -264,9 +279,11 @@ const mem = Number(d.memoryMax) || 0; const container = { name: d.name.trim(), image: d.image, autostart: d.autostart, restart: d.restart, network: d.network, bridge: d.network === 'bridge' ? (d.bridge || '') : '', address: d.network === 'bridge' ? d.address.trim() : '', gateway: d.network === 'bridge' ? d.gateway.trim() : '', dns: words(d.dns), args: words(d.args), entrypoint: words(d.entrypoint), env: lines(d.env), binds: lines(d.binds), tmpfs: lines(d.tmpfs), bridge: d.network === 'bridge' ? (d.bridge || '') : '', address: d.network !== 'bridge' ? '' : d.dhcp ? 'dhcp' : d.address.trim(), gateway: d.network === 'bridge' && !d.dhcp ? d.gateway.trim() : '', mac: d.network === 'bridge' ? d.mac.trim() : '', dns: words(d.dns), args: words(d.args), entrypoint: words(d.entrypoint), env: lines(d.env), binds: lines(d.binds), tmpfs: lines(d.tmpfs), devices: lines(d.devices), user: d.user.trim(), workdir: d.workdir.trim(), hostname: d.hostname.trim(), memoryMax: mem * 1048576, tasksMax: Number(d.tasksMax) || 0, capabilities: words(d.capabilities), dropCapabilities: words(d.dropCapabilities), Loading Loading @@ -309,7 +326,9 @@ {title: t('Name'), render: c => h('strong', {}, c.name)}, {title: t('Image'), render: c => h('span', {class: 'mono'}, c.image || '')}, {title: t('State'), render: stateCell}, {title: t('Network'), render: c => c.network === 'bridge' ? h('span', {class: 'mono'}, c.address || t('bridge')) : t(c.network || '')}, {title: t('Network'), render: c => c.network !== 'bridge' ? t(c.network || '') : c.address === 'dhcp' ? h('span', {class: 'mono'}, c.lease ? t('DHCP: {0}', c.lease) : t('DHCP')) : h('span', {class: 'mono'}, c.address || t('bridge'))}, {title: t('Memory'), render: c => c.state === 'running' && c.memory ? mib(c.memory) : ''}, {title: '', class: 'actions', render: c => { const up = c.state === 'running' || c.state === 'starting' || c.state === 'stopping'; Loading Loading
modules/containers/containers.cpp +4 −3 Changes for modules/containers/containers.cpp: 4 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -31,7 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * list -> {containers, images, jobs, bridges} or * {unavailable} when containerd isn't there * containers: [{name, image, state, pid, started, exitCode, restarts, * error?, network, address?, autostart, restart, memory, tasks}] * error?, network, address?, lease?, autostart, restart, memory, tasks}] * images: [{image, id, digest, size, layers, created, pulled, * architecture, entrypoint?, cmd?, workdir?, user?, env[], ports[], * volumes[], usedBy[]}] Loading @@ -41,7 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * create {container} a container: name, image, entrypoint[], args[], * update {name, container} env[], user, workdir, hostname, boot, * autostart, restart, network, bridge, address, * gateway, dns[], binds[], tmpfs[], readOnly, * gateway, mac, dns[], binds[], tmpfs[], devices[], readOnly, * memoryMax, tasksMax, capabilities[], * dropCapabilities[] (update: name renames) * delete {name} (its files go in the background) Loading Loading @@ -78,7 +78,7 @@ const std::set<std::string> Numbers = {"pid", "started", "exit_code", "restarts" // repeated keys -> JSON list names const std::vector<std::pair<std::string, std::string>> Lists = { {"entrypoint", "entrypoint"}, {"arg", "args"}, {"env", "env"}, {"dns", "dns"}, {"bind", "binds"}, {"tmpfs", "tmpfs"}, {"capability", "capabilities"}, {"drop_capability", "dropCapabilities"}, {"tmpfs", "tmpfs"}, {"device", "devices"}, {"capability", "capabilities"}, {"drop_capability", "dropCapabilities"}, {"port", "ports"}, {"volume", "volumes"}, {"used_by", "usedBy"}}; std::string camel(const std::string &k) { Loading Loading @@ -240,6 +240,7 @@ std::string settingsFields(json_object *c, Fields &f, bool create) { text("bridge", "bridge"); text("address", "address"); text("gateway", "gateway"); text("mac", "mac"); flag("readOnly", "read_only"); number("memoryMax", "memory_max"); number("tasksMax", "tasks_max"); Loading
www/modules/containers.js +25 −6 Changes for www/modules/containers.js: 25 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,12 @@ 'Host network (like nspawn without --private-network)': 'Netzwerk des Hosts (wie nspawn ohne --private-network)', 'Only loopback': 'Nur Loopback', 'Own address in a bridge': 'Eigene Adresse in einer Bridge', 'Bridge': 'Bridge', 'Address (CIDR)': 'Adresse (CIDR)', 'Gateway': 'Gateway', 'DNS servers': 'DNS-Server', 'Address from DHCP': 'Adresse per DHCP', 'MAC address': 'MAC-Adresse', 'default: made from the name (stays the same)': 'Vorgabe: aus dem Namen (bleibt gleich)', 'Devices (one per line, e.g. /dev/dri for an Intel GPU)': 'Geräte (eines pro Zeile, z. B. /dev/dri für eine Intel-GPU)', 'The container may use only these devices (and /dev/null & co.); its user gets their groups (render, video).': 'Der Container darf nur diese Geräte benutzen (und /dev/null & Co.); sein Benutzer bekommt ihre Gruppen (render, video).', 'DHCP: {0}': 'DHCP: {0}', 'DHCP': 'DHCP', 'separated by blanks; empty: the host\'s': 'durch Leerzeichen getrennt; leer: die des Hosts', 'No bridge yet: create one under Network.': 'Noch keine Bridge: unter Netzwerk anlegen.', 'The image listens on {0}.': 'Das Image lauscht auf {0}.', Loading Loading @@ -188,14 +194,21 @@ const net = h('select', {name: 'network'}, [['host', t('Host network (like nspawn without --private-network)')], ['none', t('Only loopback')], ['bridge', t('Own address in a bridge')]].map(([v, l]) => h('option', {value: v, selected: c.network === v}, l))); const dhcp = h('input', {type: 'checkbox', name: 'dhcp', checked: c.address === 'dhcp'}); const staticBox = h('div', {class: 'row'}, h('label', {}, t('Address (CIDR)'), h('input', {name: 'address', value: c.address === 'dhcp' ? '' : (c.address || ''), placeholder: '192.168.1.50/24'})), h('label', {}, t('Gateway'), h('input', {name: 'gateway', value: c.gateway || '', placeholder: '192.168.1.1'}))); const bridgeBox = h('div', {}, h('div', {class: 'row'}, h('label', {}, t('Bridge'), h('select', {name: 'bridge'}, bridges.map(b => h('option', {value: b, selected: b === c.bridge}, b)), c.bridge && !bridges.includes(c.bridge) ? h('option', {value: c.bridge, selected: true}, c.bridge) : null)), h('label', {}, t('Address (CIDR)'), h('input', {name: 'address', value: c.address || '', placeholder: '192.168.1.50/24'})), h('label', {}, t('Gateway'), h('input', {name: 'gateway', value: c.gateway || '', placeholder: '192.168.1.1'}))), h('label', {}, t('MAC address'), h('input', {name: 'mac', value: c.mac || '', placeholder: t('default: made from the name (stays the same)')}))), h('label', {class: 'check'}, dhcp, t('Address from DHCP')), staticBox, bridges.length ? null : h('p', {class: 'hint'}, t('No bridge yet: create one under Network.'))); dhcp.addEventListener('change', () => { staticBox.hidden = dhcp.checked; }); staticBox.hidden = dhcp.checked; const portsHint = h('p', {class: 'hint'}); const volumesHint = h('p', {class: 'hint'}); const entry = h('input', {name: 'entrypoint', value: joinWords(c.entrypoint), class: 'mono'}); Loading Loading @@ -246,6 +259,8 @@ h('label', {}, t('Working directory'), workdir), h('label', {}, t('Host name'), h('input', {name: 'hostname', value: c.hostname || '', placeholder: c.name || ''}))), h('label', {}, t('tmpfs directories (one per line)'), area('tmpfs', c.tmpfs, 2)), h('label', {}, t('Devices (one per line, e.g. /dev/dri for an Intel GPU)'), area('devices', c.devices, 2)), h('p', {class: 'hint'}, t('The container may use only these devices (and /dev/null & co.); its user gets their groups (render, video).')), h('div', {class: 'row'}, h('label', {}, t('Memory limit (MiB, 0: none)'), h('input', {name: 'memoryMax', type: 'number', min: 0, value: Math.round((c.memoryMax || 0) / 1048576)})), Loading @@ -264,9 +279,11 @@ const mem = Number(d.memoryMax) || 0; const container = { name: d.name.trim(), image: d.image, autostart: d.autostart, restart: d.restart, network: d.network, bridge: d.network === 'bridge' ? (d.bridge || '') : '', address: d.network === 'bridge' ? d.address.trim() : '', gateway: d.network === 'bridge' ? d.gateway.trim() : '', dns: words(d.dns), args: words(d.args), entrypoint: words(d.entrypoint), env: lines(d.env), binds: lines(d.binds), tmpfs: lines(d.tmpfs), bridge: d.network === 'bridge' ? (d.bridge || '') : '', address: d.network !== 'bridge' ? '' : d.dhcp ? 'dhcp' : d.address.trim(), gateway: d.network === 'bridge' && !d.dhcp ? d.gateway.trim() : '', mac: d.network === 'bridge' ? d.mac.trim() : '', dns: words(d.dns), args: words(d.args), entrypoint: words(d.entrypoint), env: lines(d.env), binds: lines(d.binds), tmpfs: lines(d.tmpfs), devices: lines(d.devices), user: d.user.trim(), workdir: d.workdir.trim(), hostname: d.hostname.trim(), memoryMax: mem * 1048576, tasksMax: Number(d.tasksMax) || 0, capabilities: words(d.capabilities), dropCapabilities: words(d.dropCapabilities), Loading Loading @@ -309,7 +326,9 @@ {title: t('Name'), render: c => h('strong', {}, c.name)}, {title: t('Image'), render: c => h('span', {class: 'mono'}, c.image || '')}, {title: t('State'), render: stateCell}, {title: t('Network'), render: c => c.network === 'bridge' ? h('span', {class: 'mono'}, c.address || t('bridge')) : t(c.network || '')}, {title: t('Network'), render: c => c.network !== 'bridge' ? t(c.network || '') : c.address === 'dhcp' ? h('span', {class: 'mono'}, c.lease ? t('DHCP: {0}', c.lease) : t('DHCP')) : h('span', {class: 'mono'}, c.address || t('bridge'))}, {title: t('Memory'), render: c => c.state === 'running' && c.memory ? mib(c.memory) : ''}, {title: '', class: 'actions', render: c => { const up = c.state === 'running' || c.state === 'starting' || c.state === 'stopping'; Loading