mirror of
https://github.com/unraid/webgui.git
synced 2026-01-13 13:09:58 -06:00
Fixes and improvements for Docker
- fixed deletion of orphan images - inline list update when removing container or image - fixed list updating after execution error - added animation when removing container or image - delete old xml file when container is renamed - fixed IP assignment with multiple containers based on the same image
This commit is contained in:
@@ -381,8 +381,8 @@ function xmlToCommand($xml, $create_paths=false) {
|
||||
global $var;
|
||||
global $docroot;
|
||||
$xml = xmlToVar($xml);
|
||||
$cmdName = (strlen($xml['Name'])) ? '--name='.escapeshellarg($xml['Name']) : '';
|
||||
$cmdPrivileged = (strtolower($xml['Privileged']) == 'true') ? '--privileged=true' : '';
|
||||
$cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : '';
|
||||
$cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : '';
|
||||
$cmdNetwork = '--net='.escapeshellarg(strtolower($xml['Network']));
|
||||
$cmdMyIP = $xml['MyIP'] ? '--ip='.escapeshellarg($xml['MyIP']) : '';
|
||||
$Volumes = [''];
|
||||
@@ -434,9 +434,7 @@ function xmlToCommand($xml, $create_paths=false) {
|
||||
$xml['ExtraParams'],
|
||||
escapeshellarg($xml['Repository']),
|
||||
$xml['PostArgs']);
|
||||
|
||||
$cmd = trim(preg_replace('/\s+/', ' ', $cmd));
|
||||
return [$cmd, $xml['Name'], $xml['Repository']];
|
||||
return [preg_replace('/\s+/', ' ', $cmd), $xml['Name'], $xml['Repository']];
|
||||
}
|
||||
|
||||
function execCommand($command) {
|
||||
@@ -532,11 +530,10 @@ function getUsedIPs() {
|
||||
##
|
||||
## CREATE CONTAINER
|
||||
##
|
||||
|
||||
if (isset($_POST['contName'])) {
|
||||
|
||||
$postXML = postToXML($_POST, true);
|
||||
$dry_run = ($_POST['dryRun'] == "true") ? true : false;
|
||||
$dry_run = $_POST['dryRun']=='true' ? true : false;
|
||||
$existing = $_POST['existingContainer'] ?? false;
|
||||
$create_paths = $dry_run ? false : true;
|
||||
|
||||
// Get the command line
|
||||
@@ -547,10 +544,8 @@ if (isset($_POST['contName'])) {
|
||||
|
||||
// Saving the generated configuration file.
|
||||
$userTmplDir = $dockerManPaths['templates-user'];
|
||||
if (!is_dir($userTmplDir)) {
|
||||
mkdir($userTmplDir, 0777, true);
|
||||
}
|
||||
if (!empty($Name)) {
|
||||
if (!is_dir($userTmplDir)) mkdir($userTmplDir, 0777, true);
|
||||
if ($Name) {
|
||||
$filename = sprintf('%s/my-%s.xml', $userTmplDir, $Name);
|
||||
file_put_contents($filename, $postXML);
|
||||
}
|
||||
@@ -591,7 +586,6 @@ if (isset($_POST['contName'])) {
|
||||
}
|
||||
|
||||
// Remove old container if renamed
|
||||
$existing = isset($_POST['existingContainer']) ? $_POST['existingContainer'] : false;
|
||||
if ($existing && $DockerClient->doesContainerExist($existing)) {
|
||||
// determine if the container is still running
|
||||
$oldContainerDetails = $DockerClient->getContainerDetails($existing);
|
||||
@@ -605,6 +599,8 @@ if (isset($_POST['contName'])) {
|
||||
|
||||
// force kill container if still running after 10 seconds
|
||||
removeContainer($existing);
|
||||
// remove old template
|
||||
@unlink("$userTmplDir/my-$existing.xml");
|
||||
}
|
||||
|
||||
if ($startContainer) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2017, Lime Technology
|
||||
* Copyright 2014-2017, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
/* Copyright 2005-2018, Lime Technology
|
||||
* Copyright 2014-2018, Guilherme Jardim, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2018, Bergware International.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2,
|
||||
@@ -46,13 +47,11 @@ class DockerTemplates {
|
||||
if ($this->verbose) echo $m."\n";
|
||||
}
|
||||
|
||||
|
||||
public function download_url($url, $path = "", $bg = false) {
|
||||
exec("curl --max-time 60 --silent --insecure --location --fail ".($path ? " -o ".escapeshellarg($path) : "")." ".escapeshellarg($url)." ".($bg ? ">/dev/null 2>&1 &" : "2>/dev/null"), $out, $exit_code);
|
||||
return ($exit_code === 0) ? implode("\n", $out) : false;
|
||||
}
|
||||
|
||||
|
||||
public function listDir($root, $ext = null) {
|
||||
$iter = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($root,
|
||||
@@ -68,7 +67,6 @@ class DockerTemplates {
|
||||
return $paths;
|
||||
}
|
||||
|
||||
|
||||
public function getTemplates($type) {
|
||||
global $dockerManPaths;
|
||||
$tmpls = [];
|
||||
@@ -90,7 +88,6 @@ class DockerTemplates {
|
||||
return $tmpls;
|
||||
}
|
||||
|
||||
|
||||
private function removeDir($path) {
|
||||
if (is_dir($path)) {
|
||||
$files = array_diff(scandir($path), ['.', '..']);
|
||||
@@ -104,11 +101,10 @@ class DockerTemplates {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function downloadTemplates($Dest = null, $Urls = null) {
|
||||
global $dockerManPaths;
|
||||
$Dest = ($Dest) ? $Dest : $dockerManPaths['templates-storage'];
|
||||
$Urls = ($Urls) ? $Urls : $dockerManPaths['template-repos'];
|
||||
$Dest = $Dest ?: $dockerManPaths['templates-storage'];
|
||||
$Urls = $Urls ?: $dockerManPaths['template-repos'];
|
||||
$repotemplates = [];
|
||||
$output = [];
|
||||
$tmp_dir = "/tmp/tmp-".mt_rand();
|
||||
@@ -129,10 +125,10 @@ class DockerTemplates {
|
||||
$github_api = ['url' => ''];
|
||||
foreach ($github_api_regexes as $api_regex) {
|
||||
if (preg_match($api_regex, $url, $matches)) {
|
||||
$github_api['user'] = (isset($matches[1])) ? $matches[1] : "";
|
||||
$github_api['repo'] = (isset($matches[2])) ? $matches[2] : "";
|
||||
$github_api['branch'] = (isset($matches[3])) ? $matches[3] : "master";
|
||||
$github_api['path'] = (isset($matches[4])) ? $matches[4] : "";
|
||||
$github_api['user'] = $matches[1] ?? "";
|
||||
$github_api['repo'] = $matches[2] ?? "";
|
||||
$github_api['branch'] = $matches[3] ?? "master";
|
||||
$github_api['path'] = $matches[4] ?? "";
|
||||
$github_api['url'] = sprintf("https://github.com/%s/%s/archive/%s.tar.gz", $github_api['user'], $github_api['repo'], $github_api['branch']);
|
||||
break;
|
||||
}
|
||||
@@ -151,10 +147,10 @@ class DockerTemplates {
|
||||
];
|
||||
foreach ($custom_api_regexes as $api_regex) {
|
||||
if (preg_match($api_regex, $url, $matches)) {
|
||||
$github_api['user'] = (isset($matches[1])) ? $matches[1] : "";
|
||||
$github_api['repo'] = (isset($matches[2])) ? $matches[2] : "";
|
||||
$github_api['branch'] = (isset($matches[3])) ? $matches[3] : "master";
|
||||
$github_api['path'] = (isset($matches[4])) ? $matches[4] : "";
|
||||
$github_api['user'] = $matches[1] ?? "";
|
||||
$github_api['repo'] = $matches[2] ?? "";
|
||||
$github_api['branch'] = $matches[3] ?? "master";
|
||||
$github_api['path'] = $matches[4] ?? "";
|
||||
$github_api['url'] = sprintf("https://".$parse['host']."/%s/%s/repository/archive.tar.gz?ref=%s", $github_api['user'], $github_api['repo'], $github_api['branch']);
|
||||
break;
|
||||
}
|
||||
@@ -213,7 +209,6 @@ class DockerTemplates {
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
public function getTemplateValue($Repository, $field, $scope = "all") {
|
||||
foreach ($this->getTemplates($scope) as $file) {
|
||||
$doc = new DOMDocument();
|
||||
@@ -228,7 +223,6 @@ class DockerTemplates {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getUserTemplate($Container) {
|
||||
foreach ($this->getTemplates("user") as $file) {
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
@@ -241,11 +235,9 @@ class DockerTemplates {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function getControlURL($name) {
|
||||
global $eth0;
|
||||
$DockerClient = new DockerClient();
|
||||
|
||||
$Repository = "";
|
||||
foreach ($DockerClient->getDockerContainers() as $ct) {
|
||||
if ($ct['Name'] == $name) {
|
||||
@@ -254,12 +246,10 @@ class DockerTemplates {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$WebUI = $this->getTemplateValue($Repository, "WebUI");
|
||||
$myIP = $this->getTemplateValue($Repository, "MyIP");
|
||||
$network = $this->getTemplateValue($Repository, "Network");
|
||||
if (!$myIP && preg_match('%^(br|eth|bond)[0-9]%',$network)) {
|
||||
$name = $this->getTemplateValue($Repository, "Name");
|
||||
$myIP = exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name");
|
||||
}
|
||||
if (preg_match("%\[IP\]%", $WebUI)) {
|
||||
@@ -279,7 +269,6 @@ class DockerTemplates {
|
||||
return $WebUI;
|
||||
}
|
||||
|
||||
|
||||
public function removeContainerInfo($container) {
|
||||
global $dockerManPaths;
|
||||
|
||||
@@ -290,7 +279,6 @@ class DockerTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function removeImageInfo($image) {
|
||||
global $dockerManPaths;
|
||||
$image = DockerUtil::ensureImageTag($image);
|
||||
@@ -302,57 +290,33 @@ class DockerTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getAllInfo($reload = false) {
|
||||
global $dockerManPaths;
|
||||
$DockerClient = new DockerClient();
|
||||
$DockerUpdate = new DockerUpdate();
|
||||
$DockerUpdate->verbose = $this->verbose;
|
||||
$new_info = [];
|
||||
|
||||
$info = DockerUtil::loadJSON($dockerManPaths['webui-info']);
|
||||
|
||||
$autostart_file = $dockerManPaths['autostart-file'];
|
||||
$allAutoStart = @file($autostart_file, FILE_IGNORE_NEW_LINES);
|
||||
if ($allAutoStart === false) $allAutoStart = [];
|
||||
$allAutoStart = @file($dockerManPaths['autostart-file'], FILE_IGNORE_NEW_LINES) ?: [];
|
||||
|
||||
foreach ($DockerClient->getDockerContainers() as $ct) {
|
||||
$name = $ct['Name'];
|
||||
$image = $ct['Image'];
|
||||
$tmp = is_array($info[$name]) ? $info[$name] : [];
|
||||
|
||||
$tmp = $info[$name] ?? [];
|
||||
$tmp['running'] = $ct['Running'];
|
||||
$tmp['autostart'] = in_array($name, $allAutoStart);
|
||||
|
||||
if (!$tmp['icon'] || $reload) {
|
||||
$icon = $this->getIcon($image);
|
||||
$tmp['icon'] = $icon ?: null;
|
||||
}
|
||||
$WebUI = $this->getControlURL($name);
|
||||
$tmp['url'] = $WebUI ?: null;
|
||||
|
||||
$Registry = $this->getTemplateValue($image, "Registry");
|
||||
$tmp['registry'] = ($Registry) ? $Registry : null;
|
||||
|
||||
$Support = $this->getTemplateValue($image, "Support");
|
||||
$tmp['Support'] = ($Support) ? $Support : null;
|
||||
|
||||
$Project = $this->getTemplateValue($image, "Project");
|
||||
$tmp['Project'] = ($Project) ? $Project : null;
|
||||
|
||||
if (!$tmp['icon'] || $reload) $tmp['icon'] = $this->getIcon($image) ?: null;
|
||||
$tmp['url'] = $this->getControlURL($name) ?: null;
|
||||
$tmp['registry'] = $this->getTemplateValue($image, "Registry") ?: null;
|
||||
$tmp['Support'] = $this->getTemplateValue($image, "Support") ?: null;
|
||||
$tmp['Project'] = $this->getTemplateValue($image, "Project") ?: null;
|
||||
if (!$tmp['updated'] || $reload) {
|
||||
if ($reload) $DockerUpdate->reloadUpdateStatus($image);
|
||||
$vs = $DockerUpdate->getUpdateStatus($image);
|
||||
$tmp['updated'] = ($vs === null) ? null : (($vs === true) ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if (!$tmp['template'] || $reload) {
|
||||
$tmp['template'] = $this->getUserTemplate($name);
|
||||
}
|
||||
|
||||
if ($reload) {
|
||||
$DockerUpdate->updateUserTemplate($name);
|
||||
}
|
||||
if (!$tmp['template'] || $reload) $tmp['template'] = $this->getUserTemplate($name);
|
||||
if ($reload) $DockerUpdate->updateUserTemplate($name);
|
||||
|
||||
$this->debug("\n$name");
|
||||
foreach ($tmp as $c => $d) $this->debug(sprintf(" %-10s: %s", $c, $d));
|
||||
@@ -362,7 +326,6 @@ class DockerTemplates {
|
||||
return $new_info;
|
||||
}
|
||||
|
||||
|
||||
public function getIcon($Repository) {
|
||||
global $docroot, $dockerManPaths;
|
||||
|
||||
@@ -383,7 +346,6 @@ class DockerTemplates {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################################
|
||||
## DOCKERUPDATE CLASS ##
|
||||
######################################
|
||||
@@ -394,23 +356,19 @@ class DockerUpdate{
|
||||
if ($this->verbose) echo $m."\n";
|
||||
}
|
||||
|
||||
|
||||
private function xml_encode($string) {
|
||||
return htmlspecialchars($string, ENT_XML1, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
private function xml_decode($string) {
|
||||
return strval(html_entity_decode($string, ENT_XML1, 'UTF-8'));
|
||||
}
|
||||
|
||||
|
||||
public function download_url($url, $path = "", $bg = false) {
|
||||
exec("curl --max-time 30 --silent --insecure --location --fail ".($path ? " -o ".escapeshellarg($path) : "")." ".escapeshellarg($url)." ".($bg ? ">/dev/null 2>&1 &" : "2>/dev/null"), $out, $exit_code);
|
||||
return ($exit_code === 0) ? implode("\n", $out) : false;
|
||||
}
|
||||
|
||||
|
||||
public function download_url_and_headers($url, $headers = [], $path = "", $bg = false) {
|
||||
$strHeaders = '';
|
||||
foreach ($headers as $header) {
|
||||
@@ -420,7 +378,6 @@ class DockerUpdate{
|
||||
return ($exit_code === 0) ? implode("\n", $out) : false;
|
||||
}
|
||||
|
||||
|
||||
// DEPRECATED: Only used for Docker Index V1 type update checks
|
||||
public function getRemoteVersion($image) {
|
||||
list($strRepo, $strTag) = explode(':', DockerUtil::ensureImageTag($image));
|
||||
@@ -430,7 +387,6 @@ class DockerUpdate{
|
||||
return ($apiContent === false) ? null : substr(json_decode($apiContent, true)[0]['id'], 0, 8);
|
||||
}
|
||||
|
||||
|
||||
public function getRemoteVersionV2($image) {
|
||||
list($strRepo, $strTag) = explode(':', DockerUtil::ensureImageTag($image));
|
||||
|
||||
@@ -473,14 +429,12 @@ class DockerUpdate{
|
||||
return $strDigest;
|
||||
}
|
||||
|
||||
|
||||
// DEPRECATED: Only used for Docker Index V1 type update checks
|
||||
public function getLocalVersion($image) {
|
||||
$DockerClient = new DockerClient();
|
||||
return substr($DockerClient->getImageID($image), 0, 8);
|
||||
}
|
||||
|
||||
|
||||
public function getUpdateStatus($image) {
|
||||
global $dockerManPaths;
|
||||
$image = DockerUtil::ensureImageTag($image);
|
||||
@@ -496,7 +450,6 @@ class DockerUpdate{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function reloadUpdateStatus($image = null) {
|
||||
global $dockerManPaths;
|
||||
$DockerClient = new DockerClient();
|
||||
@@ -519,7 +472,6 @@ class DockerUpdate{
|
||||
DockerUtil::saveJSON($dockerManPaths['update-status'], $updateStatus);
|
||||
}
|
||||
|
||||
|
||||
public function setUpdateStatus($image, $version) {
|
||||
global $dockerManPaths;
|
||||
$image = DockerUtil::ensureImageTag($image);
|
||||
@@ -533,7 +485,6 @@ class DockerUpdate{
|
||||
DockerUtil::saveJSON($dockerManPaths['update-status'], $updateStatus);
|
||||
}
|
||||
|
||||
|
||||
public function updateUserTemplate($Container) {
|
||||
$changed = false;
|
||||
$DockerTemplates = new DockerTemplates();
|
||||
@@ -636,7 +587,6 @@ class DockerUpdate{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################################
|
||||
## DOCKERCLIENT CLASS ##
|
||||
######################################
|
||||
@@ -646,14 +596,12 @@ class DockerClient {
|
||||
|
||||
private $allImagesCache = null;
|
||||
|
||||
|
||||
private function build_sorter($key) {
|
||||
return function ($a, $b) use ($key) {
|
||||
return strnatcmp(strtolower($a[$key]), strtolower($b[$key]));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public function humanTiming($time) {
|
||||
$time = time() - $time; // to get the time since that moment
|
||||
$tokens = [
|
||||
@@ -668,11 +616,10 @@ class DockerClient {
|
||||
foreach ($tokens as $unit => $text) {
|
||||
if ($time < $unit) continue;
|
||||
$numberOfUnits = floor($time / $unit);
|
||||
return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';
|
||||
return $numberOfUnits.' '.$text.(($numberOfUnits==1)?'':'s').' ago';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function formatBytes($size) {
|
||||
if ($size == 0) return '0 B';
|
||||
$base = log($size) / log(1024);
|
||||
@@ -680,7 +627,6 @@ class DockerClient {
|
||||
return round(pow(1024, $base - floor($base)), 0) .' '. $suffix[floor($base)];
|
||||
}
|
||||
|
||||
|
||||
public function getDockerJSON($url, $method = "GET", &$code = null, $callback = null, $unchunk = false) {
|
||||
$fp = stream_socket_client('unix:///var/run/docker.sock', $errno, $errstr);
|
||||
|
||||
@@ -713,7 +659,6 @@ class DockerClient {
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
function doesContainerExist($container) {
|
||||
foreach ($this->getDockerContainers() as $ct) {
|
||||
if ($ct['Name'] == $container) {
|
||||
@@ -723,7 +668,6 @@ class DockerClient {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function doesImageExist($image) {
|
||||
foreach ($this->getDockerImages() as $img) {
|
||||
if (strpos($img['Tags'][0], $image) !== false) {
|
||||
@@ -733,24 +677,20 @@ class DockerClient {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function getInfo() {
|
||||
$info = $this->getDockerJSON("/info");
|
||||
$version = $this->getDockerJSON("/version");
|
||||
return array_merge($info, $version);
|
||||
}
|
||||
|
||||
|
||||
public function getContainerLog($id, $callback, $tail = null, $since = null) {
|
||||
$this->getDockerJSON("/containers/${id}/logs?stderr=1&stdout=1&tail=".urlencode($tail)."&since=".urlencode($since), "GET", $code, $callback, true);
|
||||
}
|
||||
|
||||
|
||||
public function getContainerDetails($id) {
|
||||
return $this->getDockerJSON("/containers/${id}/json");
|
||||
}
|
||||
|
||||
|
||||
public function startContainer($id) {
|
||||
$this->getDockerJSON("/containers/${id}/start", "POST", $code);
|
||||
$this->allContainersCache = null; // flush cache
|
||||
@@ -763,7 +703,6 @@ class DockerClient {
|
||||
return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code;
|
||||
}
|
||||
|
||||
|
||||
public function stopContainer($id) {
|
||||
$this->getDockerJSON("/containers/${id}/stop?t=10", "POST", $code);
|
||||
$this->allContainersCache = null; // flush cache
|
||||
@@ -776,7 +715,6 @@ class DockerClient {
|
||||
return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code;
|
||||
}
|
||||
|
||||
|
||||
public function restartContainer($id) {
|
||||
$this->getDockerJSON("/containers/${id}/restart?t=10", "POST", $code);
|
||||
$this->allContainersCache = null; // flush cache
|
||||
@@ -788,7 +726,6 @@ class DockerClient {
|
||||
return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code;
|
||||
}
|
||||
|
||||
|
||||
public function removeContainer($id) {
|
||||
global $docroot, $dockerManPaths;
|
||||
// Purge cached container information
|
||||
@@ -819,14 +756,12 @@ class DockerClient {
|
||||
return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code;
|
||||
}
|
||||
|
||||
|
||||
public function pullImage($image, $callback = null) {
|
||||
$ret = $this->getDockerJSON("/images/create?fromImage=".urlencode($image), "POST", $code, $callback);
|
||||
$this->allImagesCache = null; // flush cache
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
public function removeImage($id) {
|
||||
global $dockerManPaths;
|
||||
$image = $this->getImageName($id);
|
||||
@@ -851,12 +786,10 @@ class DockerClient {
|
||||
return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code;
|
||||
}
|
||||
|
||||
|
||||
private function getImageDetails($id) {
|
||||
return $this->getDockerJSON("/images/${id}/json");
|
||||
}
|
||||
|
||||
|
||||
public function getDockerContainers() {
|
||||
// Return cached values
|
||||
if (is_array($this->allContainersCache)) {
|
||||
@@ -871,21 +804,21 @@ class DockerClient {
|
||||
$c["Image"] = DockerUtil::ensureImageTag($obj['Image']);
|
||||
$c["ImageId"] = substr(str_replace('sha256:', '', $details["Image"]), 0, 12);
|
||||
$c["Name"] = substr($details['Name'], 1);
|
||||
$c["Status"] = $obj['Status'] ? $obj['Status'] : "None";
|
||||
$c["Status"] = $obj['Status'] ?: "None";
|
||||
$c["Running"] = $details["State"]["Running"];
|
||||
$c["Cmd"] = $obj['Command'];
|
||||
$c["Id"] = substr($obj['Id'], 0, 12);
|
||||
$c['Volumes'] = $details["HostConfig"]['Binds'];
|
||||
$c["Created"] = $this->humanTiming($obj['Created']);
|
||||
$c["NetworkMode"] = $details['HostConfig']['NetworkMode'];
|
||||
$c["BaseImage"] = isset($obj["Labels"]["BASEIMAGE"]) ? $obj["Labels"]["BASEIMAGE"] : false;
|
||||
$c["BaseImage"] = $obj["Labels"]["BASEIMAGE"] ?? false;
|
||||
$c["Ports"] = [];
|
||||
|
||||
if ($c["NetworkMode"] != 'host' && !empty($details['HostConfig']['PortBindings'])) {
|
||||
foreach ($details['HostConfig']['PortBindings'] as $port => $value) {
|
||||
list($PrivatePort, $Type) = explode("/", $port);
|
||||
$c["Ports"][] = [
|
||||
'IP' => empty($value[0]['HostIP']) ? '0.0.0.0' : $value[0]['HostIP'],
|
||||
'IP' => $value[0]['HostIP'] ?? '0.0.0.0',
|
||||
'PrivatePort' => $PrivatePort,
|
||||
'PublicPort' => $value[0]['HostPort'],
|
||||
'Type' => $Type
|
||||
@@ -899,7 +832,6 @@ class DockerClient {
|
||||
return $this->allContainersCache;
|
||||
}
|
||||
|
||||
|
||||
public function getContainerID($Container) {
|
||||
foreach ($this->getDockerContainers() as $ct) {
|
||||
if (preg_match("%" . preg_quote($Container, "%") . "%", $ct["Name"])) {
|
||||
@@ -909,7 +841,6 @@ class DockerClient {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getImageID($Image) {
|
||||
if ( ! strpos($Image,":") ) {
|
||||
$Image .= ":latest";
|
||||
@@ -943,7 +874,6 @@ class DockerClient {
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
public function getDockerImages() {
|
||||
// Return cached values
|
||||
if (is_array($this->allImagesCache)) {
|
||||
@@ -958,7 +888,7 @@ class DockerClient {
|
||||
$c["ParentId"] = substr(str_replace('sha256:', '', $obj['ParentId']), 0, 12);
|
||||
$c["Size"] = $this->formatBytes($obj['Size']);
|
||||
$c["VirtualSize"] = $this->formatBytes($obj['VirtualSize']);
|
||||
$c["Tags"] = isset($obj['RepoTags']) ? array_map("htmlspecialchars", $obj['RepoTags']) : array();
|
||||
$c["Tags"] = array_map("htmlspecialchars", $obj['RepoTags'] ?? []);
|
||||
$c["Repository"] = vsprintf('%1$s/%2$s', preg_split("#[:\/]#", DockerUtil::ensureImageTag($obj['RepoTags'][0])));
|
||||
$c["usedBy"] = $this->usedBy($c["Id"]);
|
||||
|
||||
@@ -967,14 +897,12 @@ class DockerClient {
|
||||
return $this->allImagesCache;
|
||||
}
|
||||
|
||||
|
||||
public function flushCaches() {
|
||||
$this->allContainersCache = null;
|
||||
$this->allImagesCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################################
|
||||
## DOCKERUTIL CLASS ##
|
||||
######################################
|
||||
@@ -999,7 +927,6 @@ class DockerUtil {
|
||||
return trim($strRepo).':'.trim($strTag);
|
||||
}
|
||||
|
||||
|
||||
public static function loadJSON($path) {
|
||||
$objContent = (is_file($path)) ? json_decode(file_get_contents($path), true) : [];
|
||||
if (empty($objContent)) $objContent = [];
|
||||
@@ -1007,12 +934,10 @@ class DockerUtil {
|
||||
return $objContent;
|
||||
}
|
||||
|
||||
|
||||
public static function saveJSON($path, $content) {
|
||||
if (!is_dir(dirname($path))) @mkdir(dirname($path), 0755, true);
|
||||
|
||||
return file_put_contents($path, json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -113,15 +113,16 @@ foreach ($all_containers as $ct) {
|
||||
}
|
||||
foreach ($DockerClient->getDockerImages() as $image) {
|
||||
if (count($image['usedBy'])) continue;
|
||||
$menu[] = sprintf("addDockerImageContext('%s','%s');",$image['Id'],implode(', ',$image['Tags']));
|
||||
$id = $image['Id'];
|
||||
$menu[] = sprintf("addDockerImageContext('%s','%s');",$id,implode(',',$image['Tags']));
|
||||
echo "<tr class='advanced'><td style='width:48px;padding:4px'>";
|
||||
echo "<div id='context-{$image['Id']}' style='display:block;cursor:pointer'>";
|
||||
echo "<div id='$id' style='display:block;cursor:pointer'>";
|
||||
echo "<div style='position:relative;width:48px;height:48px;margin:0 auto'>";
|
||||
echo "<img src='/webGui/images/disk.png' style='position:absolute;opacity:0.3;top:0;bottom:0;left:0;right:0;width:48px;height:48px'>";
|
||||
echo "</div></div></td>";
|
||||
echo "<td data-sort-value='ZZZZZZZZZZZ'><i>(orphan image)</i><div style='width:160px;'>Image ID: ".htmlspecialchars($image['Id'])."</div>";
|
||||
if (strpos(implode($image['Tags']),"<none>:<none>")===false) echo "<div style='width:'160px'>".implode('<br>',array_map('htmlspecialchars',$image['Tags']))."</div>";
|
||||
echo "</td><td> </td><td> </td><td> </td><td> </td>";
|
||||
echo "<td><i>(orphan image)</i><div style='width:160px;'>Image ID: $id</div>";
|
||||
echo "<div style='width:160px'>".implode('<br>',array_map('htmlspecialchars',$image['Tags']))."</div></td>";
|
||||
echo "<td colspan=4'> </td>";
|
||||
echo "<td><div class='advanced' style='width:124px'>Created ".htmlspecialchars($image['Created'])."</div></td></tr>";
|
||||
}
|
||||
echo "\0".implode($menu).implode($docker);
|
||||
|
||||
@@ -37,7 +37,7 @@ function addDockerContainerContext(container, image, template, started, update,
|
||||
function addDockerImageContext(image, imageTag) {
|
||||
var opts = [{header:'(orphan image)'}];
|
||||
opts.push({text:"Remove", icon:"fa-trash", action:function(e){e.preventDefault(); rmImage(image, imageTag);}});
|
||||
context.attach('#context-'+image, opts);
|
||||
context.attach('#'+image, opts);
|
||||
}
|
||||
function execUpContainer(container) {
|
||||
var title = "Updating the container: "+container;
|
||||
@@ -70,8 +70,7 @@ function popupWithIframe(title, cmd, reload, func) {
|
||||
});
|
||||
$(".ui-dialog .ui-dialog-titlebar").addClass("menu");
|
||||
$(".ui-dialog .ui-dialog-title").css("text-align", "center").css("width", "100%");
|
||||
$(".ui-dialog .ui-dialog-content").css("padding", "0");
|
||||
//$('.ui-widget-overlay').click(function() {$("#iframe-popup").dialog("close");});
|
||||
$(".ui-dialog .ui-dialog-content").css("padding", "12");
|
||||
}
|
||||
function addContainer() {
|
||||
var path = location.pathname;
|
||||
@@ -94,12 +93,13 @@ function updateContainer(container) {
|
||||
showCancelButton:true,
|
||||
confirmButtonColor:"#8CD4F5",
|
||||
confirmButtonText:"Yes, update it!"
|
||||
}, function() {
|
||||
},function(){
|
||||
execUpContainer(container);
|
||||
});
|
||||
}
|
||||
function rmContainer(container, image, id) {
|
||||
var body = "Remove container: "+container+"<br><br><label><input id=\"removeimagechk\" type=\"checkbox\" checked style=\"display:inline; width:unset; height:unset; margin-top:unset; margin-bottom:unset\">also remove image</label>";
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
swal({
|
||||
title:"Are you sure?",
|
||||
text:body,
|
||||
@@ -108,18 +108,19 @@ function rmContainer(container, image, id) {
|
||||
showCancelButton:true,
|
||||
confirmButtonColor:"#DD6B55",
|
||||
confirmButtonText:"Yes, delete it!",
|
||||
closeOnConfirm:false,
|
||||
showLoaderOnConfirm:true
|
||||
}, function() {
|
||||
},function(){
|
||||
$('#'+id).find('i').removeClass().addClass('iconstatus fa fa-trash orange-text');
|
||||
if ($("#removeimagechk").prop('checked')) {
|
||||
eventControl({action:"remove_all", container:id, image:image});
|
||||
eventControl({action:"remove_all", container:id, image:image},'loadlist');
|
||||
} else {
|
||||
eventControl({action:"remove_container", container:id});
|
||||
eventControl({action:"remove_container", container:id},'loadlist');
|
||||
}
|
||||
});
|
||||
}
|
||||
function rmImage(image, imageName) {
|
||||
var body = "Remove image: "+$('<textarea />').html(imageName).text();
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
swal({
|
||||
title:"Are you sure?",
|
||||
text:body,
|
||||
@@ -127,32 +128,36 @@ function rmImage(image, imageName) {
|
||||
showCancelButton:true,
|
||||
confirmButtonColor:"#DD6B55",
|
||||
confirmButtonText:"Yes, delete it!",
|
||||
closeOnConfirm:false,
|
||||
showLoaderOnConfirm:true
|
||||
}, function() {
|
||||
eventControl({action:"remove_image", image:image});
|
||||
},function(){
|
||||
$('#'+image).find('i').removeClass().addClass('iconstatus fa fa-trash orange-text');
|
||||
eventControl({action:"remove_image", image:image},'loadlist');
|
||||
});
|
||||
}
|
||||
function eventControl(params, reload) {
|
||||
var spin = typeof reload != 'undefined';
|
||||
function eventControl(params, spin) {
|
||||
if (spin) $('#'+params['container']).find('i').addClass('fa-spin');
|
||||
$.post(eventURL, params, function(data) {
|
||||
if (data.success === true) {
|
||||
if (spin) setTimeout(reload+'()',500); else location=window.location.href;
|
||||
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
|
||||
} else {
|
||||
swal({title:"Execution error", text:data.success, type:"error"});
|
||||
swal({
|
||||
title:"Execution error",
|
||||
text:data.success, type:"error"
|
||||
},function(){
|
||||
if (spin) setTimeout(spin+'()',500); else location=window.location.href;
|
||||
});
|
||||
}
|
||||
}, "json");
|
||||
},'json');
|
||||
}
|
||||
function startAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state=='false') $('#'+ct.id).find('i').addClass('fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'start'}, function(){loadlist();});
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'start'},function(){loadlist();});
|
||||
}
|
||||
function stopAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
for (var i=0,ct; ct=docker[i]; i++) if (ct.state=='true') $('#'+ct.id).find('i').addClass('fa-spin');
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'stop'}, function(){loadlist();});
|
||||
$.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'stop'},function(){loadlist();});
|
||||
}
|
||||
function checkAll() {
|
||||
$('input[type=button]').prop('disabled',true);
|
||||
@@ -169,7 +174,7 @@ function updateAll() {
|
||||
function containerLogs(container, id) {
|
||||
var height = 600;
|
||||
var width = 900;
|
||||
var run = eventURL+"?action=log&container="+id+"&title=Log for:"+container;
|
||||
var run = eventURL+'?action=log&container='+id+'&title=Log for: '+container;
|
||||
var top = (screen.height-height) / 2;
|
||||
var left = (screen.width-width) / 2;
|
||||
var options = 'resizeable=yes,scrollbars=yes,height='+height+',width='+width+',top='+top+',left='+left;
|
||||
|
||||
Reference in New Issue
Block a user