From ae0b899ec9d980d8399e4aeaaa136a2b9bb0a756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Gr=C3=B3s?= Date: Sun, 4 Dec 2016 04:10:25 +0000 Subject: [PATCH] update --- src/Docker.php | 200 +++++++++++++++++++++++++++++++++++++++ src/Services.php | 241 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 src/Docker.php create mode 100644 src/Services.php diff --git a/src/Docker.php b/src/Docker.php new file mode 100644 index 0000000..a2464ae --- /dev/null +++ b/src/Docker.php @@ -0,0 +1,200 @@ + getenv('DOCKER_SOCKET') ? : 'unix:///var/run/docker.sock', + ]); + + $this->client= new $docker($client); + } + + /** + * @param $image + * @param bool $verbose + * + * @return string + */ + public function pull($image, $verbose = true) + { + return $this->docker("pull $image", $verbose); + } + + /** + * @param $options + * @param bool $verbose + * + * @return string + */ + public function run($options, $verbose = false) + { + return $this->docker("run $options", $verbose); + } + + /** + * @param $name + * + * @return bool + */ + public function isNamedContainerRunning($name) + { + $containerManager = $this->client->getContainerManager(); + $containers = $containerManager->findAll(); + + foreach ($containers as $container) { + if ($container->getNames()[0] == "/".$name && $container->getState() == "running") { + return true; + } + } + return false; + } + + /** + * @param $name + * + * @return string + */ + public function getNamedContainerIp($name) + { + $containerManager = $this->client->getContainerManager(); + $containers = $containerManager->findAll(); + + foreach ($containers as $container) { + if ($container->getNames()[0] == "/".$name) { + return $container->getNetworkSettings()->getNetworks()['bridge']->getIPAddress(); + } + } + + return ''; + } + + /** + * @param $name + * + * @return bool + */ + public function getNamedContainerPorts($name) + { + $containerManager = $this->client->getContainerManager(); + $containers = $containerManager->findAll(); + + $ports = []; + + foreach ($containers as $container) { + if ($container->getNames()[0] == "/".$name) { + $containerPorts = $container->getPorts(); + foreach ($containerPorts as $port) { + $ports[] = $port->getPrivatePort(); + } + } + } + return $ports; + } + + /** + * @param $name + * + * @return bool + */ + public function stopNamedContainer($name) + { + $this->docker("stop $name", false); + } + + /** + * @param $name + * + * @return bool + */ + public function removeNamedContainer($name) + { + $this->docker("rm $name", false); + } + + /** + * @param $tag + * + * @return bool + */ + public function imageExists($tag) + { + $imageManager = $this->client->getImageManager(); + $images = $imageManager->findAll(); + + foreach ($images as $image) { + if ($image->getRepoTags()[0] == $tag) { + return true; + } + } + return false; + } + + /** + * @param $options + * @param bool $verbose + * + * @return string + */ + private function docker($options, $verbose = true) + { + $sudo = $this->sudo ? 'sudo' : ''; + + $process = new Process("$sudo docker $options"); + $process->run(function ($type, $buffer) use ($verbose) { + if (!$verbose) { + return; + } + + if (Process::ERR === $type) { + print $buffer; + } else { + print $buffer; + } + }); + + if (!$process->isSuccessful()) { + throw new ProcessFailedException($process); + } + + return $process->getOutput(); + } + + /** + * @param bool $sudo + * + * @return $this + */ + public function sudo($sudo = true) + { + $this->sudo = $sudo; + + return $this; + } +} diff --git a/src/Services.php b/src/Services.php new file mode 100644 index 0000000..6fce164 --- /dev/null +++ b/src/Services.php @@ -0,0 +1,241 @@ + [ + 'name' => 'mysql', + 'tag' => 'latest', + 'command' => + '--name=laravel-mysql -d -v /mysql:/var/lib/mysql \\'. + '-e MYSQL_USER=ENV[DB_USERNAME] \\'. + '-e MYSQL_PASSWORD=ENV[DB_PASSWORD] \\'. + '-e MYSQL_DATABASE=ENV[DB_DATABASE] \\'. + '-e MYSQL_ROOT_PASSWORD=ENV[DB_PASSWORD] mysql \\'. + '--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci', + ], + 'Redis' => [ + 'name' => 'redis', + 'tag' => 'alpine', + 'command' => + '-d --name=laravel-redis -p ENV[REDIS_PORT]:ENV[REDIS_PORT] \\'. + 'redis redis-server --appendonly yes --requirepass ENV[REDIS_PASSWORD]', + ], + 'Beanstalkd' => [ + 'name' => 'schickling/beanstalkd', + 'tag' => 'latest', + 'command' => '-d --name=laravel-beanstalkd -p 11300:11300 schickling/beanstalkd', + ], + 'Memcached' => [ + 'name' => 'memcached', + 'tag' => 'alpine', + 'command' => '-d --name=laravel-memcached -p ENV[MEMCACHED_PORT]:ENV[MEMCACHED_PORT] memcached', + ], + ]; + /** + * @var \App\Console\Commands\Docker + */ + protected $docker; + + /** + * Create a new command instance. + * + * @param \idrislab\DockerServices\Docker $docker + */ + public function __construct(Docker $docker) + { + parent::__construct(); + + $this->docker = $docker; + } + + /** + * @throws \Exception + */ + public function handle() + { + $services = getenv('DOCKER_SERVICES'); + if (!$services) { + throw new Exception("DOCKER_SERVICES is not defined"); + } + + $network = []; + + collect(explode(",", $services)) + ->mapWithKeys(function ($service) { + //Retrieve only current service definition + $services = collect($this->services)->map(function ($attributes, $current) use (&$service) { + if (strtolower($current) === strtolower($service)) { + $service = $current; + + return $attributes; + } + })->only($service); + + return $services; + }) + ->each(function ($attributes, $service) use (&$network) { + + $containerName = strtolower('laravel-'.$service); + + switch ($this->argument('option')) { + case "start": + $this->runContainer($containerName, $service, $attributes); + break; + case 'stop': + return $this->stopContainer($containerName, $service); + break; + case 'restart': + $this->restartContainer($containerName, $service, $attributes); + break; + case 'info': + break; + } + + $network[] = $this->getServiceContainerNetwork($service, $containerName); + }); + + $this->renderNetworkTable($network); + } + + /** + * @param $string + * + * @throws Exception + * @return string + */ + public function parseDotEnvVars($string) + { + $found = preg_match_all('/((.*?)ENV\[([^|]+?)(\|(.+))?\](.*?))/', $string, $matches); + + if ($found) { + foreach ($matches[3] as $variable) { + $value = getenv($variable); + + if ($value === false) { + throw new Exception(sprintf("Environment variable '%s' was not found", $variable)); + } + + $string = preg_replace("/ENV\\[$variable\\]/su", $value, $string); + } + } + + return $string; + } + + /** + * @param $containerName + * @param $service + * @param $attribute + */ + private function runContainer($containerName, $service, $attribute) + { + $tag = $attribute['name'].':'.$attribute['tag']; + + if (!$this->docker->imageExists($tag)) { + $this->docker->pull($tag); + } + + if ($this->docker->isNamedContainerRunning($containerName)) { + if (!$this->confirm("$service is already running, do you want restart?")) { + return; + } + $this->stopContainer($containerName, $service); + } + + $this->info('Starting '.$service, false); + $command = $this->parseDotEnvVars($attribute['command']); + + try { + $this->docker->run($command); + } catch (Exception $e) { + $this->docker->removeNamedContainer($containerName); + $this->docker->run($command); + } + } + + /** + * @param $containerName + * @param $service + */ + private function stopContainer($containerName, $service) + { + if ($this->option('service') != "" && strtolower($this->option('service')) != strtolower($service)) { + return; + } + + $this->info("Stopping $service"); + $this->docker->stopNamedContainer($containerName); + $this->docker->removeNamedContainer($containerName); + } + + /** + * @param $containerName + * @param $service + * @param $attributes + */ + private function restartContainer($containerName, $service, $attributes) + { + $this->info('Restarting '.$service); + $this->stopContainer($containerName, $service); + $this->runContainer($containerName, $service, $attributes); + } + + /** + * @param $service + * @param $containerName + * + * @return array + */ + private function getServiceContainerNetwork($service, $containerName) + { + $host = $this->docker->getNamedContainerIp($containerName); + $port = $this->docker->getNamedContainerPorts($containerName); + + return [ + 'service' => ucfirst($service), + 'host' => $host, + 'port' => implode(", ", $port), + ]; + } + + /** + * @param $network + */ + private function renderNetworkTable($network) + { + if (empty($network)) { + return; + } + + $headers = ['Service', ' Host', 'Port']; + $this->table($headers, $network); + } +}