Docker code cleanup/refactor and bug fixes:

- Dont start container after update if it was in a stopped state before the update

- Dont try to stop a container if it is not running (was causing warnings to be logged)

- Reduced script tag generation for docker pull operations

- Correctly clean up old orphan images after updating a container (was broken before)

- Eliminated race condition when deleting both container and image

- Switched to use SweetAlert instead of jQuery UI dialogs for prompts and alerts
This commit is contained in:
Eric Schultz
2016-02-12 07:15:23 -08:00
parent bb7a9a8080
commit 4f560d7ee5
8 changed files with 706 additions and 687 deletions

View File

@@ -17,8 +17,8 @@ Markdown="false"
?>
<?
// Add the Docker JSON client
require_once('/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php');
$docker = new DockerClient();
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();
?>
@@ -67,20 +67,20 @@ img.stopped{opacity:0.3;}
</thead>
<tbody id="docker_list">
<?
$all_containers = $docker->getDockerContainers();
if ( ! $all_containers) {
$all_containers = array();
$all_containers = $DockerClient->getDockerContainers();
if (!$all_containers) {
$all_containers = [];
echo "<tr><td></td><td colspan=\"7\">No Docker Containers Installed</td></tr>";
}
$info = $DockerTemplates->getAllInfo();
$contextMenus = array();
$contextMenus = [];
$IP = $var["IPADDR"];
foreach($all_containers as $ct){
foreach ($all_containers as $ct) {
$name = $ct["Name"];
$is_autostart = ( $info[$name]['autostart'] ) ? 'true' : 'false';
$is_autostart = ($info[$name]['autostart']) ? 'true' : 'false';
$updateStatus = $info[$name]['updated'];
$updateStatus = ($updateStatus == "true" or $updateStatus == "undef" ) ? 'true' : 'false';
$updateStatus = ($updateStatus == "true" || $updateStatus == "undef") ? 'true' : 'false';
$running = ($ct['Running']) ? 'true' : 'false';
$webGuiUrl = $info[$name]['url'];
$contextMenus[] = sprintf("addDockerContainerContext('%s', '%s', '%s', %s, %s, %s, '%s', '%s');", addslashes($ct['Name']), addslashes($ct['ImageId']), addslashes($info[$name]['template']), $running, $updateStatus, $is_autostart, addslashes($webGuiUrl), $ct["Id"]);
@@ -88,13 +88,13 @@ img.stopped{opacity:0.3;}
$status = ($ct["Running"]) ? "started" : "stopped";
$Icon = $info[$name]['icon'];
if ( ! $Icon ){
if (!$Icon) {
$Icon = "/plugins/dynamix.docker.manager/images/question.png";
}
$ports = array();
$ports = [];
foreach ($ct['Ports'] as $p) {
if (strlen($p['PublicPort'])){
if (strlen($p['PublicPort'])) {
$ipAddr = sprintf("%s:%s", $IP, $p['PublicPort']);
$outFormat = sprintf('<a href="http://%s" target="_blank">%s/%s <i class="fa fa-arrows-h" style="margin: 0 3px"></i> %s</a>', $ipAddr, $p['PrivatePort'], $p['Type'], htmlspecialchars($ipAddr));
} else {
@@ -102,10 +102,11 @@ img.stopped{opacity:0.3;}
}
$ports[] = $outFormat;
}
$paths = array();
$paths = [];
if (count($ct['Volumes'])){
foreach ($ct['Volumes'] as $value) {
if (preg_match('/localtime/', $value) == TRUE){ continue; }
if (preg_match('/localtime/', $value) == true) continue;
list($host_path, $container_path, $access_mode) = explode(":", $value);
$tip = 'Container volume \'' . $container_path . '\' has ' . ($access_mode == 'ro' ? 'read-only' : 'read-write') . ' access to Host path \'' . $host_path . '\'';
@@ -117,10 +118,10 @@ img.stopped{opacity:0.3;}
<tr>
<td style="width: 48px; padding: 4px">
<?
echo " <div id=\"context-" . htmlspecialchars($name) . "\" style=\"display:block; cursor:pointer\">
echo " <div id=\"context-".htmlspecialchars($name)."\" style=\"display:block; cursor:pointer\">
<div style=\"position: relative; width: 48px; height: 48px; margin: 0px auto;\">
<img src=\"" . $Icon . "\" class=\"$status\" style=\"position: absolute; z-index: 1; top: 0; bottom: 0; left: 0; right: 0; width: 48px; height: 48px;\"/>
<i class=\"fa iconstatus fa-$shape $status\" title='$status'></i>
<img src=\"".htmlspecialchars($Icon)."\" class=\"".htmlspecialchars($status)."\" style=\"position: absolute; z-index: 1; top: 0; bottom: 0; left: 0; right: 0; width: 48px; height: 48px;\"/>
<i class=\"fa iconstatus fa-$shape $status\" title=\"".htmlspecialchars($status)."\"></i>
</div>
</div>";
@@ -147,9 +148,9 @@ img.stopped{opacity:0.3;}
<td class="updatecolumn">
<?
$updateStatus = $info[$ct['Name']]['updated'];
if ($updateStatus == "false"){
if ($updateStatus == "false") {
echo "<a class=\"exec\" onclick=\"updateContainer('" . addslashes($ct["Name"]) . "');\"><span style=\"white-space:nowrap;\"><i class=\"fa fa-cloud-download\"></i> update ready</span></a>";
} else if ($updateStatus == "true"){
} else if ($updateStatus == "true") {
echo "<span style=\"color:#44B012;white-space:nowrap;\"><i class=\"fa fa-check\"></i> up-to-date</span>";
echo "<div class=\"advanced\"><a class=\"exec\" onclick=\"updateContainer('" . addslashes($ct["Name"]) . "');\" style=\"color:#888;\"><span style=\"white-space:nowrap;\"><i class=\"fa fa-cloud-download\"></i> force update</span></a></div>";
} else {
@@ -167,10 +168,7 @@ img.stopped{opacity:0.3;}
<?
$all_images = $docker->getDockerImages();
if ( ! $all_images) { $all_images = array(); }
foreach($all_images as $image){
foreach ($DockerClient->getDockerImages() as $image) {
if (count($image['usedBy'])) {
continue;
}
@@ -181,7 +179,7 @@ img.stopped{opacity:0.3;}
<tr class="advanced">
<td style="width: 48px; padding: 4px">
<?
echo " <div id='context-" . $image['Id'] . "' style='display:block; cursor:pointer'>
echo " <div id=\"context-".htmlspecialchars($image['Id'])."\" style=\"display:block; cursor:pointer\">
<div style=\"position: relative; width: 48px; height: 48px; margin: 0px auto;\">
<img src=\"/webGui/images/disk.png\" style=\"position: absolute; z-index: 1; opacity: 0.3; top: 0; bottom: 0; left: 0; right: 0; width: 48px; height: 48px;\"/>
</div>

View File

@@ -16,10 +16,7 @@ Cond="(pgrep('docker')!==false)"
?>
<?
// Add the Docker JSON client
require_once('/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php');
$docker = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
// Repos file
$template_repos = $dockerManPaths['template-repos'];

View File

@@ -12,7 +12,7 @@
?>
<?
ignore_user_abort(true);
require_once("/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php");
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerTemplates = new DockerTemplates();
@@ -24,73 +24,127 @@ $DockerTemplates = new DockerTemplates();
# ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║
# ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝
$echo = function($m){echo "<pre>".print_r($m,true)."</pre>";};
$echo = function($m){ echo "<pre>".print_r($m, true)."</pre>"; };
function ContainerExist($container){
function stopContainer($name) {
global $DockerClient;
$all_containers = $DockerClient->getDockerContainers();
if ( ! $all_containers) { return FALSE; }
foreach ($all_containers as $ct) {
if ($ct['Name'] == $container){
return True;
}
}
return False;
$waitID = mt_rand();
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Stopping container: ".addslashes($name)."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
$retval = $DockerClient->stopContainer($name);
$out = ($retval === true) ? "Successfully stopped container '$name'" : "Error: ".$retval;
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes($out)."</b>');</script>\n";
@flush();
}
function ImageExist($image){
function removeContainer($name) {
global $DockerClient;
$all_images = $DockerClient->getDockerImages();
if ( ! $all_images) { return FALSE; }
foreach ($all_images as $img) {
if ( ! is_bool(strpos($img['Tags'][0], $image)) ){
return True;
}
}
return False;
$waitID = mt_rand();
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing container: ".addslashes($name)."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
$retval = $DockerClient->removeContainer($name);
$out = ($retval === true) ? "Successfully removed container '$name'" : "Error: ".$retval;
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes($out)."</b>');</script>\n";
@flush();
}
function trimLine($text){
return preg_replace("/([\n^])[\s]+/", '$1', $text);
function removeImage($image) {
global $DockerClient;
$waitID = mt_rand();
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Removing orphan image: ".addslashes($image)."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
$retval = $DockerClient->removeImage($image);
$out = ($retval === true) ? "Successfully removed image '$image'" : "Error: ".$retval;
echo "<script>stop_Wait($waitID);addLog('<b>".addslashes($out)."</b>');</script>\n";
@flush();
}
function pullImage($name, $image) {
global $DockerClient, $DockerTemplates, $DockerUpdate;
if (! preg_match("/:[\w]*$/i", $image)) $image .= ":latest";
$waitID = mt_rand();
if (!preg_match("/:[\w]*$/i", $image)) $image .= ":latest";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Pulling image: ${image}</legend><p class=\"logLine\" id=\"logBody\"></p></fieldset>');</script>\n";
echo "<p class=\"logLine\" id=\"logBody\"></p>";
echo "<script>addLog('<fieldset style=\"margin-top:1px;\" class=\"CMD\"><legend>Pulling image: ".addslashes($image)."</legend><p class=\"logLine\" id=\"logBody\"></p><span id=\"wait{$waitID}\">Please wait </span></fieldset>');show_Wait($waitID);</script>\n";
@flush();
$alltotals = array();
$alltotals = [];
$laststatus = [];
// Force information reload
$DockerTemplates->removeInfo($name, $image);
$DockerClient->pullImage($image, function ($line) use (&$alltotals, $DockerUpdate, $image) {
$cnt = json_decode( $line, TRUE );
$id = ( isset( $cnt['id'] )) ? $cnt['id'] : "";
$status = ( isset( $cnt['status'] )) ? $cnt['status'] : "";
if (strlen(trim($status)) && strlen(trim($id))) {
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
$alltotals[$cnt['id']] = $cnt['progressDetail']['total'];
}
echo "<script>addToID('${id}','${status}');</script>\n";
$DockerClient->pullImage($image, function ($line) use (&$alltotals, &$laststatus, &$waitID, $image, $DockerClient, $DockerUpdate) {
$cnt = json_decode($line, true);
$id = (isset($cnt['id'])) ? trim($cnt['id']) : '';
$status = (isset($cnt['status'])) ? trim($cnt['status']) : '';
if ($waitID !== false) {
echo "<script>stop_Wait($waitID);</script>\n";
@flush();
$waitID = false;
}
if ($status == "Downloading") {
$total = $cnt['progressDetail']['total'];
$current = $cnt['progressDetail']['current'];
$alltotals[$cnt['id']] = $cnt['progressDetail']['current'];
if ($total > 0) {
$percentage = round(($current/$total) * 100);
echo "<script>progress('${id}',' ". $percentage ."% of " . sizeToHuman($total) . "');</script>\n";
} else {
// Docker must not know the total download size (http-chunked or something?)
// just show the current download progress without the percentage
echo "<script>progress('${id}',' " . sizeToHuman($current) . "');</script>\n";
if (empty($status)) return;
if (!empty($id)) {
if (!empty($cnt['progressDetail']) && !empty($cnt['progressDetail']['total'])) {
$alltotals[$id] = $cnt['progressDetail']['total'];
}
} else if (!empty($status) && empty($id)) {
if (empty($laststatus[$id])) {
$laststatus[$id] = '';
}
switch ($status) {
case 'Waiting':
// Omit
break;
case 'Downloading':
if ($laststatus[$id] != $status) {
echo "<script>addToID('${id}','".addslashes($status)."');</script>\n";
}
$total = $cnt['progressDetail']['total'];
$current = $cnt['progressDetail']['current'];
if ($total > 0) {
$percentage = round(($current / $total) * 100);
echo "<script>progress('${id}',' ".$percentage."% of ".$DockerClient->formatBytes($total)."');</script>\n";
} else {
// Docker must not know the total download size (http-chunked or something?)
// just show the current download progress without the percentage
$alltotals[$id] = $current;
echo "<script>progress('${id}',' ".$DockerClient->formatBytes($current)."');</script>\n";
}
break;
default:
if ($laststatus[$id] == "Downloading") {
echo "<script>progress('${id}',' 100% of ".$DockerClient->formatBytes($alltotals[$id])."');</script>\n";
}
if ($laststatus[$id] != $status) {
echo "<script>addToID('${id}','".addslashes($status)."');</script>\n";
}
break;
}
$laststatus[$id] = $status;
} else {
if (strpos($status, 'Status: ') === 0) {
echo "<script>addLog('${status}');</script>\n";
echo "<script>addLog('".addslashes($status)."');</script>\n";
}
if (strpos($status, 'Digest: ') === 0) {
$DockerUpdate->setUpdateStatus($image, substr($status, 8));
@@ -99,20 +153,10 @@ function pullImage($name, $image) {
@flush();
});
echo "<script>addLog('<br><b>TOTAL DATA PULLED:</b> " . sizeToHuman(array_sum($alltotals)) . "<span class=\"progress\"></span>');</script>\n";
echo "<script>addLog('<br><b>TOTAL DATA PULLED:</b> " . $DockerClient->formatBytes(array_sum($alltotals)) . "');</script>\n";
@flush();
}
function sizeToHuman($size) {
$units = ['B','KB','MB','GB'];
$unitsIndex = 0;
while ($size > 1024 && (($unitsIndex+1) < count($units))) {
$size /= 1024;
$unitsIndex++;
}
return ceil($size) . " " . $units[$unitsIndex];
}
function xml_encode($string) {
return htmlspecialchars($string, ENT_XML1, 'UTF-8');
}
@@ -120,10 +164,10 @@ function xml_decode($string) {
return strval(html_entity_decode($string, ENT_XML1, 'UTF-8'));
}
function postToXML($post, $setOwnership = FALSE){
function postToXML($post, $setOwnership = false) {
$dom = new domDocument;
$dom->appendChild($dom->createElement( "Container" ));
$xml = simplexml_import_dom( $dom );
$dom->appendChild($dom->createElement("Container"));
$xml = simplexml_import_dom($dom);
$xml["version"] = 2;
$xml->Name = xml_encode($post['contName']);
$xml->Repository = xml_encode($post['contRepository']);
@@ -144,7 +188,6 @@ function postToXML($post, $setOwnership = FALSE){
$xml->addChild("Data");
$xml->addChild("Environment");
for ($i = 0; $i < count($post["confName"]); $i++) {
$Type = $post['confType'][$i];
$config = $xml->addChild('Config');
@@ -159,17 +202,17 @@ function postToXML($post, $setOwnership = FALSE){
$config['Required'] = xml_encode($post['confRequired'][$i]);
$config['Mask'] = xml_encode($post['confMask'][$i]);
# V1 compatibility
if ($Type == "Port"){
if ($Type == "Port") {
$port = $xml->Networking->Publish->addChild("Port");
$port->HostPort = $post['confValue'][$i];
$port->ContainerPort = $post['confTarget'][$i];
$port->Protocol = $post['confMode'][$i];
} else if ($Type == "Path"){
} else if ($Type == "Path") {
$path = $xml->Data->addChild("Volume");
$path->HostDir = $post['confValue'][$i];
$path->ContainerDir = $post['confTarget'][$i];
$path->Mode = $post['confMode'][$i];
} else if ($Type == "Variable"){
} else if ($Type == "Variable") {
$variable = $xml->Environment->addChild("Variable");
$variable->Value = $post['confValue'][$i];
$variable->Name = $post['confTarget'][$i];
@@ -185,9 +228,9 @@ function postToXML($post, $setOwnership = FALSE){
function xmlToVar($xml) {
global $var;
$out = array();
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$out = [];
$out['Name'] = xml_decode($xml->Name);
$out['Repository'] = xml_decode($xml->Repository);
$out['Registry'] = xml_decode($xml->Registry);
@@ -200,19 +243,19 @@ function xmlToVar($xml) {
$out['Icon'] = xml_decode($xml->Icon);
$out['ExtraParams'] = xml_decode($xml->ExtraParams);
$out['Config'] = array();
$out['Config'] = [];
if (isset($xml->Config)) {
foreach ($xml->Config as $config) {
$c = array();
$c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']) ;
$c = [];
$c['Value'] = strlen(xml_decode($config)) ? xml_decode($config) : xml_decode($config['Default']);
foreach ($config->attributes() as $key => $value) $c[$key] = xml_decode(xml_decode($value));
$out['Config'][] = $c;
}
}
# V1 compatibility
if ($xml["version"] != "2"){
if (isset($xml->Networking->Mode)){
if ($xml["version"] != "2") {
if (isset($xml->Networking->Mode)) {
$out['Network'] = xml_decode($xml->Networking->Mode);
}
if (isset($xml->Description)) {
@@ -224,17 +267,18 @@ function xmlToVar($xml) {
foreach ($xml->Networking->Publish->Port as $port) {
if (empty(xml_decode($port->ContainerPort))) continue;
$portNum += 1;
$out['Config'][] = array('Name' => "Port ${portNum}",
'Target' => xml_decode($port->ContainerPort),
'Default' => xml_decode($port->HostPort),
'Value' => xml_decode($port->HostPort),
'Mode' => xml_decode($port->Protocol),
'Description' => '',
'Type' => 'Port',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false',
);
$out['Config'][] = [
'Name' => "Port ${portNum}",
'Target' => xml_decode($port->ContainerPort),
'Default' => xml_decode($port->HostPort),
'Value' => xml_decode($port->HostPort),
'Mode' => xml_decode($port->Protocol),
'Description' => '',
'Type' => 'Port',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false'
];
}
}
@@ -243,17 +287,18 @@ function xmlToVar($xml) {
foreach ($xml->Data->Volume as $vol) {
if (empty(xml_decode($vol->ContainerDir))) continue;
$volNum += 1;
$out['Config'][] = array('Name' => "Path ${volNum}",
'Target' => xml_decode($vol->ContainerDir),
'Default' => xml_decode($vol->HostDir),
'Value' => xml_decode($vol->HostDir),
'Mode' => xml_decode($vol->Mode),
'Description' => '',
'Type' => 'Path',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false',
);
$out['Config'][] = [
'Name' => "Path ${volNum}",
'Target' => xml_decode($vol->ContainerDir),
'Default' => xml_decode($vol->HostDir),
'Value' => xml_decode($vol->HostDir),
'Mode' => xml_decode($vol->Mode),
'Description' => '',
'Type' => 'Path',
'Display' => 'always',
'Required' => 'true',
'Mask' => 'false'
];
}
}
@@ -262,17 +307,18 @@ function xmlToVar($xml) {
foreach ($xml->Environment->Variable as $var) {
if (empty(xml_decode($var->Name))) continue;
$varNum += 1;
$out['Config'][] = array('Name' => "Variable ${varNum}",
'Target' => xml_decode($var->Name),
'Default' => xml_decode($var->Value),
'Value' => xml_decode($var->Value),
'Mode' => '',
'Description' => '',
'Type' => 'Variable',
'Display' => 'always',
'Required' => 'false',
'Mask' => 'false',
);
$out['Config'][] = [
'Name' => "Variable ${varNum}",
'Target' => xml_decode($var->Name),
'Default' => xml_decode($var->Value),
'Value' => xml_decode($var->Value),
'Mode' => '',
'Description' => '',
'Type' => 'Variable',
'Display' => 'always',
'Required' => 'false',
'Mask' => 'false'
];
}
}
}
@@ -283,13 +329,13 @@ function xmlToVar($xml) {
function xmlToCommand($xml) {
global $var;
$xml = xmlToVar($xml);
$cmdName = (strlen($xml['Name'])) ? '--name="' . $xml['Name'] . '"' : "";
$cmdPrivileged = (strtolower($xml['Privileged']) == 'true') ? '--privileged="true"' : "";
$cmdName = (strlen($xml['Name'])) ? '--name="'.$xml['Name'].'"' : "";
$cmdPrivileged = (strtolower($xml['Privileged']) == 'true') ? '--privileged="true"' : "";
$cmdNetwork = '--net="'.strtolower($xml['Network']).'"';
$Volumes = array('');
$Ports = array('');
$Variables = array('');
$Devices = array('');
$Volumes = [''];
$Ports = [''];
$Variables = [''];
$Devices = [''];
# Bind Time
$Variables[] = 'TZ="' . $var['timeZone'] . '"';
# Add HOST_OS variable
@@ -300,9 +346,9 @@ function xmlToCommand($xml) {
$hostConfig = strlen($config['Value']) ? $config['Value'] : $config['Default'];
$containerConfig = strval($config['Target']);
$Mode = strval($config['Mode']);
if (! strlen($containerConfig)) continue;
if (!strlen($containerConfig)) continue;
if ($confType == "path") {
$Volumes[] = sprintf( '"%s":"%s":%s', $hostConfig, $containerConfig, $Mode);
$Volumes[] = sprintf('"%s":"%s":%s', $hostConfig, $containerConfig, $Mode);
} elseif ($confType == 'port') {
$Ports[] = sprintf("%s:%s/%s", $hostConfig, $containerConfig, $Mode);
} elseif ($confType == "variable") {
@@ -311,7 +357,7 @@ function xmlToCommand($xml) {
$Devices[] = '"'.$containerConfig.'"';
}
}
$cmd = sprintf('/plugins/dynamix.docker.manager/scripts/docker run -d %s %s %s %s %s %s %s %s %s',
$cmd = sprintf('/plugins/dynamix.docker.manager/scripts/docker create %s %s %s %s %s %s %s %s %s',
$cmdName,
$cmdNetwork,
$cmdPrivileged,
@@ -323,20 +369,20 @@ function xmlToCommand($xml) {
$xml['Repository']);
$cmd = preg_replace('/\s+/', ' ', $cmd);
return array($cmd, $xml['Name'], $xml['Repository']);
return [$cmd, $xml['Name'], $xml['Repository']];
}
function getXmlVal($xml, $element, $attr=null, $pos=0) {
function getXmlVal($xml, $element, $attr = null, $pos = 0) {
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$element = $xml->xpath("//$element")[$pos];
return isset($element) ? (isset($element[$attr]) ? strval($element[$attr]) : strval($element)) : "";
}
function setXmlVal(&$xml, $value, $el, $attr=null, $pos=0) {
function setXmlVal(&$xml, $value, $el, $attr = null, $pos = 0) {
global $echo;
$xml = (is_file($xml)) ? simplexml_load_file($xml) : simplexml_load_string($xml);
$element = $xml->xpath("//$el")[$pos];
if (! isset($element)) $element = $xml->addChild($el);
if (!isset($element)) $element = $xml->addChild($el);
if ($attr) {
$element[$attr] = $value;
} else {
@@ -363,7 +409,7 @@ function setXmlVal(&$xml, $value, $el, $attr=null, $pos=0) {
if (isset($_POST['contName'])) {
$postXML = postToXML($_POST, TRUE);
$postXML = postToXML($_POST, true);
// Get the command line
list($cmd, $Name, $Repository) = xmlToCommand($postXML);
@@ -382,7 +428,7 @@ if (isset($_POST['contName'])) {
@flush();
// Will only pull image if it's absent
if (! ImageExist($Repository)) {
if (!$DockerClient->doesImageExist($Repository)) {
// Pull image
pullImage($Name, $Repository);
}
@@ -397,27 +443,40 @@ if (isset($_POST['contName'])) {
file_put_contents($filename, $postXML);
}
$startContainer = true;
// Remove existing container
if (ContainerExist($Name)) {
if ($DockerClient->doesContainerExist($Name)) {
// attempt graceful stop of container first
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker stop $Name";
include($dockerManPaths['plugin'] . "/include/Exec.php");
$oldContainerDetails = $DockerClient->getContainerDetails($Name);
if (!empty($oldContainerDetails) && !empty($oldContainerDetails['State']) && !empty($oldContainerDetails['State']['Running'])) {
// attempt graceful stop of container first
stopContainer($Name);
}
// force kill container if still running after 10 seconds
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker rm -f $Name";
include($dockerManPaths['plugin'] . "/include/Exec.php");
removeContainer($Name);
}
// Remove old container if renamed
$existing = isset($_POST['existingContainer']) ? $_POST['existingContainer'] : FALSE;
if ($existing && ContainerExist($existing)) {
// attempt graceful stop of container first
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker stop $existing";
include($dockerManPaths['plugin'] . "/include/Exec.php");
$existing = isset($_POST['existingContainer']) ? $_POST['existingContainer'] : false;
if ($existing && $DockerClient->doesContainerExist($existing)) {
// determine if the container is still running
$oldContainerDetails = $DockerClient->getContainerDetails($existing);
if (!empty($oldContainerDetails) && !empty($oldContainerDetails['State']) && !empty($oldContainerDetails['State']['Running'])) {
// attempt graceful stop of container first
stopContainer($existing);
} else {
// old container was stopped already, ensure newly created container doesn't start up automatically
$startContainer = false;
}
// force kill container if still running after 10 seconds
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker rm -f $existing";
include($dockerManPaths['plugin'] . "/include/Exec.php");
removeContainer($existing);
}
if ($startContainer) {
$cmd = str_replace('/plugins/dynamix.docker.manager/scripts/docker create ', '/plugins/dynamix.docker.manager/scripts/docker run -d ', $cmd);
}
// Injecting the command in $_GET variable and executing.
@@ -436,10 +495,8 @@ if ($_GET['updateContainer']){
@flush();
foreach ($_GET['ct'] as $value) {
$Name = urldecode($value);
$tmpl = $DockerTemplates->getUserTemplate($Name);
if (! $tmpl){
$tmpl = $DockerTemplates->getUserTemplate(urldecode($value));
if (!$tmpl) {
echo "<script>addLog('<p>Configuration not found. Was this container created using this plugin?</p>');</script>";
@flush();
continue;
@@ -449,29 +506,34 @@ if ($_GET['updateContainer']){
list($cmd, $Name, $Repository) = xmlToCommand($tmpl);
$Registry = getXmlVal($xml, "Registry");
echo "<script>addLog('<p>Preparing to update: " . $Repository . "</p>');</script>";
@flush();
$oldContainerID = $DockerClient->getImageID($Repository);
$oldImageID = $DockerClient->getImageID($Repository);
// Pull image
pullImage($Name, $Repository);
// attempt graceful stop of container first
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker stop $Name";
include($dockerManPaths['plugin'] . "/include/Exec.php");
$oldContainerDetails = $DockerClient->getContainerDetails($Name);
// determine if the container is still running
if (!empty($oldContainerDetails) && !empty($oldContainerDetails['State']) && !empty($oldContainerDetails['State']['Running'])) {
// since container was already running, put it back it to a running state after update
$cmd = str_replace('/plugins/dynamix.docker.manager/scripts/docker create ', '/plugins/dynamix.docker.manager/scripts/docker run -d ', $cmd);
// attempt graceful stop of container first
stopContainer($Name);
}
// force kill container if still running after 10 seconds
$_GET['cmd'] = "/plugins/dynamix.docker.manager/scripts/docker rm -f $Name";
include($dockerManPaths['plugin'] . "/include/Exec.php");
removeContainer($Name);
$_GET['cmd'] = $cmd;
include($dockerManPaths['plugin'] . "/include/Exec.php");
$newContainerID = $DockerClient->getImageID($Repository);
if ( $oldContainerID and $oldContainerID != $newContainerID){
$_GET['cmd'] = sprintf("/plugins/dynamix.docker.manager/scripts/docker rmi %s", $oldContainerID);
include($dockerManPaths['plugin'] . "/include/Exec.php");
$DockerClient->flushCaches();
$newImageID = $DockerClient->getImageID($Repository);
if ($oldImageID && $oldImageID != $newImageID) {
// remove old orphan image since it's no longer used by this container
removeImage($oldImageID);
}
}
@@ -483,7 +545,7 @@ if ($_GET['updateContainer']){
## REMOVE TEMPLATE
##
if($_GET['rmTemplate']){
if ($_GET['rmTemplate']) {
unlink($_GET['rmTemplate']);
}
@@ -493,7 +555,7 @@ if($_GET['rmTemplate']){
if ($_GET['xmlTemplate']) {
list($xmlType, $xmlTemplate) = split(':', urldecode($_GET['xmlTemplate']));
if(is_file($xmlTemplate)){
if (is_file($xmlTemplate)) {
$xml = xmlToVar($xmlTemplate);
$templateName = $xml["Name"];
if ($xmlType == "default") {
@@ -519,21 +581,22 @@ if ($_GET['xmlTemplate']) {
}
}
if (!$boolFound) {
$xml['Config'][] = array('Name' => 'unRAID Share Path',
'Target' => '/unraid',
'Default' => realpath($dockercfg["DOCKER_APP_UNRAID_PATH"]),
'Value' => realpath($dockercfg["DOCKER_APP_UNRAID_PATH"]),
'Mode' => 'rw',
'Description' => '',
'Type' => 'Path',
'Display' => 'hidden',
'Required' => 'false',
'Mask' => 'false',
);
$xml['Config'][] = [
'Name' => 'unRAID Share Path',
'Target' => '/unraid',
'Default' => realpath($dockercfg["DOCKER_APP_UNRAID_PATH"]),
'Value' => realpath($dockercfg["DOCKER_APP_UNRAID_PATH"]),
'Mode' => 'rw',
'Description' => '',
'Type' => 'Path',
'Display' => 'hidden',
'Required' => 'false',
'Mask' => 'false'
];
}
}
}
$xml['Description'] = str_replace(array('[', ']'), array('<', '>'), $xml['Overview']);
$xml['Description'] = str_replace(['[', ']'], ['<', '>'], $xml['Overview']);
echo "<script>var Settings=".json_encode($xml).";</script>";
}
}
@@ -542,7 +605,7 @@ $showAdditionalInfo = '';
?>
<link type="text/css" rel="stylesheet" href="/webGui/styles/jquery.ui.css">
<link type="text/css" rel="stylesheet" href="/webGui/styles/jquery.switchbutton.css">
<link type="text/css" rel="stylesheet" href="/webGui/styles/jquery.filetree.css" >
<link type="text/css" rel="stylesheet" href="/webGui/styles/jquery.filetree.css">
<style>
body{-webkit-overflow-scrolling:touch;}
.fileTree{width:240px;height:150px;overflow:scroll;position:absolute;z-index:100;display:none;margin-bottom: 100px;}
@@ -629,34 +692,18 @@ $showAdditionalInfo = '';
$('#tab'+this_tab).bind({click:function(){$('#'+elementId).show();}});
for (var x=1; x<=last; x++) if(x != this_tab) $('#tab'+x).bind({click:function(){$('#'+elementId).hide();}});
<?endif;?>
// $('.advanced-switch').switchButton({ labels_placement: "left", on_label: 'Advanced', off_label: 'Basic', checked: $.cookie('docker-advanced-view') != 'false'});
$('.advanced-switch').switchButton({ labels_placement: "left", on_label: 'Advanced View', off_label: 'Basic View'});
$('.advanced-switch').change(function () {
var status = $(this).is(':checked');
toggleRows('advanced,.hidden', status, 'basic');
//$('.basic').toggle(!status);
//$('.advanced').toggle(status);
//$('.hidden').toggle(status);
// $.cookie('docker-advanced-view', status ? 'true' : 'false', { expires: 3650 });
});
/*
$("#app_config_tab").html("<div class='switch-wrapper'><input type='checkbox' class='hidden-switch'></div>");
// $('.hidden-switch').switchButton({ labels_placement: "left", on_label: 'Show Hidden', off_label: 'Show Hidden', checked: $.cookie('docker-hidden-view') != 'false'});
$('.hidden-switch').switchButton({ labels_placement: "left", on_label: 'Show Hidden', off_label: 'Show Hidden'});
$('.hidden-switch').change(function () {
status = $(this).is(':checked');
$('.hidden').toggle(status);
// $.cookie('docker-hidden-view', status ? 'true' : 'false', { expires: 3650 });
});
*/
});
var confNum = 0;
if ( !Array.prototype.forEach ) {
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn, scope) {
for(var i = 0, len = this.length; i < len; ++i) {
for (var i = 0, len = this.length; i < len; ++i) {
fn.call(scope, this[i], i, this);
}
};
@@ -721,7 +768,7 @@ $showAdditionalInfo = '';
}
}
function addConfigPopup(){
function addConfigPopup() {
var title = 'Add Configuration';
var popup = $( "#dialogAddConfig" );
@@ -745,7 +792,7 @@ $showAdditionalInfo = '';
hide : {effect: 'fade' , duration: 250},
buttons: {
"Add": function() {
$( this ).dialog( "close" );
$(this).dialog("close");
confNum += 1;
var Opts = Object;
var Element = this;
@@ -766,7 +813,7 @@ $showAdditionalInfo = '';
reloadTriggers();
},
Cancel: function() {
$( this ).dialog( "close" );
$(this).dialog("close");
}
}
});
@@ -776,7 +823,7 @@ $showAdditionalInfo = '';
$(".ui-button-text").css('padding','0px 5px');
}
function editConfigPopup(num){
function editConfigPopup(num) {
var title = 'Edit Configuration';
var popup = $("#dialogAddConfig");
@@ -784,10 +831,10 @@ $showAdditionalInfo = '';
popup.html($("#templatePopupConfig").html());
// Load existing config info
var config = $( "#ConfigNum" + num );
var config = $("#ConfigNum" + num);
config.find("input").each(function(){
var name = $(this).attr( "name" ).replace("conf", "").replace("[]", "");
popup.find("*[name='"+name+"']").val( $(this).val() );
var name = $(this).attr("name").replace("conf", "").replace("[]", "");
popup.find("*[name='"+name+"']").val($(this).val());
});
// Hide passwords if needed
@@ -814,7 +861,7 @@ $showAdditionalInfo = '';
hide : {effect: 'fade' , duration: 250},
buttons: {
"Save": function() {
$( this ).dialog( "close" );
$(this).dialog("close");
var Opts = Object;
var Element = this;
["Name","Target","Default","Mode","Description","Type","Display","Required","Mask","Value"].forEach(function(e){
@@ -835,7 +882,7 @@ $showAdditionalInfo = '';
reloadTriggers();
},
Cancel: function() {
$( this ).dialog( "close" );
$(this).dialog("close");
}
}
});
@@ -868,20 +915,20 @@ $showAdditionalInfo = '';
mode.html('');
var index = $(el)[0].selectedIndex;
if(index == 0){
if (index == 0) {
// Path
mode.html("<dt>Mode</dt><dd><select name='Mode'><option value='rw'>Read/Write</option><option value='ro'>Read Only</option></select></dd>");
value.bind("click",function(){openFileBrowser(this,$(this).val(),'sh',true,false);});
} else if(index == 1){
value.bind("click", function(){openFileBrowser(this,$(this).val(), 'sh', true, false);});
} else if (index == 1) {
// Port
mode.html("<dt>Mode</dt><dd><select name='Mode'><option value='tcp'>TCP</option><option value='udp'>UDP</option></select></dd>");
value.addClass("numbersOnly");
target.addClass("numbersOnly");
} else if(index == 3){
} else if (index == 3) {
// Device
targetDiv.css('display', 'none');
defaultDiv.css('display', 'none');
value.bind("click",function(){openFileBrowser(this,$(this).val(),'',true,true);});
value.bind("click", function(){openFileBrowser(this,$(this).val(), '', true, true);});
}
reloadTriggers();
}
@@ -889,7 +936,7 @@ $showAdditionalInfo = '';
function loadTemplate(el) {
var template = $(el).val();
if (template.length) {
$('#formTemplate').find( "input[name='xmlTemplate']" ).val(template);
$('#formTemplate').find("input[name='xmlTemplate']").val(template);
$('#formTemplate').submit();
}
}
@@ -902,11 +949,11 @@ $showAdditionalInfo = '';
function openFileBrowser(el, root, filter, on_folders, on_files, close_on_select) {
if (on_folders === undefined) on_folders = true;
if (on_files === undefined) on_files = true;
if (! filter && ! on_files) filter = 'HIDE_FILES_FILTER';
if (! root.trim() ) root = "/mnt/user/";
if (!filter && !on_files) filter = 'HIDE_FILES_FILTER';
if (!root.trim()) root = "/mnt/user/";
p = $(el);
// Skip is fileTree is already open
if ( p.next().hasClass('fileTree') ){return null;}
if (p.next().hasClass('fileTree')) return null;
// create a random id
var r = Math.floor((Math.random()*1000)+1);
// Add a new span and load fileTree
@@ -915,7 +962,7 @@ $showAdditionalInfo = '';
ft.fileTree({
root: root,
filter: filter,
allowBrowsing : true
allowBrowsing: true
},
function(file){if(on_files){p.val(file);if(close_on_select){ft.slideUp('fast',function (){ft.remove();});}}},
function(folder){if(on_folders){p.val(folder);if(close_on_select){$(ft).slideUp('fast',function (){$(ft).remove();});}}}
@@ -925,7 +972,7 @@ $showAdditionalInfo = '';
// close if click elsewhere
$(document).mouseup(function(e){if(!ft.is(e.target) && ft.has(e.target).length === 0){ft.slideUp('fast',function (){$(ft).remove();});}});
// close if parent changed
p.bind("keydown",function(){ft.slideUp('fast',function (){$(ft).remove();});});
p.bind("keydown", function(){ft.slideUp('fast', function (){$(ft).remove();});});
// Open fileTree
ft.slideDown('fast');
}
@@ -948,8 +995,8 @@ $showAdditionalInfo = '';
<div id="canvas" style="z-index:1;margin-top:-21px;">
<form method="POST" autocomplete="off">
<table>
<? if($xmlType == "edit"):
if (ContainerExist($templateName)): echo "<input type='hidden' name='existingContainer' value='${templateName}'>\n"; endif;
<? if ($xmlType == "edit"):
if ($DockerClient->doesContainerExist($templateName)): echo "<input type='hidden' name='existingContainer' value='${templateName}'>\n"; endif;
else:?>
<tr>
<td>Template:</td>
@@ -958,7 +1005,7 @@ $showAdditionalInfo = '';
<option value="">Select a template</option>
<?
$rmadd = '';
$all_templates = array();
$all_templates = [];
$all_templates['user'] = $DockerTemplates->getTemplates("user");
$all_templates['default'] = $DockerTemplates->getTemplates("default");
foreach ($all_templates as $key => $templates) {
@@ -985,7 +1032,7 @@ $showAdditionalInfo = '';
?>
</select>
<? if (!empty($rmadd)) {
echo "<a onclick=\"rmTemplate('" . addslashes($rmadd) . "');\" style=\"cursor:pointer;\"><img src=\"/plugins/dynamix.docker.manager/images/remove.png\" title=\"" . htmlspecialchars($rmadd) . "\" width=\"30px\"></a>";
echo "<a onclick=\"rmTemplate('".addslashes($rmadd)."');\" style=\"cursor:pointer;\"><img src=\"/plugins/dynamix.docker.manager/images/remove.png\" title=\"".htmlspecialchars($rmadd)."\" width=\"30px\"></a>";
}?>
</td>
</tr>
@@ -1134,8 +1181,6 @@ $showAdditionalInfo = '';
</td>
</tr>
</table>
<!--div id="title"><span class="left"><img src="/plugins/dynamix.docker.manager/icons/addcontainer.png" class="icon">App Configuration:</span></div>
<div style='display: inline; float: right; margin: -47px -5px;' id='app_config_tab'></div-->
<div id="configLocation"></div>
<table class="advanced">
<tr>
@@ -1149,7 +1194,7 @@ $showAdditionalInfo = '';
<td>&nbsp;</td>
<td>
<input type="submit" value="<?= ($xmlType != 'edit') ? 'Create' : 'Save' ?>">
<button class="advanced" type="submit" name="dryRun" value="true" onclick="$('*[required]').prop( 'required', null );">Dry Run</button>
<button class="advanced" type="submit" name="dryRun" value="true" onclick="$('*[required]').prop('required', null);">Dry Run</button>
<input type="button" value="Cancel" onclick="done()">
</td>
</tr>
@@ -1265,10 +1310,9 @@ $showAdditionalInfo = '';
$(".basic").toggle(!$(".advanced-switch:first").is(":checked"));
$(".advanced").toggle($(".advanced-switch:first").is(":checked"));
$(".hidden").toggle($(".advanced-switch:first").is(":checked"));
//$(".hidden").toggle($(".hidden-switch:first").is(":checked"));
$(".numbersOnly").keypress(function(e){if(e.which != 45 && e.which != 8 && e.which != 0 && (e.which < 48 || e.which > 57)){return false;}});
}
$(function(){
$(function() {
// Load container info on page load
if (typeof Settings != 'undefined') {
for (var key in Settings) {
@@ -1277,8 +1321,8 @@ $showAdditionalInfo = '';
if (target.length) {
var value = Settings[key];
if (target.attr("type") == 'checkbox') {
target.prop('checked', (value == 'true') ? true : false );
} else if ($(target).prop('nodeName') == 'DIV'){
target.prop('checked', (value == 'true'));
} else if ($(target).prop('nodeName') == 'DIV') {
target.html(value);
} else {
target.val(value);
@@ -1288,7 +1332,7 @@ $showAdditionalInfo = '';
}
// Remove empty description
if (! Settings.Description.length) {
if (!Settings.Description.length) {
$('#canvas').find('#Overview:first').hide();
}
@@ -1315,7 +1359,6 @@ $showAdditionalInfo = '';
// Add switchButton
$('.switch-on-off').each(function(){var checked = $(this).is(":checked");$(this).switchButton({labels_placement: "right", checked:checked});});
});
</script>
<?END:?>

View File

@@ -21,12 +21,12 @@ $dockerManPaths = [
'images-ram' => '/usr/local/emhttp/state/plugins/dynamix.docker.manager/images',
'images-storage' => '/boot/config/plugins/dockerMan/images',
'webui-info' => '/usr/local/emhttp/state/plugins/dynamix.docker.manager/docker.json',
'update-status' => '/var/lib/docker/unraid-update-status.json',
'update-status' => '/var/lib/docker/unraid-update-status.json'
];
#load emhttp variables if needed.
if (! isset($var)){
if (! is_file("/usr/local/emhttp/state/var.ini")) shell_exec("wget -qO /dev/null localhost:$(lsof -nPc emhttp | grep -Po 'TCP[^\d]*\K\d+')");
if (!isset($var)) {
if (!is_file("/usr/local/emhttp/state/var.ini")) shell_exec("wget -qO /dev/null localhost:$(lsof -nPc emhttp | grep -Po 'TCP[^\d]*\K\d+')");
$var = @parse_ini_file("/usr/local/emhttp/state/var.ini");
}
@@ -54,33 +54,33 @@ if (!file_exists($docker_cfgfile)) {
}
######################################
## DOCKERTEMPLATES CLASS ##
## DOCKERTEMPLATES CLASS ##
######################################
class DockerTemplates {
public $verbose = FALSE;
public $verbose = false;
private function debug($m) {
if($this->verbose) echo $m."\n";
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 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) {
public function listDir($root, $ext = null) {
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root,
RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD);
$paths = array();
$paths = [];
foreach ($iter as $path => $fileinfo) {
$fext = $fileinfo->getExtension();
if ($ext && ( $ext != $fext )) continue;
if ( $fileinfo->isFile()) $paths[] = array('path' => $path, 'prefix' => basename(dirname($path)), 'name' => $fileinfo->getBasename(".$fext"));
if ($ext && ($ext != $fext)) continue;
if ($fileinfo->isFile()) $paths[] = ['path' => $path, 'prefix' => basename(dirname($path)), 'name' => $fileinfo->getBasename(".$fext")];
}
return $paths;
}
@@ -88,90 +88,87 @@ class DockerTemplates {
public function getTemplates($type) {
global $dockerManPaths;
$tmpls = array();
$dirs = array();
if ($type == "all"){
$tmpls = [];
$dirs = [];
if ($type == "all") {
$dirs[] = $dockerManPaths['templates-user'];
$dirs[] = $dockerManPaths['templates-storage'];
} else if ($type == "user"){
} else if ($type == "user") {
$dirs[] = $dockerManPaths['templates-user'];
} else if ($type == "default"){
} else if ($type == "default") {
$dirs[] = $dockerManPaths['templates-storage'];
} else {
$dirs[] = $type;
}
foreach ($dirs as $dir) {
if (! is_dir( $dir)) @mkdir( $dir, 0770, true);
if (!is_dir($dir)) @mkdir($dir, 0770, true);
$tmpls = array_merge($tmpls, $this->listDir($dir, "xml"));
}
return $tmpls;
}
private function removeDir($path){
if (is_dir($path) === true) {
$files = array_diff(scandir($path), array('.', '..'));
private function removeDir($path) {
if (is_dir($path)) {
$files = array_diff(scandir($path), ['.', '..']);
foreach ($files as $file) {
$this->removeDir(realpath($path) . '/' . $file);
}
return rmdir($path);
} else if (is_file($path) === true) {
} else if (is_file($path)) {
return unlink($path);
}
return false;
}
public function downloadTemplates($Dest=NULL, $Urls=NULL){
public function downloadTemplates($Dest = null, $Urls = null) {
global $dockerManPaths;
$Dest = ($Dest) ? $Dest : $dockerManPaths['templates-storage'];
$Urls = ($Urls) ? $Urls : $dockerManPaths['template-repos'];
$repotemplates = array();
$repotemplates = [];
$output = "";
$tmp_dir = "/tmp/tmp-".mt_rand();
if (!file_exists($dockerManPaths['template-repos'])) {
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");
}
$urls = @file($Urls, FILE_IGNORE_NEW_LINES);
if ( ! is_array($urls)) return false;
if (!is_array($urls)) return false;
$this->debug("\nURLs:\n " . implode("\n ", $urls));
$api_regexes = [
'%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)/(.*)$%i',
'%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)$%i',
'%/.*github.com/([^/]*)/(.*).git%i',
'%/.*github.com/([^/]*)/(.*)%i'
];
foreach ($urls as $url) {
$api_regexes = array(
0 => '%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)/(.*)$%i',
1 => '%/.*github.com/([^/]*)/([^/]*)/tree/([^/]*)$%i',
2 => '%/.*github.com/([^/]*)/(.*).git%i',
3 => '%/.*github.com/([^/]*)/(.*)%i',
);
for ($i=0; $i < count($api_regexes); $i++) {
if ( preg_match($api_regexes[$i], $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] : "";
foreach ($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']);
break;
}
}
if ( $this->download_url($github_api['url'], "$tmp_dir.tar.gz") === FALSE) {
$this->debug("\n Download ". $github_api['url'] ." has failed.");
return NULL;
if ($this->download_url($github_api['url'], "$tmp_dir.tar.gz") === false) {
$this->debug("\n Download ".$github_api['url']." has failed.");
return null;
} else {
@mkdir($tmp_dir, 0777, TRUE);
@mkdir($tmp_dir, 0777, true);
shell_exec("tar -zxf $tmp_dir.tar.gz --strip=1 -C $tmp_dir/ 2>&1");
unlink("$tmp_dir.tar.gz");
}
$tmplsStor = array();
$templates = $this->getTemplates($tmp_dir);
$this->debug("\n Templates found in ". $github_api['url']);
foreach ($templates as $template) {
$storPath = sprintf("%s/%s", $Dest, str_replace($tmp_dir."/", "", $template['path']) );
$tmplsStor = [];
$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']));
$tmplsStor[] = $storPath;
if (! is_dir( dirname( $storPath ))) @mkdir( dirname( $storPath ), 0777, true);
if ( is_file($storPath) ){
if ( sha1_file( $template['path'] ) === sha1_file( $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']);
continue;
} else {
@@ -193,7 +190,7 @@ class DockerTemplates {
unlink($arrLocalTemplate['path']);
$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'])), array('.', '..'));
$files = array_diff(scandir(dirname($arrLocalTemplate['path'])), ['.', '..']);
if (empty($files)) {
rmdir(dirname($arrLocalTemplate['path']));
$this->debug(" Removed: ".$arrLocalTemplate['prefix']);
@@ -204,48 +201,44 @@ class DockerTemplates {
}
public function getTemplateValue($Repository, $field, $scope = "all"){
$tmpls = $this->getTemplates($scope);
foreach ($tmpls as $file) {
public function getTemplateValue($Repository, $field, $scope = "all") {
foreach ($this->getTemplates($scope) as $file) {
$doc = new DOMDocument();
$doc->load($file['path']);
$TemplateRepository = $doc->getElementsByTagName( "Repository" )->item(0)->nodeValue;
if (! preg_match("/:[\w]*$/i", $TemplateRepository)) {
$TemplateRepository = $doc->getElementsByTagName("Repository")->item(0)->nodeValue;
if (!preg_match("/:[\w]*$/i", $TemplateRepository)) {
$Repo = preg_replace("/:[\w]*$/i", "", $Repository);
}else{
} else {
$Repo = $Repository;
}
if ( $Repo == $TemplateRepository ) {
$TemplateField = $doc->getElementsByTagName( $field )->item(0)->nodeValue;
if ($Repo == $TemplateRepository) {
$TemplateField = $doc->getElementsByTagName($field)->item(0)->nodeValue;
return trim($TemplateField);
break;
}
}
return NULL;
return null;
}
public function getUserTemplate($Container){
public function getUserTemplate($Container) {
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){
$doc->load($file['path']);
$Name = $doc->getElementsByTagName("Name")->item(0)->nodeValue;
if ($Name == $Container) {
return $file['path'];
}
}
return FALSE;
return false;
}
public function getControlURL($name){
public function getControlURL($name) {
global $var;
$DockerClient = new DockerClient();
$IP = $var["IPADDR"];
$Repository = "";
$Repository = "";
foreach ($DockerClient->getDockerContainers() as $ct) {
if ($ct['Name'] == $name) {
$Repository = preg_replace("/:[\w]*$/i", "", $ct['Image']);
@@ -257,12 +250,12 @@ class DockerTemplates {
$WebUI = $this->getTemplateValue($Repository, "WebUI");
if (preg_match("%\[IP\]%", $WebUI)) {
$WebUI = preg_replace("%\[IP\]%", $IP, $WebUI);
$WebUI = preg_replace("%\[IP\]%", $var["IPADDR"], $WebUI);
preg_match("%\[PORT:(\d+)\]%", $WebUI, $matches);
$ConfigPort = $matches[1];
if ($ct["NetworkMode"] == "bridge"){
foreach ($Ports as $key){
if ($key["PrivatePort"] == $ConfigPort){
if ($ct["NetworkMode"] == "bridge") {
foreach ($Ports as $key) {
if ($key["PrivatePort"] == $ConfigPort) {
$ConfigPort = $key["PublicPort"];
}
}
@@ -273,75 +266,73 @@ class DockerTemplates {
}
public function removeInfo($container, $image){
public function removeInfo($container, $image) {
global $dockerManPaths;
$image = ($image && count(preg_split("#[:\/]#", $image)) < 3) ? "${image}:latest" : $image;
$dockerIni = $dockerManPaths['webui-info'];
if (! is_dir( dirname( $dockerIni ))) @mkdir( dirname( $dockerIni ), 0770, true);
$info = (is_file($dockerIni)) ? json_decode(file_get_contents($dockerIni), TRUE) : array();
if (! count($info) ) $info = array();
if (!is_dir(dirname($dockerIni))) @mkdir(dirname($dockerIni), 0770, true);
$info = (is_file($dockerIni)) ? json_decode(file_get_contents($dockerIni), true) : [];
if (!count($info)) $info = [];
if (isset($info[$container])) unset($info[$container]);
file_put_contents($dockerIni, json_encode($info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
$update_file = $dockerManPaths['update-status'];
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), TRUE) : array();
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), true) : [];
if (isset($updateStatus[$image])) unset($updateStatus[$image]);
file_put_contents($update_file, json_encode($updateStatus, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
public function getAllInfo($reload = FALSE){
public function getAllInfo($reload = false) {
global $dockerManPaths;
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
$DockerUpdate->verbose = $this->verbose;
$new_info = array();
$new_info = [];
$dockerIni = $dockerManPaths['webui-info'];
if (! is_dir( dirname( $dockerIni ))) @mkdir( dirname( $dockerIni ), 0770, true);
$info = (is_file($dockerIni)) ? json_decode(file_get_contents($dockerIni), TRUE) : array();
if (! count($info) ) $info = array();
$containers = $DockerClient->getDockerContainers();
if (! count($containers) ) $containers = array();
if (!is_dir(dirname($dockerIni))) @mkdir(dirname($dockerIni), 0770, true);
$info = (is_file($dockerIni)) ? json_decode(file_get_contents($dockerIni), true) : [];
if (!count($info)) $info = [];
$autostart_file = $dockerManPaths['autostart-file'];
$allAutoStart = @file($autostart_file, FILE_IGNORE_NEW_LINES);
if ($allAutoStart===FALSE) $allAutoStart = array();
if ($allAutoStart === false) $allAutoStart = [];
foreach ($containers as $ct) {
foreach ($DockerClient->getDockerContainers() as $ct) {
$name = $ct['Name'];
$image = $ct['Image'];
$tmp = ( count($info[$name]) ) ? $info[$name] : array() ;
$tmp = (count($info[$name])) ? $info[$name] : [];
$tmp['running'] = $ct['Running'];
$tmp['autostart'] = in_array($name, $allAutoStart);
if (! $tmp['icon'] || ! $tmp['banner'] || $reload) {
$img = $this->getBannerIcon( $image );
$tmp['banner'] = ( $img['banner'] ) ? $img['banner'] : null;
$tmp['icon'] = ( $img['icon'] ) ? $img['icon'] : null;
if (!$tmp['icon'] || !$tmp['banner'] || $reload) {
$img = $this->getBannerIcon($image);
$tmp['banner'] = ($img['banner']) ? $img['banner'] : null;
$tmp['icon'] = ($img['icon']) ? $img['icon'] : null;
}
if (! $tmp['url'] || $reload) {
if (!$tmp['url'] || $reload) {
$WebUI = $this->getControlURL($name);
$tmp['url'] = ($WebUI) ? $WebUI : null;
}
$Registry = $this->getTemplateValue($image, "Registry");
$tmp['registry'] = ( $Registry ) ? $Registry : null;
$tmp['registry'] = ($Registry) ? $Registry : null;
if (! $tmp['updated'] || $reload) {
if (!$tmp['updated'] || $reload) {
if ($reload) $DockerUpdate->reloadUpdateStatus($image);
$vs = $DockerUpdate->getUpdateStatus($image);
$tmp['updated'] = ($vs === NULL) ? null : ( ($vs === TRUE) ? 'true' : 'false' );
$tmp['updated'] = ($vs === null) ? null : ($vs === true) ? 'true' : 'false';
}
if (! $tmp['template'] || $reload){
if (!$tmp['template'] || $reload) {
$tmp['template'] = $this->getUserTemplate($name);
}
$this->debug("\n$name");foreach ($tmp as $c => $d) $this->debug(sprintf(" %-10s: %s", $c, $d));
$this->debug("\n$name");
foreach ($tmp as $c => $d) $this->debug(sprintf(" %-10s: %s", $c, $d));
$new_info[$name] = $tmp;
}
file_put_contents($dockerIni, json_encode($new_info, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
@@ -349,29 +340,28 @@ class DockerTemplates {
}
public function getBannerIcon($Repository){
public function getBannerIcon($Repository) {
global $dockerManPaths;
$out = array();
$Images = array();
$out = [];
$Images = array('banner' => $this->getTemplateValue($Repository, "Banner"),
'icon' => $this->getTemplateValue($Repository, "Icon") );
$Images = [
'banner' => $this->getTemplateValue($Repository, "Banner"),
'icon' => $this->getTemplateValue($Repository, "Icon")
];
foreach ($Images as $type => $imgUrl) {
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], $type);
$storagePath = sprintf("%s/%s-%s-%s.png", $dockerManPaths[ 'images-storage' ], preg_replace('%\/|\\\%', '-', $matches[1][0]), $matches[2][0], $type);
if (! is_dir( dirname( $tempPath ))) @mkdir( dirname( $tempPath ), 0770, true);
if (! is_dir( dirname( $storagePath ))) @mkdir( dirname( $storagePath ), 0770, true);
if (! is_file( $tempPath )) {
if ( is_file( $storagePath )){
@copy($storagePath, $tempPath);
} else {
$tempPath = sprintf("%s/%s-%s-%s.png", $dockerManPaths['images-ram'], preg_replace('%\/|\\\%', '-', $matches[1][0]), $matches[2][0], $type);
$storagePath = sprintf("%s/%s-%s-%s.png", $dockerManPaths['images-storage'], preg_replace('%\/|\\\%', '-', $matches[1][0]), $matches[2][0], $type);
if (!is_dir(dirname($tempPath))) @mkdir(dirname($tempPath), 0770, true);
if (!is_dir(dirname($storagePath))) @mkdir(dirname($storagePath), 0770, true);
if (!is_file($tempPath)) {
if (!is_file($storagePath)) {
$this->download_url($imgUrl, $storagePath);
@copy($storagePath, $tempPath);
}
@copy($storagePath, $tempPath);
}
$out[ $type ] = ( is_file( $tempPath ) ) ? str_replace('/usr/local/emhttp', '', $tempPath) : "";
$out[$type] = (is_file($tempPath)) ? str_replace('/usr/local/emhttp', '', $tempPath) : "";
}
return $out;
}
@@ -385,37 +375,37 @@ class DockerUpdate{
public $verbose = false;
private function debug($m) {
if($this->verbose) echo $m."\n";
if ($this->verbose) echo $m."\n";
}
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($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){
exec("curl --max-time 30 --silent --insecure --location --fail -i ".($headers ? " -H ".escapeshellarg($headers) : "").($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) {
exec("curl --max-time 30 --silent --insecure --location --fail -i ".($headers ? " -H ".escapeshellarg($headers) : "").($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){
$apiUrl = vsprintf("http://index.docker.io/v1/repositories/%s/%s/tags/%s", preg_split("#[:\/]#", $image));
public function getRemoteVersion($image) {
$apiUrl = vsprintf("http://index.docker.io/v1/repositories/%s/%s/tags/%s", preg_split("#[:\/]#", $image));
$this->debug("API URL: $apiUrl");
$apiContent = $this->download_url($apiUrl);
return ( $apiContent === FALSE ) ? NULL : substr(json_decode($apiContent, TRUE)[0]['id'],0,8);
return ($apiContent === false) ? null : substr(json_decode($apiContent, true)[0]['id'], 0, 8);
}
public function getRemoteVersionV2($image){
public function getRemoteVersionV2($image) {
// First - get auth token:
// https://auth.docker.io/token?service=registry.docker.io&scope=repository:needo/nzbget:pull
$strAuthURL = vsprintf("https://auth.docker.io/token?service=registry.docker.io&scope=repository:%s:pull", strstr($image.':', ':', true));
$this->debug("Auth URL: $strAuthURL");
$arrAuth = json_decode($this->download_url($strAuthURL), TRUE);
$arrAuth = json_decode($this->download_url($strAuthURL), true);
if (empty($arrAuth) || empty($arrAuth['token'])) {
$this->debug("Error: Auth Token was missing/empty");
return null;
@@ -452,7 +442,7 @@ class DockerUpdate{
// DEPRECATED: Only used for Docker Index V1 type update checks
public function getLocalVersion($image){
public function getLocalVersion($image) {
$DockerClient = new DockerClient();
return substr($DockerClient->getImageID($image), 0, 8);
}
@@ -460,14 +450,14 @@ class DockerUpdate{
public function getUpdateStatus($image) {
global $dockerManPaths;
$DockerClient = new DockerClient();
$update_file = $dockerManPaths['update-status'];
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), TRUE) : array();
// Add :latest tag to image if it's absent
$image = ($image && count(preg_split("#[:\/]#", $image)) < 3) ? "${image}:latest" : $image;
if(isset($updateStatus[$image])) {
if ($updateStatus[$image]['local'] || $updateStatus[$image]['remote']) {
return ($updateStatus[$image]['local'] == $updateStatus[$image]['remote']) ? true : false;
if (is_file($dockerManPaths['update-status'])) {
$updateStatus = json_decode(file_get_contents($dockerManPaths['update-status']), true);
// Add :latest tag to image if it's absent
$image = ($image && count(preg_split("#[:\/]#", $image)) < 3) ? "${image}:latest" : $image;
if (isset($updateStatus[$image])) {
if ($updateStatus[$image]['local'] || $updateStatus[$image]['remote']) {
return ($updateStatus[$image]['local'] == $updateStatus[$image]['remote']) ? true : false;
}
}
}
return null;
@@ -478,11 +468,11 @@ class DockerUpdate{
global $dockerManPaths;
$DockerClient = new DockerClient();
$update_file = $dockerManPaths['update-status'];
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), TRUE) : array();
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), true) : [];
// Add :latest tag to image if it's absent
$image = ($image && count(preg_split("#[:\/]#", $image)) < 3) ? "${image}:latest" : $image;
$images = ($image) ? array($image) : array_map(function($ar){return $ar['Tags'][0];}, $DockerClient->getDockerImages());
$images = ($image) ? [$image] : array_map(function($ar){return $ar['Tags'][0];}, $DockerClient->getDockerImages());
foreach ($images as $img) {
$localVersion = null;
if (!empty($updateStatus[$img]) && array_key_exists('local', $updateStatus[$img])) {
@@ -491,10 +481,10 @@ class DockerUpdate{
$remoteVersion = $this->getRemoteVersionV2($img);
$status = ($localVersion && $remoteVersion) ? (($remoteVersion == $localVersion) ? "true" : "false") : "undef";
$updateStatus[$img] = [
'local' => $localVersion,
'remote' => $remoteVersion,
'status' => $status
];
'local' => $localVersion,
'remote' => $remoteVersion,
'status' => $status
];
$this->debug("Update status: Image='${img}', Local='${localVersion}', Remote='${remoteVersion}'");
}
file_put_contents($update_file, json_encode($updateStatus, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
@@ -505,12 +495,12 @@ class DockerUpdate{
global $dockerManPaths;
$DockerClient = new DockerClient();
$update_file = $dockerManPaths['update-status'];
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), TRUE) : array();
$updateStatus = (is_file($update_file)) ? json_decode(file_get_contents($update_file), true) : [];
$updateStatus[$image] = [
'local' => $version,
'remote' => $version,
'status' => 'true'
];
'local' => $version,
'remote' => $version,
'status' => 'true'
];
$this->debug("Update status: Image='${image}', Local='${version}', Remote='${version}'");
file_put_contents($update_file, json_encode($updateStatus, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
@@ -524,7 +514,6 @@ class DockerClient {
private $allContainersCache = null;
private $allImagesCache = null;
@@ -535,38 +524,39 @@ class DockerClient {
}
private function humanTiming($time){
public function humanTiming($time) {
$time = time() - $time; // to get the time since that moment
$tokens = array (31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
);
$tokens = [
31536000 => 'year',
2592000 => 'month',
604800 => 'week',
86400 => 'day',
3600 => 'hour',
60 => 'minute',
1 => 'second'
];
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';
}
}
private function formatBytes($size){
if ($size == 0){ return "0 B";}
public function formatBytes($size) {
if ($size == 0) return '0 B';
$base = log($size) / log(1024);
$suffix = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
return round(pow(1024, $base - floor($base)), 1) ." ". $suffix[floor($base)];
$suffix = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
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;
return null;
}
$protocol = ($unchunk) ? "HTTP/1.0" : "HTTP/1.1";
$out="${method} {$url} ${protocol}\r\nConnection: Close\r\n\r\n";
@@ -574,7 +564,7 @@ class DockerClient {
// Strip headers out
$headers = '';
while (($line = fgets($fp)) !== false) {
if (! is_bool(strpos($line, "HTTP/1"))) {
if (strpos($line, "HTTP/1") !== false) {
$code = vsprintf('%2$s',preg_split("#\s+#", $line));
}
$headers .= $line;
@@ -582,7 +572,7 @@ class DockerClient {
break;
}
}
$data = array();
$data = [];
while (($line = fgets($fp)) !== false) {
if (is_array($j = json_decode($line, true))) {
$data = array_merge($data, $j);
@@ -594,13 +584,27 @@ class DockerClient {
}
public function createDockerContainer() {
function doesContainerExist($container) {
foreach ($this->getDockerContainers() as $ct) {
if ($ct['Name'] == $container) {
return true;
}
}
return false;
}
public function getInfo(){
function doesImageExist($image) {
foreach ($this->getDockerImages() as $img) {
if (strpos($img['Tags'][0], $image) !== false) {
return true;
}
}
return false;
}
public function getInfo() {
$info = $this->getDockerJSON("/info");
$version = $this->getDockerJSON("/version");
return array_merge($info, $version);
@@ -612,156 +616,164 @@ class DockerClient {
}
public function getContainerDetails($id){
public function getContainerDetails($id) {
return $this->getDockerJSON("/containers/${id}/json");
}
public function startContainer($id){
public function startContainer($id) {
$this->getDockerJSON("/containers/${id}/start", "POST", $code);
$codes = array("204" => "No error",
"304" => "Container already started",
"404" => "No such container",
"500" => "Server error");
return ($code == "204") ? true : $codes[$code];
$this->allContainersCache = null; // flush cache
$codes = [
"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){
public function stopContainer($id) {
$this->getDockerJSON("/containers/${id}/stop", "POST", $code);
$codes = array("204" => "No error.",
"304" => "Container already started.",
"404" => "No such container.",
"500" => "Server error.");
return ($code == "204") ? true : $codes[$code];
$this->allContainersCache = null; // flush cache
$codes = [
"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){
$json = $this->getDockerJSON("/containers/${id}/restart", "POST", $code);
$codes = array("204" => "No error.",
"404" => "No such container.",
"500" => "Server error.");
return ($code == "204") ? true : $codes[$code];
public function restartContainer($id) {
$this->getDockerJSON("/containers/${id}/restart", "POST", $code);
$this->allContainersCache = null; // flush cache
$codes = [
"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){
$json = $this->getDockerJSON("/containers/${id}?force=1", "DELETE", $code);
$codes = array("204" => "No error.",
"400" => "Bad parameter.",
"404" => "No such container.",
"500" => "Server error.");
return ($code == "204") ? true : $codes[$code];
public function removeContainer($id) {
$this->getDockerJSON("/containers/${id}?force=1", "DELETE", $code);
$this->allContainersCache = null; // flush cache
$codes = [
"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) {
return $this->getDockerJSON("/images/create?fromImage=".urlencode($image), "POST", $code, $callback);
$ret = $this->getDockerJSON("/images/create?fromImage=".urlencode($image), "POST", $code, $callback);
$this->allImagesCache = null; // flush cache
return $ret;
}
public function removeImage($id){
$json = $this->getDockerJSON("/images/${id}?force=1", "DELETE", $code);
$codes = array("200" => "No error.",
"404" => "No such image.",
"409" => "Conflict: image used by container ".$this->usedBy($id)[0].".",
"500" => "Server error.");
return ($code == "200") ? true : $codes[$code];
public function removeImage($id) {
$this->getDockerJSON("/images/${id}?force=1", "DELETE", $code);
$this->allImagesCache = null; // flush cache
$codes = [
"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){
private function getImageDetails($id) {
return $this->getDockerJSON("/images/${id}/json");
}
public function getDockerContainers(){
public function getDockerContainers() {
// Return cached values
if (is_array($this->allContainersCache)){
if (is_array($this->allContainersCache)) {
return $this->allContainersCache;
}
$containers = array();
$json = $this->getDockerJSON("/containers/json?all=1");
if (! $json ){ return $containers; }
foreach($json as $obj){
$c = array();
$status = $obj['Status'] ? $obj['Status'] : "None";
preg_match("/\b^Up\b/", $status, $matches);
$running = $matches ? TRUE : FALSE;
$this->allContainersCache = [];
foreach ($this->getDockerJSON("/containers/json?all=1") as $obj) {
$details = $this->getContainerDetails($obj['Id']);
// echo "<pre>".print_r($obj,TRUE)."</pre>";
// Docker 1.7 uses full image ID when there aren't tags, so lets crop it
$Image = (strlen($obj['Image']) == 64) ? substr($obj['Image'],0,12) : $obj['Image'];
$Image = (strlen($obj['Image']) == 64) ? substr($obj['Image'], 0, 12) : $obj['Image'];
// Docker 1.7 doesn't automatically append the tag 'latest', so we do that now if there's no tag
$c["Image"] = ($Image && count(preg_split("#[:\/]#", $Image)) < 3) ? "${Image}:latest" : $Image;
$c["ImageId"] = substr($details["Image"],0,12);
if ($Image && count(preg_split("#[:\/]#", $Image)) < 3) {
$Image .= ':latest';
}
$c = [];
$c["Image"] = $Image;
$c["ImageId"] = substr(str_replace('sha256:', '', $details["Image"]), 0, 12);
$c["Name"] = substr($details['Name'], 1);
$c["Status"] = $status;
$c["Running"] = $running;
$c["Status"] = $obj['Status'] ? $obj['Status'] : "None";
$c["Running"] = $details["State"]["Running"];
$c["Cmd"] = $obj['Command'];
$c["Id"] = substr($obj['Id'],0,12);
$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"] = [];
$Ports = $details['HostConfig']['PortBindings'];
$Ports = (count ( $Ports )) ? $Ports : array();
$c["Ports"] = array();
if ($c["NetworkMode"] != 'host'){
foreach ($Ports as $port => $value) {
if ($c["NetworkMode"] != 'host' && !empty($details['HostConfig']['PortBindings'])) {
foreach ($details['HostConfig']['PortBindings'] as $port => $value) {
list($PrivatePort, $Type) = explode("/", $port);
$PublicPort = $value[0]['HostPort'];
$c["Ports"][] = array('PrivatePort' => $PrivatePort,
'PublicPort' => $PublicPort,
'Type' => $Type );
$c["Ports"][] = [
'IP' => empty($value[0]['HostIP']) ? '0.0.0.0' : $value[0]['HostIP'],
'PrivatePort' => $PrivatePort,
'PublicPort' => $value[0]['HostPort'],
'Type' => $Type
];
}
}
$containers[] = $c;
$this->allContainersCache[] = $c;
}
usort($containers, $this->build_sorter('Name'));
$this->allContainersCache = $containers;
return $containers;
usort($this->allContainersCache, $this->build_sorter('Name'));
return $this->allContainersCache;
}
public function getContainerID($Container){
$allContainers = $this->getDockerContainers();
foreach ($allContainers as $ct) {
public function getContainerID($Container) {
foreach ($this->getDockerContainers() as $ct) {
preg_match("%" . preg_quote($Container, "%") ."%", $ct["Name"], $matches);
if( $matches){
if ($matches) {
return $ct["Id"];
}
}
return NULL;
return null;
}
public function getImageID($Image){
$allImages = $this->getDockerImages();
foreach ($allImages as $img) {
public function getImageID($Image) {
foreach ($this->getDockerImages() as $img) {
preg_match("%" . preg_quote($Image, "%") ."%", $img["Tags"][0], $matches);
if( $matches){
if ($matches) {
return $img["Id"];
}
}
return NULL;
return null;
}
private function usedBy($imageId){
$out = array();
$Containers = $this->getDockerContainers();
$Containers = ( count( $Containers )) ? $Containers : array();
foreach ($Containers as $ct) {
if ($ct["ImageId"] == $imageId){
private function usedBy($imageId) {
$out = [];
foreach ($this->getDockerContainers() as $ct) {
if ($ct["ImageId"] == $imageId) {
$out[] = $ct["Name"];
}
}
@@ -769,37 +781,33 @@ class DockerClient {
}
public function getDockerImages(){
public function getDockerImages() {
// Return cached values
if (is_array($this->allImagesCache)){
if (is_array($this->allImagesCache)) {
return $this->allImagesCache;
}
$images = array();
$c = array();
$json = $this->getDockerJSON("/images/json?all=0");
if (! $json){ return $images; }
foreach($json as $obj){
$c = array();
$tags = array();
foreach($obj['RepoTags'] as $t){
$tags[] = htmlentities($t);
}
$c["Created"] = $this->humanTiming($obj['Created']);//date('Y-m-d H:i:s', $obj['Created']);
$c["Id"] = substr($obj['Id'],0,12);
$c["ParentId"] = substr($obj['ParentId'],0,12);
$this->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"] = $tags;
$c["Repository"] = vsprintf('%1$s/%2$s',preg_split("#[:\/]#", $tags[0]));
$c["Tags"] = array_map("htmlentities", $obj['RepoTags']);
$c["Repository"] = vsprintf('%1$s/%2$s', preg_split("#[:\/]#", $obj['RepoTags'][0]));
$c["usedBy"] = $this->usedBy($c["Id"]);
$images[$c["Id"]] = $c;
$this->allImagesCache[$c["Id"]] = $c;
}
$this->allImagesCache = $images;
return $images;
return $this->allImagesCache;
}
public function flushCaches() {
$this->allContainersCache = null;
$this->allImagesCache = null;
}
}
?>

View File

@@ -11,35 +11,51 @@
*/
?>
<?
require_once("/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php");
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
$DockerClient = new DockerClient();
$_REQUEST = array_merge($_GET, $_POST);
$action = array_key_exists('action', $_REQUEST) ? $_REQUEST['action'] : '';
$container = array_key_exists('container', $_REQUEST) ? $_REQUEST['container'] : '';
$image = array_key_exists('image', $_REQUEST) ? $_REQUEST['image'] : '';
$arrResponse = ['error' => 'Missing parameters'];
switch ($action) {
case 'start':
if ($container) echo json_encode(array('success' => $DockerClient->startContainer($container) ));
if ($container) $arrResponse = ['success' => $DockerClient->startContainer($container)];
break;
case 'stop':
if ($container) echo json_encode(array('success' => $DockerClient->stopContainer($container) ));
if ($container) $arrResponse = ['success' => $DockerClient->stopContainer($container)];
break;
case 'restart':
if ($container) echo json_encode(array('success' => $DockerClient->restartContainer($container) ));
if ($container) $arrResponse = ['success' => $DockerClient->restartContainer($container)];
break;
case 'remove_container':
if ($container) echo json_encode(array('success' => $DockerClient->removeContainer($container) ));
if ($container) $arrResponse = ['success' => $DockerClient->removeContainer($container)];
break;
case 'remove_image':
$image = array_key_exists('image', $_REQUEST) ? $_REQUEST['image'] : '';
if ($image) echo json_encode([ 'success' => $DockerClient->removeImage($image) ]);
if ($image) $arrResponse = ['success' => $DockerClient->removeImage($image)];
break;
case 'remove_all':
if ($container && $image) {
// first: try to remove container
$ret = $DockerClient->removeContainer($container);
if ($ret === true) {
// next: try to remove image
$arrResponse = ['success' => $DockerClient->removeImage($image)];
} else {
// error: container failed to remove
$arrResponse = ['success' => $ret];
}
}
break;
case 'log':
@@ -53,11 +69,22 @@ switch ($action) {
} else {
$tail = null;
}
$echo = function($s){$s=addslashes(substr(trim($s),8));echo "<script>addLog('".$s."');</script>";@flush();};
$echo = function($s) {
$s = addslashes(substr(trim($s), 8));
echo "<script>addLog('".$s."');</script>";
@flush();
};
$DockerClient->getContainerLog($container, $echo, $tail, $since);
echo "<script>setTimeout(\"loadLog('".addslashes($container)."','".time()."')\",2000);</script>";
echo '<script>setTimeout("loadLog(\''.addslashes($container).'\',\''.time().'\')", 2000);</script>';
@flush();
exit;
}
break;
default:
$arrResponse = ['error' => 'Unknown action \'' . $action . '\''];
break;
}
header('Content-Type: application/json');
die(json_encode($arrResponse));

View File

@@ -4,17 +4,17 @@ var varNum = 0;
var currentPath = "/mnt/";
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined' ? args[number] : match;
});
};
}
function rmTemplate(tmpl) {
var name = tmpl.split(/[\/]+/).pop();
swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
swal({title:"Are you sure?",text:"Remove template: "+name,type:"warning",showCancelButton:true},function(){$("#rmTemplate").val(tmpl);$("#formTemplate").submit();});
}
function toggleBrowser(N) {

View File

@@ -1,41 +1,41 @@
var eventURL = "/plugins/dynamix.docker.manager/include/Events.php";
function addDockerContainerContext(container, image, template, started, update, autostart, webui, id){
function addDockerContainerContext(container, image, template, started, update, autostart, webui, id) {
var opts = [{header: container, image: "/plugins/dynamix.docker.manager/images/dynamix.docker.manager.png"}];
if (started && (webui != "#")) {
opts.push({text: 'WebUI', icon:'fa-globe', href: webui, target: '_blank' });
opts.push({text: 'WebUI', icon: 'fa-globe', href: webui, target: '_blank'});
opts.push({divider: true});
}
if (! update){
opts.push({text: 'Update', icon:'fa-arrow-down', action: function(e){ e.preventDefault(); execUpContainer(container); }});
if (!update) {
opts.push({text: 'Update', icon: 'fa-arrow-down', action: function(e){ e.preventDefault(); execUpContainer(container); }});
opts.push({divider: true});
}
if (started){
opts.push({text: 'Stop', icon:'fa-stop', action: function(e){ e.preventDefault(); containerControl(id, 'stop', true); }});
opts.push({text: 'Restart', icon:'fa-refresh', action: function(e){ e.preventDefault(); containerControl(id, 'restart', true); }});
if (started) {
opts.push({text: 'Stop', icon: 'fa-stop', action: function(e){ e.preventDefault(); eventControl({action: "stop", container: id}); }});
opts.push({text: 'Restart', icon: 'fa-refresh', action: function(e){ e.preventDefault(); eventControl({action: "restart", container: id}); }});
} else {
opts.push({text: 'Start', icon:'fa-play', action: function(e){ e.preventDefault(); containerControl(id, 'start', true); }});
opts.push({text: 'Start', icon: 'fa-play', action: function(e){ e.preventDefault(); eventControl({action: "start", container: id}); }});
}
opts.push({divider: true});
if (location.pathname.indexOf("/Dashboard") === 0) {
opts.push({text: 'Logs', icon:'fa-navicon', action: function(e){ e.preventDefault(); containerLogs(container, id); }});
opts.push({text: 'Logs', icon: 'fa-navicon', action: function(e){ e.preventDefault(); containerLogs(container, id); }});
}
if (template) {
opts.push({text: 'Edit', icon:'fa-wrench', action: function(e){ e.preventDefault(); editContainer(container, template); }});
opts.push({text: 'Edit', icon: 'fa-wrench', action: function(e){ e.preventDefault(); editContainer(container, template); }});
}
opts.push({divider: true});
opts.push({text: 'Remove', icon:'fa-trash', action: function(e){ e.preventDefault(); rmContainer(container, image, id); }});
opts.push({text: 'Remove', icon: 'fa-trash', action: function(e){ e.preventDefault(); rmContainer(container, image, id); }});
context.attach('#context-'+container, opts);
}
function addDockerImageContext(image, imageTag){
function addDockerImageContext(image, imageTag) {
var opts = [{header: '(orphan image)'}];
opts.push({text: 'Remove', icon:'fa-trash', action: function(e){ e.preventDefault(); rmImage(image, imageTag); }});
opts.push({text: "Remove", icon: "fa-trash", action: function(e){ e.preventDefault(); rmImage(image, imageTag); }});
context.attach('#context-'+image, opts);
}
function execUpContainer(container){
var title = 'Updating the container: ' + container;
function execUpContainer(container) {
var title = "Updating the container: " + container;
var address = "/plugins/dynamix.docker.manager/include/CreateDocker.php?updateContainer=true&ct[]=" + encodeURIComponent(container);
popupWithIframe(title, address, true);
}
@@ -43,169 +43,116 @@ function execUpContainer(container){
function popupWithIframe(title, cmd, reload) {
pauseEvents();
$( "#iframe-popup" ).html('<iframe id="myIframe" frameborder="0" scrolling="yes" width="100%" height="99%"></iframe>');
$("#iframe-popup").html('<iframe id="myIframe" frameborder="0" scrolling="yes" width="100%" height="99%"></iframe>');
$("#iframe-popup").dialog({
autoOpen: true,
title: title,
draggable: true,
width : 800,
height : ((screen.height/5)*4)||0,
resizable : true,
modal : true,
show : {effect: 'fade' , duration: 250},
hide : {effect: 'fade' , duration: 250},
open: function(ev, ui){
$('#myIframe').attr('src', cmd);
width: 800,
height: ((screen.height / 5) * 4) || 0,
resizable: true,
modal: true,
show: {effect: "fade", duration: 250},
hide: {effect: "fade", duration: 250},
open: function(ev, ui) {
$("#myIframe").attr("src", cmd);
},
close: function( event, ui ) {
if (reload && !$('#myIframe').contents().find('#canvas').length){
close: function(event, ui) {
if (reload && !$("#myIframe").contents().find("#canvas").length) {
location = window.location.href;
} else {
resumeEvents();
}
}
});
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
$(".ui-dialog .ui-dialog-content").css('padding','0');
$(".ui-dialog .ui-dialog-title").css('text-align','center');
$(".ui-dialog .ui-dialog-title").css('width', "100%");
$(".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"); });
}
function addContainer() {
var path = location.pathname;
var x = path.indexOf("?");
if (x!=-1) path = path.substring(0,x);
if (x!=-1) path = path.substring(0, x);
location = path + '/AddContainer';
location = path + "/AddContainer";
}
function editContainer(container, template) {
var path = location.pathname;
var x = path.indexOf("?");
if (x!=-1) path = path.substring(0,x);
if (x!=-1) path = path.substring(0, x);
location = path + '/UpdateContainer?xmlTemplate=edit:' + template;
location = path + "/UpdateContainer?xmlTemplate=edit:" + template;
}
function rmContainer(container, image, id){
var title = 'Removing container: '+ container;
$( "#dialog-confirm" ).html('');
$( "#dialog-confirm" ).append( "<br><span style='color: #E80000;'>Are you sure?</span>" );
$( "#dialog-confirm" ).dialog({
title: title,
resizable: false,
width: 500,
modal: true,
show : {effect: 'fade' , duration: 250},
hide : {effect: 'fade' , duration: 250},
buttons: {
"Just the container": function() {
$( this ).dialog( "close" );
containerControl(id, 'remove_container', true);
},
"Container and image": function() {
$( this ).dialog( "close" );
containerControl(id, 'remove_container', false);
imageControl(image, "remove_image", true);
},
Cancel: function() {
$( this ).dialog( "close" );
$( this ).html("");
}
function updateContainer(container) {
var body = "Update container: "+container;
swal({
title: "Are you sure?",
text: body,
type: "warning",
showCancelButton: true,
confirmButtonColor: "#8CD4F5",
confirmButtonText: "Yes, update it!"
}, 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>";
swal({
title: "Are you sure?",
text: body,
type: "warning",
html: true,
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, delete it!",
closeOnConfirm: false,
showLoaderOnConfirm: true
}, function() {
if ($("#removeimagechk").prop('checked')) {
eventControl({action: "remove_all", container: id, image: image});
} else {
eventControl({action: "remove_image", image: image});
}
});
$(".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-top','15px').css('font-weight','bold');
$(".ui-button-text").css('padding','0px 5px');
}
function updateContainer(container){
var ctCmd = "&ct[]=" + encodeURIComponent(container);
var ctTitle = "";
function rmImage(image, imageName) {
var body = "Remove image: "+$('<textarea />').html(imageName).text();
var title = 'Updating container: '+container;
$( "#dialog-confirm" ).html('');
$( "#dialog-confirm" ).append( "<br><span style='color: #E80000;'>Are you sure?</span>" );
$( "#dialog-confirm" ).dialog({
title: title,
resizable: false,
width: 500,
modal: true,
show : {effect: 'fade' , duration: 250},
hide : {effect: 'fade' , duration: 250},
buttons: {
"Just do it!": function() {
$( this ).dialog( "close" );
var cmd = "/plugins/dynamix.docker.manager/include/CreateDocker.php?updateContainer=true" + ctCmd;
popupWithIframe(title, cmd, true);
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
swal({
title: "Are you sure?",
text: body,
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, delete it!",
closeOnConfirm: false,
showLoaderOnConfirm: true
}, function() {
eventControl({action: "remove_image", image: image});
});
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
$(".ui-dialog .ui-dialog-title").css('text-align','center');
$(".ui-dialog .ui-dialog-content").css('padding-top','15px');
$(".ui-dialog .ui-dialog-content").css('font-weight','bold');
$(".ui-button-text").css('padding','0px 5px');
$( ".ui-dialog .ui-dialog-title" ).css( 'width', "100%");
}
function rmImage(image, imageName){
var imageName = $('<textarea />').html(imageName).text();
var title = "Removing image: "+imageName;
$( "#dialog-confirm" ).html('');
$( "#dialog-confirm" ).append( "<br><span style='color: #E80000;'>Are you sure?</span>" );
$( "#dialog-confirm" ).dialog({
title: title,
dialogClass: "alert",
resizable: false,
width: 500,
modal: true,
show : {effect: 'fade' , duration: 250},
hide : {effect: 'fade' , duration: 250},
buttons: {
"Just do it!": function() {
$( this ).dialog( "close" );
imageControl(image, "remove_image", true);
},
Cancel: function() {
$( this ).dialog( "close" );
}
}
});
$(".ui-dialog .ui-dialog-titlebar").addClass('menu');
$(".ui-dialog .ui-dialog-title").css('text-align','center');
$(".ui-dialog .ui-dialog-content").css('padding-top','15px');
$(".ui-dialog .ui-dialog-content").css('font-weight','bold');
$(".ui-button-text").css('padding','0px 5px');
$( ".ui-dialog .ui-dialog-title" ).css( 'width', "100%");
}
function imageControl(image, action, reload){
if (typeof reload == undefined) reload = true
$.post(eventURL,{action:action, image:image},function(data){
if(data.success === true) {
function eventControl(params, reload) {
if (typeof reload == "undefined") reload = true;
$.post(eventURL, params, function(data) {
if (data.success === true) {
if (reload) location.reload();
} else {
alert("Error:\n\n"+data.success);
}},"json");
swal({title: "Execution error", text: data.success, type: "error"});
}
}, "json");
}
function containerControl(container, action, reload){
if (typeof reload == undefined) reload = true
$.post(eventURL,{action:action, container:container},function(data){
if(data.success === true) {
if (reload) location.reload();
} else {
alert("Error:\n\n"+data.success);
}},"json");
}
function reloadUpdate(){
function reloadUpdate() {
$(".updatecolumn").html("<span style=\"color:#267CA8;white-space:nowrap;\"><i class=\"fa fa-spin fa-refresh\"></i> checking...</span>");
$("#cmdStartStop").val("/plugins/dynamix.docker.manager/scripts/dockerupdate.php");
$("#cmdArg1").remove();
@@ -213,17 +160,17 @@ function reloadUpdate(){
$("#formStartStop").submit();
}
function autoStart(container, event){
function autoStart(container, event) {
document.getElementsByName("container")[0].value = container;
$("#formStartStop").submit();
}
function containerLogs(container, id){
function containerLogs(container, id) {
var height = 600;
var width = 900;
var run = eventURL+'?action=log&container='+id+'&title=Log for: '+container;
var top = (screen.height-height)/2;
var left = (screen.width-width)/2;
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;
window.open(run, 'log', options);
}

View File

@@ -12,10 +12,10 @@
*/
?>
<?
exec("pgrep docker",$pid);
if (count($pid)==1) exit(0);
exec("pgrep docker", $pid);
if (count($pid) == 1) exit(0);
require_once("/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php");
require_once '/usr/local/emhttp/plugins/dynamix.docker.manager/include/DockerClient.php';
$DockerClient = new DockerClient();
$DockerTemplates = new DockerTemplates();
@@ -32,15 +32,14 @@ if (!isset($check)) {
$DockerTemplates->getAllInfo(true);
echo " Done.";
} else {
require_once("/usr/local/emhttp/webGui/include/Wrappers.php");
require_once '/usr/local/emhttp/webGui/include/Wrappers.php';
$notify = "/usr/local/emhttp/webGui/scripts/notify";
$unraid = parse_plugin_cfg("dynamix",true);
$server = strtoupper($var['NAME']);
$output = $unraid['notify']['docker_notify'];
$list = $DockerClient->getDockerContainers();
$info = $DockerTemplates->getAllInfo(true);
foreach ($list as $ct) {
foreach ($DockerClient->getDockerContainers() as $ct) {
$name = $ct['Name'];
$image = $ct['Image'];
if ($info[$name]['updated'] == "false") {