public static function runPhpCode($code, $build = 'system') { if ($build == 'system') { $fpmPort = '9001'; } else { $phps = Config::get('php::builds'); $phpsInstalled = Util::getInstalledPhps(); if (empty($phps[$build]) || !in_array($build, $phpsInstalled)) { throw new \Exception("Unable to find php {$build}"); } if (empty($phps[$build]['fpm']['port'])) { throw new \Exception("Unable to find fpm port for php {$build}"); } $fpmPort = $phps[$build]['fpm']['port']; } $filename = uniqid('coderunner_', true) . '.php'; $filepath = '/tmp/' . $filename; $fs = self::getFilesystem(); $fs->touch($filepath); $fs->chmod($filepath, 0777); $fs->write($filepath, $code); $scname = '/' . $filename; $scfname = $filepath; $scroot = dirname($scfname); $cmd = 'SCRIPT_NAME=%s \\ SCRIPT_FILENAME=%s \\ DOCUMENT_ROOT=%s \\ REQUEST_METHOD=GET \\ cgi-fcgi -bind -connect 127.0.0.1:%s'; $cmd = sprintf($cmd, $scname, $scfname, $scroot, $fpmPort); self::exec($cmd, false, $output); $fs->remove($filepath); return $output; }
protected function __getInstalledPhps() { $phps = Config::get('php::builds'); if (empty($phps)) { return []; } $phpsInstalled = Util::getInstalledPhps(); $default_build = null; foreach ($phps as $build => $meta) { if (!in_array($build, $phpsInstalled)) { unset($phps[$build]); continue; } if (isset($meta['extensions'])) { foreach ($meta['extensions'] as $extname => $extmeta) { if (isset($extmeta['installed']) && !$extmeta['installed']) { unset($meta['extensions'][$extname]); } } } $pidfile = '/opt/phpbrew/php/' . $build . '/var/run/php-fpm.pid'; $phps[$build]['running'] = false; if (file_exists($pidfile) && trim(file_get_contents($pidfile)) != "") { $phps[$build]['running'] = true; } if (!empty($phps[$build]['default'])) { $default_build = $build; } $phps[$build]['default'] = false; } if ($default_build) { $phps[$default_build]['default'] = true; } return $phps; }
/** * @throws \Exception */ public function run() { if (!$this->command instanceof ProvisionCommand) { throw new \Exception("The ConfigDefaults task can only be run by the Provision command."); } $fs = Util::getFilesystem(); $config_files = SyncManager::getRules(SyncManager::SYNC_FILE); $config_dirs = SyncManager::getRules(SyncManager::SYNC_DIR); $config_sources = array_merge(array_column($config_files, 'source'), array_column($config_dirs, 'source')); if ($fs->exists($config_sources)) { return; } $config_sources_dirs = []; foreach ($config_sources as $config_source) { $config_source_dir = dirname($config_source); while ($config_source_dir != '/vagrant' && !in_array($config_source_dir, $config_sources_dirs)) { $config_sources_dirs[] = $config_source_dir; $config_source_dir = dirname($config_source_dir); } } $fs->mkdir($config_sources_dirs, 0777, 'vagrant'); foreach ($config_files as $file) { if (empty($file['default'])) { continue; } $target_file = $file['source']; $origin_file = $file['default']; if (!file_exists($target_file)) { $this->output->writeInfo("Writing default config file '{$target_file}'"); $fs->copy($origin_file, $target_file, true, 'vagrant'); } } foreach ($config_dirs as $dir) { if (empty($dir['default'])) { continue; } $source_dir = $dir['default']; $target_dir = $dir['source']; if (file_exists($target_dir)) { continue; } $this->output->writeInfo("Writing default config dir '{$target_dir}'"); $fs->mkdir($target_dir, 0777, 'vagrant'); $finder = new Finder(); foreach ($finder->files()->in($source_dir)->ignoreDotFiles(false)->depth('== 0') as $origin_dir_file) { $origin_dir_filename = $origin_dir_file->getFilename(); $target_dir_filepath = $target_dir . '/' . $origin_dir_filename; $origin_dir_filepath = $source_dir . '/' . $origin_dir_filename; $fs->copy($origin_dir_filepath, $target_dir_filepath, true, 'vagrant'); } } }
/** * @throws \Exception */ public function run() { if (!$this->command instanceof ProvisionCommand) { throw new \Exception("The InitialSetup task can only be run by the Provision command."); } $lock = '/etc/dashbrew/initialsetup.lock'; if (file_exists($lock)) { return; } //$this->output->writeInfo("Running initial apt-get update"); //Util::process($this->output, "apt-get -y update", ['timeout' => null]); $fs = Util::getFilesystem(); $fs->touch($lock); }
protected function execute(InputInterface $input, OutputInterface $output) { $sw = Util::getStopwatch(); $sw->start('provision'); try { // Run provisioning tasks $this->runTasks($input, $output); } catch (\Exception $e) { $output->writeError($e->getMessage()); } $duration = round($sw->stop('provision')->getDuration() / 1000, 2); $duration_unit = 's'; if ($duration > 60) { $duration = round($duration / 60, 2); $duration_unit = 'm'; } $output->writeInfo("Finished in {$duration}{$duration_unit}"); }
/** * @throws \Exception */ public function run() { if (!$this->command instanceof ProvisionCommand) { throw new \Exception("The ServiceRestart task can only be run by the Provision command."); } $this->output->writeInfo("Restarting services"); $this->output->writeDebug("Stopping monit"); Util::process($this->output, "monit quit"); // wait until monit quits while (file_exists('/var/run/monit.pid')) { usleep(500000); } $services = ServiceManager::getServices(); $this->output->writeDebug("Stopping services"); foreach ($services as $service) { Util::process($this->output, "monit stop {$service}"); } $this->output->writeDebug("Starting services"); foreach ($services as $service) { Util::process($this->output, "monit start {$service}"); } $this->output->writeDebug("Starting monit"); Util::process($this->output, "monit"); }
/** * Syncs directories files between the host and guest using bi-directional sync algorithm */ protected function syncDirs() { $fs = Util::getFilesystem(); $sync_status = []; if (file_exists(self::SYNC_STATUS_FILE)) { $sync_status = json_decode(file_get_contents(self::SYNC_STATUS_FILE), true); } $sync_status_new = []; $config_dirs = SyncManager::getRules(SyncManager::SYNC_DIR); foreach ($config_dirs as $dir) { if (empty($dir['path'])) { continue; } $source_dir = $dir['source']; $source_dir_owner = 'vagrant'; $source_dir_group = 'vagrant'; $target_dir = $dir['path']; $target_dir_owner = !empty($dir['owner']) ? $dir['owner'] : null; $target_dir_group = !empty($dir['group']) ? $dir['group'] : null; if ($source_dir == $target_dir) { continue; } $dir_sync_status = ['old' => [], 'new' => []]; if (isset($sync_status[$target_dir])) { $dir_sync_status['old'] = $sync_status[$target_dir]; } $finder = new Finder(); foreach ($finder->files()->in($source_dir)->ignoreDotFiles(false)->depth('== 0') as $source_dir_file) { $source_dir_filename = $source_dir_file->getFilename(); $source_dir_filepath = $source_dir . '/' . $source_dir_filename; $target_dir_filepath = $target_dir . '/' . $source_dir_filename; // Sync file changes if (file_exists($target_dir_filepath)) { $dir_sync_status['new'][] = $source_dir_filename; if (md5_file($source_dir_filepath) === md5_file($target_dir_filepath)) { continue; } $source_dir_filetime = filemtime($source_dir_filepath); $target_dir_filetime = filemtime($target_dir_filepath); if ($source_dir_filetime >= $target_dir_filetime) { $copy_from = $source_dir_filepath; $copy_to = $target_dir_filepath; $copy_owner = $target_dir_owner; $copy_group = $target_dir_group; } else { $copy_from = $target_dir_filepath; $copy_to = $source_dir_filepath; $copy_owner = $source_dir_owner; $copy_group = $source_dir_group; } $this->output->writeInfo("Syncing config file '{$source_dir_filepath}' " . ($copy_from == $source_dir_filepath ? "->" : "<-") . " '{$target_dir_filepath}'"); $fs->copy($copy_from, $copy_to, true, $copy_owner, $copy_group); } else { if (in_array($source_dir_filename, $dir_sync_status['old'])) { $this->output->writeInfo("Removing config file '{$source_dir_filepath}'"); $fs->remove($source_dir_filepath); } else { $dir_sync_status['new'][] = $source_dir_filename; $this->output->writeInfo("Copying config file '{$source_dir_filepath}' -> '{$target_dir_filepath}'"); $fs->copy($source_dir_filepath, $target_dir_filepath, true, $target_dir_owner, $target_dir_group); } } } $finder = new Finder(); foreach ($finder->files()->in($target_dir)->ignoreDotFiles(false)->depth('== 0') as $target_dir_file) { $target_dir_filename = $target_dir_file->getFilename(); $source_dir_filepath = $source_dir . '/' . $target_dir_filename; $target_dir_filepath = $target_dir . '/' . $target_dir_filename; if (in_array($target_dir_filename, $dir_sync_status['new'])) { continue; } // Delete file from target dir if (in_array($target_dir_filename, $dir_sync_status['old'])) { $this->output->writeInfo("Removing config file '{$target_dir_filepath}'"); $fs->remove($target_dir_filepath); } else { $this->output->writeInfo("Copying config file '{$source_dir_filepath}' <- '{$target_dir_filepath}'"); $fs->copy($target_dir_filepath, $source_dir_filepath, true, $source_dir_owner, $source_dir_group); $dir_sync_status['new'][] = $target_dir_filename; } } $sync_status_new[$target_dir] = $dir_sync_status['new']; } // Write status file $this->output->writeDebug("Writing config directories sync status file"); $fs->write(self::SYNC_STATUS_FILE, json_encode($sync_status_new), 'vagrant'); }
/** * Runs a phpbrew shell script * * @return \Symfony\Component\Process\Process * @throws \Exception */ protected function runScript() { $args = func_get_args(); $args_size = func_num_args(); if ($args_size == 0) { throw new \Exception("runScript() function requires at lease one argument, {$args_size} was given"); } $script = array_shift($args); $cmd = "/vagrant/provision/main/scripts/phpbrew/{$script}.sh"; if (count($args) > 0) { $cmd .= " " . implode(" ", $args); } $proc = Util::Process($this->output, "sudo -u vagrant bash {$cmd}", ['timeout' => null]); return $proc; }
/** * Sets the php-fpm config file that shall be included in the vhost config file based on the defined * php version in project::vhost::php * * @param string $id * @param array $project * @param array $vhost * @return array */ protected function __setVhostFpmInclude($id, $project, $vhost) { static $default_php_build; if (empty($project['php']['build'])) { return $vhost; } $php_build = $project['php']['build']; if ($php_build == 'system') { $vhost['includes'] = ['/etc/apache2/php/php-system-fpm.conf']; return $vhost; } $phps_config = Config::get('php::builds'); // set default php version if not set if (!isset($default_php_build)) { foreach ($phps_config as $build => $meta) { if (!empty($meta['default'])) { $default_php_build = $build; } } if (!isset($default_php_build)) { $default_php_build = 0; } } if ($php_build == 'default' && 0 === $default_php_build) { $this->output->writeError("Unable to use default php build for project '{$id}' because no default php build found"); return $vhost; } if ($php_build == 'default') { $php_build = $default_php_build; } $phps_installed = Util::getInstalledPhps(); if (!in_array($php_build, $phps_installed) || !isset($phps_config[$php_build])) { $this->output->writeError("Unable to use php build '{$php_build}' for project '{$id}', build isn't installed"); return $vhost; } if (empty($phps_config[$php_build]['fpm']['port'])) { $this->output->writeError("Unable to use php build '{$php_build}' for project '{$id}', php fpm port isn't configured"); return $vhost; } $php_version_fpm_conf = '/etc/apache2/php/php-' . $php_build . '-fpm.conf'; if (!file_exists($php_version_fpm_conf)) { $this->output->writeError("Unable to use php build '{$php_build}' for project '{$id}', apache php-fpm config file '{$php_version_fpm_conf}' doesn't exist"); return $vhost; } $vhost['includes'] = [$php_version_fpm_conf]; return $vhost; }
/** * @throws \Exception */ public function run() { if (!$this->command instanceof ProvisionCommand) { throw new \Exception("The ProjectsInit task can only be run by the Provision command."); } $this->output->writeInfo("Finding projects"); $projects_catalog = Projects::get(); $projects = ['skip' => [], 'check' => [], 'modify' => [], 'create' => [], 'delete' => []]; $publicdb = '/etc/dashbrew/public.fndb'; $proc = Util::process($this->output, "updatedb -U /vagrant/public -o {$publicdb}"); if (!$proc->isSuccessful()) { throw new \Exception("Failed indexing '/vagrant/public' directory"); } $files = []; $proc = Util::process($this->output, "locate -d {$publicdb} -i -b '*.dashbrew'"); if ($proc->isSuccessful()) { $files = explode("\n", trim($proc->getOutput(), "\n")); } $yaml = Util::getYamlParser(); foreach ($files as $file_path) { if (!is_file($file_path)) { continue; } try { $file_projects = $yaml->parse(file_get_contents($file_path)); } catch (\Symfony\Component\Yaml\Exception\ParseException $e) { $this->output->writeError("Failed parsing '{$file_path}': " . $e->getMessage()); continue; } foreach ($file_projects as $id => $project) { $project['_path'] = $file_path; if (isset($projects_catalog[$id])) { $project['_created'] = $projects_catalog[$id]['_created']; $project['_modified'] = $projects_catalog[$id]['_modified']; if ($project == $projects_catalog[$id]) { if ($this->__projectNeedsCheck($id, $project)) { $projects['check'][$id] = $project; } else { $projects['skip'][$id] = $project; } } else { $project['_modified'] = time(); $projects['modify'][$id] = $project; } } else { # Ignore duplicates if (isset($projects['create'][$id])) { $this->output->writeError("Unable to proccess project '{$id}' at '{$project['_path']}', " . "another project with the same name is already exist at '{$projects['create'][$id]['_path']}'."); continue; } $project['_created'] = time(); $project['_modified'] = time(); $projects['create'][$id] = $project; } if (isset($projects_catalog[$id])) { unset($projects_catalog[$id]); } } } # Prevent project duplicates foreach ($projects['create'] as $id => $project) { foreach (['skip', 'check', 'modify'] as $action) { if (isset($projects[$action][$id])) { $this->output->writeError("Unable to proccess project '{$id}' at '{$project['_path']}', " . "another project with the same name is already exist at '{$projects[$action][$id]['_path']}'."); unset($projects['create'][$id]); } } } # Projects that are no longer exist needs to be deleted foreach ($projects_catalog as $id => $project) { $projects['delete'][$id] = $project; } Registry::set('projects', $projects); $projects_total = count($projects['skip']) + count($projects['check']) + count($projects['modify']) + count($projects['create']) + count($projects['delete']); if ($projects_total > count($projects['skip'])) { $this->output->writeInfo("Found " . $projects_total . " project(s)" . (count($projects['skip']) > 0 ? "\n |--> " . count($projects['skip']) . " will be skipped" : "") . (count($projects['check']) > 0 ? "\n |--> " . count($projects['check']) . " don't have changes but needs to be checked" : "") . (count($projects['create']) > 0 ? "\n |--> " . count($projects['create']) . " will be created" : "") . (count($projects['modify']) > 0 ? "\n |--> " . count($projects['modify']) . " will be modified" : "") . (count($projects['delete']) > 0 ? "\n |--> " . count($projects['delete']) . " will be deleted" : "")); } else { $this->output->writeDebug("Found {$projects_total} project(s)"); } }
public static function getPhpsCount() { return count(Util::getInstalledPhps()) + 1; }
/** * @throws \Exception */ public function run() { if (!$this->command instanceof ProvisionCommand) { throw new \Exception("The Init task can only be run by the Provision command."); } // Check box version if (!file_exists('/etc/dashbrew/.version')) { throw new \Exception("Unable to find base box version file."); } $box_version = file_get_contents('/etc/dashbrew/.version'); if (empty($box_version)) { throw new \Exception("Invalid base box version file."); } if (false === strpos($box_version, DASHBREW_BASEBOX_VERSION, 0)) { throw new \Exception("Incompatible base box version ({$box_version}). Dashbrew requires a base box version " . DASHBREW_BASEBOX_VERSION . ".x"); } $this->output->writeDebug("Checking for available " . $box_version . " patches"); $box_patch_file = '/etc/dashbrew/.patch'; $box_patch_md5 = null; if (file_exists($box_patch_file)) { $box_patch_md5 = file_get_contents($box_patch_file); } $available_patch = file_get_contents('https://raw.githubusercontent.com/mdkholy/dashbrew-basebox/master/patches/' . $box_version . '.sh'); $apply_patch = false; if (!empty($available_patch)) { $available_patch_md5 = md5($available_patch); if ($box_patch_md5 != $available_patch_md5) { $apply_patch = true; } } if ($apply_patch) { $this->output->writeInfo("An update patch is available for your box. Updating..."); $fs = Util::getFilesystem(); $exec_patch_file = '/tmp/dashbrew-basebox-patch.sh'; $fs->write($exec_patch_file, $available_patch, 'root'); $fs->chmod($exec_patch_file, 0755); $proc = Util::Process($this->output, "sudo bash {$exec_patch_file}", ['timeout' => null]); if ($proc->isSuccessful()) { $this->output->writeInfo("Update patch has been applied successfully"); } else { $this->output->writeError("Error occured while applying update patch"); } $fs->remove($exec_patch_file); $fs->write($box_patch_file, $available_patch_md5); $fs->chmod($box_patch_file, 0644); } // Parse & initialize config.yaml file if (file_exists(Config::CONFIG_FILE)) { $this->output->writeInfo("Initializing environment.yaml"); } else { $this->output->writeInfo("Provisioning without environment.yaml config file"); } Config::init(); if (Config::get('debug')) { $this->output->setVerbosity(Output::VERBOSITY_DEBUG); } // Initialize config files and directories $this->output->writeDebug("Initializing config files"); $config_files = [['path' => '/etc/monit/monitrc', 'source' => '/vagrant/config/monit/monitrc', 'default' => '/etc/monit/monitrc', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/apache2/apache2.conf', 'source' => '/vagrant/config/apache/apache.conf', 'default' => '/etc/apache2/apache2.conf', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/fpm/php-fpm.conf', 'source' => '/vagrant/config/php/fpm/php-fpm.conf', 'default' => '/etc/php5/fpm/php-fpm.conf', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/fpm/php.ini', 'source' => '/vagrant/config/php/fpm/php.ini', 'default' => '/etc/php5/fpm/php.ini', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/mysql/my.cnf', 'source' => '/vagrant/config/mysql/my.cnf', 'default' => '/etc/mysql/my.cnf', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/cli/php.ini', 'source' => '/vagrant/config/php/cli/php.ini', 'default' => '/etc/php5/cli/php.ini', 'owner' => 'root', 'group' => 'root'], ['path' => '/opt/phpbrew/config.yaml', 'source' => '/vagrant/config/phpbrew/config.yaml', 'default' => '/opt/phpbrew/config.yaml', 'owner' => 'vagrant', 'group' => 'vagrant'], ['path' => '/usr/share/phpmyadmin/config.inc.php', 'source' => '/vagrant/config/phpmyadmin/config.inc.php', 'default' => '/usr/share/phpmyadmin/config.inc.php', 'owner' => 'vagrant', 'group' => 'www-data']]; $config_dirs = [['path' => '/home/vagrant', 'source' => '/vagrant/config/home', 'default' => '/home/vagrant', 'owner' => 'vagrant', 'group' => 'vagrant'], ['path' => '/etc/monit/conf.d', 'source' => '/vagrant/config/monit/conf.d', 'default' => '/etc/monit/conf.d', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/cli/conf.d', 'source' => '/vagrant/config/php/cli/conf.d', 'default' => '/etc/php5/cli/conf.d', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/fpm/conf.d', 'source' => '/vagrant/config/php/fpm/conf.d', 'default' => '/etc/php5/fpm/conf.d', 'owner' => 'root', 'group' => 'root'], ['path' => '/etc/php5/fpm/pool.d', 'source' => '/vagrant/config/php/fpm/pool.d', 'default' => '/etc/php5/fpm/pool.d', 'owner' => 'root', 'group' => 'root']]; $installed_phps = Util::getInstalledPhps(); $phps = Config::get('php::builds'); foreach ($phps as $build => $meta) { if (!in_array($build, $installed_phps)) { continue; } $config_dirs[] = ['path' => "/opt/phpbrew/php/{$build}/var/db", 'source' => "/vagrant/config/phpbrew/{$build}/conf.d", 'default' => "/opt/phpbrew/php/{$build}/var/db", 'owner' => 'vagrant', 'group' => 'vagrant']; $config_files[] = ['path' => "/opt/phpbrew/php/{$build}/etc/php.ini", 'source' => "/vagrant/config/phpbrew/{$build}/php.ini", 'default' => "/opt/phpbrew/php/{$build}/etc/php.ini", 'owner' => 'vagrant', 'group' => 'vagrant']; $config_files[] = ['path' => "/opt/phpbrew/php/{$build}/etc/php-fpm.conf", 'source' => "/vagrant/config/phpbrew/{$build}/php-fpm.conf", 'default' => "/opt/phpbrew/php/{$build}/etc/php-fpm.conf", 'owner' => 'vagrant', 'group' => 'vagrant']; } foreach ($config_files as $config_file) { SyncManager::addRule(SyncManager::SYNC_FILE, $config_file); } foreach ($config_dirs as $config_dir) { SyncManager::addRule(SyncManager::SYNC_DIR, $config_dir); } $services = ['apache', 'mysql', 'php-system-fpm', 'mailcatcher']; foreach ($services as $service) { ServiceManager::addService($service); } }
/** * Manages nodjs modules via npm */ protected function manageNpmPackages() { $packages_config = Config::get('npm::packages'); if (empty($packages_config)) { return; } $this->output->writeInfo("Checking nodejs modules"); $packages = ['install' => [], 'remove' => []]; foreach ($packages_config as $package => $installed) { $is_installed = false; $proc = Util::process($this->output, "npm -j ls -g {$package}", ['stderr' => false]); if ($proc->isSuccessful()) { $is_installed = false !== stripos($proc->getOutput(), '"' . $package . '": {'); } if ($installed && $is_installed || !$installed && !$is_installed) { continue; } if (!$installed) { $packages['remove'][] = $package; continue; } $packages['install'][] = $package; } foreach ($packages['remove'] as $package) { $this->output->writeInfo("Uninstalling npm package '{$package}'"); $proc = Util::process($this->output, "npm uninstall -g {$package}"); if (!$proc->isSuccessful()) { $this->output->writeError("Error occured uninstalling npm package '{$package}'"); continue; } $this->output->writeInfo("Successfully uninstalled npm package '{$package}'"); } foreach ($packages['install'] as $package) { $this->output->writeInfo("Installing npm package '{$package}'"); $proc = Util::process($this->output, "npm install -g {$package}", ['timeout' => null]); if (!$proc->isSuccessful()) { $this->output->writeError("Error occured while installing npm package '{$package}'"); continue; } $this->output->writeInfo("Successfully installed npm package '{$package}'"); } }