mirror of
https://github.com/unraid/webgui.git
synced 2026-03-07 10:30:37 -06:00
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:
@@ -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>
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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> </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:?>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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));
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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") {
|
||||
|
||||
Reference in New Issue
Block a user