diff --git a/composer.json b/composer.json index d957ee3..0271219 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "email": "luis@idris.pt" } ], - "keywords": ["laravel", "docker", "services"], + "keywords": ["laravel", "docker", "containers", "automate"], "license": "MIT", "require": { "php": ">=5.6.4", diff --git a/src/DockerContainers.php b/src/DockerContainers.php index 164fd6e..efb7b46 100644 --- a/src/DockerContainers.php +++ b/src/DockerContainers.php @@ -65,7 +65,22 @@ class DockerContainers extends Command * @var Docker */ protected $docker; + /** + * @var array + */ + protected $attributes; + /** + * @var array + */ protected $network = []; + /** + * @var array + */ + protected $instances; + /** + * @var array + */ + protected $container; /** * Create a new command instance. @@ -92,27 +107,37 @@ public function handle() collect(explode(",", $containers)) ->mapWithKeys(function ($container) { //Retrieve only current container attributes - $containers = collect($this->containers)->map(function ($attributes, $current) use (&$container) { - if (strtolower($current) === strtolower($container)) { - $container = $current; - - return $attributes; - } - })->only($container); + $containers = collect($this->containers) + ->map(function ($attributes, $current) use (&$container) { + if ($this->option('name') === null) { + if (strtolower($current) === strtolower($container)) { + $container = $current; + + return $attributes; + } + } elseif (strtolower($this->option('name')) === strtolower($current)) { + $container = $current; + + return $attributes; + } + }) + ->only($container); return $containers; }) ->each(function ($attributes, $container) { + $this->prepare($container, $attributes); + switch ($this->argument('option')) { case "start": - $this->startContainer($container, $attributes); + $this->start(); break; case 'stop': - return $this->stopContainer($container, $attributes); + return $this->stop(); break; case 'restart': - $this->restartContainer($container, $attributes); + $this->restart(); break; default: } @@ -122,6 +147,8 @@ public function handle() } /** + * Add containers + * * @param array $containers */ protected function addContainers(array $containers) @@ -130,116 +157,81 @@ protected function addContainers(array $containers) } /** - * @param string $container - * @param array $attributes + * Start docker container */ - private function startContainer($container, array $attributes) + private function start() { - $tag = $attributes['repo'].':'.$attributes['tag']; - if (!$this->docker->imageExists($tag)) { - $this->docker->pull($tag); - } + $this->pullImage(); - $instances = $this->getContainerInstances($container, $attributes); + foreach ($this->instances as $container) { + $this->container = $container; - foreach ($instances as $instance) { - if ($this->docker->isNamedContainerRunning($instance['name'])) { - if (!$this->confirm("$container is already running, do you want restart?")) { - continue; + if ($this->docker->isNamedContainerRunning($container['name'])) { + if ($this->confirm("{$container['name']} is already running, do you want restart?")) { + $this->restart(); } - - $this->restartContainer($instance, $attributes); - break; + continue; } - $this->runContainer($instance, $attributes); + $this->runContainer(); } } /** - * @param array $container - * @param array $attributes - * - * @throws Exception + * Run docker container */ - private function runContainer(array $container, array $attributes) + private function runContainer() { - $name = $container['name']; - $instance = $container['instance']; - - putenv('INSTANCE_NAME='.$instance); - - $this->info("Starting {$container['service']} $instance", false); - - if (isset($attributes['command'])) { - $command = $attributes['command']; - } else { - if (isset($attributes['commands'])) { - $command = $attributes['commands'][$instance]; - } else { - throw new Exception("Container {$container['service']} command or commands must be set"); - } - } + putenv('CURRENT_INSTANCE='.$this->container['instance']); - if (isset($attributes['docker']['pre'])) { - foreach ($attributes['docker']['pre'] as $command) { - $this->docker->docker($command); - } - } - - $command = '--name '.$name." ".$this->parseDotEnvVars($command); - $this->docker->run($command); + $this->info("Starting {$this->container['service']} #{$this->container['instance']}", false); - $network = isset($attributes['network']) ? $attributes['network'] : 'bridge'; - $this->network[] = $this->getContainerNetwork($container, $network); + $this->preCommand(); + $this->runCommand($this->getContainerCommand()); + $this->postCommand(); - if (isset($attributes['docker']['post'])) { - foreach ($attributes['docker']['post'] as $command) { - $this->docker->docker($command); - } - } + $this->setContainerNetwork(); } /** - * @param string $container - * @param array $attributes + * Stop docker container */ - private function stopContainer($container, array $attributes) + private function stop() { - if ($this->option('service') != "") { - if (strtolower($this->option('service')) != strtolower($container)) { + collect($this->instances)->each(function ($container) { + $this->info("Stopping {$container['service']} #{$container['instance']}"); + + if (!$this->docker->isNamedContainerRunning($container['name'])) { + if ($this->confirm("{$container['service']} is not running, do you want start?")) { + $this->start(); + } + return; } - } - $containers = $this->getContainers($container, $attributes); - - collect($containers)->each(function ($container) use ($container) { - $this->info("Stopping $container ".$container['instance']); $this->docker->stopNamedContainer($container['name']); $this->docker->removeNamedContainer($container['name']); }); } - /** - * @param string $container - * @param array $attributes + * Restart docker containers */ - private function restartContainer($container, array $attributes) + private function restart() { - $this->info('Restarting '.$container); - $this->stopContainer($container, $attributes); - $this->startContainer($container, $attributes); + $this->stop(); + $this->start(); } /** + * Prepare environment + * * @param string $container * @param array $attributes * * @return array */ - private function getContainerInstances($container, array $attributes) + private function prepare($container, array $attributes) { $name = strtolower('laravel-'.$container); $instances = isset($attributes['instances']) ? (int)$attributes['instances'] : 1; @@ -252,16 +244,19 @@ private function getContainerInstances($container, array $attributes) putenv($envVar); } - return $containers; + $this->instances = $containers; + $this->attributes = $attributes; } /** + * Parse environment variables + * * @param $string * * @throws Exception * @return string */ - public function parseDotEnvVars($string) + public function parseEnvVars($string) { $found = preg_match_all('/((.*?)ENV\[([^|]+?)(\|(.+))?\](.*?))/', $string, $matches); @@ -282,30 +277,103 @@ public function parseDotEnvVars($string) } /** - * @param array $container - * @param string $network + * Set container network information * * @return array */ - private function getContainerNetwork($container, $network) + private function setContainerNetwork() { - $host = $this->docker->getNamedContainerIp($container['name'], $network); - $port = $this->docker->getNamedContainerPorts($container['name']); + $network = isset($this->attributes['network']) ? $this->attributes['network'] : 'bridge'; - return [ - 'service' => ucfirst($container['service']), - 'host' => $host, - 'port' => implode(", ", $port), + $host = $this->docker->getNamedContainerIp($this->container['name'], $network); + $port = $this->docker->getNamedContainerPorts($this->container['name']); + + $this->network[] = [ + 'container' => ucfirst($this->container['service']).' #'.$this->container['instance'], + 'host' => $host, + 'port' => implode(", ", $port), ]; } + /** + * Render a console table displaying network information + * for each docker container running + */ private function renderNetworkTable() { if (empty($this->network)) { return; } - $headers = ['Service', ' Host', 'Port']; + $headers = ['Container', 'Host', 'Port']; $this->table($headers, $this->network); } + + /** + * Pull image from docker hub + */ + private function pullImage() + { + $tag = $this->attributes['repo'].':'.$this->attributes['tag']; + if (!$this->docker->imageExists($tag)) { + $this->docker->pull($tag); + } + } + + /** + * Get normalized docker run command + * + * @return string + * @throws \Exception + */ + private function getContainerCommand() + { + $attributes = $this->attributes; + + if (isset($attributes['command'])) { + $command = $attributes['command']; + } else { + if (isset($attributes['commands'])) { + $command = $attributes['commands'][$this->container['instance']]; + } else { + throw new Exception("Container {$this->container['service']} command or commands must be set"); + } + } + + return '--name '.$this->container['name'].' '.$this->parseEnvVars($command); + } + + /** + * Run pre execution commands + */ + private function preCommand() + { + if (isset($attributes['docker']['pre'])) { + foreach ($attributes['docker']['pre'] as $command) { + $this->docker->docker($this->parseEnvVars($command)); + } + } + } + + /** + * Run post execution commands + */ + private function postCommand() + { + if (isset($attributes['docker']['post'])) { + foreach ($attributes['docker']['post'] as $command) { + $this->docker->docker($this->parseEnvVars($command)); + } + } + } + + /** + * Perform docker run + * + * @param string $command + */ + private function runCommand($command) + { + $this->docker->run($command); + } }