def upgrade_app(self, appname, to): """Upgrades the given app to the given version.""" if "/" in appname or "\\" in appname: raise ValueError("Bad app name") self._check_appname(appname) log.info("Upgrading {0} to version {1}".format( appname, to)) apppath = os.path.join(self.projectdir, appname) apptype = opus.lib.builder.sources.introspect_source(apppath) log.debug("apppath is %s", apppath) log.debug("apptype is %s", apptype) opus.lib.builder.sources.upgrade_functions[apptype](apppath, to) # If there isn't a media link and there should be, add one if os.path.exists(os.path.join(self.projectdir, appname, "media")) \ and not os.path.exists(os.path.join(self.projectdir, "media",appname)): os.symlink(os.path.join("..",appname,"media"), os.path.join(self.projectdir, "media",appname)) self._touch_wsgi()
def restart_celery(self, secureops="secureops"): """Call this after you're done adding, upgrading, or deleting apps to reload the celery daemon""" pidfile = os.path.join(self.projectdir, "run", "supervisord.pid") if os.path.exists(pidfile): log.info("Restarting supervisord/celery") proc = subprocess.Popen([secureops,"-s", "opus"+self.projectname, self.projectdir, "-H", ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = proc.communicate()[0] ret = proc.wait() if ret: raise BuildException("Could not restart supervisord. {0}".format(output))
def create(request, projectname): """Create and deploy a new project. Displays the form to do so on GET, goes and does a create + deploy operation on POST. Also has the feature to pre-fill out the form from an incomming JSON token. """ if request.method == "POST" and \ request.META['CONTENT_TYPE'].find("application/x-www-form-urlencoded") != -1: # If the submitted type is not form encoded data, it's probably a json # spec of applications, which should instead go to populate and display # the forms. pform = forms.ProjectForm(request.POST) appsform = forms.AppFormSet(request.POST) dform = forms.DeploymentForm(request.POST, noactive=True) allforms = [pform, appsform, dform] # If forms aren't valid, fall through and display the (invalid) forms # with error text if all(f.is_valid() for f in allforms): log.info("Preparing to create+deploy %s", projectname) pdata = pform.cleaned_data # Create the deployment object to do some early validation checks deployment = models.DeployedProject() deployment.name = projectname deployment.owner = request.user deployment.full_clean() # Configure the new project. None of these actions actually execute # until we enter the try block below builder = opus.lib.builder.ProjectBuilder(projectname) for appdata in appsform.cleaned_data: if not appdata: # Left blank, nothing to add continue log.debug(" ... with app %r", appdata['apppath']) builder.add_app(appdata['appname'], appdata['apppath'], appdata['apptype']) if pdata['idprovider'] != 'local': log.debug(" ... and the idp app %r", pdata['idprovider']) appname, apptype, apppath = \ settings.OPUS_ALLOWED_AUTH_APPS[pdata['idprovider']] builder.add_app(appname, apppath, apptype) # Now actually execute the tasks. This is done in a try block which # catches all exceptions so that we can roll back failed partial # deployments in any error cases. log.debug("Executing create action on %r...", projectname) try: # Create the project directory projectdir = builder.create(settings.OPUS_BASE_DIR) log.info("%r created, starting deploy process", projectname) # Prepare deployment parameters info = models.DeploymentInfo() info.dbengine = dform.cleaned_data['dbengine'] # If requested, create a database for it if info.dbengine == "postgresql_psycopg2" and \ settings.OPUS_AUTO_POSTGRES_CONFIG: autodb = opus.project.deployment.database.\ setup_postgres(projectname) info.dbname, info.dbuser, info.dbpassword, \ info.dbhost, info.dbport = autodb elif info.dbengine == "sqlite3": # SQLite database locations get set automatically by the # deployment libraries. No other options are set. # SQLite is handled differently (as far as the location of # the code) since the file must be secured properly. So # the deployer handles that correctly along side its # routines to change permissions on the directory. pass else: info.dbname = dform.cleaned_data['dbname'] info.dbuser = dform.cleaned_data['dbuser'] info.dbpassword = dform.cleaned_data['dbpassword'] info.dbhost = dform.cleaned_data['dbhost'] info.dbport = dform.cleaned_data['dbport'] info.superusername = dform.cleaned_data['superusername'] info.superemail = dform.cleaned_data['superemail'] info.superpassword = dform.cleaned_data['superpassword'] # Deploy it now! But don't activate it. deployment.deploy(info, False) deployment.set_debug(dform.cleaned_data['debug']) deployment.save() except Exception, e: # The project didn't deploy for whatever reason. Delete the # project directory and re-raise the exception # Careful that this method isn't called on an existing project, # since it could be tricked into deleting an existing project. # edit_or_create() ought to check that for us, this function # shouldn't be called on an existing project. log.error("Project didn't fully create or deploy, rolling back deployment. %s", e) # Schedule a task to delete the project tasks.destroy_project_by_name.delay(deployment.name) raise log.info("Project %r successfully deployed", projectname) return redirect("opus.project.deployment.views.set_app_settings", projectname) else: log.debug(request.POST) log.debug("Create view called, but forms didn't validate")
def edit(request, project): """Configuration editor view for an already deployed project """ initial = _get_initial_edit_data(project) if request.method == "POST": form = forms.DeploymentForm(request.POST, initial=initial) if form.is_valid(): log.info("Edit form submitted and is valid. Editing project parameters") cd = form.cleaned_data # Go and modify the project/config parameters. Don't save yet for field in form.changed_data: if field == "dbengine": database['ENGINE'] = 'django.db.backends.' + cd['dbengine'] log.debug("dbengine changed to %s", cd['dbengine']) elif field == "dbpassword": database['PASSWORD'] = cd['dbpassword'] log.debug("dbpassword changed",) elif field == "dbhost": database['HOST'] = cd['dbhost'] log.debug("dbhost changed to %s", cd['dbhost']) elif field == "dbport": database['PORT'] = cd['dbport'] log.debug("dbport changed to %s", cd['dbport']) project.set_debug(cd['debug']) messages = [] # Validate the modified model try: # This may error if there's a port conflict or something project.full_clean() except ValidationError, e: log.info("Project model didn't clean. %s", e) messages.extend(e.messages) # Re-load the project object with the old data, for the "Info" # section project = models.DeployedProject.objects.get(pk=project.pk) else: log.info("Model cleaned, saving") # save model and config, activate/deactivate if requested, # add new superuser if requested project.save() if "superusername" in form.changed_data: # Should this code be offloaded to a method in the model? log.debug("Adding new superuser") deployer = opus.lib.deployer.ProjectDeployer(project.projectdir) try: deployer.create_superuser(cd['superusername'], cd['superemail'], cd['superpassword'], ) except DeploymentException, e: if "column username is not unique" in e.message: messages.append("User with that name already exists!") else: raise e else: messages.append("New superuser created") # Don't re-render the username and password in the form # First make it mutable form.data = form.data.copy() # Then delete these properties del form.data['superusername'] del form.data['superemail'] del form.data['superpassword'] del form.data['superpasswordconfirm'] # Do this after everything else. If activate fails due to # missing app settings, it redirects. Everything else should # still be saved though. if 'active' in form.changed_data: if cd['active']: if not project.all_settings_set(): log.debug("Tried to activate, but still needs settings set. Rendering app settings page") appforms = _get_app_settings_forms(project.get_app_settings(), project.config) if messages: messages.append("") messages.append("BUT...") messages.append("You asked me to activate the project, but you must set all the settings below first.") return render("deployment/appsettings.html", dict( appforms=appforms, project=project, messages=messages, ), request) log.debug("Activating") project.activate() messages.append("Project activated") else: log.debug("Deactivating") project.deactivate() messages.append("Project deactivated") return render("deployment/edit.html", {'project': project, 'form': form, 'message': "<br />".join(messages), 'applist': _get_apps(project), 'appform': forms.AppForm(), }, request)