diff --git a/plugins/dynamix.docker.manager/include/CreateDocker.php b/plugins/dynamix.docker.manager/include/CreateDocker.php index 3aa1b8bff..7c3efb862 100644 --- a/plugins/dynamix.docker.manager/include/CreateDocker.php +++ b/plugins/dynamix.docker.manager/include/CreateDocker.php @@ -1,7 +1,7 @@ Icon != $_POST['contIcon']) { - if (! strpos($Repository,":")) { + if (!strpos($Repository,":")) { $Repository .= ":latest"; } $iconPath = $DockerTemplates->getIcon($Repository); @@ -176,12 +175,12 @@ if ($_GET['updateContainer']){ // determine if the container is still running if (!empty($oldContainerInfo) && !empty($oldContainerInfo['State']) && !empty($oldContainerInfo['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); + $cmd = str_replace('/docker create ', '/docker run -d ', $cmd); // attempt graceful stop of container first stopContainer($Name); } // force kill container if still running after 10 seconds - if ( ! $_GET['communityApplications'] ) { + if (!$_GET['communityApplications']) { removeContainer($Name); } execCommand($cmd); @@ -399,7 +398,7 @@ button[type=button]{margin:0 20px 0 0} function getVal(el, name) { var el = $(el).find("*[name="+name+"]"); if (el.length) { - return ( $(el).attr('type') == 'checkbox' ) ? ($(el).is(':checked') ? "on" : "off") : $(el).val(); + return ($(el).attr('type') == 'checkbox') ? ($(el).is(':checked') ? "on" : "off") : $(el).val(); } else { return ""; } @@ -407,7 +406,7 @@ button[type=button]{margin:0 20px 0 0} function addConfigPopup() { var title = 'Add Configuration'; - var popup = $( "#dialogAddConfig" ); + var popup = $("#dialogAddConfig"); // Load popup the popup with the template info popup.html($("#templatePopupConfig").html()); @@ -436,10 +435,10 @@ button[type=button]{margin:0 20px 0 0} ["Name","Target","Default","Mode","Description","Type","Display","Required","Mask","Value"].forEach(function(e){ Opts[e] = getVal(Element, e); }); - if (! Opts.Name ){ + if (!Opts.Name){ Opts.Name = makeName(Opts.Type); } - if (! Opts.Description ) { + if (!Opts.Description) { Opts.Description = "Container "+Opts.Type+": "+Opts.Target; } if (Opts.Required == "true") { @@ -461,7 +460,7 @@ button[type=button]{margin:0 20px 0 0} } }); $(".ui-dialog .ui-dialog-titlebar").addClass('menu'); - $(".ui-dialog .ui-dialog-title").css('text-align','center').css( 'width', "100%"); + $(".ui-dialog .ui-dialog-title").css('text-align','center').css('width', "100%"); $(".ui-dialog .ui-dialog-content").css('padding-top','15px').css('vertical-align','bottom'); $(".ui-button-text").css('padding','0px 5px'); } @@ -517,10 +516,10 @@ button[type=button]{margin:0 20px 0 0} Opts.Buttons = ""; Opts.Buttons += ""; } - if (! Opts.Name ){ + if (!Opts.Name){ Opts.Name = makeName(Opts.Type); } - if (! Opts.Description ) { + if (!Opts.Description) { Opts.Description = "Container "+Opts.Type+": "+Opts.Target; } Opts.Number = num; @@ -545,7 +544,7 @@ button[type=button]{margin:0 20px 0 0} } }); $(".ui-dialog .ui-dialog-titlebar").addClass('menu'); - $(".ui-dialog .ui-dialog-title").css('text-align','center').css( 'width', "100%"); + $(".ui-dialog .ui-dialog-title").css('text-align','center').css('width', "100%"); $(".ui-dialog .ui-dialog-content").css('padding-top','15px').css('vertical-align','bottom'); $(".ui-button-text").css('padding','0px 5px'); $('.desc_readmore').readmore({maxHeight:10}); @@ -668,8 +667,7 @@ button[type=button]{margin:0 20px 0 0} allowBrowsing: true }, function(file){if(on_files){p.val(file);p.trigger('change');if(close_on_select){ft.slideUp('fast',function(){ft.remove();});}}}, - function(folder){if(on_folders){p.val(folder);p.trigger('change');if(close_on_select){$(ft).slideUp('fast',function (){$(ft).remove();});}}} - ); + function(folder){if(on_folders){p.val(folder);p.trigger('change');if(close_on_select){$(ft).slideUp('fast',function (){$(ft).remove();});}}}); // Format fileTree according to parent position, height and width ft.css({'left':p.position().left,'top':(p.position().top+p.outerHeight()),'width':(p.width())}); // close if click elsewhere @@ -1181,7 +1179,7 @@ button[type=button]{margin:0 20px 0 0} } function toggleReadmore() { var readm = $('#readmore_toggle'); - if ( readm.hasClass('readmore_collapsed') ) { + if (readm.hasClass('readmore_collapsed')) { readm.removeClass('readmore_collapsed').addClass('readmore_expanded'); $('#configLocationAdvanced').slideDown('fast'); readm.find('a').html(' Hide more settings ...'); @@ -1193,7 +1191,7 @@ button[type=button]{margin:0 20px 0 0} } function toggleAllocations() { var readm = $('#allocations_toggle'); - if ( readm.hasClass('readmore_collapsed') ) { + if (readm.hasClass('readmore_collapsed')) { readm.removeClass('readmore_collapsed').addClass('readmore_expanded'); $('#dockerAllocations').slideDown('fast'); readm.find('a').html(' Hide docker allocations ...'); @@ -1272,7 +1270,7 @@ button[type=button]{margin:0 20px 0 0} echo "$('.advanced-switch').siblings('.switch-button-background').click();"; }?> }); - if ( window.location.href.indexOf("/Apps/") > 0 ) { + if (window.location.href.indexOf("/Apps/") > 0) { $(".TemplateDropDown").hide(); } diff --git a/plugins/dynamix.docker.manager/include/DockerClient.php b/plugins/dynamix.docker.manager/include/DockerClient.php index d6dea8b07..f33fef688 100644 --- a/plugins/dynamix.docker.manager/include/DockerClient.php +++ b/plugins/dynamix.docker.manager/include/DockerClient.php @@ -1,7 +1,7 @@ download_url($imgUrl, $icon); @copy($icon, $iconRAM); } - if ( !is_file($icon) && is_file($iconRAM) ) { + if (!is_file($icon) && is_file($iconRAM)) { @copy($iconRAM,$icon); } return (is_file($iconRAM)) ? str_replace($docroot, '', $iconRAM) : ''; @@ -368,21 +368,21 @@ class DockerUpdate{ * Step 1: Check whether or not the image is in a private registry, get corresponding auth data and generate manifest url */ $DockerClient = new DockerClient(); - $registryAuth = $DockerClient->getRegistryAuth( $image ); - if ( $registryAuth ) { - $manifestURL = sprintf( '%s%s/manifests/%s', $registryAuth['apiUrl'], $registryAuth['imageName'], $registryAuth['imageTag'] ); + $registryAuth = $DockerClient->getRegistryAuth($image); + if ($registryAuth) { + $manifestURL = sprintf('%s%s/manifests/%s', $registryAuth['apiUrl'], $registryAuth['imageName'], $registryAuth['imageTag']); } else { - $manifestURL = sprintf( 'https://registry-1.docker.io/v2/%s/manifests/%s', $strRepo, $strTag ); + $manifestURL = sprintf('https://registry-1.docker.io/v2/%s/manifests/%s', $strRepo, $strTag); } - //$this->debug('Manifest URL: ' . $manifestURL); + //$this->debug('Manifest URL: '.$manifestURL); /* * Step 2: Get www-authenticate header from manifest url to generate token url */ $ch = getCurlHandle($manifestURL, 'HEAD'); - $response = curl_exec( $ch ); + $response = curl_exec($ch); if (curl_errno($ch) !== 0) { - //$this->debug('Error: curl error getting manifest: ' . curl_error($ch)); + //$this->debug('Error: curl error getting manifest: '.curl_error($ch)); return null; } @@ -402,19 +402,19 @@ class DockerUpdate{ if (empty($args['realm']) || empty($args['service']) || empty($args['scope'])) { return null; } - $url = $args['realm'] . '?service=' . urlencode($args['service']) . '&scope=' . urlencode($args['scope']); - //$this->debug('Token URL: ' . $url); + $url = $args['realm'].'?service='.urlencode($args['service']).'&scope='.urlencode($args['scope']); + //$this->debug('Token URL: '.$url); /** * Step 3: Get token from API and authenticate via username / password if in private registry and auth data was found */ $ch = getCurlHandle($url); if ($registryAuth) { - curl_setopt( $ch, CURLOPT_USERPWD, $registryAuth['username'] . ':' . $registryAuth['password'] ); + curl_setopt($ch, CURLOPT_USERPWD, $registryAuth['username'].':'.$registryAuth['password']); } - $response = curl_exec( $ch ); + $response = curl_exec($ch); if (curl_errno($ch) !== 0) { - //$this->debug('Error: curl error getting token: ' . curl_error($ch)); + //$this->debug('Error: curl error getting token: '.curl_error($ch)); return null; } $response = json_decode($response, true); @@ -428,14 +428,14 @@ class DockerUpdate{ * Step 4: Get Docker-Content-Digest header from manifest file */ $ch = getCurlHandle($manifestURL, 'HEAD'); - curl_setopt( $ch, CURLOPT_HTTPHEADER, [ + curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json', - 'Authorization: Bearer ' . $token + 'Authorization: Bearer '.$token ]); - $response = curl_exec( $ch ); + $response = curl_exec($ch); if (curl_errno($ch) !== 0) { - //$this->debug('Error: curl error getting manifest: ' . curl_error($ch)); + //$this->debug('Error: curl error getting manifest: '.curl_error($ch)); return null; } preg_match('@Docker-Content-Digest:\s*(.*)@', $response, $matches); @@ -444,7 +444,7 @@ class DockerUpdate{ return null; } $digest = trim($matches[1]); - //$this->debug('Remote Digest: ' . $digest); + //$this->debug('Remote Digest: '.$digest); return $digest; } @@ -486,19 +486,19 @@ class DockerUpdate{ DockerUtil::saveJSON($dockerManPaths['update-status'], $updateStatus); } - public function inspectLocalVersion( $image ) { + public function inspectLocalVersion($image) { $DockerClient = new DockerClient(); - $inspect = $DockerClient->getDockerJSON( '/images/' . $image . '/json' ); - if ( empty( $inspect['RepoDigests'] ) ) { + $inspect = $DockerClient->getDockerJSON('/images/'.$image.'/json'); + if (empty($inspect['RepoDigests'])) { return null; } - $shaPos = strpos( $inspect['RepoDigests'][0], '@sha256:' ); - if ( $shaPos === false ) { + $shaPos = strpos($inspect['RepoDigests'][0], '@sha256:'); + if ($shaPos === false) { return null; } - return substr( $inspect['RepoDigests'][0], $shaPos + 1 ); + return substr($inspect['RepoDigests'][0], $shaPos + 1); } public function setUpdateStatus($image, $version) { @@ -563,7 +563,7 @@ class DockerUpdate{ $local_element = $template->xpath("//Config[@Type='$type'][@Target='$target']")[0]; } // If the local template already have the pertinent Config element, loop through it's attributes and update those on validAttributes - if (! empty($local_element)) { + if (!empty($local_element)) { foreach ($remote_element->attributes() as $key => $value) { $rvalue = $this->xml_decode($value); $value = $this->xml_decode($local_element[$key]); @@ -763,13 +763,13 @@ class DockerClient { public function pullImage($image, $callback=null) { $header = null; - $registryAuth = $this->getRegistryAuth( $image ); - if ( $registryAuth ) { - $header = 'X-Registry-Auth: ' . base64_encode( json_encode( [ + $registryAuth = $this->getRegistryAuth($image); + if ($registryAuth) { + $header = 'X-Registry-Auth: '.base64_encode(json_encode([ 'username' => $registryAuth['username'], 'password' => $registryAuth['password'], 'serveraddress' => $registryAuth['apiUrl'], - ] ) ) . "\r\n"; + ]))."\r\n"; } $ret = $this->getDockerJSON("/images/create?fromImage=".urlencode($image), 'POST', $code, $callback, false, $header); @@ -789,7 +789,7 @@ class DockerClient { return false; } $dockerConfig = json_decode(file_get_contents($dockerConfig), true); - if ( empty( $dockerConfig['auths'] ) || empty( $dockerConfig['auths'][ $matches[1] ] ) ) { + if (empty($dockerConfig['auths']) || empty($dockerConfig['auths'][ $matches[1] ])) { return false; } list($user, $password) = explode(':', base64_decode($dockerConfig['auths'][ $matches[1] ]['auth'])); @@ -800,7 +800,7 @@ class DockerClient { 'registryName' => $matches[1], 'imageName' => $matches[2], 'imageTag' => $matches[3], - 'apiUrl' => 'https://' . $matches[1] . '/v2/', + 'apiUrl' => 'https://'.$matches[1].'/v2/', ]; } @@ -841,9 +841,11 @@ class DockerClient { $c['Volumes'] = $info['HostConfig']['Binds']; $c['Created'] = $this->humanTiming($ct['Created']); $c['NetworkMode'] = $ct['HostConfig']['NetworkMode']; + [$net, $id] = explode(':',$c['NetworkMode']); $c['CPUset'] = $info['HostConfig']['CpusetCpus']; $c['BaseImage'] = $ct['Labels']['BASEIMAGE'] ?? false; $c['Ports'] = []; + if ($id) $c['NetworkMode'] = $net.str_replace('/',':',DockerUtil::docker("inspect --format='{{.Name}}' $id")?:'/???'); if ($driver[$c['NetworkMode']]=='bridge') { $ports = &$info['HostConfig']['PortBindings']; $nat = true; @@ -949,7 +951,7 @@ class DockerUtil { public static function driver() { $list = []; - foreach (static::docker("network ls --format='{{.Name}}={{.Driver}}'",true) as $network) {list($name,$driver) = explode('=',$network); $list[$name] = $driver;} + foreach (static::docker("network ls --format='{{.Name}}={{.Driver}}'",true) as $network) {list($net,$driver) = explode('=',$network); $list[$net] = $driver;} return $list; } @@ -957,10 +959,15 @@ class DockerUtil { return static::docker("network ls --filter driver='bridge' --filter driver='macvlan' --format='{{.Name}}'|grep -v '^bridge$'",true); } - public static function network($more) { + public static function network($custom) { $list = ['bridge'=>'', 'host'=>'', 'none'=>'']; - foreach ($more as $net) $list[$net] = substr(static::docker("network inspect --format='{{range .IPAM.Config}}{{.Subnet}}, {{end}}' $net"),0,-1); + foreach ($custom as $net) $list[$net] = implode(', ',array_filter(static::docker("network inspect --format='{{range .IPAM.Config}}{{println .Subnet}}{{end}}' $net",true))); return $list; } + + public static function cpus() { + exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus); + return $cpus; + } } ?> diff --git a/plugins/dynamix.docker.manager/include/DockerContainers.php b/plugins/dynamix.docker.manager/include/DockerContainers.php index 3741416b6..ad06dbbf2 100644 --- a/plugins/dynamix.docker.manager/include/DockerContainers.php +++ b/plugins/dynamix.docker.manager/include/DockerContainers.php @@ -1,7 +1,7 @@ ",$tempElement); - if ( preg_match('#(.*?)#is',$tempElement) || preg_match('#(.*?)#is',$tempElement) ) { + if (preg_match('#(.*?)#is',$tempElement) || preg_match('#(.*?)#is',$tempElement)) { $element = "REMOVED"; } } @@ -258,7 +258,7 @@ function xmlToCommand($xml, $create_paths=false) { $xml = xmlToVar($xml); $cmdName = strlen($xml['Name']) ? '--name='.escapeshellarg($xml['Name']) : ''; $cmdPrivileged = strtolower($xml['Privileged'])=='true' ? '--privileged=true' : ''; - $cmdNetwork = '--net='.escapeshellarg(strtolower($xml['Network'])); + $cmdNetwork = preg_match('/\-\-net(work)?=/',$xml['ExtraParams']) ? "" : '--net='.escapeshellarg(strtolower($xml['Network'])); $cmdMyIP = ''; foreach (explode(' ',str_replace(',',' ',$xml['MyIP'])) as $myIP) if ($myIP) $cmdMyIP .= (strpos($myIP,':')?'--ip6=':'--ip=').escapeshellarg($myIP).' '; $cmdCPUset = strlen($xml['CPUset']) ? '--cpuset-cpus='.escapeshellarg($xml['CPUset']) : ''; @@ -268,7 +268,7 @@ function xmlToCommand($xml, $create_paths=false) { $Labels = ['']; $Devices = ['']; // Bind Time - $Variables[] = 'TZ="' . $var['timeZone'] . '"'; + $Variables[] = 'TZ="'.$var['timeZone'].'"'; // Add HOST_OS variable $Variables[] = 'HOST_OS="Unraid"'; @@ -280,7 +280,7 @@ function xmlToCommand($xml, $create_paths=false) { if ($confType != "device" && !strlen($containerConfig)) continue; if ($confType == "path") { $Volumes[] = escapeshellarg($hostConfig).':'.escapeshellarg($containerConfig).':'.escapeshellarg($Mode); - if ( ! file_exists($hostConfig) && $create_paths ) { + if (!file_exists($hostConfig) && $create_paths) { @mkdir($hostConfig, 0777, true); @chown($hostConfig, 99); @chgrp($hostConfig, 100); @@ -439,7 +439,7 @@ function pullImage($name, $image, $echo=true) { if ($echo) @flush(); }); if ($echo) { - echo "\n"; + echo "\n"; @flush(); } if (!empty($strError)) { @@ -469,7 +469,7 @@ function execCommand($command, $echo=true) { @flush(); } $proc = proc_open($command." 2>&1", $descriptorspec, $pipes, '/', []); - while ($out = fgets( $pipes[1] )) { + while ($out = fgets($pipes[1])) { $out = preg_replace("%[\t\n\x0B\f\r]+%", '', $out); if ($echo) { echo ''; @@ -524,16 +524,15 @@ function getAllocations() { return $ports; } -function getCurlHandle($url, $method = 'GET') { - $ch = curl_init(); - curl_setopt( $ch, CURLOPT_URL, $url ); - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); - if ($method === 'HEAD') { - curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'HEAD' ); - curl_setopt( $ch, CURLOPT_HEADER, 1 ); - curl_setopt( $ch, CURLOPT_NOBODY, true ); - } - - return $ch; +function getCurlHandle($url, $method='GET') { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + if ($method === 'HEAD') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_NOBODY, true); + } + return $ch; } ?>