def cli(ctx, dname, site): """ Enable the <site> under the specified <domain> """ assert isinstance(ctx, Context) dname = domain_parse(dname).hostname domain = Session.query(Domain).filter(Domain.name == dname).first() if not domain: click.secho('No such domain: {dn}'.format(dn=dname), fg='red', bold=True, err=True) return site_name = site site = Site.get(domain, site_name) if not site: click.secho('No such site: {site}'.format(site=site_name), fg='red', bold=True, err=True) return p = Echo('Constructing paths and configuration files...') site.enable() p.done() # Restart Nginx p = Echo('Restarting web server...') FNULL = open(os.devnull, 'w') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done()
def cli(ctx, dname): """ Disable installations under the specified <domain> """ assert isinstance(ctx, Context) dname = domain_parse(dname).hostname domain = Session.query(Domain).filter(Domain.name == dname).first() if not domain: click.secho('No such domain: {dn}'.format(dn=dname), fg='red', bold=True, err=True) return sites = domain.sites for site in sites: if site.enabled: click.secho('Disabling site {sn}'.format(sn=site.name), bold=True) site.disable() # Restart Nginx p = Echo('Restarting web server...') FNULL = open(os.devnull, 'w') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done()
def server_details(self): """ Input server details (database information, etc.) """ self._check_title(self.browser.title()) p = Echo('Creating MySQL database...') # Create the database md5hex = md5(self.site.domain.name + self.site.slug).hexdigest() db_name = 'ipsv_{md5}'.format(md5=md5hex) # MySQL usernames are limited to 16 characters max db_user = '******'.format(md5=md5hex[:11]) rand_pass = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(random.randint(16, 24))) db_pass = rand_pass try: self.mysql.execute('CREATE DATABASE `{db}`'.format(db=db_name)) except SQLAlchemyError: if not self.force: click.confirm( 'A previous database for this installation already exists.\n' 'Would you like to drop it now? The installation will be aborted if you do not', abort=True) self.log.info( 'Dropping existing database: {db}'.format(db=db_name)) self.mysql.execute( 'DROP DATABASE IF EXISTS `{db}`'.format(db=db_name)) self.mysql.execute('DROP USER `{u}`'.format(u=db_user)) self.mysql.execute('CREATE DATABASE `{db}`'.format(db=db_name)) self.mysql.execute( "GRANT ALL ON {db}.* TO '{u}'@'localhost' IDENTIFIED BY '{p}'". format(db=db_name, u=db_user, p=db_pass)) # Save the database connection information self.site.db_host = 'localhost' self.site.db_name = db_name self.site.db_user = db_user self.site.db_pass = db_pass self.ctx.db.commit() self.log.debug('MySQL Database Name: %s', db_name) self.log.debug('MySQL Database User: %s', db_user) self.log.debug('MySQL Database Password: %s', db_pass) # Set form fields and submit self.browser.select_form(nr=0) self.browser.form[self.FIELD_SERVER_SQL_HOST] = 'localhost' self.browser.form[self.FIELD_SERVER_SQL_USER] = db_user self.browser.form[self.FIELD_SERVER_SQL_PASS] = db_pass self.browser.form[self.FIELD_SERVER_SQL_DATABASE] = db_name self.browser.submit() p.done() self.admin()
def server_details(self): """ Input server details (database information, etc.) """ self._check_title(self.browser.title()) p = Echo('Creating MySQL database...') # Create the database md5hex = md5(self.site.domain.name + self.site.slug).hexdigest() db_name = 'ipsv_{md5}'.format(md5=md5hex) # MySQL usernames are limited to 16 characters max db_user = '******'.format(md5=md5hex[:11]) rand_pass = ''.join(random.SystemRandom() .choice(string.ascii_letters + string.digits) for _ in range(random.randint(16, 24))) db_pass = rand_pass try: self.mysql.execute('CREATE DATABASE `{db}`'.format(db=db_name)) except SQLAlchemyError: if not self.force: click.confirm('A previous database for this installation already exists.\n' 'Would you like to drop it now? The installation will be aborted if you do not', abort=True) self.log.info('Dropping existing database: {db}'.format(db=db_name)) self.mysql.execute('DROP DATABASE IF EXISTS `{db}`'.format(db=db_name)) self.mysql.execute('DROP USER IF EXISTS `{u}`'.format(u=db_user)) self.mysql.execute('CREATE DATABASE `{db}`'.format(db=db_name)) self.mysql.execute("GRANT ALL ON {db}.* TO '{u}'@'localhost' IDENTIFIED BY '{p}'" .format(db=db_name, u=db_user, p=db_pass)) # Save the database connection information self.site.db_host = 'localhost' self.site.db_name = db_name self.site.db_user = db_user self.site.db_pass = db_pass self.ctx.db.commit() self.log.debug('MySQL Database Name: %s', db_name) self.log.debug('MySQL Database User: %s', db_user) self.log.debug('MySQL Database Password: %s', db_pass) # Set form fields and submit self.browser.select_form(nr=0) self.browser.form[self.FIELD_SERVER_SQL_HOST] = 'localhost' self.browser.form[self.FIELD_SERVER_SQL_USER] = db_user self.browser.form[self.FIELD_SERVER_SQL_PASS] = db_pass self.browser.form[self.FIELD_SERVER_SQL_DATABASE] = db_name self.browser.submit() p.done() self.admin()
def applications(self): """ Select the applications to install (currently hardcoded to install all applications) """ # Check for license submission errors try: self._check_title(self.browser.title()) except InstallationError: rsoup = BeautifulSoup(self.browser.response().read(), "html.parser") error = rsoup.find('li', id='license_lkey').find('span', {'class': 'ipsType_warning'}).text raise InstallationError(error) # TODO: Make this configurable p = Echo('Setting applications to install...') self.browser.select_form(nr=0) self.browser.submit() p.done() self.server_details()
def admin(self): """ Provide admin login credentials """ self._check_title(self.browser.title()) self.browser.select_form(nr=0) # Get the admin credentials prompted = [] user = self.ctx.config.get('User', 'AdminUser') if not user: user = click.prompt('Admin display name') prompted.append('user') password = self.ctx.config.get('User', 'AdminPass') if not password: password = click.prompt('Admin password', hide_input=True, confirmation_prompt='Confirm admin password') prompted.append('password') email = self.ctx.config.get('User', 'AdminEmail') if not email: email = click.prompt('Admin email') prompted.append('email') self.browser.form[self.FIELD_ADMIN_USER] = user self.browser.form[self.FIELD_ADMIN_PASS] = password self.browser.form[self.FIELD_ADMIN_PASS_CONFIRM] = password self.browser.form[self.FIELD_ADMIN_EMAIL] = email p = Echo('Submitting admin information...') self.browser.submit() p.done() if len(prompted) >= 3: save = click.confirm('Would you like to save and use these admin credentials for future installations?') if save: self.log.info('Saving admin login credentials') self.ctx.config.set('User', 'AdminUser', user) self.ctx.config.set('User', 'AdminPass', password) self.ctx.config.set('User', 'AdminEmail', email) with open(self.ctx.config_path, 'wb') as cf: self.ctx.config.write(cf) self.install()
def license(self): """ Submit our license to IPS' servers """ p = Echo('Submitting license key...') self._check_title(self.browser.title()) self.browser.select_form(nr=0) # Set the fields self.browser.form[self.FIELD_LICENSE_KEY] = '{license}-TESTINSTALL'.format(license=self.site.license_key) self.browser.find_control('eula_checkbox').items[0].selected = True # TODO: User prompt? # Submit the request self.log.debug('Submitting our license') self.browser.submit() self.log.debug('Response code: %s', self.browser.response().code) p.done() self.applications()
def install(self): """ Run the actual installation """ self._start_install() mr_link = self._get_mr_link() # Set up the progress bar pbar = ProgressBar(100, 'Running installation...') pbar.start() mr_j, mr_r = self._ajax(mr_link) # Loop until we get a redirect json response while True: mr_link = self._parse_response(mr_link, mr_j) stage = self._get_stage(mr_j) progress = self._get_progress(mr_j) mr_j, mr_r = self._ajax(mr_link) pbar.update( min([progress, 100]), stage) # NOTE: Response may return progress values above 100 # If we're done, finalize the installation and break redirect = self._check_if_complete(mr_link, mr_j) if redirect: pbar.finish() break p = Echo('Finalizing...') mr_r = self._request(redirect, raise_request=False) p.done() # Install developer tools if self.site.in_dev: DevToolsInstaller(self.ctx, self.site).install() # Get the link to our community homepage self._finalize(mr_r)
def system_check(self): """ System requirements check """ self._check_title(self.browser.title()) p = Echo('Running system check...') rsoup = BeautifulSoup(self.browser.response().read(), "html.parser") # Check for any errors errors = [] for ul in rsoup.find_all('ul', {'class': 'ipsList_checks'}): for li in ul.find_all('li', {'class': 'fail'}): errors.append(li.text) if errors: raise InstallationError(errors) # Continue continue_link = next(self.browser.links(text_regex='Continue')) p.done() self.browser.follow_link(continue_link) self.license()
def applications(self): """ Select the applications to install (currently hardcoded to install all applications) """ # Check for license submission errors try: self._check_title(self.browser.title()) except InstallationError: rsoup = BeautifulSoup(self.browser.response().read(), "html.parser") error = rsoup.find('li', id='license_lkey').find( 'span', { 'class': 'ipsType_warning' }).text raise InstallationError(error) # TODO: Make this configurable p = Echo('Setting applications to install...') self.browser.select_form(nr=0) self.browser.submit() p.done() self.server_details()
def license(self): """ Submit our license to IPS' servers """ p = Echo('Submitting license key...') self._check_title(self.browser.title()) self.browser.select_form(nr=0) # Set the fields self.browser.form[ self.FIELD_LICENSE_KEY] = '{license}-TESTINSTALL'.format( license=self.site.license_key) self.browser.find_control( 'eula_checkbox').items[0].selected = True # TODO: User prompt? # Submit the request self.log.debug('Submitting our license') self.browser.submit() self.log.debug('Response code: %s', self.browser.response().code) p.done() self.applications()
def install(self): """ Run the actual installation """ self._start_install() mr_link = self._get_mr_link() # Set up the progress bar pbar = ProgressBar(100, 'Running installation...') pbar.start() mr_j, mr_r = self._ajax(mr_link) # Loop until we get a redirect json response while True: mr_link = self._parse_response(mr_link, mr_j) stage = self._get_stage(mr_j) progress = self._get_progress(mr_j) mr_j, mr_r = self._ajax(mr_link) pbar.update(min([progress, 100]), stage) # NOTE: Response may return progress values above 100 # If we're done, finalize the installation and break redirect = self._check_if_complete(mr_link, mr_j) if redirect: pbar.finish() break p = Echo('Finalizing...') mr_r = self._request(redirect, raise_request=False) p.done() # Install developer tools if self.site.in_dev: DevToolsInstaller(self.ctx, self.site).install() # Get the link to our community homepage self._finalize(mr_r)
def system_check(self): """ System requirements check """ self.browser.open(self.url) self._check_title(self.browser.title()) p = Echo('Running system check...') rsoup = BeautifulSoup(self.browser.response().read(), "html.parser") # Check for any errors errors = [] for ul in rsoup.find_all('ul', {'class': 'ipsList_checks'}): for li in ul.find_all('li', {'class': 'fail'}): errors.append(li.text) if errors: raise InstallationError(errors) # Continue continue_link = next(self.browser.links(text_regex='Continue')) p.done() self.browser.follow_link(continue_link) self.license()
def cli(ctx): """ Run setup after a fresh Vagrant installation. """ log = logging.getLogger('ipsv.setup') assert isinstance(ctx, Context) lock_path = os.path.join(ctx.config.get('Paths', 'Data'), 'setup.lck') if os.path.exists(lock_path): raise Exception('Setup is locked, please remove the setup lock file to continue') # Create our package directories p = Echo('Creating IPS Vagrant system directories...') dirs = ['/etc/ipsv', ctx.config.get('Paths', 'Data'), ctx.config.get('Paths', 'Log'), ctx.config.get('Paths', 'NginxSitesAvailable'), ctx.config.get('Paths', 'NginxSitesEnabled'), ctx.config.get('Paths', 'NginxSSL')] for d in dirs: if not os.path.exists(d): os.makedirs(d, 0o755) p.done() p = Echo('Copying IPS Vagrant configuration files...') with open('/etc/ipsv/ipsv.conf', 'w+') as f: ctx.config.write(f) p.done() # Set up alembic alembic_cfg = Config(os.path.join(ctx.basedir, 'alembic.ini')) alembic_cfg.set_main_option("script_location", os.path.join(ctx.basedir, 'migrations')) alembic_cfg.set_main_option("sqlalchemy.url", "sqlite:////{path}" .format(path=os.path.join(ctx.config.get('Paths', 'Data'), 'sites.db'))) command.current(alembic_cfg) command.downgrade(alembic_cfg, 'base') command.upgrade(alembic_cfg, 'head') # Update the system p = Echo('Updating package cache...') cache = apt.Cache() cache.update() cache.open(None) p.done() p = Echo('Upgrading system packages...') cache.upgrade() cache.commit() p.done() # Install our required packages requirements = ['nginx', 'php5-fpm', 'php5-curl', 'php5-gd', 'php5-imagick', 'php5-json', 'php5-mysql', 'php5-readline', 'php5-apcu'] for requirement in requirements: # Make sure the package is available p = Echo('Marking package {pkg} for installation'.format(pkg=requirement)) if requirement not in cache: log.warn('Required package {pkg} not available'.format(pkg=requirement)) p.done(p.FAIL) continue # Mark the package for installation cache[requirement].mark_install() p.done() log.info('Committing package cache') p = Echo('Downloading and installing packages...') cache.commit() p.done() # Disable the default server block p = Echo('Configuring Nginx...') default_available = os.path.join(ctx.config.get('Paths', 'NginxSitesAvailable'), 'default') default_enabled = os.path.join(ctx.config.get('Paths', 'NginxSitesEnabled'), 'default') if os.path.isfile(default_available): os.remove(default_available) if os.path.islink(default_enabled): os.unlink(default_enabled) p.done() # Restart Nginx FNULL = open(os.devnull, 'w') p = Echo('Restarting Nginx...') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # php5-fpm configuration p = Echo('Configuring php5-fpm...') if os.path.isfile('/etc/php5/fpm/pool.d/www.conf'): os.remove('/etc/php5/fpm/pool.d/www.conf') fpm_config = FpmPoolConfig().template with open('/etc/php5/fpm/pool.d/ips.conf', 'w') as f: f.write(fpm_config) p.done() # Restart php5-fpm p = Echo('Restarting php5-fpm...') subprocess.check_call(['service', 'php5-fpm', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # Copy the man pages and rebuild the manual database p = Echo('Writing manual pages...') man_path = os.path.join(ctx.basedir, 'man', 'ipsv.1') sys_man_path = '/usr/local/share/man/man1' if not os.path.exists(sys_man_path): os.makedirs(sys_man_path) shutil.copyfile(man_path, os.path.join(sys_man_path, 'ipsv.1')) subprocess.check_call(['mandb'], stdout=FNULL, stderr=subprocess.STDOUT) # Enable the welcome message log.debug('Writing welcome message') wm_header = '## DO NOT REMOVE :: AUTOMATICALLY GENERATED BY IPSV ##' wm_remove = False # Remove old profile data for line in fileinput.input('/etc/profile', inplace=True): # Header / footer match? if line == wm_header: # Footer match (Stop removing) if wm_remove: wm_remove = False continue # Header match (Start removing) wm_remove = True continue # Removing lines? if wm_remove: continue # Print line and continue as normal sys.stdout.write(line) # Write new profile data with open('/etc/profile', 'a') as f: f.write("\n" + wm_header + "\n") fl_lock_path = os.path.join(ctx.config.get('Paths', 'Data'), 'first_login.lck') f.write('if [ ! -f "{lp}" ]; then'.format(lp=fl_lock_path) + "\n") f.write(' less "{wp}"'.format(wp=os.path.join(ctx.basedir, 'WELCOME.rst')) + "\n") f.write(' sudo touch "{lp}"'.format(lp=fl_lock_path) + "\n") f.write('fi' + "\n") f.write(wm_header + "\n") p.done() log.debug('Writing setup lock file') with open(os.path.join(ctx.config.get('Paths', 'Data'), 'setup.lck'), 'w') as f: f.write('1')
def cli(ctx, name, dname, license_key, ips_version, force, enable, ssl, spdy, gzip, cache, install, dev): """ Downloads and installs a new instance of the latest Invision Power Suite release. """ assert isinstance(ctx, Context) login_session = ctx.get_login() log = logging.getLogger("ipsv.new") ctx.cache = cache # Prompt for our desired license def get_license(): """ Prompt the user for a license selection @rtype: ips_vagrant.scraper.licenses.LicenseMeta """ licenses = Licenses(login_session).get() user_license = license_key or ctx.config.get("User", "LicenseKey") # If we already have a license key saved, skip the prompt and use it instead if user_license: licenses = {license.license_key: license for license in licenses} if user_license in licenses: return licenses[user_license] # Ask the user to select a license key opt = choice( [ (key, "{u} ({k})".format(u=license.community_url, k=license.license_key)) for key, license in enumerate(licenses) ], 1, "Which license key would you like to use?", ) license = licenses[opt] # Should we save this license? if click.confirm("Would you like to save and use this license for future requests?", True): ctx.log.debug("Saving license key {k}".format(k=license.license_key)) ctx.config.set("User", "LicenseKey", license.license_key) with open(ctx.config_path, "wb") as configfile: ctx.config.write(configfile) return license # Get the latest IPS release lmeta = get_license() p = Echo("Fetching IPS version information...") ips = IpsManager(ctx, lmeta) p.done() if ips_version: if ips_version == "latest_dev": v = ips.dev_version if not v: click.secho("There is no IPS development release available for download", err=True, fg="red", bold=True) raise Exception("There is no IPS development release available for download") p = Echo("Downloading IPS development release {vs}...".format(vs=v.version.vstring)) else: ips_version = Version(ips_version) v = ips.versions[ips_version.vtuple] p = Echo("Fetching IPS version {iv}".format(iv=ips_version.vstring)) else: v = ips.latest p = Echo("Downloading IPS release {vs}...".format(vs=v.version.vstring)) filename = ips.get(v, cache) p.done() # Parse the specific domain and make sure it's valid log.debug("Parsing domain name: %s", dname) dname = domain_parse(dname) if ssl is None: ssl = dname.scheme == "https" log.debug("Domain name parsed: %s", dname) domain = Domain.get_or_create(dname) # Make sure this site does not already exist p = Echo("Constructing site data...") site = ctx.db.query(Site).filter(Site.domain == domain).filter(collate(Site.name, "NOCASE") == name).count() if site: p.done(p.FAIL) log.error("Site already exists") click.secho( 'An installation named "{s}" has already been created for the domain {d}'.format(s=name, d=dname.hostname), err=True, fg="red", bold=True, ) raise click.Abort # Create the site database entry site = Site( name=name, domain=domain, license_key=lmeta.license_key, version=v.version.vstring, ssl=ssl, spdy=spdy, gzip=gzip, enabled=enable, in_dev=dev, ) status = p.OK if os.path.exists(site.root): if not force: p.done(p.FAIL) click.secho( "Installation path already exists and --force was not passed:\n{p}".format(p=site.root), err=True, fg="red", bold=True, ) log.info("Aborting installation, path already exists: {p}".format(p=site.root)) raise click.Abort log.warn("Overwriting existing installation path: {p}".format(p=site.root)) status = p.WARN ctx.db.add(site) ctx.db.commit() p.done(status) # Construct the HTTP path p = Echo("Constructing paths and configuration files...") site.write_nginx_config() p.done() # Generate SSL certificates if enabled if ssl: p = Echo("Generating SSL certificate...") ssl_path = os.path.join(ctx.config.get("Paths", "NginxSSL"), domain.name) if not os.path.exists(ssl_path): log.debug("Creating new SSL path: %s", ssl_path) os.makedirs(ssl_path, 0o755) sc = CertificateFactory(site).get() site.ssl_key = sc.key site.ssl_certificate = sc.certificate with open(os.path.join(ssl_path, "{s}.key".format(s=site.slug)), "w") as f: f.write(sc.key) with open(os.path.join(ssl_path, "{s}.pem").format(s=site.slug), "w") as f: f.write(sc.certificate) p.done() # Create a symlink if this site is being enabled if site.enabled: site.enable(force) # Restart Nginx p = Echo("Restarting web server...") FNULL = open(os.devnull, "w") subprocess.check_call(["service", "nginx", "restart"], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # Extract IPS setup files p = Echo("Extracting setup files to tmp...") tmpdir = tempfile.mkdtemp("ips") setup_zip = os.path.join(tmpdir, "setup.zip") setup_dir = os.path.join(tmpdir, "setup") os.mkdir(setup_dir) log.info("Extracting setup files") shutil.copyfile(filename, setup_zip) with zipfile.ZipFile(setup_zip) as z: namelist = z.namelist() if re.match(r"^ips_\w{5}\/?$", namelist[0]): log.debug("Setup directory matched: %s", namelist[0]) else: log.error("No setup directory matched, unable to continue") raise Exception("Unrecognized setup file format, aborting") z.extractall(setup_dir) log.debug("Setup files extracted to: %s", setup_dir) p.done() p = MarkerProgressBar("Copying setup files...") setup_tmpdir = os.path.join(setup_dir, namelist[0]) for dirname, dirnames, filenames in p(os.walk(setup_tmpdir)): for filepath in dirnames: site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ""), filepath) if not os.path.exists(site_path): log.debug("Creating directory: %s", site_path) os.mkdir(site_path, 0o755) for filepath in filenames: tmp_path = os.path.join(dirname, filepath) site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ""), filepath) shutil.copy(tmp_path, site_path) log.info("Setup files copied to: %s", site.root) shutil.rmtree(tmpdir) # Apply proper permissions # p = MarkerProgressBar('Setting file permissions...') writeable_dirs = ["uploads", "plugins", "applications", "datastore"] for wdir in writeable_dirs: log.debug("Setting file permissions in %s", wdir) os.chmod(os.path.join(site.root, wdir), 0o777) p = MarkerProgressBar("Setting file permissions...", nl=False) for dirname, dirnames, filenames in p(os.walk(os.path.join(site.root, wdir))): for filename in filenames: os.chmod(os.path.join(dirname, filename), 0o666) for filename in dirnames: os.chmod(os.path.join(dirname, filename), 0o777) Echo("Setting file permissions...").done() shutil.move(os.path.join(site.root, "conf_global.dist.php"), os.path.join(site.root, "conf_global.php")) os.chmod(os.path.join(site.root, "conf_global.php"), 0o777) # Run the installation if install: p = Echo("Initializing installer...") i = installer(v.version, ctx, site, force) p.done() i.start() else: db_info = None if click.confirm("Would you like to create the database for this installation now?", default=True): db_info = create_database(site) click.echo("------") if db_info: db_name, db_user, db_pass = db_info log.debug("MySQL Database Name: %s", db_name) log.debug("MySQL Database User: %s", db_user) log.debug("MySQL Database Password: %s", db_pass) click.secho("MySQL Database Name: {dbn}".format(dbn=db_name), bold=True) click.secho("MySQL Database User: {dbu}".format(dbu=db_user), bold=True) click.secho("MySQL Database Password: {dbp}".format(dbp=db_pass), bold=True) click.secho( "IPS is now ready to be installed. To proceed with the installation, follow the link below", fg="yellow", bold=True, ) click.echo("{schema}://{host}".format(schema="https" if site.ssl else "http", host=site.domain.name))
def cli(ctx, name, dname, license_key, ips_version, force, enable, ssl, spdy, gzip, cache, install, dev): """ Downloads and installs a new instance of the latest Invision Power Suite release. """ assert isinstance(ctx, Context) login_session = ctx.get_login() log = logging.getLogger('ipsv.new') ctx.cache = cache # Prompt for our desired license def get_license(): """ Prompt the user for a license selection @rtype: ips_vagrant.scraper.licenses.LicenseMeta """ licenses = Licenses(login_session).get() user_license = license_key or ctx.config.get('User', 'LicenseKey') # If we already have a license key saved, skip the prompt and use it instead if user_license: licenses = {license.license_key: license for license in licenses} if user_license in licenses: return licenses[user_license] # Ask the user to select a license key opt = choice([ (key, '{u} ({k})'.format(u=license.community_url, k=license.license_key)) for key, license in enumerate(licenses) ], 1, 'Which license key would you like to use?') license = licenses[opt] # Should we save this license? if click.confirm('Would you like to save and use this license for future requests?', True): ctx.log.debug('Saving license key {k}'.format(k=license.license_key)) ctx.config.set('User', 'LicenseKey', license.license_key) with open(ctx.config_path, 'wb') as configfile: ctx.config.write(configfile) return license # Get the latest IPS release lmeta = get_license() p = Echo('Fetching IPS version information...') ips = IpsManager(ctx, lmeta) p.done() if ips_version: if ips_version == 'latest_dev': v = ips.dev_version if not v: click.secho('There is no IPS development release available for download', err=True, fg='red', bold=True) raise Exception('There is no IPS development release available for download') p = Echo('Downloading IPS development release {vs}...'.format(vs=v.version.vstring)) else: ips_version = Version(ips_version) v = ips.versions[ips_version.vtuple] p = Echo('Fetching IPS version {iv}'.format(iv=ips_version.vstring)) else: v = ips.latest p = Echo('Downloading IPS release {vs}...'.format(vs=v.version.vstring)) filename = ips.get(v, cache) p.done() # Parse the specific domain and make sure it's valid log.debug('Parsing domain name: %s', dname) dname = domain_parse(dname) if ssl is None: ssl = dname.scheme == 'https' log.debug('Domain name parsed: %s', dname) domain = Domain.get_or_create(dname) # Make sure this site does not already exist p = Echo('Constructing site data...') site = ctx.db.query(Site).filter(Site.domain == domain).filter(collate(Site.name, 'NOCASE') == name).count() if site: p.done(p.FAIL) log.error('Site already exists') click.secho('An installation named "{s}" has already been created for the domain {d}' .format(s=name, d=dname.hostname), err=True, fg='red', bold=True) raise click.Abort # Create the site database entry site = Site(name=name, domain=domain, license_key=lmeta.license_key, version=v.version.vstring, ssl=ssl, spdy=spdy, gzip=gzip, enabled=enable, in_dev=dev) status = p.OK if os.path.exists(site.root): if not force: p.done(p.FAIL) click.secho("Installation path already exists and --force was not passed:\n{p}".format(p=site.root), err=True, fg='red', bold=True) log.info('Aborting installation, path already exists: {p}'.format(p=site.root)) raise click.Abort log.warn('Overwriting existing installation path: {p}'.format(p=site.root)) status = p.WARN ctx.db.add(site) ctx.db.commit() p.done(status) # Construct the HTTP path p = Echo('Constructing paths and configuration files...') site.write_nginx_config() p.done() # Generate SSL certificates if enabled if ssl: p = Echo('Generating SSL certificate...') ssl_path = os.path.join(ctx.config.get('Paths', 'NginxSSL'), domain.name) if not os.path.exists(ssl_path): log.debug('Creating new SSL path: %s', ssl_path) os.makedirs(ssl_path, 0o755) sc = CertificateFactory(site).get() site.ssl_key = sc.key site.ssl_certificate = sc.certificate with open(os.path.join(ssl_path, '{s}.key'.format(s=site.slug)), 'w') as f: f.write(sc.key) with open(os.path.join(ssl_path, '{s}.pem').format(s=site.slug), 'w') as f: f.write(sc.certificate) p.done() # Create a symlink if this site is being enabled if site.enabled: site.enable(force) # Restart Nginx p = Echo('Restarting web server...') FNULL = open(os.devnull, 'w') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # Extract IPS setup files p = Echo('Extracting setup files to tmp...') tmpdir = tempfile.mkdtemp('ips') setup_zip = os.path.join(tmpdir, 'setup.zip') setup_dir = os.path.join(tmpdir, 'setup') os.mkdir(setup_dir) log.info('Extracting setup files') shutil.copyfile(filename, setup_zip) with zipfile.ZipFile(setup_zip) as z: namelist = z.namelist() if re.match(r'^ips_\w{5}\/?$', namelist[0]): log.debug('Setup directory matched: %s', namelist[0]) else: log.error('No setup directory matched, unable to continue') raise Exception('Unrecognized setup file format, aborting') z.extractall(setup_dir) log.debug('Setup files extracted to: %s', setup_dir) p.done() p = MarkerProgressBar('Copying setup files...') setup_tmpdir = os.path.join(setup_dir, namelist[0]) for dirname, dirnames, filenames in p(os.walk(setup_tmpdir)): for filepath in dirnames: site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ''), filepath) if not os.path.exists(site_path): log.debug('Creating directory: %s', site_path) os.mkdir(site_path, 0o755) for filepath in filenames: tmp_path = os.path.join(dirname, filepath) site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ''), filepath) shutil.copy(tmp_path, site_path) log.info('Setup files copied to: %s', site.root) shutil.rmtree(tmpdir) # Apply proper permissions # p = MarkerProgressBar('Setting file permissions...') writeable_dirs = ['uploads', 'plugins', 'applications', 'datastore'] for wdir in writeable_dirs: log.debug('Setting file permissions in %s', wdir) os.chmod(os.path.join(site.root, wdir), 0o777) p = MarkerProgressBar('Setting file permissions...', nl=False) for dirname, dirnames, filenames in p(os.walk(os.path.join(site.root, wdir))): for filename in filenames: os.chmod(os.path.join(dirname, filename), 0o666) for filename in dirnames: os.chmod(os.path.join(dirname, filename), 0o777) Echo('Setting file permissions...').done() shutil.move(os.path.join(site.root, 'conf_global.dist.php'), os.path.join(site.root, 'conf_global.php')) os.chmod(os.path.join(site.root, 'conf_global.php'), 0o777) # Run the installation if install: p = Echo('Initializing installer...') i = installer(v.version, ctx, site, force) p.done() i.start() else: click.echo('------') click.secho('IPS is now ready to be installed. To proceed with the installation, follow the link below', fg='yellow', bold=True) click.echo('{schema}://{host}'.format(schema='https' if site.ssl else 'http', host=site.domain.name))
def cli(ctx): """ Run setup after a fresh Vagrant installation. """ log = logging.getLogger('ipsv.setup') assert isinstance(ctx, Context) lock_path = os.path.join(ctx.config.get('Paths', 'Data'), 'setup.lck') if os.path.exists(lock_path): raise Exception('Setup is locked, please remove the setup lock file to continue') # Create our package directories p = Echo('Creating IPS Vagrant system directories...') dirs = ['/etc/ipsv', ctx.config.get('Paths', 'Data'), ctx.config.get('Paths', 'Log'), ctx.config.get('Paths', 'NginxSitesAvailable'), ctx.config.get('Paths', 'NginxSitesEnabled'), ctx.config.get('Paths', 'NginxSSL')] for d in dirs: if not os.path.exists(d): os.makedirs(d, 0o755) p.done() p = Echo('Copying IPS Vagrant configuration files...') with open('/etc/ipsv/ipsv.conf', 'w+') as f: ctx.config.write(f) p.done() # Set up alembic alembic_cfg = Config(os.path.join(ctx.basedir, 'alembic.ini')) alembic_cfg.set_main_option("script_location", os.path.join(ctx.basedir, 'migrations')) alembic_cfg.set_main_option("sqlalchemy.url", "sqlite:////{path}" .format(path=os.path.join(ctx.config.get('Paths', 'Data'), 'sites.db'))) command.current(alembic_cfg) command.downgrade(alembic_cfg, 'base') command.upgrade(alembic_cfg, 'head') # Update the system p = Echo('Updating package cache...') cache = apt.Cache() cache.update() cache.open(None) p.done() p = Echo('Upgrading system packages...') cache.upgrade() cache.commit() p.done() # Install our required packages requirements = ['nginx', 'php5-fpm', 'php5-curl', 'php5-gd', 'php5-imagick', 'php5-json', 'php5-mysql', 'php5-readline', 'php5-apcu', 'php5-xdebug'] for requirement in requirements: # Make sure the package is available p = Echo('Marking package {pkg} for installation'.format(pkg=requirement)) if requirement not in cache: log.warn('Required package {pkg} not available'.format(pkg=requirement)) p.done(p.FAIL) continue # Mark the package for installation cache[requirement].mark_install() p.done() log.info('Committing package cache') p = Echo('Downloading and installing packages...') cache.commit() p.done() # Disable the default server block p = Echo('Configuring Nginx...') default_available = os.path.join(ctx.config.get('Paths', 'NginxSitesAvailable'), 'default') default_enabled = os.path.join(ctx.config.get('Paths', 'NginxSitesEnabled'), 'default') if os.path.isfile(default_available): os.remove(default_available) if os.path.islink(default_enabled): os.unlink(default_enabled) p.done() # Restart Nginx FNULL = open(os.devnull, 'w') p = Echo('Restarting Nginx...') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # php.ini configuration p = Echo('Configuring php...') with open('/etc/php5/fpm/php.ini', 'a') as f: f.write('\n[XDebug]') f.write('\nxdebug.cli_color=1') temp_fh, temp_path = mkstemp() with open(temp_path, 'w') as nf: with open('/etc/php5/fpm/php.ini') as of: # Configuration options we are replacing upload_max_filesize = re.compile( '^upload_max_filesize\s+=\s+(\d+[a-zA-Z])\s*$' ) post_max_size = re.compile( '^post_max_size\s+=\s+(\d+[a-zA-Z])\s*$' ) for line in of: match = upload_max_filesize.match( line ) if upload_max_filesize is not True else False if match: nf.write( 'upload_max_filesize = 1000M\n' ) upload_max_filesize = True continue match = post_max_size.match( line ) if post_max_size is not True else False if match: nf.write( 'post_max_size = 1000M\n' ) post_max_size = True continue nf.write(line) os.close(temp_fh) os.remove('/etc/php5/fpm/php.ini') shutil.move(temp_path, '/etc/php5/fpm/php.ini') os.chmod('/etc/php5/fpm/php.ini', 0o644) p.done() # php5-fpm configuration p = Echo('Configuring php5-fpm...') if os.path.isfile('/etc/php5/fpm/pool.d/www.conf'): os.remove('/etc/php5/fpm/pool.d/www.conf') fpm_config = FpmPoolConfig().template with open('/etc/php5/fpm/pool.d/ips.conf', 'w') as f: f.write(fpm_config) p.done() # Restart php5-fpm p = Echo('Restarting php5-fpm...') subprocess.check_call(['service', 'php5-fpm', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # Copy the man pages and rebuild the manual database p = Echo('Writing manual pages...') man_path = os.path.join(ctx.basedir, 'man', 'ipsv.1') sys_man_path = '/usr/local/share/man/man1' if not os.path.exists(sys_man_path): os.makedirs(sys_man_path) shutil.copyfile(man_path, os.path.join(sys_man_path, 'ipsv.1')) subprocess.check_call(['mandb'], stdout=FNULL, stderr=subprocess.STDOUT) # Enable the welcome message log.debug('Writing welcome message') wm_header = '## DO NOT REMOVE :: AUTOMATICALLY GENERATED BY IPSV ##' wm_remove = False # Remove old profile data for line in fileinput.input('/etc/profile', inplace=True): # Header / footer match? if line == wm_header: # Footer match (Stop removing) if wm_remove: wm_remove = False continue # Header match (Start removing) wm_remove = True continue # Removing lines? if wm_remove: continue # Print line and continue as normal sys.stdout.write(line) # Write new profile data with open('/etc/profile', 'a') as f: f.write("\n" + wm_header + "\n") fl_lock_path = os.path.join(ctx.config.get('Paths', 'Data'), 'first_login.lck') f.write('if [ ! -f "{lp}" ]; then'.format(lp=fl_lock_path) + "\n") f.write(' less "{wp}"'.format(wp=os.path.join(ctx.basedir, 'WELCOME.rst')) + "\n") f.write(' sudo touch "{lp}"'.format(lp=fl_lock_path) + "\n") f.write('fi' + "\n") f.write(wm_header + "\n") p.done() log.debug('Writing setup lock file') with open(os.path.join(ctx.config.get('Paths', 'Data'), 'setup.lck'), 'w') as f: f.write('1')
def install(self): """ Run the actual installation """ p = Echo('Fetching Developer Tools version information...') dev_tools = DevToolsManager(self.ctx, self.site) status = p.OK if dev_tools.latest.request else p.FAIL p.done(status) p = Echo('Fetching the required Developer Tools release...') if self.site.version in dev_tools.versions: self.log.info('Dev Tools version matched for this IPS version') dev_version = dev_tools.versions[self.site.version] status = p.OK else: dev_version = None for dv in dev_tools.versions: if (dev_version is None) or (dv <= self.site.version): dev_version = dev_tools.versions[dv] self.log.warn('No Dev Tools for IPS release %s found, using closest match (%s)', self.site.version, dev_version.version.vstring) status = p.WARN filename = dev_tools.get(dev_version) p.done(status) # Extract dev files p = Echo('Extracting Developer Tools...') tmpdir = mkdtemp('ips') dev_tools_zip = os.path.join(tmpdir, 'dev_tools.zip') dev_tools_dir = os.path.join(tmpdir, 'dev_tools') os.mkdir(dev_tools_dir) shutil.copyfile(filename, dev_tools_zip) with ZipFile(dev_tools_zip) as z: namelist = z.namelist() if re.match(r'^(\d+)|(dev_[0-9a-zA-Z]{5})/?$', namelist[0]): self.log.debug('Developer Tools directory matched: %s', namelist[0]) else: self.log.error('No developer tools directory matched, unable to continue') raise Exception('Unrecognized dev tools file format, aborting') z.extractall(dev_tools_dir) self.log.debug('Developer Tools extracted to: %s', dev_tools_dir) dev_tmpdir = os.path.join(dev_tools_dir, namelist[0]) path = dev_tmpdir for dirname, dirnames, filenames in os.walk(dev_tmpdir): for filepath in dirnames: site_path = os.path.join(self.site.root, dirname.replace(path, ''), filepath) if not os.path.exists(site_path): self.log.debug('Creating directory: %s', site_path) os.mkdir(site_path, 0o755) for filepath in filenames: tmp_path = os.path.join(dirname, filepath) site_path = os.path.join(self.site.root, dirname.replace(path, ''), filepath) shutil.copy(tmp_path, site_path) self.log.info('Developer Tools copied to: %s', self.site.root) shutil.rmtree(tmpdir) p.done() p = Echo('Putting IPS into IN_DEV mode...') const_path = os.path.join(self.site.root, 'constants.php') with open(const_path, 'w+') as f: f.write("<?php\n") f.write("\n") f.write("define( 'IN_DEV', TRUE );") p.done()
def cli(ctx, name, dname, license_key, ips_version, force, enable, ssl, spdy, gzip, cache, install, dev): """ Downloads and installs a new instance of the latest Invision Power Suite release. """ assert isinstance(ctx, Context) login_session = ctx.get_login() log = logging.getLogger('ipsv.new') ctx.cache = cache # Prompt for our desired license def get_license(): """ Prompt the user for a license selection @rtype: ips_vagrant.scraper.licenses.LicenseMeta """ licenses = Licenses(login_session).get() user_license = license_key or ctx.config.get('User', 'LicenseKey') # If we already have a license key saved, skip the prompt and use it instead if user_license: licenses = {license.license_key: license for license in licenses} if user_license in licenses: return licenses[user_license] # Ask the user to select a license key opt = choice([(key, '{u} ({k})'.format(u=license.community_url, k=license.license_key)) for key, license in enumerate(licenses)], 1, 'Which license key would you like to use?') license = licenses[opt] # Should we save this license? if click.confirm( 'Would you like to save and use this license for future requests?', True): ctx.log.debug( 'Saving license key {k}'.format(k=license.license_key)) ctx.config.set('User', 'LicenseKey', license.license_key) with open(ctx.config_path, 'wb') as configfile: ctx.config.write(configfile) return license # Get the latest IPS release lmeta = get_license() p = Echo('Fetching IPS version information...') ips = IpsManager(ctx, lmeta) p.done() if ips_version: if ips_version == 'latest_dev': v = ips.dev_version if not v: click.secho( 'There is no IPS development release available for download', err=True, fg='red', bold=True) raise Exception( 'There is no IPS development release available for download' ) p = Echo('Downloading IPS development release {vs}...'.format( vs=v.version.vstring)) else: ips_version = Version(ips_version) v = ips.versions[ips_version.vtuple] p = Echo( 'Fetching IPS version {iv}'.format(iv=ips_version.vstring)) else: v = ips.latest p = Echo( 'Downloading IPS release {vs}...'.format(vs=v.version.vstring)) filename = ips.get(v, cache) p.done() # Parse the specific domain and make sure it's valid log.debug('Parsing domain name: %s', dname) dname = domain_parse(dname) if ssl is None: ssl = dname.scheme == 'https' log.debug('Domain name parsed: %s', dname) domain = Domain.get_or_create(dname) # Make sure this site does not already exist p = Echo('Constructing site data...') site = ctx.db.query(Site).filter(Site.domain == domain).filter( collate(Site.name, 'NOCASE') == name).count() if site: p.done(p.FAIL) log.error('Site already exists') click.secho( 'An installation named "{s}" has already been created for the domain {d}' .format(s=name, d=dname.hostname), err=True, fg='red', bold=True) raise click.Abort # Create the site database entry site = Site(name=name, domain=domain, license_key=lmeta.license_key, version=v.version.vstring, ssl=ssl, spdy=spdy, gzip=gzip, enabled=enable, in_dev=dev) status = p.OK if os.path.exists(site.root): if not force: p.done(p.FAIL) click.secho( "Installation path already exists and --force was not passed:\n{p}" .format(p=site.root), err=True, fg='red', bold=True) log.info('Aborting installation, path already exists: {p}'.format( p=site.root)) raise click.Abort log.warn( 'Overwriting existing installation path: {p}'.format(p=site.root)) status = p.WARN ctx.db.add(site) ctx.db.commit() p.done(status) # Construct the HTTP path p = Echo('Constructing paths and configuration files...') site.write_nginx_config() p.done() # Generate SSL certificates if enabled if ssl: p = Echo('Generating SSL certificate...') ssl_path = os.path.join(ctx.config.get('Paths', 'NginxSSL'), domain.name) if not os.path.exists(ssl_path): log.debug('Creating new SSL path: %s', ssl_path) os.makedirs(ssl_path, 0o755) sc = CertificateFactory(site).get() site.ssl_key = sc.key site.ssl_certificate = sc.certificate with open(os.path.join(ssl_path, '{s}.key'.format(s=site.slug)), 'w') as f: f.write(sc.key) with open(os.path.join(ssl_path, '{s}.pem').format(s=site.slug), 'w') as f: f.write(sc.certificate) p.done() # Create a symlink if this site is being enabled if site.enabled: site.enable(force) # Restart Nginx p = Echo('Restarting web server...') FNULL = open(os.devnull, 'w') subprocess.check_call(['service', 'nginx', 'restart'], stdout=FNULL, stderr=subprocess.STDOUT) p.done() # Extract IPS setup files p = Echo('Extracting setup files to tmp...') tmpdir = tempfile.mkdtemp('ips') setup_zip = os.path.join(tmpdir, 'setup.zip') setup_dir = os.path.join(tmpdir, 'setup') os.mkdir(setup_dir) log.info('Extracting setup files') shutil.copyfile(filename, setup_zip) with zipfile.ZipFile(setup_zip) as z: namelist = z.namelist() if re.match(r'^ips_\w{5}\/?$', namelist[0]): log.debug('Setup directory matched: %s', namelist[0]) else: log.error('No setup directory matched, unable to continue') raise Exception('Unrecognized setup file format, aborting') z.extractall(setup_dir) log.debug('Setup files extracted to: %s', setup_dir) p.done() p = MarkerProgressBar('Copying setup files...') setup_tmpdir = os.path.join(setup_dir, namelist[0]) for dirname, dirnames, filenames in p(os.walk(setup_tmpdir)): for filepath in dirnames: site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ''), filepath) if not os.path.exists(site_path): log.debug('Creating directory: %s', site_path) os.mkdir(site_path, 0o755) for filepath in filenames: tmp_path = os.path.join(dirname, filepath) site_path = os.path.join(site.root, dirname.replace(setup_tmpdir, ''), filepath) shutil.copy(tmp_path, site_path) log.info('Setup files copied to: %s', site.root) shutil.rmtree(tmpdir) # Apply proper permissions # p = MarkerProgressBar('Setting file permissions...') writeable_dirs = ['uploads', 'plugins', 'applications', 'datastore'] for wdir in writeable_dirs: log.debug('Setting file permissions in %s', wdir) os.chmod(os.path.join(site.root, wdir), 0o777) p = MarkerProgressBar('Setting file permissions...', nl=False) for dirname, dirnames, filenames in p( os.walk(os.path.join(site.root, wdir))): for filename in filenames: os.chmod(os.path.join(dirname, filename), 0o666) for filename in dirnames: os.chmod(os.path.join(dirname, filename), 0o777) Echo('Setting file permissions...').done() shutil.move(os.path.join(site.root, 'conf_global.dist.php'), os.path.join(site.root, 'conf_global.php')) os.chmod(os.path.join(site.root, 'conf_global.php'), 0o777) # Run the installation if install: p = Echo('Initializing installer...') i = installer(v.version, ctx, site, force) p.done() i.start() else: db_info = None if click.confirm( 'Would you like to create the database for this installation now?', default=True): db_info = create_database(site) click.echo('------') if db_info: db_name, db_user, db_pass = db_info log.debug('MySQL Database Name: %s', db_name) log.debug('MySQL Database User: %s', db_user) log.debug('MySQL Database Password: %s', db_pass) click.secho('MySQL Database Name: {dbn}'.format(dbn=db_name), bold=True) click.secho('MySQL Database User: {dbu}'.format(dbu=db_user), bold=True) click.secho('MySQL Database Password: {dbp}'.format(dbp=db_pass), bold=True) click.secho( 'IPS is now ready to be installed. To proceed with the installation, follow the link below', fg='yellow', bold=True) click.echo('{schema}://{host}'.format( schema='https' if site.ssl else 'http', host=site.domain.name))