Kcko
10/3/2019 - 5:22 PM

Ref hell (yml docker container)

<?php

namespace Andweb\Yml;
use Nette;

class DefaultSettings
{
	use Nette\SmartObject;

	protected $arr;

	public function getDefaultSettings()
	{
		
		$this->arr['version'] = '3.6';
		$this->arr['ports']['mode'] = 'host';


		$this->arr['labels']['name'] = 'aw-awesome-hosting';
		$this->arr['labels']['value'] = 'yes';


		$this->arr['deploy']['replicas'] = 1;
		$this->arr['deploy']['restart_policy']['delay'] = "10s";
		$this->arr['deploy']['restart_policy']['window'] = "30s";
		$this->arr['deploy']['update_config'] = [
			'parallelism' => 1,
			'delay' => '30s',
			'order' => 'stop-first',
		];

		
		$this->arr['deploy']['resources']['reservations'] = [
			'cpus' => '0.25',
			'memory' => '1G',
		];

		$this->arr['networks']['external'] = "true"; 

		return $this->arr;
	}


}
<?php

namespace App\ServiceModule\Presenters;

use Nette,
	Andweb,
	Symfony\Component\Yaml\Dumper,
	Symfony\Component\Yaml\Yaml,
	Andweb\Yml;



class YmlGeneratorPresenter extends Andweb\Application\UI\Presenter
{
    /**
     * Database connection
     *
     * @var Andweb\Database\Context
     * @inject
     */
    public $connection;

	/* defaultni hodnoty */
	protected $defaults;

	/* pole ktere drzi hodnoty nase */
	protected $arr;
	

	public function __construct()
	{
		
	}


	public function getDataFromRow($id = 1)
	{
		/* [2] Vyber dat z DB
		---------------------------------------------------------------------------------------------------- */
		// kontejner
		$container = $this->connection->table('hosting_service_docker_container')->where('id', $id)->fetch();
		$containerArr = $container->toArray();
		// networks
		$networks = $container->related('hosting_service_docker_container_network');
		$networksArr = [];
		foreach ($networks as $network)
		{
			$networksArr[] = $network->hosting_service_docker_network->name;
		}
		// ports
		$ports = $container->related('hosting_service_docker_expose');
		$portsArr = [];
		$index = 0;
		foreach ($ports as $port)
		{
			$portsArr[$index++] = [
				'target'    => $port->destination,
				'published' => $port->source,
				'protocol'  => $port->proto,
				//'mode'      => $this->defaults['ports']['mode'], // doplneno v builderu
			];
		}
		// volumes
		$volumes = $container->related('hosting_service_docker_volume');
		$volumesArr = [];
		foreach ($volumes as $volume)
		{
			$volumesArr[] = implode(':', [ implode('/', [$volume->volume_dir, $volume->volume_file]), $volume->destination]);
		}
		// environments
		$environments = $container->related('hosting_service_docker_env');
		$environmentsArr = [];
		foreach ($environments as $env)
		{
			$environmentsArr[$env->env_name] = $env->env_value;
		}

		return [
			'container' => $container,
			'containerArr' => $containerArr,
			'networksArr' => $networksArr,
			'volumesArr' => $volumesArr,
			'portsArr' => $portsArr,
			'environmentsArr' => $environmentsArr

		];
	}





	public function actionYml()
	{
		$defaults = (new Yml\DefaultSettings)->getDefaultSettings();
		$builder = new Yml\DockerComposeBuilder($defaults);

		// hlavicka
		$builder->createHeader();
		
		// jednotlivy kontejnery
		$builder->createSection($container1 = $this->getDataFromRow(1));
		$builder->createSection($container2 = $this->getDataFromRow(2));

		// paticka
		$builder->createFooter($container1['networksArr']);
		$builder->createFooter($container2['networksArr']);

		// DUMP
		$dumper = new Dumper();
		$yaml = $dumper->dump($builder->getArr(), 7);
		
		echo $yaml;
		file_put_contents(INDEX_DIR . '/yml/demo.yml', $yaml);

		$this->terminate();
	}



	public function actionArr()
	{
		$defaults = (new Yml\DefaultSettings)->getDefaultSettings();
		$restorer = new Yml\RestoreResolver($defaults);
		
		$restorer->resolveData(Yaml::parse(file_get_contents(INDEX_DIR . '/yml/demo-test.yml')));

		\Tracy\Debugger::dump($restorer->getSanitizedData());
		$this->terminate();
	}



}
<?php

namespace Andweb\Yml;
use Nette;

class RestoreResolver
{
	use Nette\SmartObject;

	protected $defaults;
	protected $arr;

	public function __construct($defaults)
	{
		$this->defaults = $defaults;
	}

	public function resolveData($arr)
	{
		$this->arr = &$arr;

		// version
		$this->arr['version'] = $this->defaults['version'];

		foreach ($arr['services'] as $sectionName => &$section)
		{
			$this->restoreSection($sectionName, $section);
		}
		
		// networks section
		$this->reverseNetworks();

	}

	public function getSanitizedData()
	{
		return $this->arr;
	}

	protected function restoreSection($containerName, &$sectionArr)
	{
		$allowedKeys = [
			'image',
			'networks',
			'ports',
			'volumes',
			'labels',
			'deploy',
			'environments'
		];

		foreach ($sectionArr as $itemName => &$vals)
		{
			if (!in_array($itemName, $allowedKeys))
			{
				unset($this->arr['services'][$containerName][$itemName]);
			}

			if (method_exists($this, 'serviceRestore'.$itemName))
			{
				$this->{'serviceRestore'.$itemName}($vals);
			}
		}
		
	}



