diff --git a/composer.json b/composer.json index ba94891..907ae6a 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,10 @@ "zendframework/zend-hydrator": "2.2.1", "zendframework/zend-filter": "2.7.1", "dasprid/container-interop-doctrine": "0.2.3", - "los/basepath": "^1.0" + "los/basepath": "^1.0", + "zendframework/zend-http": "^2.6", + "symfony/css-selector": "^3.2", + "zendframework/zend-cache": "^2.7" }, "require-dev": { "phpunit/phpunit": "^4.8", diff --git a/composer.lock b/composer.lock index d5160f0..dd7b54c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "26537d5a4676e28b7df3a39ea661f76e", + "content-hash": "7486df53c7fc215192835f2616d3e3a9", "packages": [ { "name": "container-interop/container-interop", @@ -860,6 +860,42 @@ ], "time": "2017-01-19T11:35:12+00:00" }, + { + "name": "pepegar/streams-php", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/pepegar/streams-php.git", + "reference": "a89f155641043f8f00931f9901cc1efaea77f8bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pepegar/streams-php/zipball/a89f155641043f8f00931f9901cc1efaea77f8bc", + "reference": "a89f155641043f8f00931f9901cc1efaea77f8bc", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pepe García", + "email": "jl.garhdez@gmail.com" + } + ], + "description": "A port of the Streams library for Java8. Let's make PHP cooler.", + "time": "2014-07-03T20:46:44+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -1154,6 +1190,59 @@ "homepage": "https://symfony.com", "time": "2017-02-06T12:04:21+00:00" }, + { + "name": "symfony/css-selector", + "version": "v3.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/f0e628f04fc055c934b3211cfabdb1c59eefbfaa", + "reference": "f0e628f04fc055c934b3211cfabdb1c59eefbfaa", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2017-01-02T20:32:22+00:00" + }, { "name": "symfony/debug", "version": "v3.2.3", @@ -1270,6 +1359,75 @@ ], "time": "2016-11-14T01:06:16+00:00" }, + { + "name": "zendframework/zend-cache", + "version": "2.7.2", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-cache.git", + "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/c98331b96d3b9d9b24cf32d02660602edb34d039", + "reference": "c98331b96d3b9d9b24cf32d02660602edb34d039", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.10.0", + "phpunit/phpunit": "^4.8", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-serializer": "^2.6", + "zendframework/zend-session": "^2.6.2" + }, + "suggest": { + "ext-apc": "APC or compatible extension, to use the APC storage adapter", + "ext-apcu": "APCU >= 5.1.0, to use the APCu storage adapter", + "ext-dba": "DBA, to use the DBA storage adapter", + "ext-memcache": "Memcache >= 2.0.0 to use the Memcache storage adapter", + "ext-memcached": "Memcached >= 1.0.0 to use the Memcached storage adapter", + "ext-mongo": "Mongo, to use MongoDb storage adapter", + "ext-redis": "Redis, to use Redis storage adapter", + "ext-wincache": "WinCache, to use the WinCache storage adapter", + "ext-xcache": "XCache, to use the XCache storage adapter", + "mongofill/mongofill": "Alternative to ext-mongo - a pure PHP implementation designed as a drop in replacement", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-session": "Zend\\Session component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev", + "dev-develop": "2.8-dev" + }, + "zf": { + "component": "Zend\\Cache", + "config-provider": "Zend\\Cache\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a generic way to cache any data", + "homepage": "https://github.com/zendframework/zend-cache", + "keywords": [ + "cache", + "zf2" + ], + "time": "2016-12-16T11:35:47+00:00" + }, { "name": "zendframework/zend-diactoros", "version": "1.3.10", @@ -1364,6 +1522,60 @@ ], "time": "2016-06-30T19:48:38+00:00" }, + { + "name": "zendframework/zend-eventmanager", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-eventmanager.git", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "reference": "c3bce7b7d47c54040b9ae51bc55491c72513b75d", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "athletic/athletic": "^0.1", + "container-interop/container-interop": "^1.1.0", + "phpunit/phpunit": "^5.6", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0" + }, + "suggest": { + "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature", + "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev", + "dev-develop": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Trigger and listen to events within a PHP application", + "homepage": "https://github.com/zendframework/zend-eventmanager", + "keywords": [ + "event", + "eventmanager", + "events", + "zf2" + ], + "time": "2016-12-19T21:47:12+00:00" + }, { "name": "zendframework/zend-expressive", "version": "1.0.6", @@ -1704,6 +1916,56 @@ ], "time": "2016-04-18T18:32:43+00:00" }, + { + "name": "zendframework/zend-http", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-http.git", + "reference": "09f4d279f46d86be63171ff62ee0f79eca878678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/09f4d279f46d86be63171ff62ee0f79eca878678", + "reference": "09f4d279f46d86be63171ff62ee0f79eca878678", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-loader": "^2.5", + "zendframework/zend-stdlib": "^2.5 || ^3.0", + "zendframework/zend-uri": "^2.5", + "zendframework/zend-validator": "^2.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.0", + "zendframework/zend-coding-standard": "~1.0.0", + "zendframework/zend-config": "^2.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev", + "dev-develop": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://github.com/zendframework/zend-http", + "keywords": [ + "http", + "zf2" + ], + "time": "2017-01-31T14:41:02+00:00" + }, { "name": "zendframework/zend-hydrator", "version": "2.2.1", @@ -1816,6 +2078,50 @@ ], "time": "2016-04-01T02:34:00+00:00" }, + { + "name": "zendframework/zend-loader", + "version": "2.5.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-loader.git", + "reference": "c5fd2f071bde071f4363def7dea8dec7393e135c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/c5fd2f071bde071f4363def7dea8dec7393e135c", + "reference": "c5fd2f071bde071f4363def7dea8dec7393e135c", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev", + "dev-develop": "2.6-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Loader\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-loader", + "keywords": [ + "loader", + "zf2" + ], + "time": "2015-06-03T14:05:47+00:00" + }, { "name": "zendframework/zend-servicemanager", "version": "3.2.0", @@ -1974,6 +2280,124 @@ "psr-7" ], "time": "2017-01-23T22:59:03+00:00" + }, + { + "name": "zendframework/zend-uri", + "version": "2.5.2", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-uri.git", + "reference": "0bf717a239432b1a1675ae314f7c4acd742749ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/0bf717a239432b1a1675ae314f7c4acd742749ed", + "reference": "0bf717a239432b1a1675ae314f7c4acd742749ed", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "zendframework/zend-escaper": "^2.5", + "zendframework/zend-validator": "^2.5" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev", + "dev-develop": "2.6-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Uri\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://github.com/zendframework/zend-uri", + "keywords": [ + "uri", + "zf2" + ], + "time": "2016-02-17T22:38:51+00:00" + }, + { + "name": "zendframework/zend-validator", + "version": "2.8.2", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-validator.git", + "reference": "99b528e01276054458da9553b587cfb959dfa436" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/99b528e01276054458da9553b587cfb959dfa436", + "reference": "99b528e01276054458da9553b587cfb959dfa436", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "php": "^5.5 || ^7.0", + "zendframework/zend-stdlib": "^2.7 || ^3.0" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "^4.0", + "zendframework/zend-cache": "^2.6.1", + "zendframework/zend-config": "^2.6", + "zendframework/zend-db": "^2.7", + "zendframework/zend-filter": "^2.6", + "zendframework/zend-http": "^2.5.4", + "zendframework/zend-i18n": "^2.6", + "zendframework/zend-math": "^2.6", + "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", + "zendframework/zend-session": "^2.6.2", + "zendframework/zend-uri": "^2.5" + }, + "suggest": { + "zendframework/zend-db": "Zend\\Db component", + "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", + "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages as well as to use the various Date validators", + "zendframework/zend-i18n-resources": "Translations of validator messages", + "zendframework/zend-math": "Zend\\Math component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "zendframework/zend-session": "Zend\\Session component", + "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev", + "dev-develop": "2.9-dev" + }, + "zf": { + "component": "Zend\\Validator", + "config-provider": "Zend\\Validator\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Zend\\Validator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a set of commonly needed validators", + "homepage": "https://github.com/zendframework/zend-validator", + "keywords": [ + "validator", + "zf2" + ], + "time": "2017-01-29T17:24:24+00:00" } ], "packages-dev": [ diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 8679f4a..cdf2dbd 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -15,6 +15,7 @@ return [ 'invokables' => [ // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, Helper\ServerUrlHelper::class => Helper\ServerUrlHelper::class, + \App\Service\CiNodeService::class => \App\Service\CiNodeService::class, ], // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ @@ -23,6 +24,7 @@ return [ 'doctrine.entity_manager.orm_default' => \ContainerInteropDoctrine\EntityManagerFactory::class, 'doctrine.hydrator' => \App\Hydrator\DoctrineObjectFactory::class, \App\Service\CiConfigService::class => \App\Service\CiConfigServiceFactory::class, + \App\Service\CiExecutorService::class => \App\Service\CiExecutorServiceFactory::class, ], ], ]; diff --git a/config/autoload/routes.global.php b/config/autoload/routes.global.php index f4aad0d..1e0406b 100644 --- a/config/autoload/routes.global.php +++ b/config/autoload/routes.global.php @@ -9,6 +9,9 @@ return [ 'factories' => [ App\Action\CiConfigAction::class => App\Action\CiConfigFactory::class, App\Action\CiConfigItemAction::class => App\Action\CiConfigItemFactory::class, + App\Action\CiExecutorAction::class => App\Action\CiExecutorFactory::class, + App\Action\JcatPackageAction::class => App\Action\JcatPackageFactory::class, + App\Action\CiStreamAction::class => App\Action\CiStreamFactory::class, App\Action\HomePageAction::class => App\Action\HomePageFactory::class, ], ], @@ -51,5 +54,23 @@ return [ 'allowed_methods' => ['DELETE'], // 'allowed_methods' => ['GET','DELETE'], ], + [ + 'name' => 'api.ci-executor', + 'path' => '/api/ci-executor', + 'middleware' => App\Action\CiExecutorAction::class, + 'allowed_methods' => ['GET'], + ], + [ + 'name' => 'api.jcat-package', + 'path' => '/api/jcat-package', + 'middleware' => App\Action\JcatPackageAction::class, + 'allowed_methods' => ['GET'], + ], + [ + 'name' => 'api.ci-streams', + 'path' => '/api/ci-stream', + 'middleware' => App\Action\CiStreamAction::class, + 'allowed_methods' => ['GET'], + ], ], ]; diff --git a/src/App/Action/CiExecutorAction.php b/src/App/Action/CiExecutorAction.php new file mode 100644 index 0000000..58dd99e --- /dev/null +++ b/src/App/Action/CiExecutorAction.php @@ -0,0 +1,27 @@ +ciExecutorService = $ciExecutorService; + } + + public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null) + { + return new JsonCorsResponse($this->ciExecutorService->debug()); + } +} diff --git a/src/App/Action/CiExecutorFactory.php b/src/App/Action/CiExecutorFactory.php new file mode 100644 index 0000000..e14ddd6 --- /dev/null +++ b/src/App/Action/CiExecutorFactory.php @@ -0,0 +1,15 @@ +get(CiExecutorService::class); + return new CiExecutorAction($service); + } +} diff --git a/src/App/Action/CiStreamAction.php b/src/App/Action/CiStreamAction.php new file mode 100644 index 0000000..df73d99 --- /dev/null +++ b/src/App/Action/CiStreamAction.php @@ -0,0 +1,27 @@ +ciExecutorService = $ciExecutorService; + } + + public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null) + { + return new JsonCorsResponse($this->ciExecutorService->getCiStreams()); + } +} diff --git a/src/App/Action/CiStreamFactory.php b/src/App/Action/CiStreamFactory.php new file mode 100644 index 0000000..8e05fab --- /dev/null +++ b/src/App/Action/CiStreamFactory.php @@ -0,0 +1,15 @@ +get(CiExecutorService::class); + return new CiStreamAction($service); + } +} diff --git a/src/App/Action/JcatPackageAction.php b/src/App/Action/JcatPackageAction.php new file mode 100644 index 0000000..5a8408d --- /dev/null +++ b/src/App/Action/JcatPackageAction.php @@ -0,0 +1,27 @@ +ciExecutorService = $ciExecutorService; + } + + public function getList(ServerRequestInterface $request, ResponseInterface $response, callable $next = null) + { + return new JsonCorsResponse($this->ciExecutorService->getJcatPackages()); + } +} diff --git a/src/App/Action/JcatPackageFactory.php b/src/App/Action/JcatPackageFactory.php new file mode 100644 index 0000000..fe7f6f8 --- /dev/null +++ b/src/App/Action/JcatPackageFactory.php @@ -0,0 +1,15 @@ +get(CiExecutorService::class); + return new JcatPackageAction($service); + } +} diff --git a/src/App/Entity/CiConfig.php b/src/App/Entity/CiConfig.php index 1f7cb8d..316ef2b 100644 --- a/src/App/Entity/CiConfig.php +++ b/src/App/Entity/CiConfig.php @@ -21,6 +21,7 @@ class CiConfig implements JsonSerializable /** * @ORM\OneToMany(targetEntity="CiConfigItem", mappedBy="dailyConfig", cascade={"persist", "remove"}, orphanRemoval=true) + * @var CiConfigItem[] */ private $configItems; @@ -51,7 +52,7 @@ class CiConfig implements JsonSerializable } /** - * @return mixed + * @return CiConfigItem[]|ArrayCollection */ public function getConfigItems() { diff --git a/src/App/Service/CiExecutorService.php b/src/App/Service/CiExecutorService.php new file mode 100644 index 0000000..d93e03d --- /dev/null +++ b/src/App/Service/CiExecutorService.php @@ -0,0 +1,783 @@ + 'rollback_before', + 'isDoForcedRollback' => 'ForcedRollback', + 'isDoUpgrade' => 'upgrade', + 'isDoDummyUpgrade' => 'SystemDummy', + 'isDoLrt' => 'lrt', + 'isDoDsv' => 'cwosdsv', +// 'isDoNmDsv' => '', + 'isDoForcedRollbackDummy' => 'ForcedRollbackDummy', + 'isDoRollbackAfter' => 'rollback_after', + ]; + + private $jenkinsBaseUrl = null; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @var Client + */ + private $httpClient; + + /** + * @var CiNodeService + */ + private $nodeService; + + /** + * CiExecutorService constructor. + * @param EntityManager $entityManager + * @param Client $httpClient + * @param CiNodeService $nodeService + */ + public function __construct(EntityManager $entityManager, Client $httpClient, CiNodeService $nodeService) + { + $this->entityManager = $entityManager; + $this->httpClient = $httpClient; + $this->nodeService = $nodeService; + $this->jenkinsBaseUrl = $this->getActiveJenkinsUrl(); + } + + public function debug() + { + /** @var CiConfig $config */ + $config = $this->entityManager->find(CiConfig::class, 20170317); + return $this->runTest($config->getConfigItems()->first()); + } + + /** + * Get available jcat versions from config2 page + * + * @param bool $cached + * @return array + */ + public function getJcatPackages($cached = true) + { + if (!$cached) { + $config2Html = $this->getConfig2Html(); + $staplerTokens = $this->getStaplerTokens($config2Html, "JCAT_TYPE"); + return $this->getJcatVersions($staplerTokens['url'], $staplerTokens['token']); +// return $this->getJcatVersions($config2Html); + } + + $cache = $this->getCache(self::JCAT_CACHE_KEY); + $result = $cache->getItem(self::PACKAGE_CACHE_KEY, $success); + if(!$success) { + $config2Html = $this->getConfig2Html(); + $staplerTokens = $this->getStaplerTokens($config2Html, "JCAT_TYPE"); + $packages =$this->getJcatVersions($staplerTokens['url'], $staplerTokens['token']); +// $packages =$this->getJcatVersions($config2Html); + $cache->setItem(self::PACKAGE_CACHE_KEY, serialize($packages)); + } else { + $packages = unserialize($result); + } + return $packages; + } + + /** + * @param bool $cached + * @return array|mixed + */ + public function getCiStreams($cached = true) + { + if (!$cached) { + $config2Html = $this->getConfig2Html(); + return $this->getStreams($config2Html); + } + + $cache = $this->getCache(self::STREAM_CACHE_KEY); + $result = $cache->getItem(self::STREAM_CACHE_KEY, $success); + if(!$success) { + $config2Html = $this->getConfig2Html(); + $streams =$this->getStreams($config2Html); + $cache->setItem(self::STREAM_CACHE_KEY, serialize($streams)); + } else { + $streams = unserialize($result); + } + return $streams; + } + + /** + * Executes a daily configuration from the database. + * This method is meant to be called periodically from cron + * + * @todo implement locking mechanism, so cli task won't start if there is a job already running + * @param CiConfig $ciConfig + */ + public function executePendingTests(CiConfig $ciConfig) + { + foreach ($ciConfig->getConfigItems() as $configItem) { + $this->runTest($configItem); + } + } + + /** + * Run a single test configuration item in a daily job + * + * @param CiConfigItem $configItem + * @return array|bool + */ + public function runTest(CiConfigItem $configItem) + { + $nodes = $this->nodeService->getTestNodeList($configItem->getNodes()); + + // @todo implement marking of "sub-job" to track if a node already has the tests going +// $availableNodes = array_filter($nodes, function($node) { +// return $this->isNodeOnline($node) && $this->isNodeIdle($node); +// }); +// +// if(0 == count($availableNodes)) { +// return false; +// } + // @todo remove this part, and check for sub-job success that also needs to be saved, if any node fails, all nodes fail now + foreach($nodes as $node) { + if(!$this->isNodeOnline($node) || !$this->isNodeIdle($node)) { + return false; + } + } + + // Build Config1 and wait till it's finished + // @todo reorganize this into config1build? + $config1Status = $this->buildConfigOne($nodes); + $config1JobId = $this->getConfig1JobId(); + while(!$this->isConfig1Built($config1JobId)) { + sleep(2); + set_time_limit(30); + } + + // Get the generated config2 page and parse it for required data + $config2Html = $this->getConfig2Html(); + $streams = $this->getStreams($config2Html); + $staplerTokens = $this->getStaplerTokens($config2Html, "STREAM"); + if(true === ($streamExists = in_array($configItem->getStream(), $streams))) { + $package = $this->getPackage($configItem->getStream(), $staplerTokens['url'], $staplerTokens['token']); + } else { + $package = false; + } + $jcatVersions = $this->getJcatVersions($staplerTokens['url'], $staplerTokens['token']); + + // Check if the desired JCAT version is available, try to create it if it's not + if(!in_array($configItem->getJcat(), $jcatVersions)) { + // wait for jcat package to become available + $this->addJcatPackageToJenkins($configItem->getJcat()); + $jcatDeployJobId = $this->getJcatDeployJobId(); + while(!$this->isJcatPackageDeployed($jcatDeployJobId)) { + sleep(2); + set_time_limit(30); + } + // @todo check if we have to rebuild config1 or can just proceed with knowing the right parameters + } + + $nodeBases = []; + $nodeTraffics = []; + foreach($nodes as $type => $typeNodes) { + foreach($typeNodes as $node => $backupPostfix) { + if(false === strpos($node, "-")) { + $nodeBaseName = sprintf("%s_%s", $node, $backupPostfix); + $nodeBases[$node] = in_array($nodeBaseName, $this->getNodeBaseNames($config2Html, $node)) ? $nodeBaseName : false; + $nodeTraffics[$node] = $this->getNodeTrafficConfigs($config2Html, $node); + } else { + list($zone1, $zone2) = explode("-", $node); + $zone1BaseName = sprintf("%s_%s", $zone1, $backupPostfix); + $zone2BaseName = sprintf("%s_%s", $zone2, $backupPostfix); + $nodeBases[$zone1] = in_array($zone1BaseName, $this->getNodeBaseNames($config2Html, $zone1)) ? $zone1BaseName : false; + $nodeBases[$zone2] = in_array($zone2BaseName, $this->getNodeBaseNames($config2Html, $zone2)) ? $zone2BaseName : false; + $nodeTraffics[$node] = $this->getNodeTrafficConfigs($config2Html, str_replace("-", "_", $node)); + } + } + } + + $this->buildConfigTwo($configItem, $package, $nodes, $nodeBases, $nodeTraffics); + + return [ + "config1ok"=> $config1Status, +// "stapler" => $staplerTokens, +// "packages" => $package, + "jcat" => $jcatVersions, + "bases" => $nodeBases, + "traffic" => $nodeTraffics, + "configsOk" => $this->config1PreCheck(), + "active.jenkins" => $this->getActiveJenkinsUrl(), + "jcat.exists" => $this->getJcatDeployUrl("TSP_JCAT-138.0_CORE"), + "jcat.nodes" => $this->getAvailableJcatTargetNodes(), + "cwis8.on" => $this->isNodeOnline("cwis8"), + ]; + } + + /** + * Checks is both Config1 and Config2 jobs are idle. + * We can only start building new configs if there is no job running at the moment. + * + * @return bool + */ + private function config1PreCheck(): bool + { + $config1JobId = $this->getConfig1JobId(); + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_1_PATH . "/${config1JobId}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + if($parsedResponse->building == true) { + return false; + } + + $config2JobId = $this->getConfig2JobId(); + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_1_PATH . "/${config2JobId}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + if($parsedResponse->building == true) { + return false; + } + return true; + } + + /** + * @param $nodes + * @return bool + * @todo build uri parameters OR switch to the other uri for api stuff + */ + private function buildConfigOne($nodes): bool + { + $appendParams = []; + foreach($nodes as $type => $typeNodes) { + foreach($typeNodes as $node => $basePostfix) { + $appendParams[] = sprintf("%s=%s", $type, $node); + } + } + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_1_PATH . '/buildWithParameters?delay=0sec&' . implode("&", $appendParams)); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('POST'); + $response = $this->httpClient->send(); + sleep(2); + return $response->isSuccess(); + } + + private function getConfig1JobId() + { + return $this->getJobId($this->jenkinsBaseUrl . self::CONFIG_1_PATH); + } + + private function getConfig2JobId() + { + return $this->getJobId($this->jenkinsBaseUrl . self::CONFIG_2_PATH); + } + + private function getJobId($uri) + { + $this->httpClient->setUri($uri . '/api/json'); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + return $parsedResponse->lastBuild->number; + } + + private function isConfig1Built(int $jobId): bool + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_1_PATH . "/${jobId}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + if($parsedResponse->building == false) { + if ($parsedResponse->result == 'SUCCESS') { + return true; + } + throw new \Exception("Build failed with result: " . $parsedResponse->result); + } + return false; + } + + /** + * @return string + * @throws \Exception + * @todo investigate why GET method on the config page returns HTTP405 + */ + private function getConfig2Html(): string + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_2_PATH . "/build"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + if($response->getStatusCode() == 401) { + throw new \Exception("Not authenticated"); + } + return $response->getBody(); + } + + private function getStaplerTokens($config2Body, $blockName = "STREAM") + { + if(0 === ($didMatch = preg_match(sprintf('#referencedParameters\.push\("%s"\);.*?makeStaplerProxy\(\'(.*?)\',\'(.*?)\'.*?// Create Jenkins proxy#msi', $blockName), $config2Body, $matches))) { + throw new \Exception("Couldn't match stapler tokens in response body"); + } + return [ + 'url' => $matches[1], + 'token' => $matches[2], + ]; + } + + private function getStreams($config2Body) + { + $cssToXpathConverter = new CssSelectorConverter(); + $xpathQuery = $cssToXpathConverter->toXPath('input[value=STREAM]'); + + $domDocument = new \DOMDocument(); + $domDocument->loadHTML($config2Body); + + $xpath = new \DOMXPath($domDocument); + $element = $xpath->query($xpathQuery); + + if($element->length == 0) { + throw new \Exception("Can't find STREAM"); + } + + if($element[0]->nextSibling->nodeName != "select") { + throw new \Exception("Can't find STREAM dropdown"); + } + + $streams = []; + foreach ($element[0]->nextSibling->childNodes as $option) { + $streams[] = $option->textContent; + } + + sort($streams, SORT_NATURAL); + return array_values(array_filter(array_unique($streams), function($item){ + return $item != "" && $item != "none"; + })); + } + + private function getPackage($stream, $url, $token): string + { + $this->httpClient->setUri($this->jenkinsBaseUrl . $url . '/doUpdate'); + $this->httpClient->resetParameters(false, false); + $headers = $this->httpClient->getRequest()->getHeaders(); + $headers->addHeaderLine("Crumb", $token); + $headers->addHeaderLine("Content-type", "application/x-stapler-method-invocation;charset=UTF-8"); + $request = $this->httpClient->getRequest(); + $request->setMethod('POST'); + $request->setContent(sprintf('["STREAM=%s"]', $stream)); + $updateResponse = $this->httpClient->send(); + if(!$updateResponse->isSuccess()) { + throw new \Exception("Stapler doUpdate error"); + } + sleep(1); + $this->httpClient->setUri($this->jenkinsBaseUrl . $url . '/getChoicesForUI'); + $request->setContent("[]"); + $choicesResponse = $this->httpClient->send(); + if(!$choicesResponse->isSuccess()) { + throw new \Exception("Stapler getChoicesForUI error"); + } + $packages = Json::decode($choicesResponse->getBody()); + if($packages[0][0] == "Script error") { + throw new \Exception("STREAM stapler script error"); + } + return array_shift($packages[0]); + } + + /** + * @param $url + * @param $token + * @return mixed + * @throws \Exception + */ + private function getJcatVersions($url, $token) + { + // v1 +// $cssToXpathConverter = new CssSelectorConverter(); +// $xpathQuery = $cssToXpathConverter->toXPath('input[value=JCAT_DIR__MAIN]'); +// +// $domDocument = new \DOMDocument(); +// $domDocument->loadHTML($config2Body); +// +// $xpath = new \DOMXPath($domDocument); +// $element = $xpath->query($xpathQuery); +// +// if($element->length == 0) { +// throw new \Exception("Can't find JCAT_DIR__MAIN"); +// } +// +// if($element[0]->nextSibling->nodeName != "select") { +// throw new \Exception("Can't find JCAT dropdown"); +// } +// +// $jcatVersions = []; +// foreach ($element[0]->nextSibling->childNodes as $option) { +// $jcatVersions[] = $option->textContent; +// } +// +// rsort($jcatVersions, SORT_NATURAL); +// return array_values(array_filter(array_unique($jcatVersions), function($item){return $item != "";})); + + // v2 + $this->httpClient->setUri($this->jenkinsBaseUrl . $url . '/doUpdate'); + $this->httpClient->resetParameters(false, false); + $headers = $this->httpClient->getRequest()->getHeaders(); + $headers->addHeaderLine("Crumb", $token); + $headers->addHeaderLine("Content-type", "application/x-stapler-method-invocation;charset=UTF-8"); + $request = $this->httpClient->getRequest(); + $request->setMethod('POST'); + $request->setContent('["JCAT_TYPE=INSTALLED"]'); + $updateResponse = $this->httpClient->send(); + if(!$updateResponse->isSuccess()) { + throw new \Exception("Stapler doUpdate error"); + } + sleep(1); + $this->httpClient->setUri($this->jenkinsBaseUrl . $url . '/getChoicesForUI'); + $request->setContent("[]"); + $choicesResponse = $this->httpClient->send(); + if(!$choicesResponse->isSuccess()) { + throw new \Exception("Stapler getChoicesForUI error"); + } + $jcats = Json::decode($choicesResponse->getBody()); + rsort($jcats[0]); + if($jcats[0][0] == "Script error") { + throw new \Exception("JCAT stapler script error"); + } + return $jcats[0]; + } + + private function getNodeBaseNames($config2Body, $nodeName) + { + $cssToXpathConverter = new CssSelectorConverter(); + $xpathQuery = $cssToXpathConverter->toXPath(sprintf('input[value=%s_BASE_BACKUP]', $nodeName)); + + $domDocument = new \DOMDocument(); + $domDocument->loadHTML($config2Body); + + $xpath = new \DOMXPath($domDocument); + $element = $xpath->query($xpathQuery); + + if($element->length == 0) { + throw new \Exception(sprintf("Can't find %s_BASE_BACKUP", $nodeName)); + } + + if($element[0]->nextSibling->nodeName != "select") { + throw new \Exception(sprintf("Can't find %s_BASE_BACKUP dropdown", $nodeName)); + } + + $baseNames = []; + foreach ($element[0]->nextSibling->childNodes as $option) { + $baseNames[] = $option->textContent; + } + + rsort($baseNames, SORT_NATURAL); + return array_filter($baseNames, function($item){return $item != "";}); + } + + private function getNodeTrafficConfigs($config2Body, $nodeName) + { + $cssToXpathConverter = new CssSelectorConverter(); + $xpathQuery = $cssToXpathConverter->toXPath(sprintf('input[value=%s_TRAFFIC]', $nodeName)); + + $domDocument = new \DOMDocument(); + $domDocument->loadHTML($config2Body); + + $xpath = new \DOMXPath($domDocument); + $element = $xpath->query($xpathQuery); + + if($element->length == 0) { + throw new \Exception(sprintf("Can't find %s_TRAFFIC", $nodeName)); + } + + if($element[0]->nextSibling->nodeName != "select") { + throw new \Exception(sprintf("Can't find %s_TRAFFIC dropdown", $nodeName)); + } + + $trafficConfigs = []; + foreach ($element[0]->nextSibling->childNodes as $option) { + $trafficConfigs[] = $option->textContent; + } + + rsort($trafficConfigs, SORT_NATURAL); + return array_filter($trafficConfigs, function($item){return $item != "";}); + } + + + /** + * @param CiConfigItem $configItem + * @param bool|string $package + * @param array $nodes + * @param array $nodeBases + * @param array $nodeTraffics + * @return bool + */ + private function buildConfigTwo(CiConfigItem $configItem, $package, array $nodes, array $nodeBases, array $nodeTraffics) + { + $params = []; + if(false === $package) { + // stream doesn't exist, have to be created as new. enforces test to be nightly + $params[] = "MODE=nightly"; + $params[] = "NEW_STREAM=" . $configItem->getStream(); + } else { + $params[] = "MODE=" . ($configItem->isNightly() ? 'nightly' : 'manual'); + $params[] = "STREAM=" . $configItem->getStream(); + $params[] = "PACKAGE_NAME=" . $configItem->getStream(); + } + $params[] = "RECIPIENTS=PDLSPCORED@pdl.internal.ericsson.com"; + $params[] = "JCAT_DIR__MAIN=" . $configItem->getJcat(); + $params[] = "JCAT_DIR__UPGRADE=" . $configItem->getJcat(); + + foreach($nodes as $node) { + if(false === strpos($node, "-")) { + $params[] = sprintf("%s_BASE_BACKUP=%s_%s", $node, $node, $nodeBases[$node]); + } else { + list($zone1, $zone2) = explode("-", $node); + $params[] = sprintf("%s_BASE_BACKUP=%s_%s",$zone1, $zone1, $nodeBases[$zone1]); + $params[] = sprintf("%s_BASE_BACKUP=%s_%s",$zone2, $zone2, $nodeBases[$zone2]); + } + + // todo create suites parameters + foreach ($this->testTypeMap as $callable => $paramName) { + if($configItem->$callable()) { + $params[] = sprintf("%s_SUITES=%s", $node, $paramName); + } + } + + $params[] = sprintf("%s_TRAFFIC=%s", $node, $nodeTraffics[$node]); + } + + $this->httpClient->setUri($this->jenkinsBaseUrl . self::CONFIG_2_PATH . '/buildWithParameters?delay=0sec&' . implode("&", $params)); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('POST'); + $response = $this->httpClient->send(); + sleep(2); + return $response->isSuccess(); + } + + /** + * @param string $namespace + * @return Filesystem + */ + private function getCache(string $namespace): Filesystem + { + $cache = new Filesystem(); + $cache->getOptions() + ->setCacheDir("data/cache") + ->setTtl(3600) + ->setNamespace($namespace) + ; + return $cache; + } + + /** + * Returns uri of jcat deploy or boolean FALSE, if it can't find/guess it. + * + * @param string $jcatVersion The jcat version in the format of TSP-JCATxxx.y[type] + * @return bool|string + */ + private function getJcatDeployUrl(string $jcatVersion) + { + preg_match('#TSP_JCAT-([0-9]{3,}\.[0-9]+)(.*)?#', $jcatVersion, $matches); + + $version = $matches[1]; + $deployType = trim($matches[2],"_"); + unset($matches); + + // package has no type specified, or type is COMMON + if($deployType == "" || $deployType == "COMMON") { + $uri = self::JCAT_DEPLOY_SOURCE_URL . sprintf("/JCAT-%s/TSP_JCAT-%s.tar.gz", $version, $version); + $this->httpClient->setUri($uri); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('HEAD'); + $response = $this->httpClient->send(); + if($response->isOk()) { + return $uri; + } + return false; + } + + // package type specified exists on jcat server + $uri = self::JCAT_DEPLOY_SOURCE_URL . sprintf("/JCAT-%s-%s/TSP_JCAT-%s-%s.tar.gz", $deployType, $version, $deployType, $version); + $this->httpClient->setUri($uri); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('HEAD'); + $response = $this->httpClient->send(); + if($response->isOk()) { + return $uri; + } + + // package type specified exists with 'TSP' prefix on jcat server + $uri = self::JCAT_DEPLOY_SOURCE_URL . sprintf("/JCAT-TSP%s-%s/TSP_JCAT-TSP%s-%s.tar.gz", $deployType, $version, $deployType, $version); + $this->httpClient->setUri($uri); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('HEAD'); + $response = $this->httpClient->send(); + if($response->isOk()) { + return $uri; + } + return false; + } + + private function addJcatPackageToJenkins($jcatVersion) + { + $availableJcatTargetNodes = $this->getAvailableJcatTargetNodes(); + $jcatDeployUri = $this->getJcatDeployUrl($jcatVersion); + $appendParams = [ + "NEW_DEPLOY_NAME=$jcatVersion", + "NEW_DEPLOY_URL=$jcatDeployUri", + "ACTION=add", + ]; + foreach($availableJcatTargetNodes as $node) { + $appendParams[] = "NODES=$node"; + } + $this->httpClient->setUri($this->jenkinsBaseUrl . self::ADD_JCAT_PATH . '/buildWithParameters?delay=0sec&' . implode("&", $appendParams)); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('POST'); + $response = $this->httpClient->send(); + return $response->isSuccess(); + } + + /** + * Returns last started jcat deploy creator jobId + * + * @return int + */ + private function getJcatDeployJobId(): ?int + { + return $this->getJobId($this->jenkinsBaseUrl . self::ADD_JCAT_PATH); + } + + /** + * @todo check if build result is instant or 'inQueue' reflects build state at all + * @param int $jobId + * @return bool + * @throws \Exception + */ + private function isJcatPackageDeployed(int $jobId): bool + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::ADD_JCAT_PATH . "/${jobId}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + if($parsedResponse->inQueue == false) { + if ($parsedResponse->lastBuild->number == $parsedResponse->lastSuccessfulBuild->number) { + return true; + } + throw new \Exception("Build failed."); + } + return false; + } + + private function getAvailableJcatTargetNodes() + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::ADD_JCAT_PATH . "/build"); + $this->httpClient->resetParameters(false, false); + $response = $this->httpClient->send(); + $jcatHandlerPageBody = $response->getBody(); + + $cssToXpathConverter = new CssSelectorConverter(); + $xpathQuery = $cssToXpathConverter->toXPath('input[value=NODES]'); + + $domDocument = new \DOMDocument(); + $domDocument->loadHTML($jcatHandlerPageBody); + + $xpath = new \DOMXPath($domDocument); + $element = $xpath->query($xpathQuery); + + if($element->length == 0) { + throw new \Exception("Can't find NODES"); + } + + if($element[0]->nextSibling->nodeName != "select") { + throw new \Exception("Can't find NODES dropdown"); + } + + $trafficConfigs = []; + foreach ($element[0]->nextSibling->childNodes as $option) { + $trafficConfigs[] = $option->textContent; + } + + sort($trafficConfigs, SORT_NATURAL); + return $trafficConfigs; + } + + private function getActiveJenkinsUrl(): string + { + $this->httpClient->setUri(self::JENKINS_REDIRECT_URL); + $this->httpClient->resetParameters(false, false); + $this->httpClient->setOptions(['maxredirects' => 0]); + $request = $this->httpClient->getRequest(); + $request->setMethod('HEAD'); + $response = $this->httpClient->send(); + if($response->isRedirect()) { + $this->httpClient->setOptions(['maxredirects' => 5]); + return rtrim($response->getHeaders()->get("location")->getFieldValue(), "/"); + } + throw new \Exception("Jenkins redirect url is not working, return code was: " . $response->getStatusCode()); + } + + public function isNodeOnline(string $node): bool + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::NODE_STATUS_PATH . "/${node}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + return !$parsedResponse->offline; + } + + public function isNodeIdle(string $node): bool + { + $this->httpClient->setUri($this->jenkinsBaseUrl . self::NODE_STATUS_PATH . "/${node}/api/json"); + $this->httpClient->resetParameters(false, false); + $request = $this->httpClient->getRequest(); + $request->setMethod('GET'); + $response = $this->httpClient->send(); + $body = $response->getBody(); + $parsedResponse = Json::decode($body); + return $parsedResponse->idle; + } +} diff --git a/src/App/Service/CiExecutorServiceFactory.php b/src/App/Service/CiExecutorServiceFactory.php new file mode 100644 index 0000000..90cefe6 --- /dev/null +++ b/src/App/Service/CiExecutorServiceFactory.php @@ -0,0 +1,19 @@ +get(CiNodeService::class); + $entityManager = $container->get('doctrine.entity_manager.orm_default'); + $config = $container->get('config'); + $httpClient = new Client(); + $httpClient->setAuth($config['ci.auth']['user'], $config['ci.auth']['pass']); + return new CiExecutorService($entityManager, $httpClient, $nodeService); + } +} diff --git a/src/App/Service/CiNodeService.php b/src/App/Service/CiNodeService.php new file mode 100644 index 0000000..eddc184 --- /dev/null +++ b/src/App/Service/CiNodeService.php @@ -0,0 +1,155 @@ + 'esx4b', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => ["7101_base_Jenkins_CI_20161006"], "defaultBase" => "7101_base_Jenkins_CI_20161006", "canAutoStart" => true, + ], + [ + "name" => 'esx10a', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'esx10b', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'asumi', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'michiyo', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'sakura', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'takumi', "type" => 'esx', "size" => 1, "team" => "detoqs", + "bases" => ["7000_base_Jenkins_CI_20160701"], "defaultBase" => "7000_base_Jenkins_CI_20160701", "canAutoStart" => true, + ], + [ + "name" => 'cwsled', "type" => 'maia', "size" => 1, "team" => "taurus", + "bases" => [], "defaultBase" => "", "canAutoStart" => false, + ], + [ + "name" => 'g8z1', "type" => 'nsp5', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'to22', "type" => 'nsp5', "size" => 1, "team" => "detoqs", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'cwis8', "type" => 'nsp6', "size" => 3, "team" => "vikinx", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'cwis9', "type" => 'nsp6', "size" => 3, "team" => "oorio", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'ivis1', "type" => 'nsp6', "size" => 1, "team" => "vikinx", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'ivis2', "type" => 'nsp6', "size" => 1, "team" => "oorio", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'nmis1', "type" => 'nsp6', "size" => 2, "team" => "unknown", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'tina', "type" => 'nsp6', "size" => 1, "team" => "oorio", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'alla', "type" => 'nsp6.1', "size" => 3, "team" => "unknown", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'katyusa', "type" => 'nsp6.1', "size" => 2, "team" => "unknown", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'elena', "type" => 'nsp6.1', "size" => 2, "team" => "oorio", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'natasha', "type" => 'nsp6.1', "size" => 2, "team" => "trex", + "bases" => ["7101_base_Jenkins_CI_20161217"], "defaultBase" => "7101_base_Jenkins_CI_20161217", "canAutoStart" => true, + ], + [ + "name" => 'mazsola-tade', "type" => 'nsp6.1', "size" => 1, "team" => "vikinx", + "bases" => ["7111_base_Jenkins_CI_20170130"], "defaultBase" => "7111_base_Jenkins_CI_20170130", "canAutoStart" => true, + ], + [ + "name" => 'pikkolo', "type" => 'nsp6.1', "size" => 1, "team" => "taurus", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'sonia', "type" => 'nsp6.1', "size" => 1, "team" => "trex", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'tatiana', "type" => 'nsp6.1', "size" => 2, "team" => "taurus", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'opossum', "type" => 'gep5', "size" => 1, "team" => "taurus", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'petymeg', "type" => 'gep5', "size" => 2, "team" => "vikinx", + "bases" => [], "defaultBase" => "", "canAutoStart" => true, + ], + [ + "name" => 'gicnode4', "type" => 'gep5', "size" => 2, "team" => "oorio", + "bases" => [], "defaultBase" => "", "canAutoStart" => false, + ], + [ + "name" => 'gicnode10', "type" => 'gep5', "size" => 1, "team" => "trex", + "bases" => [], "defaultBase" => "", "canAutoStart" => false, + ], + [ + "name" => 'gicnode19', "type" => 'gep5', "size" => 2, "team" => "trex", + "bases" => [], "defaultBase" => "", "canAutoStart" => false, + ], + ]; + + public function getNodes(): array + { + return $this->nodes; + } + + public function getNode($node): ?array + { + return array_pop(array_filter($this->nodes, function($item) use ($node) { + return $item['name'] == $node; + })); + } + + public function getTestNodeList(array $nodes): ?array + { + $filteredNodes = array_filter($this->nodes, function($item) use ($nodes) { + return in_array($item['name'], $nodes); + }); + + $result = []; + foreach ($filteredNodes as $filteredNode) { + $type = strtoupper($filteredNode['type']); + if(!isset($result[$type])) { + $result[$type] = []; + } + $result[$type][$filteredNode['name']] = $filteredNode['defaultBase']; + } + return $result; + } +}