2021-11-30 11:20:42 +01:00
< ? php
namespace AIO\Docker ;
use AIO\Container\Container ;
use AIO\Container\State\IContainerState ;
use AIO\Container\State\ImageDoesNotExistState ;
use AIO\Container\State\StartingState ;
use AIO\Container\State\RunningState ;
2022-03-15 12:45:31 +01:00
use AIO\Container\State\RestartingState ;
use AIO\Container\State\NotRestartingState ;
2021-11-30 11:20:42 +01:00
use AIO\Container\State\VersionDifferentState ;
use AIO\Container\State\StoppedState ;
use AIO\Container\State\VersionEqualState ;
use AIO\Data\ConfigurationManager ;
2022-01-20 13:30:39 +01:00
use GuzzleHttp\Exception\RequestException ;
2021-11-30 11:20:42 +01:00
use AIO\ContainerDefinitionFetcher ;
use http\Env\Response ;
class DockerActionManager
{
2024-05-13 18:07:40 +02:00
private const string API_VERSION = 'v1.41' ;
2021-11-30 11:20:42 +01:00
private \GuzzleHttp\Client $guzzleClient ;
private ConfigurationManager $configurationManager ;
private ContainerDefinitionFetcher $containerDefinitionFetcher ;
private DockerHubManager $dockerHubManager ;
public function __construct (
ConfigurationManager $configurationManager ,
ContainerDefinitionFetcher $containerDefinitionFetcher ,
DockerHubManager $dockerHubManager
) {
$this -> configurationManager = $configurationManager ;
$this -> containerDefinitionFetcher = $containerDefinitionFetcher ;
$this -> dockerHubManager = $dockerHubManager ;
$this -> guzzleClient = new \GuzzleHttp\Client (
[
'curl' => [
CURLOPT_UNIX_SOCKET_PATH => '/var/run/docker.sock' ,
],
]
);
}
private function BuildApiUrl ( string $url ) : string {
2024-05-28 17:24:28 +02:00
return sprintf ( 'http://127.0.0.1/%s/%s' , self :: API_VERSION , $url );
2021-11-30 11:20:42 +01:00
}
private function BuildImageName ( Container $container ) : string {
2023-08-17 16:28:42 +02:00
$tag = $container -> GetImageTag ();
2023-09-07 14:26:42 +02:00
if ( $tag === '%AIO_CHANNEL%' ) {
2023-08-17 16:28:42 +02:00
$tag = $this -> GetCurrentChannel ();
}
return $container -> GetContainerName () . ':' . $tag ;
2021-11-30 11:20:42 +01:00
}
public function GetContainerRunningState ( Container $container ) : IContainerState
{
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , urlencode ( $container -> GetIdentifier ())));
try {
$response = $this -> guzzleClient -> get ( $url );
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
if ( $e -> getCode () === 404 ) {
2021-11-30 11:20:42 +01:00
return new ImageDoesNotExistState ();
}
2022-01-20 13:30:39 +01:00
throw $e ;
2021-11-30 11:20:42 +01:00
}
$responseBody = json_decode (( string ) $response -> getBody (), true );
if ( $responseBody [ 'State' ][ 'Running' ] === true ) {
return new RunningState ();
} else {
return new StoppedState ();
}
}
2022-03-15 12:45:31 +01:00
public function GetContainerRestartingState ( Container $container ) : IContainerState
{
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , urlencode ( $container -> GetIdentifier ())));
try {
$response = $this -> guzzleClient -> get ( $url );
} catch ( RequestException $e ) {
if ( $e -> getCode () === 404 ) {
return new ImageDoesNotExistState ();
}
throw $e ;
}
$responseBody = json_decode (( string ) $response -> getBody (), true );
if ( $responseBody [ 'State' ][ 'Restarting' ] === true ) {
return new RestartingState ();
} else {
return new NotRestartingState ();
}
}
2021-11-30 11:20:42 +01:00
public function GetContainerUpdateState ( Container $container ) : IContainerState
{
2023-08-17 16:28:42 +02:00
$tag = $container -> GetImageTag ();
2023-09-07 14:26:42 +02:00
if ( $tag === '%AIO_CHANNEL%' ) {
2023-08-17 16:28:42 +02:00
$tag = $this -> GetCurrentChannel ();
}
2021-11-30 11:20:42 +01:00
2022-04-17 13:07:29 +02:00
$runningDigests = $this -> GetRepoDigestsOfContainer ( $container -> GetIdentifier ());
if ( $runningDigests === null ) {
return new VersionDifferentState ();
}
2021-11-30 11:20:42 +01:00
$remoteDigest = $this -> dockerHubManager -> GetLatestDigestOfTag ( $container -> GetContainerName (), $tag );
2022-04-17 13:07:29 +02:00
if ( $remoteDigest === null ) {
return new VersionEqualstate ();
}
2021-11-30 11:20:42 +01:00
2022-04-17 13:07:29 +02:00
foreach ( $runningDigests as $runningDigest ) {
if ( $runningDigest === $remoteDigest ) {
return new VersionEqualState ();
}
2021-11-30 11:20:42 +01:00
}
2022-04-17 13:07:29 +02:00
return new VersionDifferentState ();
2021-11-30 11:20:42 +01:00
}
public function GetContainerStartingState ( Container $container ) : IContainerState
{
$runningState = $this -> GetContainerRunningState ( $container );
if ( $runningState instanceof StoppedState ) {
return new StoppedState ();
} elseif ( $runningState instanceof ImageDoesNotExistState ) {
return new ImageDoesNotExistState ();
}
$containerName = $container -> GetIdentifier ();
2022-12-25 03:10:20 +01:00
$internalPort = $container -> GetInternalPort ();
2022-12-25 17:08:41 +01:00
if ( $internalPort === '%APACHE_PORT%' ) {
$internalPort = $this -> configurationManager -> GetApachePort ();
} elseif ( $internalPort === '%TALK_PORT%' ) {
$internalPort = $this -> configurationManager -> GetTalkPort ();
}
2024-06-29 19:23:26 +02:00
2022-12-25 03:10:20 +01:00
if ( $internalPort !== " " && $internalPort !== 'host' ) {
2023-05-04 12:43:55 +02:00
$connection = @ fsockopen ( $containerName , ( int ) $internalPort , $errno , $errstr , 0.2 );
2022-12-25 01:40:37 +01:00
if ( $connection ) {
fclose ( $connection );
return new RunningState ();
} else {
return new StartingState ();
2021-11-30 11:20:42 +01:00
}
} else {
return new RunningState ();
}
}
2022-03-01 11:44:59 +01:00
public function DeleteContainer ( Container $container ) : void {
2022-01-14 19:02:03 +01:00
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s?v=true' , urlencode ( $container -> GetIdentifier ())));
2021-11-30 11:20:42 +01:00
try {
$this -> guzzleClient -> delete ( $url );
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
2022-01-01 16:01:31 +01:00
if ( $e -> getCode () !== 404 ) {
throw $e ;
}
}
2021-11-30 11:20:42 +01:00
}
2022-03-09 16:50:53 +01:00
public function GetLogs ( string $id ) : string
2021-11-30 11:20:42 +01:00
{
$url = $this -> BuildApiUrl (
sprintf (
2024-06-12 12:24:55 +02:00
'containers/%s/logs?stdout=true&stderr=true×tamps=true' ,
2022-03-09 16:50:53 +01:00
urlencode ( $id )
2021-11-30 11:20:42 +01:00
));
$responseBody = ( string ) $this -> guzzleClient -> get ( $url ) -> getBody ();
$response = " " ;
$separator = " \r \n " ;
$line = strtok ( $responseBody , $separator );
2022-08-31 13:45:23 +02:00
$response = substr ( $line , 8 ) . $separator ;
2021-11-30 11:20:42 +01:00
while ( $line !== false ) {
$line = strtok ( $separator );
2022-08-31 13:45:23 +02:00
$response .= substr ( $line , 8 ) . $separator ;
2021-11-30 11:20:42 +01:00
}
return $response ;
}
2022-03-01 11:44:59 +01:00
public function StartContainer ( Container $container ) : void {
2021-11-30 11:20:42 +01:00
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/start' , urlencode ( $container -> GetIdentifier ())));
2024-02-20 14:39:20 +01:00
try {
$this -> guzzleClient -> post ( $url );
} catch ( RequestException $e ) {
throw new \Exception ( " Could not start container " . $container -> GetIdentifier () . " : " . $e -> getMessage ());
}
2021-11-30 11:20:42 +01:00
}
2022-03-01 11:44:59 +01:00
public function CreateVolumes ( Container $container ) : void
2021-11-30 11:20:42 +01:00
{
$url = $this -> BuildApiUrl ( 'volumes/create' );
foreach ( $container -> GetVolumes () -> GetVolumes () as $volume ) {
$forbiddenChars = [
'/' ,
];
2022-05-23 17:19:23 +02:00
if ( $volume -> name === 'nextcloud_aio_nextcloud_datadir' || $volume -> name === 'nextcloud_aio_backupdir' ) {
return ;
}
2021-11-30 11:20:42 +01:00
$firstChar = substr ( $volume -> name , 0 , 1 );
2022-05-23 16:41:00 +02:00
if ( ! in_array ( $firstChar , $forbiddenChars )) {
2021-11-30 11:20:42 +01:00
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
2022-05-23 16:41:00 +02:00
'name' => $volume -> name ,
2021-11-30 11:20:42 +01:00
],
]
);
}
}
}
2022-03-01 11:44:59 +01:00
public function CreateContainer ( Container $container ) : void {
2021-11-30 11:20:42 +01:00
$volumes = [];
2023-05-26 12:00:05 +02:00
foreach ( $container -> GetVolumes () -> GetVolumes () as $volume ) {
2024-06-03 10:58:58 +02:00
// // NEXTCLOUD_MOUNT gets added via bind-mount later on
// if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') {
// if ($volume->name === $this->configurationManager->GetNextcloudMount()) {
// continue;
// }
// }
2023-05-26 12:00:05 +02:00
2021-11-30 11:20:42 +01:00
$volumeEntry = $volume -> name . ':' . $volume -> mountPoint ;
2023-05-26 12:00:05 +02:00
if ( $volume -> isWritable ) {
2021-11-30 11:20:42 +01:00
$volumeEntry = $volumeEntry . ':' . 'rw' ;
} else {
$volumeEntry = $volumeEntry . ':' . 'ro' ;
}
$volumes [] = $volumeEntry ;
}
$requestBody = [
'Image' => $this -> BuildImageName ( $container ),
];
2023-05-26 12:00:05 +02:00
if ( count ( $volumes ) > 0 ) {
2021-11-30 11:20:42 +01:00
$requestBody [ 'HostConfig' ][ 'Binds' ] = $volumes ;
}
2022-12-25 02:26:32 +01:00
foreach ( $container -> GetSecrets () as $secret ) {
$this -> configurationManager -> GetAndGenerateSecret ( $secret );
}
2023-09-29 21:44:12 +02:00
$aioVariables = $container -> GetAioVariables () -> GetVariables ();
foreach ( $aioVariables as $variable ) {
$config = $this -> configurationManager -> GetConfig ();
$variableArray = explode ( '=' , $variable );
$config [ $variableArray [ 0 ]] = $variableArray [ 1 ];
$this -> configurationManager -> WriteConfig ( $config );
sleep ( 1 );
}
2021-11-30 11:20:42 +01:00
$envs = $container -> GetEnvironmentVariables () -> GetVariables ();
2023-05-01 18:37:33 +02:00
// Special thing for the nextcloud container
if ( $container -> GetIdentifier () === 'nextcloud-aio-nextcloud' ) {
$envs [] = $this -> GetAllNextcloudExecCommands ();
}
2021-11-30 11:20:42 +01:00
foreach ( $envs as $key => $env ) {
2023-01-03 21:01:01 +01:00
// TODO: This whole block below is a hack and needs to get reworked in order to support multiple substitutions per line by default for all envs
if ( str_starts_with ( $env , 'extra_params=' )) {
$env = str_replace ( '%COLLABORA_SECCOMP_POLICY%' , $this -> configurationManager -> GetCollaboraSeccompPolicy (), $env );
$env = str_replace ( '%NC_DOMAIN%' , $this -> configurationManager -> GetDomain (), $env );
$envs [ $key ] = $env ;
continue ;
}
2021-11-30 11:20:42 +01:00
2023-01-03 21:01:01 +01:00
// Original implementation
$patterns = [ '/%(.*)%/' ];
2021-11-30 11:20:42 +01:00
if ( preg_match ( $patterns [ 0 ], $env , $out ) === 1 ) {
$replacements = array ();
if ( $out [ 1 ] === 'NC_DOMAIN' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetDomain ();
2024-04-04 10:26:42 +02:00
} elseif ( $out [ 1 ] === 'NC_BASE_DN' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetBaseDN ();
2021-11-30 11:20:42 +01:00
} elseif ( $out [ 1 ] === 'AIO_TOKEN' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetToken ();
} elseif ( $out [ 1 ] === 'BORGBACKUP_MODE' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetBackupMode ();
} elseif ( $out [ 1 ] === 'AIO_URL' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetAIOURL ();
2021-12-07 19:10:05 +01:00
} elseif ( $out [ 1 ] === 'SELECTED_RESTORE_TIME' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetSelectedRestoreTime ();
2021-12-08 18:12:56 +01:00
} elseif ( $out [ 1 ] === 'APACHE_PORT' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetApachePort ();
2022-06-07 00:43:48 +02:00
} elseif ( $out [ 1 ] === 'TALK_PORT' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetTalkPort ();
2022-03-14 15:35:37 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_MOUNT' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudMount ();
2022-03-21 13:23:17 +01:00
} elseif ( $out [ 1 ] === 'BACKUP_RESTORE_PASSWORD' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetBorgRestorePassword ();
2022-03-15 23:46:58 +01:00
} elseif ( $out [ 1 ] === 'CLAMAV_ENABLED' ) {
if ( $this -> configurationManager -> isClamavEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2023-06-05 15:05:14 +02:00
} elseif ( $out [ 1 ] === 'TALK_RECORDING_ENABLED' ) {
if ( $this -> configurationManager -> isTalkRecordingEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2022-03-17 10:13:21 +01:00
} elseif ( $out [ 1 ] === 'ONLYOFFICE_ENABLED' ) {
if ( $this -> configurationManager -> isOnlyofficeEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
} elseif ( $out [ 1 ] === 'COLLABORA_ENABLED' ) {
if ( $this -> configurationManager -> isCollaboraEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
} elseif ( $out [ 1 ] === 'TALK_ENABLED' ) {
if ( $this -> configurationManager -> isTalkEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2022-07-10 21:47:25 +02:00
} elseif ( $out [ 1 ] === 'UPDATE_NEXTCLOUD_APPS' ) {
if ( $this -> configurationManager -> isDailyBackupRunning () && $this -> configurationManager -> areAutomaticUpdatesEnabled ()) {
2022-04-04 19:12:07 +02:00
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2022-05-18 18:36:51 +02:00
} elseif ( $out [ 1 ] === 'TIMEZONE' ) {
2022-05-26 01:57:31 +02:00
if ( $this -> configurationManager -> GetTimezone () === '' ) {
2023-08-09 12:13:15 +02:00
$replacements [ 1 ] = 'Etc/UTC' ;
2022-05-26 01:57:31 +02:00
} else {
$replacements [ 1 ] = $this -> configurationManager -> GetTimezone ();
}
2022-06-27 15:29:17 +02:00
} elseif ( $out [ 1 ] === 'COLLABORA_DICTIONARIES' ) {
if ( $this -> configurationManager -> GetCollaboraDictionaries () === '' ) {
$replacements [ 1 ] = 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru' ;
} else {
$replacements [ 1 ] = $this -> configurationManager -> GetCollaboraDictionaries ();
}
2022-08-17 15:09:22 +02:00
} elseif ( $out [ 1 ] === 'IMAGINARY_ENABLED' ) {
if ( $this -> configurationManager -> isImaginaryEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2022-08-22 13:04:44 +02:00
} elseif ( $out [ 1 ] === 'FULLTEXTSEARCH_ENABLED' ) {
if ( $this -> configurationManager -> isFulltextsearchEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2023-08-24 14:09:21 +02:00
} elseif ( $out [ 1 ] === 'DOCKER_SOCKET_PROXY_ENABLED' ) {
if ( $this -> configurationManager -> isDockerSocketProxyEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2022-08-17 18:34:02 +02:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_UPLOAD_LIMIT' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudUploadLimit ();
2022-11-09 21:25:10 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_MEMORY_LIMIT' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudMemoryLimit ();
2022-08-17 19:43:05 +02:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_MAX_TIME' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudMaxTime ();
2023-04-27 16:01:16 +02:00
} elseif ( $out [ 1 ] === 'BORG_RETENTION_POLICY' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetBorgRetentionPolicy ();
2022-11-09 20:28:50 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_TRUSTED_CACERTS_DIR' ) {
2022-08-31 22:50:19 +02:00
$replacements [ 1 ] = $this -> configurationManager -> GetTrustedCacertsDir ();
2022-08-22 17:35:03 +02:00
} elseif ( $out [ 1 ] === 'ADDITIONAL_DIRECTORIES_BACKUP' ) {
if ( $this -> configurationManager -> GetAdditionalBackupDirectoriesString () !== '' ) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
} elseif ( $out [ 1 ] === 'BORGBACKUP_HOST_LOCATION' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetBorgBackupHostLocation ();
2022-09-20 18:16:17 +02:00
} elseif ( $out [ 1 ] === 'APACHE_MAX_SIZE' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetApacheMaxSize ();
2022-09-14 11:21:23 +02:00
} elseif ( $out [ 1 ] === 'COLLABORA_SECCOMP_POLICY' ) {
$replacements [ 1 ] = $this -> configurationManager -> GetCollaboraSeccompPolicy ();
2022-11-10 13:22:52 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_STARTUP_APPS' ) {
2022-09-26 20:27:35 +02:00
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudStartupApps ();
2022-11-10 17:31:06 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_ADDITIONAL_APKS' ) {
2022-11-08 21:38:31 +01:00
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudAdditionalApks ();
2022-11-10 17:31:06 +01:00
} elseif ( $out [ 1 ] === 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS' ) {
2022-11-08 21:38:31 +01:00
$replacements [ 1 ] = $this -> configurationManager -> GetNextcloudAdditionalPhpExtensions ();
2023-03-29 17:39:31 +02:00
} elseif ( $out [ 1 ] === 'INSTALL_LATEST_MAJOR' ) {
if ( $this -> configurationManager -> shouldLatestMajorGetInstalled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2023-07-19 21:29:24 +02:00
} elseif ( $out [ 1 ] === 'REMOVE_DISABLED_APPS' ) {
if ( $this -> configurationManager -> shouldDisabledAppsGetRemoved ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2023-10-26 12:09:57 +02:00
// Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then)
} elseif ( $out [ 1 ] === 'AIO_DATABASE_HOST' ) {
$replacements [ 1 ] = gethostbyname ( 'nextcloud-aio-database' );
2024-01-24 15:25:43 +01:00
// Allow to get local ip-address of caddy container and add it to trusted proxies automatically
} elseif ( $out [ 1 ] === 'CADDY_IP_ADDRESS' ) {
$replacements [ 1 ] = '' ;
$communityContainers = $this -> configurationManager -> GetEnabledCommunityContainers ();
if ( in_array ( 'caddy' , $communityContainers , true )) {
$replacements [ 1 ] = gethostbyname ( 'nextcloud-aio-caddy' );
}
2024-09-16 17:59:15 +02:00
} elseif ( $out [ 1 ] === 'WHITEBOARD_ENABLED' ) {
if ( $this -> configurationManager -> isWhiteboardEnabled ()) {
$replacements [ 1 ] = 'yes' ;
} else {
$replacements [ 1 ] = '' ;
}
2021-11-30 11:20:42 +01:00
} else {
2022-12-25 02:26:32 +01:00
$secret = $this -> configurationManager -> GetSecret ( $out [ 1 ]);
if ( $secret === " " ) {
2023-06-17 12:13:40 +02:00
throw new \Exception ( " The secret " . $out [ 1 ] . " is empty. Cannot substitute its value. Please check if it is defined in secrets of containers.json. " );
2022-12-25 02:26:32 +01:00
}
$replacements [ 1 ] = $secret ;
2021-11-30 11:20:42 +01:00
}
$envs [ $key ] = preg_replace ( $patterns , $replacements , $env );
}
}
if ( count ( $envs ) > 0 ) {
$requestBody [ 'Env' ] = $envs ;
}
$requestBody [ 'HostConfig' ][ 'RestartPolicy' ][ 'Name' ] = $container -> GetRestartPolicy ();
2023-05-31 16:46:19 +02:00
$requestBody [ 'HostConfig' ][ 'ReadonlyRootfs' ] = $container -> GetReadOnlySetting ();
2024-06-29 19:23:26 +02:00
2022-12-25 17:08:41 +01:00
$exposedPorts = [];
if ( $container -> GetInternalPort () !== 'host' ) {
foreach ( $container -> GetPorts () -> GetPorts () as $value ) {
2023-10-04 14:05:05 +02:00
$port = $value -> port ;
2023-10-09 16:38:18 +02:00
$protocol = $value -> protocol ;
2023-10-04 14:05:05 +02:00
if ( $port === '%APACHE_PORT%' ) {
$port = $this -> configurationManager -> GetApachePort ();
2023-10-09 16:38:18 +02:00
// Do not expose udp if AIO is in reverse proxy mode
if ( $port !== '443' && $protocol === 'udp' ) {
continue ;
}
2023-10-04 14:05:05 +02:00
} else if ( $port === '%TALK_PORT%' ) {
$port = $this -> configurationManager -> GetTalkPort ();
}
2023-10-09 16:38:18 +02:00
$portWithProtocol = $port . '/' . $protocol ;
2023-01-02 17:37:57 +01:00
$exposedPorts [ $portWithProtocol ] = null ;
2022-12-25 17:08:41 +01:00
}
2023-08-24 17:30:48 +02:00
$requestBody [ 'HostConfig' ][ 'NetworkMode' ] = 'nextcloud-aio' ;
2023-01-02 16:42:08 +01:00
} else {
$requestBody [ 'HostConfig' ][ 'NetworkMode' ] = 'host' ;
2022-12-25 17:08:41 +01:00
}
2021-11-30 11:20:42 +01:00
if ( count ( $exposedPorts ) > 0 ) {
2023-01-02 17:37:57 +01:00
$requestBody [ 'ExposedPorts' ] = $exposedPorts ;
2022-12-25 17:08:41 +01:00
foreach ( $container -> GetPorts () -> GetPorts () as $value ) {
$port = $value -> port ;
2023-10-09 16:38:18 +02:00
$protocol = $value -> protocol ;
2023-10-04 14:05:05 +02:00
if ( $port === '%APACHE_PORT%' ) {
$port = $this -> configurationManager -> GetApachePort ();
2023-10-09 16:38:18 +02:00
// Do not expose udp if AIO is in reverse proxy mode
if ( $port !== '443' && $protocol === 'udp' ) {
continue ;
}
2023-10-04 14:05:05 +02:00
} else if ( $port === '%TALK_PORT%' ) {
$port = $this -> configurationManager -> GetTalkPort ();
}
2022-12-25 17:08:41 +01:00
$ipBinding = $value -> ipBinding ;
2023-10-04 14:05:05 +02:00
if ( $ipBinding === '%APACHE_IP_BINDING%' ) {
$ipBinding = $this -> configurationManager -> GetApacheIPBinding ();
2024-06-29 19:23:26 +02:00
// Do not expose if AIO is in internal network mode
if ( $ipBinding === '@INTERNAL' ) {
continue ;
}
2023-10-04 14:05:05 +02:00
}
2022-12-25 17:08:41 +01:00
$portWithProtocol = $port . '/' . $protocol ;
2023-01-02 17:37:57 +01:00
$requestBody [ 'HostConfig' ][ 'PortBindings' ][ $portWithProtocol ] = [
2022-12-25 17:08:41 +01:00
[
'HostPort' => $port ,
'HostIp' => $ipBinding ,
]
];
2021-11-30 11:20:42 +01:00
}
}
2023-01-03 02:01:03 +01:00
$devices = [];
foreach ( $container -> GetDevices () as $device ) {
if ( $device === '/dev/dri' && ! $this -> configurationManager -> isDriDeviceEnabled ()) {
continue ;
}
$devices [] = [ " PathOnHost " => $device , " PathInContainer " => $device , " CgroupPermissions " => " rwm " ];
}
if ( count ( $devices ) > 0 ) {
$requestBody [ 'HostConfig' ][ 'Devices' ] = $devices ;
}
2023-03-29 10:57:44 +02:00
$shmSize = $container -> GetShmSize ();
2023-03-31 11:27:11 +02:00
if ( $shmSize > 0 ) {
2023-03-29 10:57:44 +02:00
$requestBody [ 'HostConfig' ][ 'ShmSize' ] = $shmSize ;
}
2023-06-19 14:10:02 +02:00
$tmpfs = [];
foreach ( $container -> GetTmpfs () as $tmp ) {
2023-07-28 17:08:44 +02:00
$mode = " " ;
if ( str_contains ( $tmp , ':' )) {
$mode = explode ( ':' , $tmp )[ 1 ];
2023-08-09 11:15:14 +02:00
$tmp = explode ( ':' , $tmp )[ 0 ];
2023-07-28 17:08:44 +02:00
}
$tmpfs [ $tmp ] = $mode ;
2023-06-19 14:10:02 +02:00
}
2023-06-19 13:04:39 +02:00
if ( count ( $tmpfs ) > 0 ) {
2023-06-19 14:10:02 +02:00
$requestBody [ 'HostConfig' ][ 'Tmpfs' ] = $tmpfs ;
2023-06-19 13:04:39 +02:00
}
2023-08-10 16:41:47 +02:00
$requestBody [ 'HostConfig' ][ 'Init' ] = $container -> GetInit ();
2023-03-06 11:36:36 +01:00
$capAdds = $container -> GetCapAdds ();
if ( count ( $capAdds ) > 0 ) {
$requestBody [ 'HostConfig' ][ 'CapAdd' ] = $capAdds ;
}
2023-09-19 21:26:11 +02:00
// Disable arp spoofing
2023-05-31 12:00:44 +02:00
if ( ! in_array ( 'NET_RAW' , $capAdds , true )) {
$requestBody [ 'HostConfig' ][ 'CapDrop' ] = [ 'NET_RAW' ];
}
2023-09-19 21:26:11 +02:00
2024-01-12 16:22:22 +01:00
// Disable SELinux for AIO containers so that it does not break them
$requestBody [ 'HostConfig' ][ 'SecurityOpt' ] = [ " label:disable " ];
2024-01-29 09:34:26 +01:00
if ( $container -> isApparmorUnconfined ()) {
$requestBody [ 'HostConfig' ][ 'SecurityOpt' ] = [ " apparmor:unconfined " , " label:disable " ];
}
2024-01-12 16:22:22 +01:00
2023-05-26 12:00:05 +02:00
$mounts = [];
2021-11-30 11:20:42 +01:00
// Special things for the backup container which should not be exposed in the containers.json
if ( $container -> GetIdentifier () === 'nextcloud-aio-borgbackup' ) {
2022-08-22 17:35:03 +02:00
// Additional backup directories
2023-05-17 20:48:08 +02:00
foreach ( $this -> getAllBackupVolumes () as $additionalBackupVolumes ) {
if ( $additionalBackupVolumes !== '' ) {
$mounts [] = [ " Type " => " volume " , " Source " => $additionalBackupVolumes , " Target " => " /nextcloud_aio_volumes/ " . $additionalBackupVolumes , " ReadOnly " => false ];
}
}
2022-08-22 17:35:03 +02:00
foreach ( $this -> configurationManager -> GetAdditionalBackupDirectoriesArray () as $additionalBackupDirectories ) {
if ( $additionalBackupDirectories !== '' ) {
if ( ! str_starts_with ( $additionalBackupDirectories , '/' )) {
2022-08-25 19:03:29 +02:00
$mounts [] = [ " Type " => " volume " , " Source " => $additionalBackupDirectories , " Target " => " /docker_volumes/ " . $additionalBackupDirectories , " ReadOnly " => true ];
2022-08-22 17:35:03 +02:00
} else {
2022-08-25 19:03:29 +02:00
$mounts [] = [ " Type " => " bind " , " Source " => $additionalBackupDirectories , " Target " => " /host_mounts " . $additionalBackupDirectories , " ReadOnly " => true , " BindOptions " => [ " NonRecursive " => true ]];
2022-08-22 17:35:03 +02:00
}
}
}
2023-01-04 15:57:54 +01:00
// Special things for the talk container which should not be exposed in the containers.json
} elseif ( $container -> GetIdentifier () === 'nextcloud-aio-talk' ) {
// This is needed due to a bug in libwebsockets which cannot handle unlimited ulimits
$requestBody [ 'HostConfig' ][ 'Ulimits' ] = [[ " Name " => " nofile " , " Hard " => 200000 , " Soft " => 200000 ]];
2024-06-03 10:58:58 +02:00
// // Special things for the nextcloud container which should not be exposed in the containers.json
// } elseif ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') {
// foreach ($container->GetVolumes()->GetVolumes() as $volume) {
// if ($volume->name !== $this->configurationManager->GetNextcloudMount()) {
// continue;
// }
// $mounts[] = ["Type" => "bind", "Source" => $volume->name, "Target" => $volume->mountPoint, "ReadOnly" => !$volume->isWritable, "BindOptions" => [ "Propagation" => "rshared"]];
// }
2024-03-04 12:04:05 +01:00
// Special things for the caddy community container
} elseif ( $container -> GetIdentifier () === 'nextcloud-aio-caddy' ) {
$requestBody [ 'HostConfig' ][ 'ExtraHosts' ] = [ 'host.docker.internal:host-gateway' ];
2023-05-26 12:00:05 +02:00
}
if ( count ( $mounts ) > 0 ) {
$requestBody [ 'HostConfig' ][ 'Mounts' ] = $mounts ;
2021-11-30 11:20:42 +01:00
}
$url = $this -> BuildApiUrl ( 'containers/create?name=' . $container -> GetIdentifier ());
2022-03-09 18:25:03 +01:00
try {
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => $requestBody
]
);
} catch ( RequestException $e ) {
2024-02-20 14:39:20 +01:00
throw new \Exception ( " Could not create container " . $container -> GetIdentifier () . " : " . $e -> getMessage ());
2022-03-09 18:25:03 +01:00
}
2021-11-30 11:20:42 +01:00
}
2024-02-20 15:20:43 +01:00
public function isDockerHubReachable ( Container $container ) : bool {
$tag = $container -> GetImageTag ();
if ( $tag === '%AIO_CHANNEL%' ) {
$tag = $this -> GetCurrentChannel ();
}
$remoteDigest = $this -> dockerHubManager -> GetLatestDigestOfTag ( $container -> GetContainerName (), $tag );
if ( $remoteDigest === null ) {
return false ;
} else {
return true ;
}
}
2023-11-20 13:16:59 +01:00
public function PullImage ( Container $container ) : void
2021-11-30 11:20:42 +01:00
{
2023-12-11 10:04:30 +01:00
$imageName = $this -> BuildImageName ( $container );
$encodedImageName = urlencode ( $imageName );
$url = $this -> BuildApiUrl ( sprintf ( 'images/create?fromImage=%s' , $encodedImageName ));
2024-01-15 11:24:09 +01:00
$imageIsThere = true ;
2023-11-20 13:27:51 +01:00
try {
2023-12-11 10:04:30 +01:00
$imageUrl = $this -> BuildApiUrl ( sprintf ( 'images/%s/json' , $encodedImageName ));
2023-11-20 13:27:51 +01:00
$this -> guzzleClient -> get ( $imageUrl ) -> getBody () -> getContents ();
} catch ( \Throwable $e ) {
2024-01-15 11:24:09 +01:00
$imageIsThere = false ;
}
try {
$this -> guzzleClient -> post ( $url );
} catch ( RequestException $e ) {
if ( $imageIsThere === false ) {
throw new \Exception ( " Could not pull image " . $imageName . " . Please run 'sudo docker exec -it nextcloud-aio-mastercontainer docker pull " . $imageName . " ' in order to find out why it failed. " );
}
2023-11-20 13:27:51 +01:00
}
2021-11-30 11:20:42 +01:00
}
private function isContainerUpdateAvailable ( string $id ) : string
{
$container = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
$updateAvailable = " " ;
if ( $container -> GetUpdateState () instanceof VersionDifferentState ) {
$updateAvailable = '1' ;
}
foreach ( $container -> GetDependsOn () as $dependency ) {
$updateAvailable .= $this -> isContainerUpdateAvailable ( $dependency );
}
return $updateAvailable ;
}
2022-03-01 11:44:59 +01:00
public function isAnyUpdateAvailable () : bool {
2023-05-03 15:29:19 +02:00
// return early if instance is not installed
if ( ! $this -> configurationManager -> wasStartButtonClicked ()) {
return false ;
}
2021-11-30 11:20:42 +01:00
$id = 'nextcloud-aio-apache' ;
if ( $this -> isContainerUpdateAvailable ( $id ) !== " " ) {
return true ;
} else {
return false ;
}
}
2023-05-30 11:44:57 +02:00
private function getBackupVolumes ( string $id ) : string
2023-05-17 20:48:08 +02:00
{
$container = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
2023-05-30 11:44:57 +02:00
$backupVolumes = '' ;
foreach ( $container -> GetBackupVolumes () as $backupVolume ) {
$backupVolumes .= $backupVolume . ' ' ;
}
2023-05-17 20:48:08 +02:00
foreach ( $container -> GetDependsOn () as $dependency ) {
2023-05-30 11:44:57 +02:00
$backupVolumes .= $this -> getBackupVolumes ( $dependency );
2023-05-17 20:48:08 +02:00
}
return $backupVolumes ;
}
private function getAllBackupVolumes () : array {
$id = 'nextcloud-aio-apache' ;
2023-05-30 11:44:57 +02:00
$backupVolumesArray = explode ( ' ' , $this -> getBackupVolumes ( $id ));
return array_unique ( $backupVolumesArray );
2023-05-17 20:48:08 +02:00
}
2023-05-01 18:37:33 +02:00
private function GetNextcloudExecCommands ( string $id ) : string
{
$container = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
$nextcloudExecCommands = '' ;
foreach ( $container -> GetNextcloudExecCommands () as $execCommand ) {
$nextcloudExecCommands .= $execCommand . PHP_EOL ;
}
foreach ( $container -> GetDependsOn () as $dependency ) {
$nextcloudExecCommands .= $this -> GetNextcloudExecCommands ( $dependency );
}
return $nextcloudExecCommands ;
}
private function GetAllNextcloudExecCommands () : string
{
$id = 'nextcloud-aio-apache' ;
return 'NEXTCLOUD_EXEC_COMMANDS=' . $this -> GetNextcloudExecCommands ( $id );
}
2022-04-17 13:07:29 +02:00
private function GetRepoDigestsOfContainer ( string $containerName ) : ? array {
2021-11-30 11:20:42 +01:00
try {
$containerUrl = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , $containerName ));
$containerOutput = json_decode ( $this -> guzzleClient -> get ( $containerUrl ) -> getBody () -> getContents (), true );
$imageName = $containerOutput [ 'Image' ];
$imageUrl = $this -> BuildApiUrl ( sprintf ( 'images/%s/json' , $imageName ));
$imageOutput = json_decode ( $this -> guzzleClient -> get ( $imageUrl ) -> getBody () -> getContents (), true );
2022-04-17 13:07:29 +02:00
if ( ! isset ( $imageOutput [ 'RepoDigests' ])) {
error_log ( 'RepoDigests is not set of container ' . $containerName );
return null ;
2024-06-29 19:23:26 +02:00
}
2021-11-30 11:20:42 +01:00
2022-04-17 13:07:29 +02:00
if ( ! is_array ( $imageOutput [ 'RepoDigests' ])) {
error_log ( 'RepoDigests of ' . $containerName . ' is not an array which is not allowed!' );
return null ;
}
$repoDigestArray = [];
$oneDigestGiven = false ;
foreach ( $imageOutput [ 'RepoDigests' ] as $repoDigest ) {
$digestPosition = strpos ( $repoDigest , '@' );
if ( $digestPosition === false ) {
error_log ( 'Somehow the RepoDigest of ' . $containerName . ' does not contain a @.' );
return null ;
}
$repoDigestArray [] = substr ( $repoDigest , $digestPosition + 1 );
$oneDigestGiven = true ;
}
if ( $oneDigestGiven ) {
return $repoDigestArray ;
2021-11-30 11:20:42 +01:00
}
return null ;
} catch ( \Exception $e ) {
return null ;
}
}
public function GetCurrentChannel () : string {
$cacheKey = 'aio-ChannelName' ;
$channelName = apcu_fetch ( $cacheKey );
if ( $channelName !== false && is_string ( $channelName )) {
return $channelName ;
}
$containerName = 'nextcloud-aio-mastercontainer' ;
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , $containerName ));
try {
$output = json_decode ( $this -> guzzleClient -> get ( $url ) -> getBody () -> getContents (), true );
$containerChecksum = $output [ 'Image' ];
$tagArray = explode ( ':' , $output [ 'Config' ][ 'Image' ]);
$tag = $tagArray [ 1 ];
apcu_add ( $cacheKey , $tag );
2022-06-11 14:52:49 +02:00
/**
* @ psalm - suppress TypeDoesNotContainNull
2023-01-24 11:07:08 +01:00
* @ psalm - suppress DocblockTypeContradiction
2022-06-11 14:52:49 +02:00
*/
2022-06-11 14:45:46 +02:00
if ( $tag === null ) {
error_log ( " No tag was found when getting the current channel. You probably did not follow the documentation correctly. Changing the channel to the default 'latest'. " );
$tag = 'latest' ;
}
2021-11-30 11:20:42 +01:00
return $tag ;
2022-01-20 13:30:39 +01:00
} catch ( \Exception $e ) {
2022-03-09 18:25:03 +01:00
error_log ( 'Could not get current channel ' . $e -> getMessage ());
2021-11-30 11:20:42 +01:00
}
return 'latest' ;
}
public function IsMastercontainerUpdateAvailable () : bool
{
$imageName = 'nextcloud/all-in-one' ;
$containerName = 'nextcloud-aio-mastercontainer' ;
2021-12-05 14:03:05 +01:00
$tag = $this -> GetCurrentChannel ();
2021-11-30 11:20:42 +01:00
2022-04-17 13:07:29 +02:00
$runningDigests = $this -> GetRepoDigestsOfContainer ( $containerName );
if ( $runningDigests === null ) {
return true ;
}
2021-11-30 11:20:42 +01:00
$remoteDigest = $this -> dockerHubManager -> GetLatestDigestOfTag ( $imageName , $tag );
2022-04-17 13:07:29 +02:00
if ( $remoteDigest === null ) {
2021-11-30 11:20:42 +01:00
return false ;
}
2022-04-17 13:07:29 +02:00
foreach ( $runningDigests as $runningDigest ) {
if ( $remoteDigest === $runningDigest ) {
return false ;
}
}
return true ;
2021-11-30 11:20:42 +01:00
}
2022-11-17 13:38:09 +01:00
public function sendNotification ( Container $container , string $subject , string $message , string $file = '/notify.sh' ) : void
2021-11-30 13:11:19 +01:00
{
if ( $this -> GetContainerStartingState ( $container ) instanceof RunningState ) {
$containerName = $container -> GetIdentifier ();
// schedule the exec
2021-11-30 14:25:10 +01:00
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/exec' , urlencode ( $containerName )));
2021-11-30 13:11:19 +01:00
$response = json_decode (
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
'AttachStdout' => true ,
'Tty' => true ,
'Cmd' => [
'bash' ,
2022-11-17 13:38:09 +01:00
$file ,
2021-11-30 13:11:19 +01:00
$subject ,
$message
],
],
]
2021-11-30 14:25:10 +01:00
) -> getBody () -> getContents (),
true
2021-11-30 13:11:19 +01:00
);
$id = $response [ 'Id' ];
// start the exec
2021-11-30 14:25:10 +01:00
$url = $this -> BuildApiUrl ( sprintf ( 'exec/%s/start' , $id ));
2021-11-30 13:11:19 +01:00
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
'Detach' => false ,
'Tty' => true ,
],
]
);
}
}
2023-02-14 23:00:38 +01:00
private function DisconnectContainerFromBridgeNetwork ( string $id ) : void
2021-11-30 11:20:42 +01:00
{
$url = $this -> BuildApiUrl (
2023-02-14 23:00:38 +01:00
sprintf ( 'networks/%s/disconnect' , 'bridge' )
2021-11-30 11:20:42 +01:00
);
try {
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
2023-02-14 23:00:38 +01:00
'container' => $id ,
2021-11-30 11:20:42 +01:00
],
]
);
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
}
2021-11-30 11:20:42 +01:00
}
2023-08-24 14:09:21 +02:00
private function ConnectContainerIdToNetwork ( string $id , string $internalPort , string $network = 'nextcloud-aio' ) : void
2021-11-30 11:20:42 +01:00
{
2022-12-25 03:10:20 +01:00
if ( $internalPort === 'host' ) {
2023-01-02 16:42:08 +01:00
return ;
}
$url = $this -> BuildApiUrl ( 'networks/create' );
try {
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
2023-08-24 14:09:21 +02:00
'Name' => $network ,
2023-01-02 16:42:08 +01:00
'CheckDuplicate' => true ,
'Driver' => 'bridge' ,
'Internal' => false ,
2021-11-30 11:20:42 +01:00
]
2023-01-02 16:42:08 +01:00
]
);
} catch ( RequestException $e ) {
// 409 is undocumented and gets thrown if the network already exists.
if ( $e -> getCode () !== 409 ) {
2024-03-19 12:08:31 +01:00
throw new \Exception ( " Could not create the nextcloud-aio network: " . $e -> getMessage ());
2022-01-21 14:25:29 +01:00
}
2021-11-30 11:20:42 +01:00
}
$url = $this -> BuildApiUrl (
2022-12-25 03:10:20 +01:00
sprintf ( 'networks/%s/connect' , $network )
2021-11-30 11:20:42 +01:00
);
try {
$this -> guzzleClient -> request (
'POST' ,
$url ,
[
'json' => [
'container' => $id ,
]
]
);
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
2022-01-21 14:36:43 +01:00
// 403 is undocumented and gets thrown if a specific container is already part of a network
if ( $e -> getCode () !== 403 ) {
throw $e ;
}
2021-11-30 11:20:42 +01:00
}
}
2022-03-01 11:44:59 +01:00
public function ConnectMasterContainerToNetwork () : void
2021-11-30 11:20:42 +01:00
{
2022-12-25 03:10:20 +01:00
$this -> ConnectContainerIdToNetwork ( 'nextcloud-aio-mastercontainer' , '' );
2023-06-03 18:05:29 +02:00
// Don't disconnect here since it slows down the initial login by a lot. Is getting done during cron.sh instead.
// $this->DisconnectContainerFromBridgeNetwork('nextcloud-aio-mastercontainer');
2021-11-30 11:20:42 +01:00
}
2022-03-01 11:44:59 +01:00
public function ConnectContainerToNetwork ( Container $container ) : void
2021-11-30 11:20:42 +01:00
{
2023-08-24 17:30:48 +02:00
$this -> ConnectContainerIdToNetwork ( $container -> GetIdentifier (), $container -> GetInternalPort ());
2021-11-30 11:20:42 +01:00
}
2022-03-01 11:44:59 +01:00
public function StopContainer ( Container $container ) : void {
2021-11-30 11:20:42 +01:00
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/stop?t=%s' , urlencode ( $container -> GetIdentifier ()), $container -> GetMaxShutdownTime ()));
2022-01-01 16:01:31 +01:00
try {
$this -> guzzleClient -> post ( $url );
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
2022-01-10 18:27:20 +01:00
if ( $e -> getCode () !== 404 && $e -> getCode () !== 304 ) {
throw $e ;
}
}
2021-11-30 11:20:42 +01:00
}
public function GetBackupcontainerExitCode () : int
{
$containerName = 'nextcloud-aio-borgbackup' ;
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , urlencode ( $containerName )));
try {
$response = $this -> guzzleClient -> get ( $url );
2022-01-20 13:30:39 +01:00
} catch ( RequestException $e ) {
if ( $e -> getCode () === 404 ) {
2021-11-30 11:20:42 +01:00
return - 1 ;
}
2022-01-20 13:30:39 +01:00
throw $e ;
2021-11-30 11:20:42 +01:00
}
$responseBody = json_decode (( string ) $response -> getBody (), true );
$exitCode = $responseBody [ 'State' ][ 'ExitCode' ];
if ( is_int ( $exitCode )) {
return $exitCode ;
} else {
return - 1 ;
}
}
2021-12-30 14:57:49 +01:00
2022-03-04 15:36:08 +01:00
public function GetDatabasecontainerExitCode () : int
{
$containerName = 'nextcloud-aio-database' ;
$url = $this -> BuildApiUrl ( sprintf ( 'containers/%s/json' , urlencode ( $containerName )));
try {
$response = $this -> guzzleClient -> get ( $url );
} catch ( RequestException $e ) {
if ( $e -> getCode () === 404 ) {
return - 1 ;
}
throw $e ;
}
$responseBody = json_decode (( string ) $response -> getBody (), true );
$exitCode = $responseBody [ 'State' ][ 'ExitCode' ];
if ( is_int ( $exitCode )) {
return $exitCode ;
} else {
return - 1 ;
}
}
2021-12-30 14:57:49 +01:00
public function isLoginAllowed () : bool {
$id = 'nextcloud-aio-apache' ;
$apacheContainer = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
if ( $this -> GetContainerStartingState ( $apacheContainer ) instanceof RunningState ) {
return false ;
}
return true ;
}
2022-03-30 17:50:09 +02:00
public function isBackupContainerRunning () : bool {
$id = 'nextcloud-aio-borgbackup' ;
$backupContainer = $this -> containerDefinitionFetcher -> GetContainerById ( $id );
if ( $this -> GetContainerRunningState ( $backupContainer ) instanceof RunningState ) {
return true ;
}
return false ;
}
2022-11-17 13:38:09 +01:00
private function GetCreatedTimeOfNextcloudImage () : ? string {
$imageName = 'nextcloud/aio-nextcloud' . ':' . $this -> GetCurrentChannel ();
try {
$imageUrl = $this -> BuildApiUrl ( sprintf ( 'images/%s/json' , $imageName ));
$imageOutput = json_decode ( $this -> guzzleClient -> get ( $imageUrl ) -> getBody () -> getContents (), true );
if ( ! isset ( $imageOutput [ 'Created' ])) {
error_log ( 'Created is not set of image ' . $imageName );
return null ;
}
2023-08-09 08:54:11 +02:00
return str_replace ( 'T' , ' ' , ( string ) $imageOutput [ 'Created' ]);
2022-11-17 13:38:09 +01:00
} catch ( \Exception $e ) {
return null ;
}
}
public function isNextcloudImageOutdated () : bool {
$createdTime = $this -> GetCreatedTimeOfNextcloudImage ();
if ( $createdTime === null ) {
return false ;
}
// If the image is older than 90 days, it is outdated.
if (( time () - ( 60 * 60 * 24 * 90 )) > strtotime ( $createdTime )) {
return true ;
}
return false ;
}
2021-11-30 11:20:42 +01:00
}