webapp php complete
This commit is contained in:
parent
cefbe379b7
commit
ee469a0c78
|
@ -6,7 +6,7 @@ from django.core.exceptions import ValidationError
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from orchestra.forms import UserCreationForm, UserChangeForm
|
||||
from orchestra.contrib.webapps.settings import WEBAPP_NEW_SERVERS
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
|
||||
from . import settings
|
||||
from .models import SystemUser
|
||||
|
@ -176,7 +176,7 @@ class WebappUserFormMixin(object):
|
|||
if not self.instance.pk:
|
||||
server = self.cleaned_data.get('target_server')
|
||||
if server:
|
||||
if server.name not in WEBAPP_NEW_SERVERS:
|
||||
if server.name not in NEW_SERVERS:
|
||||
self.add_error("target_server", _(f"{server} does not belong to the new servers"))
|
||||
return self.cleaned_data
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
|||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not change:
|
||||
user = form.cleaned_data['username']
|
||||
user = form.cleaned_data.get('username')
|
||||
if user:
|
||||
user = WebappUsers(
|
||||
username=form.cleaned_data['username'],
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import pkgutil
|
||||
import textwrap
|
||||
|
||||
from django.template import Template, Context
|
||||
from .. import settings
|
||||
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
|
||||
class WebAppServiceMixin(object):
|
||||
model = 'webapps.WebApp'
|
||||
|
@ -19,6 +19,7 @@ class WebAppServiceMixin(object):
|
|||
CREATED=0
|
||||
if [[ ! -e %(app_path)s ]]; then
|
||||
mkdir -p %(app_path)s
|
||||
chown %(sftpuser)s:%(sftpuser)s %(app_path)s
|
||||
CREATED=1
|
||||
elif [[ -z $( ls -A %(app_path)s ) ]]; then
|
||||
CREATED=1
|
||||
|
@ -38,6 +39,15 @@ class WebAppServiceMixin(object):
|
|||
|
||||
def set_under_construction(self, context):
|
||||
if context['under_construction_path']:
|
||||
# cambios de permisos en servidores nuevos
|
||||
perms = Template(textwrap.dedent("""\
|
||||
{% if sftpuser %}
|
||||
chown -R {{ sftpuser }}:{{ sftpuser }} {{ app_path }}/* {% else %}
|
||||
chown -R {{ user }}:{{ group }} {{ app_path }}/*
|
||||
{% endif %}
|
||||
"""
|
||||
))
|
||||
context.update({'perms' : perms.render(Context(context))})
|
||||
self.append(textwrap.dedent("""
|
||||
# Set under construction if needed
|
||||
if [[ $CREATED == 1 && ! $(ls -A %(app_path)s | head -n1) ]]; then
|
||||
|
@ -46,11 +56,11 @@ class WebAppServiceMixin(object):
|
|||
sleep 2
|
||||
if [[ ! $(ls -A %(app_path)s | head -n1) ]]; then
|
||||
cp -r %(under_construction_path)s %(app_path)s
|
||||
chown -R %(user)s:%(group)s %(app_path)s/*
|
||||
%(perms)s
|
||||
fi' &> /dev/null &
|
||||
fi""") % context
|
||||
)
|
||||
|
||||
|
||||
def delete_webapp_dir(self, context):
|
||||
if context['deleted_app_path']:
|
||||
self.append(textwrap.dedent("""\
|
||||
|
@ -68,8 +78,8 @@ class WebAppServiceMixin(object):
|
|||
def get_context(self, webapp):
|
||||
context = webapp.type_instance.get_directive_context()
|
||||
context.update({
|
||||
'user': webapp.sftpuser.username if webapp.target_server.name in settings.WEBAPP_NEW_SERVERS else webapp.get_username(),
|
||||
'group': webapp.sftpuser.username if webapp.target_server.name in settings.WEBAPP_NEW_SERVERS else webapp.get_groupname(),
|
||||
'user': webapp.get_username(),
|
||||
'group': webapp.get_groupname(),
|
||||
'app_name': webapp.name,
|
||||
'app_type': webapp.type,
|
||||
'app_path': webapp.get_path(),
|
||||
|
@ -77,6 +87,7 @@ class WebAppServiceMixin(object):
|
|||
'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
|
||||
'is_mounted': webapp.content_set.exists(),
|
||||
'target_server': webapp.target_server,
|
||||
'sftpuser' : webapp.sftpuser.username if webapp.target_server.name in NEW_SERVERS else None
|
||||
})
|
||||
context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context
|
||||
return context
|
||||
|
|
|
@ -5,6 +5,7 @@ from collections import OrderedDict
|
|||
from django.template import Template, Context
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
from orchestra.contrib.orchestration import ServiceController
|
||||
|
||||
from . import WebAppServiceMixin
|
||||
|
@ -34,7 +35,12 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
|||
def save(self, webapp):
|
||||
self.delete_old_config(webapp)
|
||||
context = self.get_context(webapp)
|
||||
self.create_webapp_dir(context)
|
||||
|
||||
if context.get('target_server').name in NEW_SERVERS:
|
||||
self.check_webapp_dir(context)
|
||||
else:
|
||||
self.create_webapp_dir(context)
|
||||
|
||||
if webapp.type_instance.is_fpm:
|
||||
self.save_fpm(webapp, context)
|
||||
elif webapp.type_instance.is_fcgid:
|
||||
|
@ -122,11 +128,10 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
|||
def delete(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
self.delete_old_config(webapp)
|
||||
# if webapp.type_instance.is_fpm:
|
||||
# self.delete_fpm(webapp, context)
|
||||
# elif webapp.type_instance.is_fcgid:
|
||||
# self.delete_fcgid(webapp, context)
|
||||
self.delete_webapp_dir(context)
|
||||
if context.get('target_server').name in NEW_SERVERS:
|
||||
webapp.sftpuser.delete()
|
||||
else:
|
||||
self.delete_webapp_dir(context)
|
||||
|
||||
def has_sibilings(self, webapp, context):
|
||||
return type(webapp).objects.filter(
|
||||
|
@ -229,12 +234,21 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
|||
fpm_config = Template(textwrap.dedent("""\
|
||||
;; {{ banner }}
|
||||
[{{ user }}-{{app_name}}]
|
||||
{% if sftpuser %}
|
||||
user = {{ sftpuser }}
|
||||
group = {{ sftpuser }}
|
||||
|
||||
listen = {{ fpm_listen | safe }}
|
||||
listen.owner = root
|
||||
listen.group = {{ sftpuser }}
|
||||
{% else %}
|
||||
user = {{ user }}
|
||||
group = {{ group }}
|
||||
|
||||
listen = {{ fpm_listen | safe }}
|
||||
listen.owner = {{ user }}
|
||||
listen.group = {{ group }}
|
||||
{% endif %}
|
||||
|
||||
pm = ondemand
|
||||
pm.max_requests = {{ max_requests }}
|
||||
|
@ -313,6 +327,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
|
|||
context = super().get_context(webapp)
|
||||
context.update({
|
||||
'max_requests': settings.WEBAPPS_PHP_MAX_REQUESTS,
|
||||
'target_server': webapp.target_server,
|
||||
})
|
||||
self.update_fpm_context(webapp, context)
|
||||
self.update_fcgid_context(webapp, context)
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from orchestra.contrib.orchestration import ServiceController
|
||||
|
||||
from . import WebAppServiceMixin
|
||||
from ..settings import WEBAPP_NEW_SERVERS
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
|
||||
class StaticController(WebAppServiceMixin, ServiceController):
|
||||
"""
|
||||
|
@ -15,7 +15,7 @@ class StaticController(WebAppServiceMixin, ServiceController):
|
|||
|
||||
def save(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
if context.get('target_server').name in WEBAPP_NEW_SERVERS:
|
||||
if context.get('target_server').name in NEW_SERVERS:
|
||||
self.check_webapp_dir(context)
|
||||
self.set_under_construction(context)
|
||||
else:
|
||||
|
@ -24,7 +24,7 @@ class StaticController(WebAppServiceMixin, ServiceController):
|
|||
|
||||
def delete(self, webapp):
|
||||
context = self.get_context(webapp)
|
||||
if context.get('target_server').name in WEBAPP_NEW_SERVERS:
|
||||
if context.get('target_server').name in NEW_SERVERS:
|
||||
webapp.sftpuser.delete()
|
||||
else:
|
||||
self.delete_webapp_dir(context)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.2.28 on 2023-07-28 14:39
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('orchestration', '__first__'),
|
||||
('webapps', '0002_webapp_sftpuser'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='webapp',
|
||||
unique_together={('name', 'account', 'target_server')},
|
||||
),
|
||||
]
|
|
@ -34,7 +34,7 @@ class WebApp(models.Model):
|
|||
databaseusers = VirtualDatabaseUserRelation('databases.DatabaseUser')
|
||||
|
||||
class Meta:
|
||||
unique_together = ('name', 'account')
|
||||
unique_together = ('name', 'account', 'target_server')
|
||||
verbose_name = _("Web App")
|
||||
verbose_name_plural = _("Web Apps")
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = Setting('WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
|
|||
|
||||
|
||||
WEBAPPS_PHPFPM_POOL_PATH = Setting('WEBAPPS_PHPFPM_POOL_PATH',
|
||||
'/etc/php%(php_version_number)s/fpm/pool.d/%(user)s-%(app_name)s.conf',
|
||||
'/etc/php/%(php_version_number)s/fpm/pool.d/%(user)s-%(app_name)s.conf',
|
||||
help_text="Available fromat names: <tt>%s</tt>" % ', '.join(_php_names),
|
||||
validators=[Setting.string_format_validator(_php_names)],
|
||||
)
|
||||
|
@ -283,10 +283,3 @@ WEBAPPS_CMS_CACHE_DIR = Setting('WEBAPPS_CMS_CACHE_DIR',
|
|||
help_text="Server-side cache directori for CMS tarballs.",
|
||||
)
|
||||
|
||||
WEBAPP_NEW_SERVERS = Setting('WEBAPP_NEW_SERVERS',
|
||||
(
|
||||
'bookworm',
|
||||
'web-11.pangea.lan',
|
||||
'web-12.pangea.lan',
|
||||
)
|
||||
)
|
||||
|
|
|
@ -2,64 +2,14 @@ import os
|
|||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from rest_framework import serializers
|
||||
|
||||
from orchestra.core import validators
|
||||
from orchestra.plugins.forms import PluginDataForm
|
||||
from orchestra.utils.python import random_ascii
|
||||
from orchestra.plugins.forms import ExtendedPluginDataForm
|
||||
|
||||
from ..options import AppOption
|
||||
from ..settings import WEBAPP_NEW_SERVERS
|
||||
|
||||
from . import AppType
|
||||
from .php import PHPApp, PHPAppForm, PHPAppSerializer
|
||||
|
||||
|
||||
class StaticForm(PluginDataForm):
|
||||
username = forms.CharField(label=_("Username"), max_length=16,
|
||||
required=False, validators=[validators.validate_name],
|
||||
help_text=_("Required. 16 characters or fewer. Letters, digits and "
|
||||
"@/./+/-/_ only."),
|
||||
error_messages={
|
||||
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
|
||||
"@/./+/-/_ characters.")})
|
||||
password1 = forms.CharField(label=_("Password"), required=False,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
validators=[validators.validate_password],
|
||||
help_text=_("Suggestion: %s") % random_ascii(15))
|
||||
password2 = forms.CharField(label=_("Password confirmation"), required=False,
|
||||
widget=forms.PasswordInput,
|
||||
help_text=_("Enter the same password as above, for verification."))
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(StaticForm, self).__init__(*args, **kwargs)
|
||||
self.fields['sftpuser'].widget = forms.HiddenInput()
|
||||
if self.instance.id is not None:
|
||||
self.fields['username'].widget = forms.HiddenInput()
|
||||
self.fields['password1'].widget = forms.HiddenInput()
|
||||
self.fields['password2'].widget = forms.HiddenInput()
|
||||
|
||||
def clean(self):
|
||||
if not self.instance.id:
|
||||
webapp_server = self.cleaned_data.get("target_server")
|
||||
username = self.cleaned_data.get('username')
|
||||
if webapp_server is None:
|
||||
self.add_error("target_server", _("choice some target_server"))
|
||||
else:
|
||||
if webapp_server.name in WEBAPP_NEW_SERVERS and username == '':
|
||||
self.add_error("username", _("SFTP user is required by new webservers"))
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
msg = _("The two password fields didn't match.")
|
||||
raise ValidationError(msg)
|
||||
return password2
|
||||
|
||||
|
||||
class StaticApp(AppType):
|
||||
name = 'static'
|
||||
verbose_name = "Static"
|
||||
|
@ -67,7 +17,7 @@ class StaticApp(AppType):
|
|||
"Apache2 will be used to serve static content and execute CGI files.")
|
||||
icon = 'orchestra/icons/apps/Static.png'
|
||||
option_groups = (AppOption.FILESYSTEM,)
|
||||
form = StaticForm
|
||||
form = ExtendedPluginDataForm
|
||||
|
||||
def get_directive(self):
|
||||
return ('static', self.instance.get_path())
|
||||
|
|
|
@ -5,9 +5,10 @@ from django import forms
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from orchestra.plugins.forms import PluginDataForm
|
||||
from orchestra.plugins.forms import PluginDataForm, ExtendedPluginDataForm
|
||||
from orchestra.utils.functional import cached
|
||||
from orchestra.utils.python import OrderedSet
|
||||
from orchestra.utils.python import OrderedSet, random_ascii
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
|
||||
from .. import settings, utils
|
||||
from ..options import AppOption
|
||||
|
@ -19,13 +20,23 @@ help_message = _("Version of PHP used to execute this webapp. <br>"
|
|||
"Changing the PHP version may result in application malfunction, "
|
||||
"make sure that everything continue to work as expected.")
|
||||
|
||||
|
||||
class PHPAppForm(PluginDataForm):
|
||||
class PHPAppForm(ExtendedPluginDataForm):
|
||||
php_version = forms.ChoiceField(label=_("PHP version"),
|
||||
choices=settings.WEBAPPS_PHP_VERSIONS,
|
||||
initial=settings.WEBAPPS_DEFAULT_PHP_VERSION,
|
||||
help_text=help_message)
|
||||
|
||||
|
||||
# def clean_php_version(self):
|
||||
# # TODO: restriccin PHP diferentes servers
|
||||
# if not self.instance.id:
|
||||
# webapp_server = self.cleaned_data.get("target_server")
|
||||
# php_version = self.cleaned_data.get('php_version')
|
||||
# if webapp_server is None:
|
||||
# pass
|
||||
# else:
|
||||
# if webapp_server.name in NEW_SERVERS and not username:
|
||||
# self.add_error("php_version", _(f"Server {webapp_server} not allow {php_version}"))
|
||||
|
||||
|
||||
class PHPAppSerializer(serializers.Serializer):
|
||||
php_version = serializers.ChoiceField(label=_("PHP version"),
|
||||
|
|
|
@ -4,6 +4,12 @@ from django.utils.encoding import force_str
|
|||
from orchestra.admin.utils import admin_link
|
||||
from orchestra.forms.widgets import SpanWidget
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from orchestra.core import validators
|
||||
from orchestra.utils.python import random_ascii
|
||||
from orchestra.settings import NEW_SERVERS
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class PluginForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -71,3 +77,51 @@ class PluginModelAdapterForm(PluginForm):
|
|||
display = '%s <a href=".">change</a>' % link
|
||||
self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
|
||||
help_text = self.fields[self.plugin_field].help_text
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
class ExtendedPluginDataForm(PluginDataForm):
|
||||
# añade campos de username para creacion de sftpuser en servidores nuevos
|
||||
username = forms.CharField(label=_("Username"), max_length=16,
|
||||
required=False, validators=[validators.validate_name],
|
||||
help_text=_("Required. 16 characters or fewer. Letters, digits and "
|
||||
"@/./+/-/_ only."),
|
||||
error_messages={
|
||||
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
|
||||
"@/./+/-/_ characters.")})
|
||||
password1 = forms.CharField(label=_("Password"), required=False,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
validators=[validators.validate_password],
|
||||
help_text=_("Suggestion: %s") % random_ascii(15))
|
||||
password2 = forms.CharField(label=_("Password confirmation"), required=False,
|
||||
widget=forms.PasswordInput,
|
||||
help_text=_("Enter the same password as above, for verification."))
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ExtendedPluginDataForm, self).__init__(*args, **kwargs)
|
||||
self.fields['sftpuser'].widget = forms.HiddenInput()
|
||||
if self.instance.id is not None:
|
||||
self.fields['username'].widget = forms.HiddenInput()
|
||||
self.fields['password1'].widget = forms.HiddenInput()
|
||||
self.fields['password2'].widget = forms.HiddenInput()
|
||||
|
||||
def clean_username(self):
|
||||
if not self.instance.id:
|
||||
webapp_server = self.cleaned_data.get("target_server")
|
||||
username = self.cleaned_data.get('username')
|
||||
if webapp_server is None:
|
||||
self.add_error("target_server", _("choice some server"))
|
||||
else:
|
||||
if webapp_server.name in NEW_SERVERS and not username:
|
||||
self.add_error("username", _("SFTP user is required by new webservers"))
|
||||
return username
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
password2 = self.cleaned_data.get("password2")
|
||||
if password1 and password2 and password1 != password2:
|
||||
msg = _("The two password fields didn't match.")
|
||||
raise ValidationError(msg)
|
||||
return password2
|
|
@ -90,3 +90,11 @@ ORCHESTRA_SSH_CONTROL_PATH = Setting('ORCHESTRA_SSH_CONTROL_PATH',
|
|||
'~/.ssh/orchestra-%r-%h-%p',
|
||||
help_text='Location for the control socket used by the multiplexed sessions, used for SSH connection reuse.'
|
||||
)
|
||||
|
||||
NEW_SERVERS = Setting('NEW_SERVERS',
|
||||
(
|
||||
'bookworm',
|
||||
'web-11.pangea.lan',
|
||||
'web-12.pangea.lan',
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue