diff --git a/plugins/dynamix.docker.manager/include/CreateDocker.php b/plugins/dynamix.docker.manager/include/CreateDocker.php index 1ac35defa..1805e149c 100644 --- a/plugins/dynamix.docker.manager/include/CreateDocker.php +++ b/plugins/dynamix.docker.manager/include/CreateDocker.php @@ -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) { diff --git a/plugins/dynamix.docker.manager/include/DockerClient.php b/plugins/dynamix.docker.manager/include/DockerClient.php index aa83dfbb2..36414d366 100644 --- a/plugins/dynamix.docker.manager/include/DockerClient.php +++ b/plugins/dynamix.docker.manager/include/DockerClient.php @@ -1,6 +1,7 @@ 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); + 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,34 +67,31 @@ class DockerTemplates { return $paths; } - public function getTemplates($type) { global $dockerManPaths; - $tmpls = []; - $dirs = []; - if ($type == "all") { + $tmpls = $dirs = []; + if ($type == 'all') { $dirs[] = $dockerManPaths['templates-user']; $dirs[] = $dockerManPaths['templates-storage']; - } elseif ($type == "user") { + } elseif ($type == 'user') { $dirs[] = $dockerManPaths['templates-user']; - } elseif ($type == "default") { + } elseif ($type == 'default') { $dirs[] = $dockerManPaths['templates-storage']; } else { $dirs[] = $type; } foreach ($dirs as $dir) { if (!is_dir($dir)) @mkdir($dir, 0755, true); - $tmpls = array_merge($tmpls, $this->listDir($dir, "xml")); + $tmpls = array_merge($tmpls, $this->listDir($dir, 'xml')); } return $tmpls; } - private function removeDir($path) { if (is_dir($path)) { $files = array_diff(scandir($path), ['.', '..']); foreach ($files as $file) { - $this->removeDir(realpath($path) . '/' . $file); + $this->removeDir(realpath($path).'/'.$file); } return rmdir($path); } elseif (is_file($path)) { @@ -104,21 +100,20 @@ 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(); + $tmp_dir = '/tmp/tmp-'.mt_rand(); if (!file_exists($dockerManPaths['template-repos'])) { @mkdir(dirname($dockerManPaths['template-repos']), 0777, true); - @file_put_contents($dockerManPaths['template-repos'], "https://github.com/limetech/docker-templates"); + @file_put_contents($dockerManPaths['template-repos'], 'https://github.com/limetech/docker-templates'); } $urls = @file($Urls, FILE_IGNORE_NEW_LINES); if (!is_array($urls)) return false; - $this->debug("\nURLs:\n " . implode("\n ", $urls)); + //$this->debug("\nURLs:\n ".implode("\n ", $urls)); $github_api_regexes = [ '%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)/(.*)$%i', '%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)$%i', @@ -129,11 +124,11 @@ 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['url'] = sprintf("https://github.com/%s/%s/archive/%s.tar.gz", $github_api['user'], $github_api['repo'], $github_api['branch']); + $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,22 +146,22 @@ 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['url'] = sprintf("https://".$parse['host']."/%s/%s/repository/archive.tar.gz?ref=%s", $github_api['user'], $github_api['repo'], $github_api['branch']); + $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; } } } } if (empty($github_api['url'])) { - $this->debug("\n Cannot parse URL ".$url." for Templates."); + //$this->debug("\n Cannot parse URL ".$url." for Templates."); continue; } if ($this->download_url($github_api['url'], "$tmp_dir.tar.gz") === false) { - $this->debug("\n Download ".$github_api['url']." has failed."); + //$this->debug("\n Download ".$github_api['url']." has failed."); @unlink("$tmp_dir.tar.gz"); return null; } else { @@ -175,22 +170,22 @@ class DockerTemplates { unlink("$tmp_dir.tar.gz"); } $tmplsStor = []; - $this->debug("\n Templates found in ".$github_api['url']); + //$this->debug("\n Templates found in ".$github_api['url']); foreach ($this->getTemplates($tmp_dir) as $template) { - $storPath = sprintf("%s/%s", $Dest, str_replace($tmp_dir."/", "", $template['path'])); + $storPath = sprintf('%s/%s', $Dest, str_replace($tmp_dir.'/', '', $template['path'])); $tmplsStor[] = $storPath; if (!is_dir(dirname($storPath))) @mkdir(dirname($storPath), 0777, true); if (is_file($storPath)) { if (sha1_file($template['path']) === sha1_file($storPath)) { - $this->debug(" Skipped: ".$template['prefix'].'/'.$template['name']); + //$this->debug(" Skipped: ".$template['prefix'].'/'.$template['name']); continue; } else { @copy($template['path'], $storPath); - $this->debug(" Updated: ".$template['prefix'].'/'.$template['name']); + //$this->debug(" Updated: ".$template['prefix'].'/'.$template['name']); } } else { @copy($template['path'], $storPath); - $this->debug(" Added: ".$template['prefix'].'/'.$template['name']); + //$this->debug(" Added: ".$template['prefix'].'/'.$template['name']); } } $repotemplates = array_merge($repotemplates, $tmplsStor); @@ -198,27 +193,26 @@ class DockerTemplates { $this->removeDir($tmp_dir); } // Delete any templates not in the repos - foreach ($this->listDir($Dest, "xml") as $arrLocalTemplate) { + foreach ($this->listDir($Dest, 'xml') as $arrLocalTemplate) { if (!in_array($arrLocalTemplate['path'], $repotemplates)) { unlink($arrLocalTemplate['path']); - $this->debug(" Removed: ".$arrLocalTemplate['prefix'].'/'.$arrLocalTemplate['name']."\n"); + //$this->debug(" Removed: ".$arrLocalTemplate['prefix'].'/'.$arrLocalTemplate['name']."\n"); // Any other files left in this template folder? if not delete the folder too $files = array_diff(scandir(dirname($arrLocalTemplate['path'])), ['.', '..']); if (empty($files)) { rmdir(dirname($arrLocalTemplate['path'])); - $this->debug(" Removed: ".$arrLocalTemplate['prefix']); + //$this->debug(" Removed: ".$arrLocalTemplate['prefix']); } } } return $output; } - - public function getTemplateValue($Repository, $field, $scope = "all") { + public function getTemplateValue($Repository, $field, $scope = 'all') { foreach ($this->getTemplates($scope) as $file) { $doc = new DOMDocument(); $doc->load($file['path']); - $TemplateRepository = DockerUtil::ensureImageTag($doc->getElementsByTagName("Repository")->item(0)->nodeValue); + $TemplateRepository = DockerUtil::ensureImageTag($doc->getElementsByTagName('Repository')->item(0)->nodeValue); if ($Repository == $TemplateRepository) { $TemplateField = $doc->getElementsByTagName($field)->item(0)->nodeValue; @@ -228,49 +222,42 @@ class DockerTemplates { return null; } - public function getUserTemplate($Container) { - foreach ($this->getTemplates("user") as $file) { + foreach ($this->getTemplates('user') as $file) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->load($file['path']); - $Name = $doc->getElementsByTagName("Name")->item(0)->nodeValue; - if ($Name == $Container) { - return $file['path']; - } + $Name = $doc->getElementsByTagName('Name')->item(0)->nodeValue; + if ($Name == $Container) return $file['path']; } return false; } - public function getControlURL($name) { global $eth0; $DockerClient = new DockerClient(); - - $Repository = ""; + $Repository = ''; foreach ($DockerClient->getDockerContainers() as $ct) { if ($ct['Name'] == $name) { $Repository = $ct['Image']; - $Ports = $ct["Ports"]; + $Ports = $ct['Ports']; 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"); + $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)) { $myIP = exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name"); } if (preg_match("%\[IP\]%", $WebUI)) { - $WebUI = preg_replace("%\[IP\]%", $myIP ?: $eth0["IPADDR:0"], $WebUI); + $WebUI = preg_replace("%\[IP\]%", $myIP ?: $eth0['IPADDR:0'], $WebUI); } if (preg_match("%\[PORT:(\d+)\]%", $WebUI, $matches)) { $ConfigPort = $matches[1]; - if ($ct["NetworkMode"] == "bridge") { + if ($ct['NetworkMode'] == 'bridge') { foreach ($Ports as $key) { - if ($key["PrivatePort"] == $ConfigPort) { - $ConfigPort = $key["PublicPort"]; + if ($key['PrivatePort'] == $ConfigPort) { + $ConfigPort = $key['PublicPort']; } } } @@ -279,7 +266,6 @@ class DockerTemplates { return $WebUI; } - public function removeContainerInfo($container) { global $dockerManPaths; @@ -290,7 +276,6 @@ class DockerTemplates { } } - public function removeImageInfo($image) { global $dockerManPaths; $image = DockerUtil::ensureImageTag($image); @@ -302,90 +287,62 @@ 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] : []; - + $name = $ct['Name']; + $image = $ct['Image']; + $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)); - $new_info[$name] = $tmp; + //$this->debug("\n$name"); + //foreach ($tmp as $c => $d) $this->debug(sprintf(' %-10s: %s', $c, $d)); } - DockerUtil::saveJSON($dockerManPaths['webui-info'], $new_info); - return $new_info; + DockerUtil::saveJSON($dockerManPaths['webui-info'], $info); + return $info; } - public function getIcon($Repository) { global $docroot, $dockerManPaths; - $imgUrl = $this->getTemplateValue($Repository, "Icon"); + $imgUrl = $this->getTemplateValue($Repository, 'Icon'); preg_match_all("/(.*?):([\w]*$)/i", $Repository, $matches); - $tempPath = sprintf("%s/%s-%s-%s.png", $dockerManPaths['images-ram'], preg_replace('%\/|\\\%', '-', $matches[1][0]), $matches[2][0], 'icon'); - $storagePath = sprintf("%s/%s-%s-%s.png", $dockerManPaths['images-storage'], preg_replace('%\/|\\\%', '-', $matches[1][0]), $matches[2][0], 'icon'); + $name = preg_replace("%\/|\\\%", '-', $matches[1][0]); + $version = $matches[2][0]; + $tempPath = sprintf('%s/%s-%s-%s.png', $dockerManPaths['images-ram'], $name, $version, 'icon'); + $storagePath = sprintf('%s/%s-%s-%s.png', $dockerManPaths['images-storage'], $name, $version, 'icon'); if (!is_dir(dirname($tempPath))) @mkdir(dirname($tempPath), 0755, true); if (!is_dir(dirname($storagePath))) @mkdir(dirname($storagePath), 0755, true); if (!is_file($tempPath)) { - if (!is_file($storagePath)) { - $this->download_url($imgUrl, $storagePath); - } + if (!is_file($storagePath)) $this->download_url($imgUrl, $storagePath); @copy($storagePath, $tempPath); } - return (is_file($tempPath)) ? str_replace($docroot, '', $tempPath) : ""; + return (is_file($tempPath)) ? str_replace($docroot, '', $tempPath) : ''; } } - ###################################### -## DOCKERUPDATE CLASS ## +## DOCKERUPDATE CLASS ## ###################################### class DockerUpdate{ public $verbose = false; @@ -394,109 +351,96 @@ 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); + 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) { + public function download_url_and_headers($url, $headers = [], $path = '', $bg = false) { $strHeaders = ''; foreach ($headers as $header) { $strHeaders .= ' -H '.escapeshellarg($header); } - exec("curl --max-time 30 --silent --insecure --location --fail -i ".$strHeaders.($path ? " -o ".escapeshellarg($path) : "")." ".escapeshellarg($url)." ".($bg ? ">/dev/null 2>&1 &" : "2>/dev/null"), $out, $exit_code); + exec('curl --max-time 30 --silent --insecure --location --fail -i '.$strHeaders.($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; } - // DEPRECATED: Only used for Docker Index V1 type update checks public function getRemoteVersion($image) { list($strRepo, $strTag) = explode(':', DockerUtil::ensureImageTag($image)); - $apiUrl = sprintf("http://index.docker.io/v1/repositories/%s/tags/%s", $strRepo, $strTag); - $this->debug("API URL: $apiUrl"); + $apiUrl = sprintf('http://index.docker.io/v1/repositories/%s/tags/%s', $strRepo, $strTag); + //$this->debug("API URL: $apiUrl"); $apiContent = $this->download_url($apiUrl); return ($apiContent === false) ? null : substr(json_decode($apiContent, true)[0]['id'], 0, 8); } - public function getRemoteVersionV2($image) { list($strRepo, $strTag) = explode(':', DockerUtil::ensureImageTag($image)); // First - get auth token: // https://auth.docker.io/token?service=registry.docker.io&scope=repository:needo/nzbget:pull - $strAuthURL = sprintf("https://auth.docker.io/token?service=registry.docker.io&scope=repository:%s:pull", $strRepo); - $this->debug("Auth URL: $strAuthURL"); + $strAuthURL = sprintf('https://auth.docker.io/token?service=registry.docker.io&scope=repository:%s:pull', $strRepo); + //$this->debug("Auth URL: $strAuthURL"); $arrAuth = json_decode($this->download_url($strAuthURL), true); if (empty($arrAuth) || empty($arrAuth['token'])) { - $this->debug("Error: Auth Token was missing/empty"); + //$this->debug("Error: Auth Token was missing/empty"); return null; } - $this->debug("Auth Token: ".$arrAuth['token']); + //$this->debug("Auth Token: ".$arrAuth['token']); // Next - get manifest: - // curl -H "Authorization: Bearer " https://registry-1.docker.io/v2/needo/nzbget/manifests/latest - $strManifestURL = sprintf("https://registry-1.docker.io/v2/%s/manifests/%s", $strRepo, $strTag); - $this->debug("Manifest URL: $strManifestURL"); - $strManifest = $this->download_url_and_headers($strManifestURL, ["Accept: application/vnd.docker.distribution.manifest.v2+json", "Authorization: Bearer ".$arrAuth['token']]); + // curl -H 'Authorization: Bearer ' https://registry-1.docker.io/v2/needo/nzbget/manifests/latest + $strManifestURL = sprintf('https://registry-1.docker.io/v2/%s/manifests/%s', $strRepo, $strTag); + //$this->debug("Manifest URL: $strManifestURL"); + $strManifest = $this->download_url_and_headers($strManifestURL, ['Accept: application/vnd.docker.distribution.manifest.v2+json', 'Authorization: Bearer '.$arrAuth['token']]); if (empty($strManifest)) { - $this->debug("Error: Manifest response was empty"); + //$this->debug("Error: Manifest response was empty"); return null; } // Look for 'Docker-Content-Digest' header in response: // Docker-Content-Digest: sha256:2070d781fc5f98f12e752b75cf39d03b7a24b9d298718b1bbb73e67f0443062d $strDigest = ''; - foreach (preg_split('/\r\n|\r|\n/', $strManifest) as $strLine) { + foreach (preg_split("/\r\n|\r|\n/", $strManifest) as $strLine) { if (strpos($strLine, 'Docker-Content-Digest: ') === 0) { $strDigest = substr($strLine, 23); break; } } if (empty($strDigest)) { - $this->debug("Error: Remote Digest was missing/empty"); + //$this->debug("Error: Remote Digest was missing/empty"); return null; } - $this->debug("Remote Digest: $strDigest"); + //$this->debug("Remote Digest: $strDigest"); 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); $updateStatus = DockerUtil::loadJSON($dockerManPaths['update-status']); if (isset($updateStatus[$image])) { - if (isset($updateStatus[$image]['status']) && $updateStatus[$image]['status']=='undef') { - return null; - } - if ($updateStatus[$image]['local'] || $updateStatus[$image]['remote']) { - return ($updateStatus[$image]['local'] == $updateStatus[$image]['remote']); - } + if (isset($updateStatus[$image]['status']) && $updateStatus[$image]['status']=='undef') return null; + if ($updateStatus[$image]['local'] || $updateStatus[$image]['remote']) return ($updateStatus[$image]['local']==$updateStatus[$image]['remote']); } return null; } - public function reloadUpdateStatus($image = null) { global $dockerManPaths; $DockerClient = new DockerClient(); @@ -508,18 +452,17 @@ class DockerUpdate{ $localVersion = $updateStatus[$img]['local']; } $remoteVersion = $this->getRemoteVersionV2($img); - $status = ($localVersion && $remoteVersion) ? (($remoteVersion == $localVersion) ? "true" : "false") : "undef"; + $status = ($localVersion && $remoteVersion) ? (($remoteVersion == $localVersion) ? 'true' : 'false') : 'undef'; $updateStatus[$img] = [ 'local' => $localVersion, 'remote' => $remoteVersion, 'status' => $status ]; - $this->debug("Update status: Image='${img}', Local='${localVersion}', Remote='${remoteVersion}', Status='${status}'"); + //$this->debug("Update status: Image='${img}', Local='${localVersion}', Remote='${remoteVersion}', Status='${status}'"); } DockerUtil::saveJSON($dockerManPaths['update-status'], $updateStatus); } - public function setUpdateStatus($image, $version) { global $dockerManPaths; $image = DockerUtil::ensureImageTag($image); @@ -529,73 +472,58 @@ class DockerUpdate{ 'remote' => $version, 'status' => 'true' ]; - $this->debug("Update status: Image='${image}', Local='${version}', Remote='${version}', Status='true'"); + //$this->debug("Update status: Image='${image}', Local='${version}', Remote='${version}', Status='true'"); DockerUtil::saveJSON($dockerManPaths['update-status'], $updateStatus); } - public function updateUserTemplate($Container) { $changed = false; $DockerTemplates = new DockerTemplates(); - $validElements = [ - "Support", - "Overview", - "Category", - "WebUI", - "Icon" - ]; - - $validAttributes = [ - "Name", - "Default", - "Description", - "Display", - "Required", - "Mask" - ]; + $validElements = ['Support', 'Overview', 'Category', 'WebUI', 'Icon']; + $validAttributes = ['Name', 'Default', 'Description', 'Display', 'Required', 'Mask']; // Get user template file and abort if fail if ( ! $file = $DockerTemplates->getUserTemplate($Container) ) { - $this->debug("User template for container '$Container' not found, aborting."); + //$this->debug("User template for container '$Container' not found, aborting."); return null; } // Load user template XML, verify if it's valid and abort if doesn't have TemplateURL element $template = simplexml_load_file($file); if ( empty($template->TemplateURL) ) { - $this->debug("Template doesn't have TemplateURL element, aborting."); + //$this->debug("Template doesn't have TemplateURL element, aborting."); return null; } // Load a user template DOM for import remote template new Config $dom_local = dom_import_simplexml($template); // Try to download the remote template and abort if it fail. if (! $dl = $this->download_url($this->xml_decode($template->TemplateURL))) { - $this->debug("Download of remote template failed, aborting."); + //$this->debug("Download of remote template failed, aborting."); return null; } // Try to load the downloaded template and abort if fail. if (! $remote_template = @simplexml_load_string($dl)) { - $this->debug("The downloaded template is not a valid XML file, aborting."); + //$this->debug("The downloaded template is not a valid XML file, aborting."); return null; } // Loop through remote template elements and compare them to local ones foreach ($remote_template->children() as $name => $remote_element) { $name = $this->xml_decode($name); // Compare through validElements - if ($name != "Config" && in_array($name, $validElements)) { + if ($name != 'Config' && in_array($name, $validElements)) { $local_element = $template->xpath("//$name")[0]; $rvalue = $this->xml_decode($remote_element); $value = $this->xml_decode($local_element); // Values changed, updating. if ($value != $rvalue) { $local_element[0] = $this->xml_encode($rvalue); - $this->debug("Updating $name from [$value] to [$rvalue]"); + //$this->debug("Updating $name from [$value] to [$rvalue]"); $changed = true; } // Compare atributes on Config if they are in the validAttributes list - } elseif ($name == "Config") { + } elseif ($name == 'Config') { $type = $this->xml_decode($remote_element['Type']); $target = $this->xml_decode($remote_element['Target']); - if ($type == "Port") { + if ($type == 'Port') { $mode = $this->xml_decode($remote_element['Mode']); $local_element = $template->xpath("//Config[@Type='$type'][@Target='$target'][@Mode='$mode']")[0]; } else { @@ -608,7 +536,7 @@ class DockerUpdate{ $value = $this->xml_decode($local_element[$key]); // Values changed, updating. if ($value != $rvalue && in_array($key, $validAttributes)) { - $this->debug("Updating $type '$target' attribute '$key' from [$value] to [$rvalue]"); + //$this->debug("Updating $type '$target' attribute '$key' from [$value] to [$rvalue]"); $local_element[$key] = $this->xml_encode($rvalue); $changed = true; } @@ -624,36 +552,29 @@ class DockerUpdate{ } if ($changed) { // Format output and save to file if there were any commited changes - $this->debug("Saving template modifications to '$file"); + //$this->debug("Saving template modifications to '$file"); $dom = new DOMDocument('1.0'); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->loadXML($template->asXML()); file_put_contents($file, $dom->saveXML()); } else { - $this->debug("Template is up to date."); + //$this->debug("Template is up to date."); } } } - ###################################### -## DOCKERCLIENT CLASS ## +## DOCKERCLIENT CLASS ## ###################################### class DockerClient { - private $allContainersCache = null; - - 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 +589,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,27 +600,24 @@ 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) { + public function getDockerJSON($url, $method = 'GET', &$code = null, $callback = null, $unchunk = false) { $fp = stream_socket_client('unix:///var/run/docker.sock', $errno, $errstr); if ($fp === false) { echo "Couldn't create socket: [$errno] $errstr"; return null; } - $protocol = ($unchunk) ? "HTTP/1.0" : "HTTP/1.1"; - $out="${method} {$url} ${protocol}\r\nHost: localhost\r\nConnection: Close\r\n\r\n"; + $protocol = $unchunk ? 'HTTP/1.0' : 'HTTP/1.1'; + $out = "${method} {$url} ${protocol}\r\nHost:127.0.0.1\r\nConnection:Close\r\n\r\n"; fwrite($fp, $out); // Strip headers out $headers = ''; while (($line = fgets($fp)) !== false) { - if (strpos($line, "HTTP/1") !== false) { + if (strpos($line, 'HTTP/1') !== false) { $code = vsprintf('%2$s',preg_split("#\s+#", $line)); } $headers .= $line; - if (rtrim($line) == '') { - break; - } + if (rtrim($line) == '') break; } $data = []; while (($line = fgets($fp)) !== false) { @@ -713,82 +630,69 @@ class DockerClient { return $data; } - function doesContainerExist($container) { foreach ($this->getDockerContainers() as $ct) { - if ($ct['Name'] == $container) { - return true; - } + if ($ct['Name'] == $container) return true; } return false; } - function doesImageExist($image) { foreach ($this->getDockerImages() as $img) { - if (strpos($img['Tags'][0], $image) !== false) { - return true; - } + if (strpos($img['Tags'][0], $image) !== false) return true; } return false; } - public function getInfo() { - $info = $this->getDockerJSON("/info"); - $version = $this->getDockerJSON("/version"); + $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); + $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 + $this->getDockerJSON("/containers/${id}/start", 'POST', $code); + DockerUtil::$allContainersCache = null; // flush cache $codes = [ - "204" => true, // No error - "304" => "Container already started", - "404" => "No such container", - "500" => "Server error" + '204' => true, // No error + '304' => 'Container already started', + '404' => 'No such container', + '500' => 'Server error' ]; 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 + $this->getDockerJSON("/containers/${id}/stop?t=10", 'POST', $code); + DockerUtil::$allContainersCache = null; // flush cache $codes = [ - "204" => true, // No error - "304" => "Container already stopped", - "404" => "No such container", - "500" => "Server error" + '204' => true, // No error + '304' => 'Container already stopped', + '404' => 'No such container', + '500' => 'Server error' ]; 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 + $this->getDockerJSON("/containers/${id}/restart?t=10", 'POST', $code); + DockerUtil::$allContainersCache = null; // flush cache $codes = [ - "204" => true, // No error - "404" => "No such container", - "500" => "Server error" + '204' => true, // No error + '404' => 'No such container', + '500' => 'Server error' ]; return (array_key_exists($code, $codes)) ? $codes[$code] : 'Error code '.$code; } - public function removeContainer($id) { global $docroot, $dockerManPaths; // Purge cached container information @@ -797,43 +701,37 @@ class DockerClient { if (isset($info[$id]['icon'])) { $iconRam = $docroot.$info[$id]['icon']; $iconFlash = str_replace($dockerManPaths['images-ram'], $dockerManPaths['images-storage'], $iconRam); - if (is_file($iconRam)) { - unlink($iconRam); - } - if (is_file($iconFlash)) { - unlink($iconFlash); - } + if (is_file($iconRam)) unlink($iconRam); + if (is_file($iconFlash)) unlink($iconFlash); } unset($info[$id]); DockerUtil::saveJSON($dockerManPaths['webui-info'], $info); } // Attempt to remove container - $this->getDockerJSON("/containers/${id}?force=1", "DELETE", $code); - $this->allContainersCache = null; // flush cache + $this->getDockerJSON("/containers/${id}?force=1", 'DELETE', $code); + DockerUtil::$allContainersCache = null; // flush cache $codes = [ - "204" => true, // No error - "400" => "Bad parameter", - "404" => "No such container", - "500" => "Server error" + '204' => true, // No error + '400' => 'Bad parameter', + '404' => 'No such container', + '500' => 'Server error' ]; 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 + $ret = $this->getDockerJSON("/images/create?fromImage=".urlencode($image), 'POST', $code, $callback); + DockerUtil::$allImagesCache = null; // flush cache return $ret; } - public function removeImage($id) { global $dockerManPaths; $image = $this->getImageName($id); // Attempt to remove image - $this->getDockerJSON("/images/${id}?force=1", "DELETE", $code); - $this->allImagesCache = null; // flush cache - if (in_array($code, ["200", "404"])) { + $this->getDockerJSON("/images/${id}?force=1", 'DELETE', $code); + DockerUtil::$allImagesCache = null; // flush cache + if (in_array($code, ['200', '404'])) { // Purge cached image information (only if delete was successful) $image = DockerUtil::ensureImageTag($image); $updateStatus = DockerUtil::loadJSON($dockerManPaths['update-status']); @@ -843,82 +741,61 @@ class DockerClient { } } $codes = [ - "200" => true, // No error - "404" => "No such image", - "409" => "Conflict: image used by container(s): ".implode(', ', $this->usedBy($id)), - "500" => "Server error" + '200' => true, // No error + '404' => 'No such image', + '409' => 'Conflict: image used by container(s): '.implode(', ', $this->usedBy($id)), + '500' => 'Server error' ]; 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)) { - return $this->allContainersCache; - } - - $this->allContainersCache = []; + if (is_array(DockerUtil::$allContainersCache)) return DockerUtil::$allContainersCache; + DockerUtil::$allContainersCache = []; foreach ($this->getDockerJSON("/containers/json?all=1") as $obj) { $details = $this->getContainerDetails($obj['Id']); - $c = []; - $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["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["Ports"] = []; - - if ($c["NetworkMode"] != 'host' && !empty($details['HostConfig']['PortBindings'])) { + $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'] ?: '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'] = $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'], - 'PrivatePort' => $PrivatePort, - 'PublicPort' => $value[0]['HostPort'], - 'Type' => $Type - ]; + list($PrivatePort, $Type) = explode('/', $port); + $c['Ports'][] = ['IP' => $value[0]['HostIP'] ?? '0.0.0.0', 'PrivatePort' => $PrivatePort, 'PublicPort' => $value[0]['HostPort'], 'Type' => $Type ]; } } - - $this->allContainersCache[] = $c; + DockerUtil::$allContainersCache[] = $c; } - usort($this->allContainersCache, $this->build_sorter('Name')); - return $this->allContainersCache; + usort(DockerUtil::$allContainersCache, $this->build_sorter('Name')); + return DockerUtil::$allContainersCache; } - public function getContainerID($Container) { foreach ($this->getDockerContainers() as $ct) { - if (preg_match("%" . preg_quote($Container, "%") . "%", $ct["Name"])) { - return $ct["Id"]; - } + if (preg_match('%'.preg_quote($Container, '%').'%', $ct['Name'])) return $ct['Id']; } return null; } - public function getImageID($Image) { - if ( ! strpos($Image,":") ) { - $Image .= ":latest"; - } + if (!strpos($Image,':')) $Image .= ':latest'; foreach ($this->getDockerImages() as $img) { foreach ($img['Tags'] as $tag) { - if ( $Image == $tag ) { - return $img["Id"]; - } + if ( $Image == $tag ) return $img['Id']; } } return null; @@ -926,9 +803,7 @@ class DockerClient { public function getImageName($id) { foreach ($this->getDockerImages() as $img) { - if ($img['Id'] == $id) { - return $img['Tags'][0]; - } + if ($img['Id'] == $id) return $img['Tags'][0]; } return null; } @@ -936,53 +811,46 @@ class DockerClient { private function usedBy($imageId) { $out = []; foreach ($this->getDockerContainers() as $ct) { - if ($ct["ImageId"] == $imageId) { - $out[] = $ct["Name"]; - } + if ($ct['ImageId'] == $imageId) $out[] = $ct['Name']; } return $out; } - public function getDockerImages() { // Return cached values - if (is_array($this->allImagesCache)) { - return $this->allImagesCache; - } - - $this->allImagesCache = []; - foreach ($this->getDockerJSON("/images/json?all=0") as $obj) { + if (is_array(DockerUtil::$allImagesCache)) return DockerUtil::$allImagesCache; + DockerUtil::$allImagesCache = []; + foreach ($this->getDockerJSON('/images/json?all=0') as $obj) { $c = []; - $c["Created"] = $this->humanTiming($obj['Created']); - $c["Id"] = substr(str_replace('sha256:', '', $obj['Id']), 0, 12); - $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["Repository"] = vsprintf('%1$s/%2$s', preg_split("#[:\/]#", DockerUtil::ensureImageTag($obj['RepoTags'][0]))); - $c["usedBy"] = $this->usedBy($c["Id"]); - - $this->allImagesCache[$c["Id"]] = $c; + $c['Created'] = $this->humanTiming($obj['Created']); + $c['Id'] = substr(str_replace('sha256:', '', $obj['Id']), 0, 12); + $c['ParentId'] = substr(str_replace('sha256:', '', $obj['ParentId']), 0, 12); + $c['Size'] = $this->formatBytes($obj['Size']); + $c['VirtualSize'] = $this->formatBytes($obj['VirtualSize']); + $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']); + DockerUtil::$allImagesCache[$c['Id']] = $c; } - return $this->allImagesCache; + return DockerUtil::$allImagesCache; } - public function flushCaches() { - $this->allContainersCache = null; - $this->allImagesCache = null; + DockerUtil::$allContainersCache = null; + DockerUtil::$allImagesCache = null; } } - ###################################### ## DOCKERUTIL CLASS ## ###################################### class DockerUtil { + public static $allContainersCache = null; + public static $allImagesCache = null; + public static function ensureImageTag($image) { list($strRepo, $strTag) = explode(':', $image.':'); - if (strpos($strRepo, 'sha256:') === 0) { // sha256 was provided instead of actual repo name so truncate it for display: $strRepo = substr(str_replace('sha256:', '', $strRepo), 0, 12); @@ -990,29 +858,20 @@ class DockerUtil { // Prefix library/ if there's no author (maybe a Docker offical image?) $strRepo = 'library/'.$strRepo; } - // Add :latest tag to image if it's absent - if (empty($strTag)) { - $strTag = 'latest'; - } - + if (empty($strTag)) $strTag = 'latest'; 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 = []; - 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)); } - } ?> diff --git a/plugins/dynamix.docker.manager/include/DockerContainers.php b/plugins/dynamix.docker.manager/include/DockerContainers.php index 75f7f4f30..1349d1ae4 100644 --- a/plugins/dynamix.docker.manager/include/DockerContainers.php +++ b/plugins/dynamix.docker.manager/include/DockerContainers.php @@ -53,9 +53,9 @@ foreach ($all_containers as $ct) { $status = $ct['Running'] ? 'started':'stopped'; $icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png'; $ports = []; - if ($mode=='bridge') { - $binds = explode('|',exec("docker inspect --format='{{range \$p,\$c := .HostConfig.PortBindings}}{{\$p}}:{{(index \$c 0).HostPort}}|{{end}}' $name 2>/dev/null")); - $ip = exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name 2>/dev/null"); + $binds = explode('|',exec("docker inspect --format='{{range \$p,\$c := .HostConfig.PortBindings}}{{\$p}}:{{(index \$c 0).HostPort}}|{{end}}' $name 2>/dev/null")); + if (count($binds)>1) { + $ip = $ct['Running'] ? exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name 2>/dev/null") : '0.0.0.0'; foreach ($binds as $bind) { if (!$bind) continue; list($container_port,$host_port) = explode(':',$bind); @@ -63,7 +63,7 @@ foreach ($all_containers as $ct) { } } else { $binds = explode('|',exec("docker inspect --format='{{range \$p,\$c := .Config.ExposedPorts}}{{\$p}}|{{end}}' $name 2>/dev/null")); - $ip = exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name 2>/dev/null") ?: $eth0['IPADDR:0']; + $ip = $ct['Running'] ? (exec("docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $name 2>/dev/null") ?: $eth0['IPADDR:0']) : '0.0.0.0'; foreach ($binds as $bind) { if (!$bind) continue; $ports[] = sprintf('%s:%s%s:%s',$ip, $bind, $ip, str_replace(['/tcp','/udp'],'',$bind)); @@ -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 ""; - echo "
"; + echo "
"; echo "
"; echo ""; echo "
"; - echo "(orphan image)
Image ID: ".htmlspecialchars($image['Id'])."
"; - if (strpos(implode($image['Tags']),"<none>:<none>")===false) echo "
".implode('
',array_map('htmlspecialchars',$image['Tags']))."
"; - echo "    "; + echo "(orphan image)
Image ID: $id
"; + echo "
".implode('
',array_map('htmlspecialchars',$image['Tags']))."
"; + echo " "; echo "
Created ".htmlspecialchars($image['Created'])."
"; } echo "\0".implode($menu).implode($docker); diff --git a/plugins/dynamix.docker.manager/javascript/docker.js b/plugins/dynamix.docker.manager/javascript/docker.js index 5229f91c8..b959c47aa 100644 --- a/plugins/dynamix.docker.manager/javascript/docker.js +++ b/plugins/dynamix.docker.manager/javascript/docker.js @@ -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+"

"; + $('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: "+$('