From d53f048bf0d62ef0e92b2388992aaa2241cffd37 Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Sat, 21 Apr 2018 01:25:21 +0200 Subject: [PATCH] Bug 20630: An attempt at multitenancy with Mojolicious This patch tries to achieve the same goal as bug 15562, but with Mojolicious and with minimal changes. It does so by accepting a X-Koha-Conf header containing the path to an existing koha-conf.xml Test plan: 1. Apply bug 20582 2. Apply this patch 3. Use etc/nginx.conf as an example and create two server blocks. Make sure you have different paths for X-Koha-Conf and the files differ at least for the database name (and memcached namespace if memcached is enabled) 4. Run `env KOHA_CONF=a-third-koha-conf.xml morbo bin/koha` 5. Try to access the first server block, change some visible sysprefs like IntranetUserCss and OpacUserCss 6. Do the same with the second server block, and again with http://yourserverip:3000/ 7. Confirm that everything is working normally for each instance 8. Test with and without memcached 9. Restart morbo without KOHA_CONF (env KOHA_CONF= morbo bin/koha) and confirm that the two first sites still work --- C4/Context.pm | 7 ++++-- Koha/App/Koha.pm | 52 +++++++++++++++++++++++++++++++++++++++++-- Koha/App/Plugin/CGIBinKoha.pm | 2 ++ etc/nginx.conf | 1 + 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/C4/Context.pm b/C4/Context.pm index 7ce91b6b35..1ae930a487 100644 --- a/C4/Context.pm +++ b/C4/Context.pm @@ -403,7 +403,6 @@ with this method. =cut -my $syspref_cache = Koha::Caches->get_instance('syspref'); my $use_syspref_cache = 1; sub preference { my $self = shift; @@ -415,7 +414,7 @@ sub preference { $var = lc $var; if ($use_syspref_cache) { - $syspref_cache = Koha::Caches->get_instance('syspref') unless $syspref_cache; + my $syspref_cache = Koha::Caches->get_instance('syspref'); my $cached_var = $syspref_cache->get_from_cache("syspref_$var"); return $cached_var if defined $cached_var; } @@ -425,6 +424,7 @@ sub preference { my $value = $syspref ? $syspref->value() : undef; if ( $use_syspref_cache ) { + my $syspref_cache = Koha::Caches->get_instance('syspref'); $syspref_cache->set_in_cache("syspref_$var", $value); } return $value; @@ -480,6 +480,7 @@ will not be seen by this process. sub clear_syspref_cache { return unless $use_syspref_cache; + my $syspref_cache = Koha::Caches->get_instance('syspref'); $syspref_cache->flush_all; } @@ -533,6 +534,7 @@ sub set_preference { } if ( $use_syspref_cache ) { + my $syspref_cache = Koha::Caches->get_instance('syspref'); $syspref_cache->set_in_cache( "syspref_$variable", $value ); } @@ -554,6 +556,7 @@ sub delete_preference { if ( Koha::Config::SysPrefs->find( $var )->delete ) { if ( $use_syspref_cache ) { + my $syspref_cache = Koha::Caches->get_instance('syspref'); $syspref_cache->clear_from_cache("syspref_$var"); } diff --git a/Koha/App/Koha.pm b/Koha/App/Koha.pm index 6e47272a81..88fb2df488 100644 --- a/Koha/App/Koha.pm +++ b/Koha/App/Koha.pm @@ -7,6 +7,8 @@ use Mojo::Base 'Mojolicious'; use Koha::Caches; use Koha::Cache::Memory::Lite; +use C4::Context (); # Do not call import + sub startup { my ($self) = @_; @@ -17,11 +19,18 @@ sub startup { # CGI::Compile $self->plugin('CGIBinKoha'); + # Plugin RESTV1 loads Koha::REST::V1 which loads C4::Context + # This line make C4::Context::import return early to avoid warning about + # config file not found + # FIXME This is dirty... + C4::Context->set_context({}); + # Create routes for API # FIXME This generates routes like this: /api/api/v1/... $self->plugin('RESTV1'); $self->hook(before_dispatch => \&_before_dispatch); + $self->hook(around_action => \&_around_action); # Everything after this comment is only needed to use Mojolicious # controllers. @@ -55,9 +64,48 @@ sub _before_dispatch { $c->req->url->parse($url); - # Flush caches before every request - Koha::Caches->flush_L1_caches(); +} + +sub _around_action { + my ($next, $c, $action, $last) = @_; + + # Flush memory caches before every request + my $caches = $Koha::Caches::singleton_caches; + if ($caches) { + foreach my $key (keys %$caches) { + my $cache = $caches->{$key}; + if (ref $cache->{cache} eq 'Cache::Memory') { + $cache->flush_all; + } + $cache->flush_L1_cache; + } + } + $Koha::Caches::singleton_caches = {}; Koha::Cache::Memory::Lite->flush(); + + # Set KOHA_CONF + my $previous_koha_conf = $ENV{KOHA_CONF}; + my $koha_conf = $c->req->headers->header('X-Koha-Conf'); + $ENV{KOHA_CONF} = $koha_conf if $koha_conf; + unless ($ENV{KOHA_CONF}) { + die "KOHA_CONF is not set. Either set the environment variable or use HTTP header X-Koha-Conf"; + } + + # Reload context and schema + eval { C4::Context->restore_context; }; + C4::Context->set_context(C4::Context->new); + + eval { Koha::Database->restore_schema }; + Koha::Database->set_schema(Koha::Database->schema({new => 1})); + + my $ret = $next->(); + + # Just in case the app receive requests with and without X-Koha-Conf + # Requests without X-Koha-Conf shouldn't be affected by the latest request + # with X-Koha-Conf + $ENV{KOHA_CONF} = $previous_koha_conf; + + return $ret; } 1; diff --git a/Koha/App/Plugin/CGIBinKoha.pm b/Koha/App/Plugin/CGIBinKoha.pm index 05e89457c2..1c1989f669 100644 --- a/Koha/App/Plugin/CGIBinKoha.pm +++ b/Koha/App/Plugin/CGIBinKoha.pm @@ -78,6 +78,8 @@ sub _psgi_env { $env->{$name} = $value; } + $env->{KOHA_CONF} = $c->req->headers->header('X-Koha-Conf') // $ENV{KOHA_CONF}; + return $env; } diff --git a/etc/nginx.conf b/etc/nginx.conf index da2184b1de..64161d8dc4 100644 --- a/etc/nginx.conf +++ b/etc/nginx.conf @@ -11,5 +11,6 @@ server { location / { include proxy_params; proxy_pass http://koha; + proxy_set_header X-Koha-Conf "/home/koha/etc/koha-conf.xml"; # CHANGEME } } -- 2.14.2