From a447484dd3b29d74ad008e4844836521af8002ef Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Wed, 10 Mar 2021 16:08:45 +0200 Subject: [PATCH 1/7] Update to using Ubuntu 20.04 as base OS image. Additional Ubuntu packages: libcgi-pm-perl (for CGI::Cookie) libdbd-mariadb-perl (for alternate DB connector) iputils-ping (prevents ping during startup from failing) imagemagick (needed by https://github.com/openwebwork/pg/pull/541) docker-compose.yml: * Set fixed container names (so names will not depend on name of directory where docker-compose is being run. * Sample lines to use some new sample DB config files + brief comments. * Sample line to make host /etc/localtime read-only available to the DB container. * Sample ulimits + timezone lines for the DB container. * Set the Docker container to mount the updated Lite.pm with the UTF-8 patch. * Sample lines to use either DB connector. Default remains the "old" DBD::mysql driver. * Modify the bottom "main" volumes section, so fixed persistant volumes are used for the OPL and the DB storage, regardless of the directory name where docker-compose is run. --- Dockerfile | 16 +- docker-compose.yml | 57 ++++- docker-config/Lite.pm | 451 +++++++++++++++++++++++++++++++++++ docker-config/db/limits.conf | 58 +++++ docker-config/db/mariadb.cnf | 5 +- docker-config/db/my.cnf | 196 +++++++++++++++ 6 files changed, 770 insertions(+), 13 deletions(-) create mode 100644 docker-config/Lite.pm create mode 100644 docker-config/db/limits.conf create mode 100644 docker-config/db/my.cnf diff --git a/Dockerfile b/Dockerfile index e6fb0506a7..3edb14de8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -81,7 +81,7 @@ RUN git clone --single-branch --branch master --depth 1 https://github.com/mathj # we need to change FROM before setting the ENV variables -FROM ubuntu:18.04 +FROM ubuntu:20.04 ENV WEBWORK_URL=/webwork2 \ WEBWORK_ROOT_URL=http://localhost \ @@ -109,14 +109,10 @@ ENV WEBWORK_ROOT=$APP_ROOT/webwork2 \ # ================================================================== -# Phase 3 - Ubuntu 18.04 base image + required packages +# Phase 3 - Ubuntu 20.04 base image + required packages -# Packages changes/added for ubuntu 18.04: - -# For ubuntu 18.04 libemail-address-xs-perl installed from Ubuntu, for 16.04 it would be installed using cpamn -# -# texlive-generic-recommended # For ubuntu 16.04 - contains path.sty -# texlive-plain-generic # For ubuntu 18.04 - contains path.sty +# Packages changes/added for ubuntu 20.04: +# libcgi-pm-perl (for CGI::Cookie), libdbd-mariadb-perl # Do NOT include "apt-get -y upgrade" # see: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ @@ -128,11 +124,13 @@ RUN apt-get update \ dvipng \ gcc \ libapache2-request-perl \ + libcgi-pm-perl \ libcrypt-ssleay-perl \ libdatetime-perl \ libdancer-perl \ libdancer-plugin-database-perl \ libdbd-mysql-perl \ + libdbd-mariadb-perl \ libemail-address-xs-perl \ libexception-class-perl \ libextutils-xsbuilder-perl \ @@ -221,6 +219,8 @@ RUN apt-get update \ fonts-linuxlibertine \ lmodern \ zip \ + iputils-ping \ + imagemagick \ jq \ npm \ && apt-get clean \ diff --git a/docker-compose.yml b/docker-compose.yml index cf76cc4ca3..b5f20820d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,12 +2,39 @@ version: '3.5' services: db: image: mariadb:10.4 + + # Set a fixed container name, so it does not depend on the directory name + container_name: webwork2_db_1 + volumes: - mysql:/var/lib/mysql # Set up UTF8MB4 in config file for the container. # Needs to be done BEFORE the database is created. + # *** Some other MariaDB settings were modified in the sample file: + # wait_timeout, interactive_timeout, max_connections, net_read_timeout + # + skip-name-resolve + # Note: It seems different versions of the MariaDB container need + # this file in different locations. Put in in both places. - "./docker-config/db/mariadb.cnf:/etc/mysql/conf.d/mariadb.cnf" + - "./docker-config/db/mariadb.cnf::/etc/mysql/mariadb.cnf" + + # Tuning MariaDB config - sample file + # The sample file modifies wait_timeout, and adds skip-name-resolve + #- "./docker-config/db/my.cnf:/etc/mysql/my.cnf" + + # Adjust open file limits for MariaDB/mySQL - sample file + #- "./docker-config/db/limits.conf:/etc/security/limits.conf" + + # Provides read only access to the host system's /etc/localtime - tested on Linux hosts + #- "/etc/localtime:/etc/localtime:ro" + + # The ulimits lines were only tested on Linux hosts in conjuction woth the limits.conf file +# ulimits: +# nofile: +# soft: 4096 +# hard: 4096 + restart: always environment: # When the MariaDB container is first started it will set the @@ -21,9 +48,22 @@ services: MYSQL_USER: ${WEBWORK_DB_USER} MYSQL_PASSWORD: ${WEBWORK_DB_PASSWORD} + # A timezone for the DB server can be set: + #TZ: zone/city + # where zone/city must be a valid setting. + # "/usr/bin/timedatectl list-timezones" on an Ubuntu system with + # that tool installed will find valid values. + # See: https://stackoverflow.com/questions/39172652/using-docker-compose-to-set-containers-timezones + app: + # Modifying the image name can be helpful to create a new image instead of + # overwriting the "standard" one during testing. image: webwork + + # Set a fixed container name, so it does not depend on the directory name + container_name: webwork2_app_1 + # Select the appropriate "build:" block: # For use/building when docker-compose.yml is in the webwork2 directory @@ -45,6 +85,9 @@ services: volumes: + # The Lite.pm patch for proper UTF-8 support + - "./docker-config/Lite.pm:/usr/share/perl5/XMLRPC/Lite.pm" + # ====================================================================== # If you are using locally modified webwork2 files, then @@ -141,12 +184,14 @@ services: WEBWORK_DB_HOST: db WEBWORK_DB_PORT: 3306 WEBWORK_DB_NAME: webwork + + # For DBD::mysql driver WEBWORK_DB_DSN: DBI:mysql:webwork:db:3306 - # We currently need to put the same data in the WEBWORK_DB_DSN line above - # as we cannot use the following form, as it would be done before the values - # needed are available. - # NO GOOD # WEBWORK_DB_DSN: DBI:mysql${WEBWORK_DB_NAME}:${WEBWORK_DB_HOST}:${WEBWORK_DB_PORT} + # For DBD::MariaDB driver + #WEBWORK_DB_DSN: DBI:MariaDB:database=webwork;host=db;port=3306 + # We currently need to put the same data in the WEBWORK_DB_DSN line above + # as we cannot set that using the other environment variables. # These are set in the .env file and import values from there WEBWORK_DB_PASSWORD: ${WEBWORK_DB_PASSWORD} @@ -196,4 +241,8 @@ services: volumes: oplVolume: + driver: local + name: webwork2_oplVolume mysql: + driver: local + name: webwork2_mysql diff --git a/docker-config/Lite.pm b/docker-config/Lite.pm new file mode 100644 index 0000000000..b18d723c2b --- /dev/null +++ b/docker-config/Lite.pm @@ -0,0 +1,451 @@ +# ====================================================================== +# +# Copyright (C) 2000-2001 Paul Kulchenko (paulclinger@yahoo.com) +# SOAP::Lite is free software; you can redistribute it +# and/or modify it under the same terms as Perl itself. +# +# $Id$ +# +# ====================================================================== + +package XMLRPC::Lite; + +use SOAP::Lite; +use strict; + +our $VERSION = 0.717; + +# ====================================================================== + +package XMLRPC::Constants; + +BEGIN { + no strict 'refs'; + for (qw( + FAULT_CLIENT FAULT_SERVER + HTTP_ON_SUCCESS_CODE HTTP_ON_FAULT_CODE + DO_NOT_USE_XML_PARSER DO_NOT_USE_CHARSET + DO_NOT_USE_LWP_LENGTH_HACK DO_NOT_CHECK_CONTENT_TYPE + )) { + *$_ = \${'SOAP::Constants::' . $_} + } + # XML-RPC spec requires content-type to be "text/xml" + $XMLRPC::Constants::DO_NOT_USE_CHARSET = 1; +} + +# ====================================================================== + +package XMLRPC::Data; + +@XMLRPC::Data::ISA = qw(SOAP::Data); + +# ====================================================================== + +package XMLRPC::Serializer; + +@XMLRPC::Serializer::ISA = qw(SOAP::Serializer); + +sub new { + my $class = shift; + + return $class if ref $class; + + return $class->SUPER::new( + typelookup => { + #base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'], + base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/ && !utf8::is_utf8($_[0])}, 'as_base64'], + int => [20, sub {$_[0] =~ /^[+-]?\d+$/}, 'as_int'], + double => [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_double'], + dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'], + string => [40, sub {1}, 'as_string'], + }, + attr => {}, + namespaces => {}, + @_, + ); +} + +sub envelope { + my $self = shift; + $self = $self->new() if not ref $self; # serves a method call if object + my $type = shift; + + my $body; + if ($type eq 'response') { + # shift off method name to make XMLRPT happy + my $method = shift + or die "Unspecified method for XMLRPC call\n"; + $body = XMLRPC::Data->name( methodResponse => \XMLRPC::Data->value( + XMLRPC::Data->type(params => [@_]) + ) + ); + } + elsif ($type eq 'method') { + # shift off method name to make XMLRPT happy + my $method = shift + or die "Unspecified method for XMLRPC call\n"; + $body = XMLRPC::Data->name( methodCall => \XMLRPC::Data->value( + XMLRPC::Data->type( + methodName => UNIVERSAL::isa($method => 'XMLRPC::Data') + ? $method->name + : $method + ), + XMLRPC::Data->type(params => [@_]) + )); + } + elsif ($type eq 'fault') { + $body = XMLRPC::Data->name(methodResponse => + \XMLRPC::Data->type(fault => {faultCode => $_[0], faultString => $_[1]}), + ); + } + else { + die "Wrong type of envelope ($type) for XMLRPC call\n"; + } + + # SOAP::Lite keeps track of objects for XML aliasing and multiref + # encoding. + # Set/reset seen() hashref before/after encode_object avoids a + # memory leak + $self->seen({}); # initialize multiref table + my $envelope = $self->xmlize($self->encode_object($body)); + $self->seen({}); # delete multi-ref table - avoids a memory hole... + return $envelope; +} + + +sub encode_object { + my $self = shift; + my @encoded = $self->SUPER::encode_object(@_); + + return $encoded[0]->[0] =~ /^(?:array|struct|i4|int|boolean|string|double|dateTime\.iso8601|base64)$/o + ? ['value', {}, [@encoded]] + : @encoded; +} + +sub encode_scalar { + my $self = shift; + return ['value', {}] unless defined $_[0]; + return $self->SUPER::encode_scalar(@_); +} + +sub encode_array { + my ($self, $array) = @_; + + return ['array', {}, [ + ['data', {}, [ map {$self->encode_object($_)} @{ $array } ] ] + ]]; +} + +sub encode_hash { + my ($self, $hash) = @_; + + return ['struct', {}, [ + map { + ['member', {}, [['name', {}, SOAP::Utils::encode_data($_)], $self->encode_object($hash->{$_})]] + } keys %{ $hash } + ]]; +} + +sub as_methodName { + my ($self, $value, $name, $type, $attr) = @_; + return [ 'methodName', $attr, $value ]; +} + +sub as_params { + my ($self, $params, $name, $type, $attr) = @_; + return ['params', $attr, [ + map { + ['param', {}, [ $self->encode_object($_) ] ] + } @$params + ]]; +} + +sub as_fault { + my ($self, $fault) = @_; + return ['fault', {}, [ $self->encode_object($fault) ] ]; +} + +sub BEGIN { + no strict 'refs'; + for my $type (qw(double i4 int)) { + my $method = 'as_' . $type; + *$method = sub { + my($self, $value) = @_; + return [ $type, {}, $value ]; + } + } +} + +sub as_base64 { + my ($self, $value) = @_; + require MIME::Base64; + return ['base64', {}, MIME::Base64::encode_base64($value,'')]; +} + +sub as_string { + my ($self, $value) = @_; + return ['string', {}, SOAP::Utils::encode_data($value)]; +} + +sub as_dateTime { + my ($self, $value) = @_; + return ['dateTime.iso8601', {}, $value]; +} + +sub as_boolean { + my ($self, $value) = @_; + return ['boolean', {}, $value ? 1 : 0]; +} + +sub typecast { + my ($self, $value, $name, $type, $attr) = @_; + + die "Wrong/unsupported datatype '$type' specified\n" if defined $type; + + $self->SUPER::typecast(@_); +} + +# ====================================================================== + +package XMLRPC::SOM; + +@XMLRPC::SOM::ISA = qw(SOAP::SOM); + +sub BEGIN { + no strict 'refs'; + my %path = ( + root => '/', + envelope => '/[1]', + method => '/methodCall/methodName', + fault => '/methodResponse/fault', + ); + + for my $method (keys %path) { + *$method = sub { + my $self = shift; + ref $self or return $path{$method}; + Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; + $self->valueof($path{$method}); + }; + } + + my %fault = ( + faultcode => 'faultCode', + faultstring => 'faultString', + ); + + for my $method (keys %fault) { + *$method = sub { + my $self = shift; + ref $self or Carp::croak "Method '$method' doesn't have shortcut"; + Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; + defined $self->fault ? $self->fault->{$fault{$method}} : undef; + }; + } + + my %results = ( + result => '/methodResponse/params/[1]', + paramsin => '/methodCall/params/param', + paramsall => '/methodResponse/params/param', + ); + + for my $method (keys %results) { + *$method = sub { + my $self = shift; + ref $self or return $results{$method}; + Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; + defined $self->fault() + ? undef + : $self->valueof($results{$method}); + }; + } +} + +# ====================================================================== + +package XMLRPC::Deserializer; + +@XMLRPC::Deserializer::ISA = qw(SOAP::Deserializer); + +BEGIN { + no strict 'refs'; + for my $method (qw(o_child o_qname o_chars)) { # import from SOAP::Utils + *$method = \&{'SOAP::Utils::'.$method}; + } +} + +sub deserialize { + # just deserialize with SOAP::Lite's deserializer, and re-bless as + # XMLRPC::SOM + bless shift->SUPER::deserialize(@_) => 'XMLRPC::SOM'; +} + +sub decode_value { + my $self = shift; + my $ref = shift; + my($name, $attrs, $children, $value) = @$ref; + + if ($name eq 'value') { + $children ? scalar(($self->decode_object($children->[0]))[1]) : $value; + } + elsif ($name eq 'array') { + return [map {scalar(($self->decode_object($_))[1])} @{o_child($children->[0]) || []}]; + } + elsif ($name eq 'struct') { + return { + map { + my %hash = map { o_qname($_) => $_ } @{o_child($_) || []}; + # v----- scalar is required here, because 5.005 evaluates 'undef' in list context as empty array + (o_chars($hash{name}) => scalar(($self->decode_object($hash{value}))[1])); + } @{$children || []}}; + } + elsif ($name eq 'base64') { + require MIME::Base64; + MIME::Base64::decode_base64($value); + } + elsif ($name =~ /^(?:int|i4|boolean|string|double|dateTime\.iso8601|methodName)$/) { + return $value; + } + elsif ($name =~ /^(?:params)$/) { + return [map {scalar(($self->decode_object($_))[1])} @{$children || []}]; + } + elsif ($name =~ /^(?:methodResponse|methodCall)$/) { + return +{map {$self->decode_object($_)} @{$children || []}}; + } + elsif ($name =~ /^(?:param|fault)$/) { + return scalar(($self->decode_object($children->[0]))[1]); + } + elsif ($name =~ /^(?:nil)$/) { + return undef; + } + else { + die "wrong element '$name'\n"; + } +} + +# ====================================================================== + +package XMLRPC::Server; + +@XMLRPC::Server::ISA = qw(SOAP::Server); + +sub initialize { + return ( + deserializer => XMLRPC::Deserializer->new, + serializer => XMLRPC::Serializer->new, + on_action => sub {}, + on_dispatch => sub { return map {s!\.!/!g; $_} shift->method =~ /^(?:(.*)\.)?(\w+)$/ }, + ); +} + +# ====================================================================== + +package XMLRPC::Server::Parameters; + +@XMLRPC::Server::Parameters::ISA = qw(SOAP::Server::Parameters); + +# ====================================================================== + +package XMLRPC; + +@XMLRPC::ISA = qw(SOAP); + +# ====================================================================== + +package XMLRPC::Lite; + +@XMLRPC::Lite::ISA = qw(SOAP::Lite); + +sub new { + my $class = shift; + + return $class if ref $class; + + return $class->SUPER::new( + serializer => XMLRPC::Serializer->new, + deserializer => XMLRPC::Deserializer->new, + on_action => sub {return}, + default_ns => 'http://unspecified/', + @_ + ); +} + +# ====================================================================== + +1; + +__END__ + +=head1 NAME + +XMLRPC::Lite - client and server implementation of XML-RPC protocol + +=head1 SYNOPSIS + +=over 4 + +=item Client + + use XMLRPC::Lite; + print XMLRPC::Lite + -> proxy('http://betty.userland.com/RPC2') + -> call('examples.getStateStruct', {state1 => 12, state2 => 28}) + -> result; + +=item CGI server + + use XMLRPC::Transport::HTTP; + + my $server = XMLRPC::Transport::HTTP::CGI + -> dispatch_to('methodName') + -> handle + ; + +=item Daemon server + + use XMLRPC::Transport::HTTP; + + my $daemon = XMLRPC::Transport::HTTP::Daemon + -> new (LocalPort => 80) + -> dispatch_to('methodName') + ; + print "Contact to XMLRPC server at ", $daemon->url, "\n"; + $daemon->handle; + +=back + +=head1 DESCRIPTION + +XMLRPC::Lite is a Perl modules which provides a simple nterface to the +XML-RPC protocol both on client and server side. Based on SOAP::Lite module, +it gives you access to all features and transports available in that module. + +See F for client examples and F for server +implementations. + +=head1 DEPENDENCIES + + SOAP::Lite + +=head1 SEE ALSO + + SOAP::Lite + +=head1 CREDITS + +The B standard is Copyright (c) 1998-2001, UserLand Software, Inc. +See for more information about the B +specification. + +=head1 COPYRIGHT + +Copyright (C) 2000-2001 Paul Kulchenko. All rights reserved. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=head1 AUTHOR + +Paul Kulchenko (paulclinger@yahoo.com) + +=cut diff --git a/docker-config/db/limits.conf b/docker-config/db/limits.conf new file mode 100644 index 0000000000..036d7d58be --- /dev/null +++ b/docker-config/db/limits.conf @@ -0,0 +1,58 @@ +# /etc/security/limits.conf +# +#Each line describes a limit for a user in the form: +# +# +# +#Where: +# can be: +# - a user name +# - a group name, with @group syntax +# - the wildcard *, for default entry +# - the wildcard %, can be also used with %group syntax, +# for maxlogin limit +# - NOTE: group and wildcard limits are not applied to root. +# To apply a limit to the root user, must be +# the literal username root. +# +# can have the two values: +# - "soft" for enforcing the soft limits +# - "hard" for enforcing hard limits +# +# can be one of the following: +# - core - limits the core file size (KB) +# - data - max data size (KB) +# - fsize - maximum filesize (KB) +# - memlock - max locked-in-memory address space (KB) +# - nofile - max number of open file descriptors +# - rss - max resident set size (KB) +# - stack - max stack size (KB) +# - cpu - max CPU time (MIN) +# - nproc - max number of processes +# - as - address space limit (KB) +# - maxlogins - max number of logins for this user +# - maxsyslogins - max number of logins on the system +# - priority - the priority to run user process with +# - locks - max number of file locks the user can hold +# - sigpending - max number of pending signals +# - msgqueue - max memory used by POSIX message queues (bytes) +# - nice - max nice priority allowed to raise to values: [-20, 19] +# - rtprio - max realtime priority +# - chroot - change root to directory (Debian-specific) +# +# +# + +#* soft core 0 +#root hard core 100000 +#* hard rss 10000 +#@student hard nproc 20 +#@faculty soft nproc 20 +#@faculty hard nproc 50 +#ftp hard nproc 0 +#ftp - chroot /ftp +#@student - maxlogins 4 + +mysql soft nofile 4096 +mysql hard nofile 4096 +# End of file diff --git a/docker-config/db/mariadb.cnf b/docker-config/db/mariadb.cnf index 88245b35b9..440d80a867 100644 --- a/docker-config/db/mariadb.cnf +++ b/docker-config/db/mariadb.cnf @@ -39,6 +39,9 @@ max_connections = 500 # wait_timeout (default usually 28800), # interactive_timeout (default usually 28800), # net_read_timeout (default usually 60) -wait_timeout = 28800 +wait_timeout = 86400 interactive_timeout = 28800 net_read_timeout = 3600 + +# WW add skip-name-resolve (saves time) +skip-name-resolve diff --git a/docker-config/db/my.cnf b/docker-config/db/my.cnf new file mode 100644 index 0000000000..287d7d2d71 --- /dev/null +++ b/docker-config/db/my.cnf @@ -0,0 +1,196 @@ +# MariaDB database server configuration file. +# +# You can copy this file to one of: +# - "/etc/mysql/my.cnf" to set global options, +# - "~/.my.cnf" to set user-specific options. +# +# One can use all long options that the program supports. +# Run program with --help to get a list of available options and with +# --print-defaults to see which it would actually understand and use. +# +# For explanations see +# http://dev.mysql.com/doc/mysql/en/server-system-variables.html + +# This will be passed to all mysql clients +# It has been reported that passwords should be enclosed with ticks/quotes +# escpecially if they contain "#" chars... +# Remember to edit /etc/mysql/debian.cnf when changing the socket location. +[client] +port = 3306 +socket = /var/run/mysqld/mysqld.sock + +# Here is entries for some specific programs +# The following values assume you have at least 32M ram + +# This was formally known as [safe_mysqld]. Both versions are currently parsed. +[mysqld_safe] +socket = /var/run/mysqld/mysqld.sock +nice = 0 + +[mysqld] +# +# * Basic Settings +# +#user = mysql +pid-file = /var/run/mysqld/mysqld.pid +socket = /var/run/mysqld/mysqld.sock +port = 3306 +basedir = /usr +datadir = /var/lib/mysql +tmpdir = /tmp +lc_messages_dir = /usr/share/mysql +lc_messages = en_US +skip-external-locking +# +# Instead of skip-networking the default is now to listen only on +# localhost which is more compatible and is not less secure. +#bind-address = 127.0.0.1 +# +# * Fine Tuning +# +max_connections = 100 +connect_timeout = 5 +# +# WW: wait_timeout 86400 instead of 600, decreases frequency of connection timeouts +wait_timeout = 86400 +# +max_allowed_packet = 16M +thread_cache_size = 128 +sort_buffer_size = 4M +bulk_insert_buffer_size = 16M +tmp_table_size = 32M +max_heap_table_size = 32M +# +# WW add skip-name-resolve (saves time) +skip-name-resolve +# +# * MyISAM +# +# This replaces the startup script and checks MyISAM tables if needed +# the first time they are touched. On error, make copy and try a repair. +myisam_recover_options = BACKUP +key_buffer_size = 128M +#open-files-limit = 2000 +table_open_cache = 400 +myisam_sort_buffer_size = 512M +concurrent_insert = 2 +read_buffer_size = 2M +read_rnd_buffer_size = 1M +# +# * Query Cache Configuration +# +# Cache only tiny result sets, so we can fit more in the query cache. +query_cache_limit = 128K +query_cache_size = 64M +# for more write intensive setups, set to DEMAND or OFF +#query_cache_type = DEMAND +# +# * Logging and Replication +# +# Both location gets rotated by the cronjob. +# Be aware that this log type is a performance killer. +# As of 5.1 you can enable the log at runtime! +#general_log_file = /var/log/mysql/mysql.log +#general_log = 1 +# +# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf. +# +# we do want to know about network errors and such +#log_warnings = 2 +# +# Enable the slow query log to see queries with especially long duration +#slow_query_log[={0|1}] +# WW DEVELOP - may want to enable next line +#slow-query-log = 1 +slow_query_log_file = /var/log/mysql/mariadb-slow.log +long_query_time = 10 +#log_slow_rate_limit = 1000 +#log_slow_verbosity = query_plan + +#log-queries-not-using-indexes +#log_slow_admin_statements +# +# The following can be used as easy to replay backup logs or for replication. +# note: if you are setting up a replication slave, see README.Debian about +# other settings you may need to change. +#server-id = 1 +#report_host = master1 +#auto_increment_increment = 2 +#auto_increment_offset = 1 +#log_bin = /var/log/mysql/mariadb-bin +#log_bin_index = /var/log/mysql/mariadb-bin.index +# not fab for performance, but safer +#sync_binlog = 1 +expire_logs_days = 10 +max_binlog_size = 100M +# slaves +#relay_log = /var/log/mysql/relay-bin +#relay_log_index = /var/log/mysql/relay-bin.index +#relay_log_info_file = /var/log/mysql/relay-bin.info +#log_slave_updates +#read_only +# +# If applications support it, this stricter sql_mode prevents some +# mistakes like inserting invalid dates etc. +#sql_mode = NO_ENGINE_SUBSTITUTION,TRADITIONAL +# +# * InnoDB +# +# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/. +# Read the manual for more InnoDB related options. There are many! +default_storage_engine = InnoDB +innodb_buffer_pool_size = 256M +innodb_log_buffer_size = 8M +innodb_file_per_table = 1 +innodb_open_files = 400 +innodb_io_capacity = 400 +innodb_flush_method = O_DIRECT +# +# * Security Features +# +# Read the manual, too, if you want chroot! +# chroot = /var/lib/mysql/ +# +# For generating SSL certificates I recommend the OpenSSL GUI "tinyca". +# +# ssl-ca=/etc/mysql/cacert.pem +# ssl-cert=/etc/mysql/server-cert.pem +# ssl-key=/etc/mysql/server-key.pem + +# +# * Galera-related settings +# +[galera] +# Mandatory settings +#wsrep_on=ON +#wsrep_provider= +#wsrep_cluster_address= +#binlog_format=row +#default_storage_engine=InnoDB +#innodb_autoinc_lock_mode=2 +# +# Allow server to accept connections on all interfaces. +# +#bind-address=0.0.0.0 +# +# Optional setting +#wsrep_slave_threads=1 +#innodb_flush_log_at_trx_commit=0 + +[mysqldump] +quick +quote-names +max_allowed_packet = 16M + +[mysql] +#no-auto-rehash # faster start of mysql but no tab completion + +[isamchk] +key_buffer = 16M + +# +# * IMPORTANT: Additional settings that can override those from this file! +# The files must end with '.cnf', otherwise they'll be ignored. +# +!include /etc/mysql/mariadb.cnf +!includedir /etc/mysql/conf.d/ From c32dcc51226f262753aa02c0031d270716630d0c Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Wed, 10 Mar 2021 17:23:34 +0200 Subject: [PATCH 2/7] Provide a sample /etc/ImageMagick-6/policy.xml file to enable PR 541 code to work, and a comment explaining how to enable it via the prepared sample line in docker-compose.yml. --- docker-compose.yml | 5 ++ docker-config/ImageMagick-6-policy.xml | 96 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 docker-config/ImageMagick-6-policy.xml diff --git a/docker-compose.yml b/docker-compose.yml index b5f20820d8..0340c3ed18 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,6 +85,11 @@ services: volumes: + # You must enable this (with the potential attendant security risks) for + # the current TikZ support from https://github.com/openwebwork/pg/pull/541 + # to work. The file gives "coder" "read" rights to PDF files. + #- "./docker-config/ImageMagick-6-policy.xml:/etc/ImageMagick-6/policy.xml" + # The Lite.pm patch for proper UTF-8 support - "./docker-config/Lite.pm:/usr/share/perl5/XMLRPC/Lite.pm" diff --git a/docker-config/ImageMagick-6-policy.xml b/docker-config/ImageMagick-6-policy.xml new file mode 100644 index 0000000000..ed78a2aa0c --- /dev/null +++ b/docker-config/ImageMagick-6-policy.xml @@ -0,0 +1,96 @@ + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9a84af56a558e19c3910f1390e92a8eef91e0d90 Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Fri, 12 Mar 2021 00:48:52 +0200 Subject: [PATCH 3/7] Changes to DB environment variables related to changes made in https://github.com/openwebwork/webwork2/pull/1160 + fix a typo --- Dockerfile | 5 ++++- docker-compose.yml | 15 ++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3edb14de8d..a06ca0f4e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -314,7 +314,10 @@ RUN cd $APP_ROOT/webwork2/conf \ && sed -i -e 's/^$/\ PerlPassEnv WEBWORK_URL\n\ PerlPassEnv WEBWORK_ROOT_URL\n\ - PerlPassEnv WEBWORK_DB_DSN\n\ + PerlPassEnv WEBWORK_DB_DRIVER\n\ + PerlPassEnv WEBWORK_DB_NAME\n\ + PerlPassEnv WEBWORK_DB_HOST\n\ + PerlPassEnv WEBWORK_DB_PORT\n\ PerlPassEnv WEBWORK_DB_USER\n\ PerlPassEnv WEBWORK_DB_PASSWORD\n\ PerlPassEnv WEBWORK_SMTP_SERVER\n\ diff --git a/docker-compose.yml b/docker-compose.yml index 0340c3ed18..5f93f7bb4d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: # Note: It seems different versions of the MariaDB container need # this file in different locations. Put in in both places. - "./docker-config/db/mariadb.cnf:/etc/mysql/conf.d/mariadb.cnf" - - "./docker-config/db/mariadb.cnf::/etc/mysql/mariadb.cnf" + - "./docker-config/db/mariadb.cnf:/etc/mysql/mariadb.cnf" # Tuning MariaDB config - sample file # The sample file modifies wait_timeout, and adds skip-name-resolve @@ -186,18 +186,15 @@ services: APACHE_RUN_GROUP: www-data # Standard database environment variables needed by WeBWorK: + + # Select which DBD driver to use + WEBWORK_DB_DRIVER: MariaDB + # WEBWORK_DB_DRIVER: mysql + WEBWORK_DB_HOST: db WEBWORK_DB_PORT: 3306 WEBWORK_DB_NAME: webwork - # For DBD::mysql driver - WEBWORK_DB_DSN: DBI:mysql:webwork:db:3306 - # For DBD::MariaDB driver - #WEBWORK_DB_DSN: DBI:MariaDB:database=webwork;host=db;port=3306 - - # We currently need to put the same data in the WEBWORK_DB_DSN line above - # as we cannot set that using the other environment variables. - # These are set in the .env file and import values from there WEBWORK_DB_PASSWORD: ${WEBWORK_DB_PASSWORD} WEBWORK_DB_USER: ${WEBWORK_DB_USER} From 807c8d541cac4f26982e17a9afa8673e0fd828eb Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Mon, 22 Mar 2021 11:43:45 -0500 Subject: [PATCH 4/7] Remove Lite.pm and ImageMagick-6-policy.xml and apply patches in the Dockerfile instead. Also a bit of tweaking of the Dockerfile. Set the args `WEBWORK2_GIT_URL`, `WEBWORK2_BRANCH`, `PG_GIT_URL`, and `PG_BRANCH` in docker-compose.yml instead of in the Dockerfile. Also, don't convert those to environment variables. That isn't needed. This requires that `docker-compose build` is used rather than `docker build .`. --- Dockerfile | 42 +- docker-compose.yml | 45 +- docker-config/ImageMagick-6-policy.xml | 96 ---- docker-config/Lite.pm | 451 ------------------ .../imagemagick-allow-pdf-read.patch | 10 + docker-config/xmlrpc-lite-utf8-fix.patch | 11 + 6 files changed, 63 insertions(+), 592 deletions(-) delete mode 100644 docker-config/ImageMagick-6-policy.xml delete mode 100644 docker-config/Lite.pm create mode 100644 docker-config/imagemagick-allow-pdf-read.patch create mode 100644 docker-config/xmlrpc-lite-utf8-fix.patch diff --git a/Dockerfile b/Dockerfile index 309ebcb9ec..f91d3c6527 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,32 +40,21 @@ FROM alpine/git AS base # build args specifying the branches for webwork2 and pg used to build the image - -# To use the master branches of webwork2 and pg -#ARG WEBWORK2_BRANCH=master -#ARG PG_BRANCH=master -# To use the 2.15 branches of webwork2 and pg from the "official" GitHub repositories -ARG WEBWORK2_GIT_URL=https://github.com/openwebwork/webwork2.git -ARG WEBWORK2_BRANCH=develop -ARG PG_GIT_URL=https://github.com/openwebwork/pg.git -ARG PG_BRANCH=develop - -# assign the build args to the ENV variables -ENV WEBWORK2_GIT_URL_ENV ${WEBWORK2_GIT_URL} -ENV WEBWORK2_BRANCH_ENV ${WEBWORK2_BRANCH} -ENV PG_GIT_URL_ENV ${PG_GIT_URL} -ENV PG_BRANCH_ENV ${PG_BRANCH} +ARG WEBWORK2_GIT_URL +ARG WEBWORK2_BRANCH +ARG PG_GIT_URL +ARG PG_BRANCH WORKDIR /opt/base -RUN echo Cloning branch $WEBWORK2_BRANCH_ENV from $WEBWORK2_GIT_URL_ENV \ - && echo git clone --single-branch --branch ${WEBWORK2_BRANCH_ENV} --depth 1 $WEBWORK2_GIT_URL_ENV \ - && git clone --single-branch --branch ${WEBWORK2_BRANCH_ENV} --depth 1 $WEBWORK2_GIT_URL_ENV \ +RUN echo Cloning branch $WEBWORK2_BRANCH from $WEBWORK2_GIT_URL \ + && echo git clone --single-branch --branch ${WEBWORK2_BRANCH} --depth 1 $WEBWORK2_GIT_URL \ + && git clone --single-branch --branch ${WEBWORK2_BRANCH} --depth 1 $WEBWORK2_GIT_URL \ && rm -rf webwork2/.git webwork2/{*ignore,Dockerfile,docker-compose.yml,docker-config} -RUN echo Cloning branch $PG_BRANCH_ENV branch from $PG_GIT_URL_ENV \ - && echo git clone --single-branch --branch ${PG_BRANCH_ENV} --depth 1 $PG_GIT_URL_ENV \ - && git clone --single-branch --branch ${PG_BRANCH_ENV} --depth 1 $PG_GIT_URL_ENV \ +RUN echo Cloning branch $PG_BRANCH branch from $PG_GIT_URL \ + && echo git clone --single-branch --branch ${PG_BRANCH} --depth 1 $PG_GIT_URL \ + && git clone --single-branch --branch ${PG_BRANCH} --depth 1 $PG_GIT_URL \ && rm -rf pg/.git RUN git clone --single-branch --branch master --depth 1 https://github.com/mathjax/MathJax \ @@ -198,6 +187,7 @@ RUN apt-get update \ libcpanel-json-xs-perl \ make \ netpbm \ + patch \ preview-latex-style \ texlive \ texlive-latex-extra \ @@ -296,6 +286,10 @@ RUN cpanm install Statistics::R::IO \ # Always provide the dummy default-ssl.conf file: COPY docker-config/ssl/default-ssl.conf /etc/apache2/sites-available/default-ssl.conf +# Patch files that are applied below +COPY docker-config/xmlrpc-lite-utf8-fix.patch /tmp +COPY docker-config/imagemagick-allow-pdf-read.patch /tmp + # However SSL will only be enabled at container startup via docker-entrypoint.sh. RUN cd $APP_ROOT/webwork2/conf \ @@ -323,7 +317,11 @@ RUN cd $APP_ROOT/webwork2/conf \ PerlPassEnv WEBWORK_SMTP_SERVER\n\ PerlPassEnv WEBWORK_SMTP_SENDER\n\ PerlPassEnv WEBWORK_TIMEZONE\n\ - \n/' /etc/apache2/conf-enabled/webwork.conf + \n/' /etc/apache2/conf-enabled/webwork.conf \ + && patch -p1 -d / < /tmp/xmlrpc-lite-utf8-fix.patch \ + && rm /tmp/xmlrpc-lite-utf8-fix.patch \ + && patch -p1 -d / < /tmp/imagemagick-allow-pdf-read.patch \ + && rm /tmp/imagemagick-allow-pdf-read.patch EXPOSE 80 WORKDIR $APP_ROOT diff --git a/docker-compose.yml b/docker-compose.yml index 5f93f7bb4d..66dabd5620 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -64,35 +64,34 @@ services: # Set a fixed container name, so it does not depend on the directory name container_name: webwork2_app_1 - # Select the appropriate "build:" block: - - # For use/building when docker-compose.yml is in the webwork2 directory - build: . - - # For use/building when docker-compose.yml is OUTSIDE the webwork2 directory. - # For example, if multiple hosts use a NFS shared webwork2/ directory, and - # each one needs customized values in docker-compose.yml. - # Under typical use, the Dockerfile should not need to be customized per host, - # but may contain some changes/additions relative to the standard webwork image. - # - #build: - # context: /Path_To/webwork2/ - # dockerfile: /Path_To/Dockerfile + # Set up the "build:" configuration: + build: + # For use/building when docker-compose.yml is in the webwork2 directory + context: . + # For use/building when docker-compose.yml is OUTSIDE the webwork2 directory. + # For example, if multiple hosts use a NFS shared webwork2/ directory, and + # each one needs customized values in docker-compose.yml. + # Under typical use, the Dockerfile should not need to be customized per host, + # but may contain some changes/additions relative to the standard webwork image. + # + # context: /Path_To/webwork2/ + # dockerfile: /Path_To/Dockerfile + args: + # build args specifying the branches for webwork2 and pg used to build the image + # To use the master branches of webwork2 and pg + #- ARG WEBWORK2_BRANCH=master + #- ARG PG_BRANCH=master + # To use the 2.15 branches of webwork2 and pg from the "official" GitHub repositories + - WEBWORK2_GIT_URL=https://github.com/openwebwork/webwork2.git + - WEBWORK2_BRANCH=develop + - PG_GIT_URL=https://github.com/openwebwork/pg.git + - PG_BRANCH=develop depends_on: - db - r volumes: - - # You must enable this (with the potential attendant security risks) for - # the current TikZ support from https://github.com/openwebwork/pg/pull/541 - # to work. The file gives "coder" "read" rights to PDF files. - #- "./docker-config/ImageMagick-6-policy.xml:/etc/ImageMagick-6/policy.xml" - - # The Lite.pm patch for proper UTF-8 support - - "./docker-config/Lite.pm:/usr/share/perl5/XMLRPC/Lite.pm" - # ====================================================================== # If you are using locally modified webwork2 files, then diff --git a/docker-config/ImageMagick-6-policy.xml b/docker-config/ImageMagick-6-policy.xml deleted file mode 100644 index ed78a2aa0c..0000000000 --- a/docker-config/ImageMagick-6-policy.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docker-config/Lite.pm b/docker-config/Lite.pm deleted file mode 100644 index b18d723c2b..0000000000 --- a/docker-config/Lite.pm +++ /dev/null @@ -1,451 +0,0 @@ -# ====================================================================== -# -# Copyright (C) 2000-2001 Paul Kulchenko (paulclinger@yahoo.com) -# SOAP::Lite is free software; you can redistribute it -# and/or modify it under the same terms as Perl itself. -# -# $Id$ -# -# ====================================================================== - -package XMLRPC::Lite; - -use SOAP::Lite; -use strict; - -our $VERSION = 0.717; - -# ====================================================================== - -package XMLRPC::Constants; - -BEGIN { - no strict 'refs'; - for (qw( - FAULT_CLIENT FAULT_SERVER - HTTP_ON_SUCCESS_CODE HTTP_ON_FAULT_CODE - DO_NOT_USE_XML_PARSER DO_NOT_USE_CHARSET - DO_NOT_USE_LWP_LENGTH_HACK DO_NOT_CHECK_CONTENT_TYPE - )) { - *$_ = \${'SOAP::Constants::' . $_} - } - # XML-RPC spec requires content-type to be "text/xml" - $XMLRPC::Constants::DO_NOT_USE_CHARSET = 1; -} - -# ====================================================================== - -package XMLRPC::Data; - -@XMLRPC::Data::ISA = qw(SOAP::Data); - -# ====================================================================== - -package XMLRPC::Serializer; - -@XMLRPC::Serializer::ISA = qw(SOAP::Serializer); - -sub new { - my $class = shift; - - return $class if ref $class; - - return $class->SUPER::new( - typelookup => { - #base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'], - base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/ && !utf8::is_utf8($_[0])}, 'as_base64'], - int => [20, sub {$_[0] =~ /^[+-]?\d+$/}, 'as_int'], - double => [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_double'], - dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'], - string => [40, sub {1}, 'as_string'], - }, - attr => {}, - namespaces => {}, - @_, - ); -} - -sub envelope { - my $self = shift; - $self = $self->new() if not ref $self; # serves a method call if object - my $type = shift; - - my $body; - if ($type eq 'response') { - # shift off method name to make XMLRPT happy - my $method = shift - or die "Unspecified method for XMLRPC call\n"; - $body = XMLRPC::Data->name( methodResponse => \XMLRPC::Data->value( - XMLRPC::Data->type(params => [@_]) - ) - ); - } - elsif ($type eq 'method') { - # shift off method name to make XMLRPT happy - my $method = shift - or die "Unspecified method for XMLRPC call\n"; - $body = XMLRPC::Data->name( methodCall => \XMLRPC::Data->value( - XMLRPC::Data->type( - methodName => UNIVERSAL::isa($method => 'XMLRPC::Data') - ? $method->name - : $method - ), - XMLRPC::Data->type(params => [@_]) - )); - } - elsif ($type eq 'fault') { - $body = XMLRPC::Data->name(methodResponse => - \XMLRPC::Data->type(fault => {faultCode => $_[0], faultString => $_[1]}), - ); - } - else { - die "Wrong type of envelope ($type) for XMLRPC call\n"; - } - - # SOAP::Lite keeps track of objects for XML aliasing and multiref - # encoding. - # Set/reset seen() hashref before/after encode_object avoids a - # memory leak - $self->seen({}); # initialize multiref table - my $envelope = $self->xmlize($self->encode_object($body)); - $self->seen({}); # delete multi-ref table - avoids a memory hole... - return $envelope; -} - - -sub encode_object { - my $self = shift; - my @encoded = $self->SUPER::encode_object(@_); - - return $encoded[0]->[0] =~ /^(?:array|struct|i4|int|boolean|string|double|dateTime\.iso8601|base64)$/o - ? ['value', {}, [@encoded]] - : @encoded; -} - -sub encode_scalar { - my $self = shift; - return ['value', {}] unless defined $_[0]; - return $self->SUPER::encode_scalar(@_); -} - -sub encode_array { - my ($self, $array) = @_; - - return ['array', {}, [ - ['data', {}, [ map {$self->encode_object($_)} @{ $array } ] ] - ]]; -} - -sub encode_hash { - my ($self, $hash) = @_; - - return ['struct', {}, [ - map { - ['member', {}, [['name', {}, SOAP::Utils::encode_data($_)], $self->encode_object($hash->{$_})]] - } keys %{ $hash } - ]]; -} - -sub as_methodName { - my ($self, $value, $name, $type, $attr) = @_; - return [ 'methodName', $attr, $value ]; -} - -sub as_params { - my ($self, $params, $name, $type, $attr) = @_; - return ['params', $attr, [ - map { - ['param', {}, [ $self->encode_object($_) ] ] - } @$params - ]]; -} - -sub as_fault { - my ($self, $fault) = @_; - return ['fault', {}, [ $self->encode_object($fault) ] ]; -} - -sub BEGIN { - no strict 'refs'; - for my $type (qw(double i4 int)) { - my $method = 'as_' . $type; - *$method = sub { - my($self, $value) = @_; - return [ $type, {}, $value ]; - } - } -} - -sub as_base64 { - my ($self, $value) = @_; - require MIME::Base64; - return ['base64', {}, MIME::Base64::encode_base64($value,'')]; -} - -sub as_string { - my ($self, $value) = @_; - return ['string', {}, SOAP::Utils::encode_data($value)]; -} - -sub as_dateTime { - my ($self, $value) = @_; - return ['dateTime.iso8601', {}, $value]; -} - -sub as_boolean { - my ($self, $value) = @_; - return ['boolean', {}, $value ? 1 : 0]; -} - -sub typecast { - my ($self, $value, $name, $type, $attr) = @_; - - die "Wrong/unsupported datatype '$type' specified\n" if defined $type; - - $self->SUPER::typecast(@_); -} - -# ====================================================================== - -package XMLRPC::SOM; - -@XMLRPC::SOM::ISA = qw(SOAP::SOM); - -sub BEGIN { - no strict 'refs'; - my %path = ( - root => '/', - envelope => '/[1]', - method => '/methodCall/methodName', - fault => '/methodResponse/fault', - ); - - for my $method (keys %path) { - *$method = sub { - my $self = shift; - ref $self or return $path{$method}; - Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; - $self->valueof($path{$method}); - }; - } - - my %fault = ( - faultcode => 'faultCode', - faultstring => 'faultString', - ); - - for my $method (keys %fault) { - *$method = sub { - my $self = shift; - ref $self or Carp::croak "Method '$method' doesn't have shortcut"; - Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; - defined $self->fault ? $self->fault->{$fault{$method}} : undef; - }; - } - - my %results = ( - result => '/methodResponse/params/[1]', - paramsin => '/methodCall/params/param', - paramsall => '/methodResponse/params/param', - ); - - for my $method (keys %results) { - *$method = sub { - my $self = shift; - ref $self or return $results{$method}; - Carp::croak "Method '$method' is readonly and doesn't accept any parameters" if @_; - defined $self->fault() - ? undef - : $self->valueof($results{$method}); - }; - } -} - -# ====================================================================== - -package XMLRPC::Deserializer; - -@XMLRPC::Deserializer::ISA = qw(SOAP::Deserializer); - -BEGIN { - no strict 'refs'; - for my $method (qw(o_child o_qname o_chars)) { # import from SOAP::Utils - *$method = \&{'SOAP::Utils::'.$method}; - } -} - -sub deserialize { - # just deserialize with SOAP::Lite's deserializer, and re-bless as - # XMLRPC::SOM - bless shift->SUPER::deserialize(@_) => 'XMLRPC::SOM'; -} - -sub decode_value { - my $self = shift; - my $ref = shift; - my($name, $attrs, $children, $value) = @$ref; - - if ($name eq 'value') { - $children ? scalar(($self->decode_object($children->[0]))[1]) : $value; - } - elsif ($name eq 'array') { - return [map {scalar(($self->decode_object($_))[1])} @{o_child($children->[0]) || []}]; - } - elsif ($name eq 'struct') { - return { - map { - my %hash = map { o_qname($_) => $_ } @{o_child($_) || []}; - # v----- scalar is required here, because 5.005 evaluates 'undef' in list context as empty array - (o_chars($hash{name}) => scalar(($self->decode_object($hash{value}))[1])); - } @{$children || []}}; - } - elsif ($name eq 'base64') { - require MIME::Base64; - MIME::Base64::decode_base64($value); - } - elsif ($name =~ /^(?:int|i4|boolean|string|double|dateTime\.iso8601|methodName)$/) { - return $value; - } - elsif ($name =~ /^(?:params)$/) { - return [map {scalar(($self->decode_object($_))[1])} @{$children || []}]; - } - elsif ($name =~ /^(?:methodResponse|methodCall)$/) { - return +{map {$self->decode_object($_)} @{$children || []}}; - } - elsif ($name =~ /^(?:param|fault)$/) { - return scalar(($self->decode_object($children->[0]))[1]); - } - elsif ($name =~ /^(?:nil)$/) { - return undef; - } - else { - die "wrong element '$name'\n"; - } -} - -# ====================================================================== - -package XMLRPC::Server; - -@XMLRPC::Server::ISA = qw(SOAP::Server); - -sub initialize { - return ( - deserializer => XMLRPC::Deserializer->new, - serializer => XMLRPC::Serializer->new, - on_action => sub {}, - on_dispatch => sub { return map {s!\.!/!g; $_} shift->method =~ /^(?:(.*)\.)?(\w+)$/ }, - ); -} - -# ====================================================================== - -package XMLRPC::Server::Parameters; - -@XMLRPC::Server::Parameters::ISA = qw(SOAP::Server::Parameters); - -# ====================================================================== - -package XMLRPC; - -@XMLRPC::ISA = qw(SOAP); - -# ====================================================================== - -package XMLRPC::Lite; - -@XMLRPC::Lite::ISA = qw(SOAP::Lite); - -sub new { - my $class = shift; - - return $class if ref $class; - - return $class->SUPER::new( - serializer => XMLRPC::Serializer->new, - deserializer => XMLRPC::Deserializer->new, - on_action => sub {return}, - default_ns => 'http://unspecified/', - @_ - ); -} - -# ====================================================================== - -1; - -__END__ - -=head1 NAME - -XMLRPC::Lite - client and server implementation of XML-RPC protocol - -=head1 SYNOPSIS - -=over 4 - -=item Client - - use XMLRPC::Lite; - print XMLRPC::Lite - -> proxy('http://betty.userland.com/RPC2') - -> call('examples.getStateStruct', {state1 => 12, state2 => 28}) - -> result; - -=item CGI server - - use XMLRPC::Transport::HTTP; - - my $server = XMLRPC::Transport::HTTP::CGI - -> dispatch_to('methodName') - -> handle - ; - -=item Daemon server - - use XMLRPC::Transport::HTTP; - - my $daemon = XMLRPC::Transport::HTTP::Daemon - -> new (LocalPort => 80) - -> dispatch_to('methodName') - ; - print "Contact to XMLRPC server at ", $daemon->url, "\n"; - $daemon->handle; - -=back - -=head1 DESCRIPTION - -XMLRPC::Lite is a Perl modules which provides a simple nterface to the -XML-RPC protocol both on client and server side. Based on SOAP::Lite module, -it gives you access to all features and transports available in that module. - -See F for client examples and F for server -implementations. - -=head1 DEPENDENCIES - - SOAP::Lite - -=head1 SEE ALSO - - SOAP::Lite - -=head1 CREDITS - -The B standard is Copyright (c) 1998-2001, UserLand Software, Inc. -See for more information about the B -specification. - -=head1 COPYRIGHT - -Copyright (C) 2000-2001 Paul Kulchenko. All rights reserved. - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. - -=head1 AUTHOR - -Paul Kulchenko (paulclinger@yahoo.com) - -=cut diff --git a/docker-config/imagemagick-allow-pdf-read.patch b/docker-config/imagemagick-allow-pdf-read.patch new file mode 100644 index 0000000000..b9857d1da6 --- /dev/null +++ b/docker-config/imagemagick-allow-pdf-read.patch @@ -0,0 +1,10 @@ +--- a/etc/ImageMagick-6/policy.xml 2020-11-22 14:46:53.591198189 -0600 ++++ b/etc/ImageMagick-6/policy.xml 2021-03-22 10:35:12.821990590 -0500 +@@ -91,6 +91,6 @@ + + + +- ++ + + diff --git a/docker-config/xmlrpc-lite-utf8-fix.patch b/docker-config/xmlrpc-lite-utf8-fix.patch new file mode 100644 index 0000000000..0a6d1571a7 --- /dev/null +++ b/docker-config/xmlrpc-lite-utf8-fix.patch @@ -0,0 +1,11 @@ +--- a/usr/share/perl5/XMLRPC/Lite.pm 2013-05-16 21:02:00.000000000 -0500 ++++ b/usr/share/perl5/XMLRPC/Lite.pm 2021-03-22 10:30:40.498229670 -0500 +@@ -52,7 +52,7 @@ + + return $class->SUPER::new( + typelookup => { +- base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/}, 'as_base64'], ++ base64 => [10, sub {$_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/ && !utf8::is_utf8($_[0])}, 'as_base64'], + int => [20, sub {$_[0] =~ /^[+-]?\d+$/}, 'as_int'], + double => [30, sub {$_[0] =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/}, 'as_double'], + dateTime => [35, sub {$_[0] =~ /^\d{8}T\d\d:\d\d:\d\d$/}, 'as_dateTime'], From d114055f8a1785824bbce657e0207b7ea57ba7ce Mon Sep 17 00:00:00 2001 From: Nathan Wallach Date: Tue, 23 Mar 2021 17:07:25 +0200 Subject: [PATCH 5/7] On ubuntu 20.04 libcgi-pm-perl is installed and provided version 4.46 so there is no need to install via CPAN. Also drop an old comment. --- Dockerfile | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index ef54971127..f747d786f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -266,16 +266,9 @@ RUN echo "PATH=$PATH:$APP_ROOT/webwork2/bin" >> /root/.bashrc \ # Phase 6 - install additional Perl modules from CPAN (not packaged for Ubuntu or outdated in Ubuntu) -# Ubuntu 18.04 has CGI.pm 4.38-1 which is too old to support the cookie samesite attribute added in CGI.pm 4.45 - so install CGI::Cookie here to get an upgraded version. - -RUN cpanm install Statistics::R::IO CGI::Cookie \ +RUN cpanm install Statistics::R::IO \ && rm -fr ./cpanm /root/.cpanm /tmp/* -# Now installed from Ubuntu packages: -# XML::Parser::EasyTree Iterator Iterator::Util Pod::WSDL Array::Utils HTML::Template Mail::Sender Email::Sender::Simple Data::Dump -# For Ubuntu 16.04 would also need: -# Email::Address::XS - # ================================================================== # Phase 7 - setup apache From 046c00a150e683bc2289339f979aba4164c9f9f3 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Thu, 25 Mar 2021 17:19:18 -0500 Subject: [PATCH 6/7] Docker doesn't need to clone MathJax anymore. --- Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f747d786f0..a2c7a7da78 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,9 +57,6 @@ RUN echo Cloning branch $PG_BRANCH branch from $PG_GIT_URL \ && git clone --single-branch --branch ${PG_BRANCH} --depth 1 $PG_GIT_URL \ && rm -rf pg/.git -RUN git clone --single-branch --branch master --depth 1 https://github.com/mathjax/MathJax \ - && rm -rf MathJax/.git - # Optional - include OPL (also need to uncomment further below when an included OPL is desired): #RUN git clone --single-branch --branch master --depth 1 https://github.com/openwebwork/webwork-open-problem-library.git \ # && rm -rf webwork-open-problem-library/.git @@ -219,14 +216,13 @@ RUN apt-get update \ # ================================================================== -# Phase 4 - Install webwork2, pg, MathJaX which were downloaded to /opt/base/ in phase 1 +# Phase 4 - Install webwork2 and pg which were downloaded to /opt/base/ in phase 1 # Option: Install the OPL in the image also (about 850 MB) RUN mkdir -p $APP_ROOT/courses $APP_ROOT/libraries $APP_ROOT/libraries/webwork-open-problem-library $APP_ROOT/webwork2 /www/www/html COPY --from=base /opt/base/webwork2 $APP_ROOT/webwork2 COPY --from=base /opt/base/pg $APP_ROOT/pg -COPY --from=base /opt/base/MathJax $APP_ROOT/MathJax # Optional - include OPL (also need to uncomment above to clone from GitHub when needed): # ??? could/should this include the main OPL = /opt/base/webwork-open-problem-library/OpenProblemLibrary and not Contrib and Pending ??? From e71498c31afead0696bea86cbee780a9f5ca36a6 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Thu, 25 Mar 2021 20:10:46 -0500 Subject: [PATCH 7/7] Update docker-entrypoint.sh for the changes in the MariaDB update pull request. --- docker-config/docker-entrypoint.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker-config/docker-entrypoint.sh b/docker-config/docker-entrypoint.sh index ac70883357..0556140552 100755 --- a/docker-config/docker-entrypoint.sh +++ b/docker-config/docker-entrypoint.sh @@ -72,7 +72,10 @@ if [ "$1" = 'apache2' ]; then if [ $i == 'site.conf' ]; then sed -i -e 's/webwork_url = '\''\/webwork2'\''/webwork_url = $ENV{"WEBWORK_URL"}/' \ -e 's/server_root_url = '\'''\''/server_root_url = $ENV{"WEBWORK_ROOT_URL"}/' \ - -e 's/database_dsn ="dbi:mysql:webwork"/database_dsn =$ENV{"WEBWORK_DB_DSN"}/' \ + -e 's/^\$database_driver="MariaDB"/$database_driver = $ENV{"WEBWORK_DB_DRIVER"}/' \ + -e 's/^\$database_host="localhost"/$database_host = $ENV{"WEBWORK_DB_HOST"}/' \ + -e 's/^\$database_port="3306"/$database_port = $ENV{"WEBWORK_DB_PORT"}/' \ + -e 's/^\$database_name="webwork"/$database_name = $ENV{"WEBWORK_DB_NAME"}/' \ -e 's/database_username ="webworkWrite"/database_username =$ENV{"WEBWORK_DB_USER"}/' \ -e 's/database_password ="passwordRW"/database_password =$ENV{"WEBWORK_DB_PASSWORD"}/' \ -e 's/mail{smtpServer} = '\'''\''/mail{smtpServer} = $ENV{"WEBWORK_SMTP_SERVER"}/' \