	// subservices -- auto calls see above line: 60

	public function serviceRestoreDeploy(&$deploy)
	{
		$allowedKeys = [
			'deploy' 
				=> [
					'replicas' => TRUE,
					'restart_policy' => [
						'condition' => TRUE,
						'delay'     => TRUE,
						'window'     => TRUE,
					],	
					'update_config' => [
						'parallelism' => TRUE,
						'delay'     => TRUE,
						'order'     => TRUE,
					],	
					'resources' => [
						'limits' => [
							'memory' => TRUE,
							'cpus' => TRUE,
						],
						'reservations' => [
							'memory' => TRUE,
							'cpus' => TRUE,
						],
					],	
				]
		];


		$this->arrayWalker($deploy, 0, $allowedKeys, ['deploy']);
		
	}

	public function arrayWalker(&$deploy, $level = 0, $allowedKeys, $key = [])
	{
		foreach ($deploy as $k => &$v)
		{
			if (is_array($v))
			{
				$merge = array_merge($key, [$k]);
				$this->arrayWalker($v, $level + 1, $allowedKeys, $merge);
			}
			else
			{
				$keyPart = $key;
				$keyPartK = array_merge($keyPart, [$k]);
				$inWhiteList = & Nette\Utils\Arrays::getRef($allowedKeys, $keyPartK);
				
				// DOPRDELE S TIM!, nema to tam co delat
				if (!$inWhiteList)
				{
					unset($deploy[$k]);
				}

				$inDefaults = Nette\Utils\Arrays::get($this->defaults, $keyPartK, NULL);
				if ($inDefaults)
				{
					$deploy[$k] = $inDefaults;
				}

				
	
			}
		}
	}

	public function serviceRestoreLabels(&$label)
	{
		$label = []; // reset
		$label[$this->defaults['labels']['name']] = $this->defaults['labels']['value'];
	}


	public function serviceRestorePorts(&$ports)
	{
		$allowedKeys = [
			'target',
			'published',
			'protocol',
			'mode',
		];

		$defaultSettingsKeys = [
			'mode',
		];
	
		foreach ($ports as $index => &$arr)
		{
			foreach ($arr as $k => $v)
			{
				if (!in_array($k, $allowedKeys))
					unset($arr[$k]);
				
				if (in_array($k, $defaultSettingsKeys))
					$arr[$k] = $this->defaults['ports'][$k];
			}
		}

	}

	protected function reverseNetworks()
	{
		foreach ($this->arr['networks'] as $networkSectionName => $network)
		{
			foreach ($network as $k => $v)
			{
				if ($k != 'external') 
				{
					unset($this->arr['networks'][$networkSectionName][$k]);
				}
				else
				{
					$this->arr['networks'][$networkSectionName][$k] = $this->defaults['networks']['external'];
				}
			}
	
		}
	}

}
<?php

namespace Andweb\Yml;
use Nette;

class DockerComposeBuilder
{
	use Nette\SmartObject;

	protected $defaults;
	protected $arr;

	public function __construct($defaults)
	{
		$this->defaults = $defaults;
	}

	public function createHeader()
	{
		$this->arr = [
			'version' => $this->defaults['version'],
			'services' => []
		];
	}


	public function createSection($data)
	{
		$containerArr    = $data['containerArr'];
		$containerArr    = $data['containerArr'];
		$networksArr     = $data['networksArr'];
		$volumesArr      = $data['volumesArr'];
		$portsArr        = $data['portsArr'];
		$environmentsArr = $data['environmentsArr'];

		// Kontejner, jmeno - unikatni
		$contName = $containerArr['name'];

		//$this->arr['services'][$contName] = [];
		$container = & $this->arr['services'][$contName];
		// image
		$container['image'] = $containerArr['image'];
		// networks
		$container['networks'] = $networksArr;
		// ports
		// doplneni defaultni hodnoty "mode"
		foreach ($portsArr as & $arr)
			$arr['mode'] = $this->defaults['ports']['mode'];
		unset($arr); // https://coderwall.com/p/qx3fpa/php-foreach-pass-by-reference-do-it-right-or-better-not-at-all
			
	
		$container['ports'] = $portsArr;
		// ports
		$container['volumes'] = $volumesArr;
		// labels
		$container['labels'] = [$this->defaults['labels']['name'] => $this->defaults['labels']['value']];
		
		// deploy
		$deploy = & $container['deploy'];
		$deploy['replicas'] = $this->defaults['deploy']['replicas'];
		$deploy['restart_policy'] = [
			'condition' => $containerArr['restart_policy'],
			'delay'     => $this->defaults['deploy']['restart_policy']['delay'],
			'window'    => $this->defaults['deploy']['restart_policy']['window'],
		];
		
		$deploy['update_config'] = $this->defaults['deploy']['update_config'];
		
		// deploy resources
		$resources = & $deploy['resources'];
		$resources['limits']['memory']       = $containerArr['quota_memory'];
		$resources['limits']['cpus']         = $containerArr['quota_cpu'];
		$resources['reservations']['cpus']   = $this->defaults['deploy']['resources']['reservations']['cpus'];
		$resources['reservations']['memory'] = $this->defaults['deploy']['resources']['reservations']['memory'];
		
		// environments
		$container['environments'] = $environmentsArr;

	}

	public function createFooter($networks)
	{
		foreach ($networks as $network)
		{
			$this->arr['networks'][$network]['external'] = $this->defaults['networks']['external'];
		}
	}


	public function getArr()
	{
		return $this->arr;
	}
}