Add support for provisioning and rebuilding via interface
This commit is contained in:
parent
f5c5999f44
commit
ddb4bb3546
76 changed files with 1306 additions and 625 deletions
|
|
@ -7,4 +7,6 @@ HOST_RULE=${HOST_RULE}
|
|||
DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME}
|
||||
|
||||
HTTPS_ENABLED=${HTTPS_ENABLED}
|
||||
|
||||
BARREL_BASE_IMAGE=${BARREL_BASE_IMAGE}
|
||||
OPCACHE_MODE=${OPCACHE_MODE}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package barrel
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||
|
|
@ -17,6 +15,11 @@ type Barrel struct {
|
|||
}
|
||||
}
|
||||
|
||||
func (barrel *Barrel) DataPath() string {
|
||||
return filepath.Join(barrel.FilesystemBase, "data")
|
||||
}
|
||||
const (
|
||||
BaseDirectory = "/var/www/data"
|
||||
ComposerDirectory = BaseDirectory + "/project"
|
||||
WebDirectory = ComposerDirectory + "/web"
|
||||
OntologyDirectory = SitesDirectory + "/default/files/ontology"
|
||||
SitesDirectory = WebDirectory + "/sites"
|
||||
WissKIDirectory = WebDirectory + "/modules/contrib/wisski"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,5 @@
|
|||
# allow the following files:
|
||||
!conf/*
|
||||
!scripts/*
|
||||
!patch/*
|
||||
!ssh/*
|
||||
!wisskiutils/*
|
||||
!php.ini.d/*
|
||||
|
|
@ -1,6 +1,15 @@
|
|||
ARG BARREL_BASE_IMAGE
|
||||
# ============================
|
||||
# WissKI Distillery Dockerfile
|
||||
# ============================
|
||||
|
||||
# This file is part of the WissKI Distillery and sets up an image
|
||||
# to be used for individual WissKIs.
|
||||
|
||||
# Start from a base image (configured by the build argument).
|
||||
ARG BARREL_BASE_IMAGE=docker.io/library/php:8.1-apache-bullseye
|
||||
FROM $BARREL_BASE_IMAGE
|
||||
ARG COMPOSER_VERSION=2.3.8
|
||||
|
||||
# Setup in /var/www
|
||||
WORKDIR /var/www
|
||||
|
||||
# install and enable the various required php extensions and dropbear ssh server
|
||||
|
|
@ -69,39 +78,46 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|||
# enable the apache rewrite mod
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# install composer and add it to path
|
||||
|
||||
# Install composer.
|
||||
ARG COMPOSER_VERSION=2.3.8
|
||||
RUN curl -sS https://getcomposer.org/installer | php -- --version=$COMPOSER_VERSION && \
|
||||
mv composer.phar /usr/local/bin/composer
|
||||
|
||||
# Add it to the path
|
||||
ENV PATH "/usr/local/bin:/var/www/data/project/vendor/bin:$PATH"
|
||||
|
||||
# remove default configuration
|
||||
# Configure PHP
|
||||
ADD php.ini.d/wisski.ini /usr/local/etc/php/conf.d/wisski.ini
|
||||
|
||||
# Configure opcache with whatever the user configured
|
||||
ARG OPCACHE_MODE=prod
|
||||
ADD php.ini.d/opcache-$OPCACHE_MODE.ini /usr/local/etc/php/conf.d/opcache.ini
|
||||
|
||||
|
||||
# Configure Apache.
|
||||
|
||||
# first remove the default configuration
|
||||
RUN rm /etc/apache2/sites-available/*.conf && \
|
||||
rm /etc/apache2/sites-enabled/*.conf
|
||||
|
||||
ADD patch/easyrdf.patch /patch/easyrdf.patch
|
||||
ADD patch/triples.patch /patch/triples.patch
|
||||
|
||||
# Add wisski configuration
|
||||
# Then add the WissKI site
|
||||
ADD conf/ports.conf /etc/apache2/ports.conf
|
||||
ADD conf/wisski.conf /etc/apache2/sites-available/wisski.conf
|
||||
ADD conf/wisski.ini /usr/local/etc/php/conf.d/wisski.ini
|
||||
|
||||
# And enable it
|
||||
RUN a2ensite wisski
|
||||
|
||||
# volumes for composer
|
||||
VOLUME /var/www/.composer
|
||||
VOLUME /var/www/data
|
||||
|
||||
|
||||
# Add and configure the entrypoint
|
||||
ADD scripts/entrypoint.sh /entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "/bin/bash", "/entrypoint.sh" ]
|
||||
CMD ["apache2-foreground"]
|
||||
|
||||
# Add the provision script and WissKI utils
|
||||
ADD scripts/provision_container.sh /provision_container.sh
|
||||
ADD wisskiutils/ /wisskiutils
|
||||
|
||||
# Add the user_shell.sh
|
||||
ADD scripts/user_shell.sh /user_shell.sh
|
||||
ADD ssh/ /ssh/
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ services:
|
|||
context: .
|
||||
args:
|
||||
BARREL_BASE_IMAGE: ${BARREL_BASE_IMAGE}
|
||||
OPCACHE_MODE: ${OPCACHE_MODE}
|
||||
logging:
|
||||
driver: none
|
||||
|
||||
restart: always
|
||||
hostname: ${WISSKI_HOSTNAME}
|
||||
|
|
@ -32,11 +35,10 @@ services:
|
|||
|
||||
# volumes that are mounted
|
||||
volumes:
|
||||
- ${DATA_PATH}/.composer:/var/www/.composer
|
||||
- ${DATA_PATH}/data:/var/www/data
|
||||
- ${DATA_PATH}/home:/var/www/
|
||||
- ${DATA_PATH}/.composer:/var/www/.composer:rw
|
||||
- ${DATA_PATH}/data:/var/www/data:rw
|
||||
- ${DATA_PATH}/home:/var/www:rw
|
||||
- ${DATA_PATH}/hostkeys:/ssh/hostkeys:rw
|
||||
- ${RUNTIME_DIR}:/runtime:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
281c281
|
||||
< if (preg_match("|^HTTP/([\d\.x]+) (\d+) ([^\r\n]+)|", $status, $m)) {
|
||||
---
|
||||
> if(preg_match("|^HTTP/([\d\.x]+) (\d+) ([^\r\n]*)|", $status, $m)) {
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
100c100
|
||||
< if($result->o instanceof \EasyRdf_Resource) {
|
||||
---
|
||||
> if($result->o instanceof \EasyRdf\Resource) {
|
||||
118c118
|
||||
< $object_text = $result->o->getValue();
|
||||
---
|
||||
> $object_text = $result->o->dumpValue('string');
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
; ======== Distillery php.ini =============
|
||||
; Opcache Development Settings
|
||||
; =========================================
|
||||
|
||||
opcache.revalidate_freq=0
|
||||
opcache.enable_cli=0
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
; ======== Distillery php.ini =============
|
||||
; Opcache Production Settings
|
||||
; =========================================
|
||||
|
||||
opcache.memory_consumption=128
|
||||
opcache.interned_strings_buffer=8
|
||||
opcache.max_accelerated_files=4000
|
||||
opcache.revalidate_freq=60
|
||||
opcache.enable_cli=1
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
; ======== Distillery php.ini =============
|
||||
; Main Configuration File (always included)
|
||||
; =========================================
|
||||
|
||||
|
||||
; File Uploads up to 1GB
|
||||
file_uploads = On
|
||||
upload_max_filesize = 1000M
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
function log_info() {
|
||||
echo -e "\033[1m$1\033[0m"
|
||||
}
|
||||
|
||||
function log_ok() {
|
||||
echo -e "\033[0;32m$1\033[0m"
|
||||
}
|
||||
|
||||
log_info " => Reading configuration variables"
|
||||
|
||||
INSTANCE_DOMAIN="$1"
|
||||
echo "INSTANCE_DOMAIN=$INSTANCE_DOMAIN"
|
||||
shift 1
|
||||
|
||||
MYSQL_DATABASE="$1"
|
||||
echo "MYSQL_DATABASE=$MYSQL_DATABASE"
|
||||
MYSQL_USER="$2"
|
||||
echo "MYSQL_USER=$MYSQL_USER"
|
||||
MYSQL_PASSWORD="$3"
|
||||
echo "MYSQL_PASSWORD=$MYSQL_PASSWORD"
|
||||
|
||||
shift 3
|
||||
|
||||
GRAPHDB_REPO="$1"
|
||||
echo "GRAPHDB_REPO=$GRAPHDB_REPO"
|
||||
GRAPHDB_USER="$2"
|
||||
echo "GRAPHDB_USER=$GRAPHDB_USER"
|
||||
GRAPHDB_PASSWORD="$3"
|
||||
echo "GRAPHDB_PASSWORD=$GRAPHDB_PASSWORD"
|
||||
shift 3
|
||||
|
||||
GRAPHDB_HEADER="$(printf "%s:%s" "$GRAPHDB_USER" "$GRAPHDB_PASSWORD" | base64 -w 0)"
|
||||
|
||||
DRUPAL_USER="$1"
|
||||
echo "DRUPAL_USER=$DRUPAL_USER"
|
||||
DRUPAL_PASS="$2"
|
||||
echo "DRUPAL_PASS=$DRUPAL_PASS"
|
||||
shift 2
|
||||
|
||||
DRUPAL_VERSION="$1"
|
||||
echo "DRUPAL_VERSION=$DRUPAL_VERSION"
|
||||
shift 1
|
||||
|
||||
WISSKI_VERSION="$1"
|
||||
echo "WISSKI_VERSION=$WISSKI_VERSION"
|
||||
shift 1
|
||||
|
||||
log_info " => Preparing installation environment"
|
||||
BASE_DIR="/var/www/data"
|
||||
COMPOSER_DIR="$BASE_DIR/project"
|
||||
WEB_DIR="$COMPOSER_DIR/web"
|
||||
ONTOLOGY_DIR="$WEB_DIR/sites/default/files/ontology"
|
||||
|
||||
log_info " => Creating '$COMPOSER_DIR'"
|
||||
mkdir -p "$COMPOSER_DIR"
|
||||
cd "$COMPOSER_DIR"
|
||||
|
||||
# workaround for making the drupal sites directory writable
|
||||
function drupal_sites_permission_workaround() {
|
||||
chmod -R u+w "$WEB_DIR/sites/" || true
|
||||
}
|
||||
|
||||
# install a module with composer and enable it with drush
|
||||
# Example:
|
||||
#
|
||||
# composer_install_and_enable << EOF
|
||||
# drupal/some_module:1.23 some_module
|
||||
# drupal/other_module:2.34
|
||||
# EOF
|
||||
#
|
||||
# Will install both modules, but only enable the first one.
|
||||
function composer_install_and_enable() {
|
||||
while IFS= read -r line; do
|
||||
echo "$line" | (
|
||||
read composer drush;
|
||||
drupal_sites_permission_workaround
|
||||
composer require "$composer"
|
||||
if [ -n "$drush" ]; then
|
||||
drush pm-enable --yes "$drush"
|
||||
fi
|
||||
)
|
||||
done
|
||||
}
|
||||
|
||||
function try_variants() {
|
||||
for var in "$@"
|
||||
do
|
||||
if composer require --dry-run "$var" > /dev/null 2>&1; then
|
||||
composer require "$var"
|
||||
return 0;
|
||||
fi
|
||||
done
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
# Create a new composer project.
|
||||
log_info " => Creating composer project"
|
||||
if [ -z "${DRUPAL_VERSION}" ]; then
|
||||
composer --no-interaction create-project 'drupal/recommended-project:^9.0.0' .
|
||||
else
|
||||
composer --no-interaction create-project "drupal/recommended-project:$DRUPAL_VERSION" .
|
||||
fi
|
||||
|
||||
# needed for composer > 2.2
|
||||
composer --no-interaction config allow-plugins true
|
||||
|
||||
# Install drush so that we can automate a lot of things
|
||||
log_info " => Installing 'drush'"
|
||||
try_variants 'drush/drush' 'drush/drush:^12' 'drush/drush:^11' || (echo "No version of Drush is installable" && false)
|
||||
|
||||
# Use 'drush' to run the site-installation.
|
||||
# Here we need to use the username, password and database creds we made above.
|
||||
log_info " => Running drupal installation scripts"
|
||||
drush site-install standard --yes --site-name=${INSTANCE_DOMAIN} \
|
||||
--account-name=$DRUPAL_USER --account-pass=$DRUPAL_PASS \
|
||||
--db-url=mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@sql/${MYSQL_DATABASE}
|
||||
drupal_sites_permission_workaround
|
||||
|
||||
# create a directory for ontologies.
|
||||
log_info " => Creating '$ONTOLOGY_DIR'"
|
||||
mkdir -p "$ONTOLOGY_DIR"
|
||||
|
||||
# Install some additional modules
|
||||
# These neeed to go before WissKI because some are WissKI dependencies
|
||||
|
||||
log_info " => Installing and enabling modules"
|
||||
composer_install_and_enable << EOF
|
||||
drupal/inline_entity_form:^1.0@RC
|
||||
drupal/imagemagick
|
||||
drupal/image_effects
|
||||
drupal/colorbox
|
||||
drupal/devel:^4.1 devel
|
||||
drupal/geofield:^1.40 geofield
|
||||
drupal/geofield_map:^2.85 geofield_map
|
||||
drupal/imce:^2.4 imce
|
||||
drupal/remove_generator:^2.0 remove_generator
|
||||
EOF
|
||||
|
||||
|
||||
# Install the Wisski packages.
|
||||
log_info " => Installing Wisski packages"
|
||||
cd "$COMPOSER_DIR"
|
||||
|
||||
# install the development version when requested
|
||||
if [ -z "${WISSKI_VERSION}" ]; then
|
||||
composer require 'drupal/wisski'
|
||||
else
|
||||
composer require "drupal/wisski:$WISSKI_VERSION"
|
||||
fi
|
||||
|
||||
# Install dependencies of WissKI
|
||||
log_info " => Installing and patching Wisski dependencies"
|
||||
pushd "$WEB_DIR/modules/contrib/wisski"
|
||||
composer install
|
||||
|
||||
# Patch EasyRDF (for now)
|
||||
EASYRDF_RESPONSE="./vendor/easyrdf/easyrdf/lib/EasyRdf/Http/Response.php"
|
||||
if [ -f "$EASYRDF_RESPONSE" ]; then
|
||||
patch -N "$EASYRDF_RESPONSE" < "/patch/easyrdf.patch"
|
||||
fi
|
||||
popd
|
||||
|
||||
|
||||
|
||||
log_info " => Enable Wisski modules"
|
||||
drush pm-enable --yes wisski_core wisski_linkblock wisski_pathbuilder wisski_adapter_sparql11_pb wisski_salz
|
||||
drupal_sites_permission_workaround
|
||||
|
||||
log_info " => Setting up WissKI Salz Adapter"
|
||||
drush php:script /wisskiutils/create_adapter.php "$INSTANCE_DOMAIN" "$GRAPHDB_REPO" "$GRAPHDB_HEADER"
|
||||
|
||||
log_info " => Updating TRUSTED_HOST_PATTERNS in settings.php"
|
||||
|
||||
/bin/bash /wisskiutils/set_trusted_host.sh
|
||||
|
||||
log_info " => Running initial cron"
|
||||
drush core-cron
|
||||
|
||||
log_info " => Provisioning is now complete. "
|
||||
log_ok "Your installation details are as follows:"
|
||||
function printdetails() {
|
||||
echo "URL: http://$INSTANCE_DOMAIN"
|
||||
echo "Username: $DRUPAL_USER"
|
||||
echo "Password: $DRUPAL_PASS"
|
||||
}
|
||||
printdetails
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This script will automatically create a WissKI Salz Adapter for use within the distillery.
|
||||
* It will not update any existing adapter and is rather primitive.
|
||||
*/
|
||||
|
||||
$argc = $_SERVER['argc']-3;
|
||||
$argv = array_slice($_SERVER['argv'], 3);
|
||||
|
||||
// read parameters from the command line
|
||||
if ($argc != 3) {
|
||||
die("Usage: drush php:script create_adapter.php INSTANCE_DOMAIN GRAPHDB_REPO HEADER");
|
||||
}
|
||||
$INSTANCE_DOMAIN = $argv[0];
|
||||
$GRAPHDB_REPO = $argv[1];
|
||||
$HEADER = $argv[2];
|
||||
|
||||
//
|
||||
// PROPERTIES FOR THE ADAPTER
|
||||
//
|
||||
|
||||
$id = 'default'; // id
|
||||
$type = 'sparql11_with_pb'; // plugin
|
||||
$machine_name = 'default'; // machine-name
|
||||
$label = 'Default WissKI Distillery Adapter';
|
||||
$description = 'Adapter for ' . $INSTANCE_DOMAIN; // description
|
||||
$writable = TRUE; // writable
|
||||
$is_preferred_local_store = TRUE; // is_preferred_local_store
|
||||
$header = $HEADER; // header
|
||||
$read_url = 'http://triplestore:7200/repositories/' . $GRAPHDB_REPO; // read_url
|
||||
$write_url = 'http://triplestore:7200/repositories/' . $GRAPHDB_REPO . '/statements'; // write_url
|
||||
$is_federatable = TRUE; // is_federatable
|
||||
$default_graph_uri = 'https://' . $INSTANCE_DOMAIN . '/';
|
||||
$same_as_properties = ['http://www.w3.org/2002/07/owl#sameAs']; // same_as_properties
|
||||
$ontology_graphs = []; // ontology_graphs
|
||||
|
||||
//
|
||||
// Do the creation!
|
||||
//
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('wisski_salz_adapter');
|
||||
$adapter = $storage->create([
|
||||
"id" => $id,
|
||||
"label" => $label,
|
||||
"description" => $description,
|
||||
]);
|
||||
$adapter->setEngineConfig([
|
||||
"id" => $type,
|
||||
"machine-name" => $machine_name,
|
||||
"header" => $header,
|
||||
"writeable" => $writable,
|
||||
"is_preferred_local_store" => $is_preferred_local_store,
|
||||
"read_url" => $read_url,
|
||||
"write_url" => $write_url,
|
||||
"is_federatable" => $is_federatable,
|
||||
"default_graph" => $default_graph_uri,
|
||||
"same_as_properties" => $same_as_properties,
|
||||
"ontology_graphs" => $ontology_graphs,
|
||||
]);
|
||||
$adapter->save();
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This utility script can be used to configure the trusted host settings inside of settings.php.
|
||||
# It doesn't take care of corner cases and should only be used when needed.
|
||||
|
||||
INSTANCE_DOMAIN="$(hostname -f)"
|
||||
INSTANCE_DOMAIN="${INSTANCE_DOMAIN%.wisski}"
|
||||
|
||||
TRUSTED_HOST_PATTERN="${INSTANCE_DOMAIN//\./\\\\.}"
|
||||
TRUSTED_HOST_PATTERNS='["'$TRUSTED_HOST_PATTERN'"]'
|
||||
|
||||
echo "Setting 'trusted_host_patterns' to $TRUSTED_HOST_PATTERNS"
|
||||
bash /wisskiutils/settings_php_set.sh 'trusted_host_patterns' "$TRUSTED_HOST_PATTERNS"
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# settings_php_get.sh name
|
||||
# Gets the 'settings_php_get.php' setting 'name' as json-encoded value, or null when it does not exist.
|
||||
|
||||
NAME=$1
|
||||
|
||||
if [ -z "$NAME" ]; then
|
||||
echo "Usage: get_settings_setting.sh NAME"
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
echo "$NAME" | drush php:eval '
|
||||
use \Drupal\Core\Site\Settings;
|
||||
$name=trim(file_get_contents("php://stdin"));
|
||||
echo json_encode(Settings::get($name));
|
||||
';
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# settings_php_set.sh name value
|
||||
# Sets the 'settings.php' setting 'name' to 'value'.
|
||||
# Value must be json-encoded.
|
||||
|
||||
NAME=$1
|
||||
VALUE=$2
|
||||
|
||||
if [ -z "$NAME" ]; then
|
||||
echo "Usage: settings_php_set.sh NAME VALUE"
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
if [ -z "$VALUE" ]; then
|
||||
echo "Usage: settings_php_set.sh NAME VALUE"
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
cd /var/www/data/project
|
||||
chmod u+w web/sites/default/settings.php
|
||||
|
||||
(echo "$NAME"; echo "$VALUE" ) | drush php:eval '
|
||||
if(is_file(DRUPAL_ROOT . "/internal/")) {
|
||||
include_once DRUPAL_ROOT . "/internal/core/includes/install.inc";
|
||||
} else {
|
||||
include_once DRUPAL_ROOT . "/core/includes/install.inc";
|
||||
}
|
||||
|
||||
// read NAME and VALUE from STDIN
|
||||
$content=file_get_contents("php://stdin");
|
||||
$newline=strpos($content, "\n");
|
||||
$name=trim(substr($content, 0, $newline));
|
||||
$jvalue=trim(substr($content, $newline + 1));
|
||||
|
||||
// decode json values
|
||||
$value = @json_decode($jvalue);
|
||||
if ($value === null && json_last_error() !== JSON_ERROR_NONE) {
|
||||
echo "Invalid JSON, cannot update settings.php. \n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// make parameters to drush_rewrite_settings
|
||||
$settings["settings"][$name] = (object)[
|
||||
"value" => $value,
|
||||
"required" => TRUE,
|
||||
];
|
||||
|
||||
// find the actual settings.php file to rewrite
|
||||
$filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php";
|
||||
drupal_rewrite_settings($settings, $filename);
|
||||
|
||||
echo "Wrote " . $filename . "\n";
|
||||
return 0;
|
||||
';
|
||||
EXIT=$?
|
||||
|
||||
chmod u-w web/sites/default/settings.php
|
||||
|
||||
exit $?
|
||||
59
internal/wisski/ingredient/barrel/composer/composer.go
Normal file
59
internal/wisski/ingredient/barrel/composer/composer.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package composer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Drush implements commands related to drush
|
||||
type Composer struct {
|
||||
ingredient.Base
|
||||
Dependencies struct {
|
||||
Barrel *barrel.Barrel
|
||||
// PHP *php.PHP
|
||||
}
|
||||
}
|
||||
|
||||
// Exec executes a composer command
|
||||
func (composer *Composer) Exec(ctx context.Context, progress io.Writer, command ...string) error {
|
||||
if err := composer.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), append([]string{"composer", "--no-interaction", "--working-dir", barrel.ComposerDirectory}, command...)...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FixPermissions fixes the permissions of the sites directory.
|
||||
// This needs to be run after every installation of a composer module.
|
||||
func (composer *Composer) FixPermission(ctx context.Context, progress io.Writer) error {
|
||||
composer.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), "chmod", "-R", "u+w", barrel.SitesDirectory)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Install attempts runs 'composer require' with the given arguments
|
||||
// Spec is like a specification on the command line.
|
||||
func (composer *Composer) Install(ctx context.Context, progress io.Writer, args ...string) error {
|
||||
composer.FixPermission(ctx, progress)
|
||||
|
||||
requires := append([]string{"require"}, args...)
|
||||
if err := composer.Exec(ctx, progress, requires...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrNotInstalled = errors.New("Composer: Not installed")
|
||||
|
||||
// TryInstall attempts to install the given package.
|
||||
// If it cannot be installed, returns ErrNotInstalled.
|
||||
func (composer *Composer) TryInstall(ctx context.Context, progress io.Writer, spec string) error {
|
||||
if err := composer.Exec(ctx, io.Discard, "require", "--dry-run", spec); err != nil {
|
||||
return ErrNotInstalled
|
||||
}
|
||||
|
||||
return composer.Install(ctx, progress, spec)
|
||||
}
|
||||
14
internal/wisski/ingredient/barrel/composer/name.go
Normal file
14
internal/wisski/ingredient/barrel/composer/name.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package composer
|
||||
|
||||
import "strings"
|
||||
|
||||
// ModuleName extracts the module name from a specification.
|
||||
// If the module name cannot be found, returns the string unchanged
|
||||
func ModuleName(spec string) string {
|
||||
_, name, found := strings.Cut(spec, "/")
|
||||
if !found {
|
||||
return spec
|
||||
}
|
||||
name, _, _ = strings.Cut(name, ":")
|
||||
return name
|
||||
}
|
||||
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
var errCronFailed = exit.Error{
|
||||
|
|
@ -20,8 +20,9 @@ var errCronFailed = exit.Error{
|
|||
}
|
||||
|
||||
func (drush *Drush) Cron(ctx context.Context, progress io.Writer) error {
|
||||
code := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/cron.sh")()
|
||||
if code != 0 {
|
||||
err := drush.Exec(ctx, progress, "core-cron")
|
||||
if err != nil {
|
||||
code := err.(barrel.ExitError).Code
|
||||
// keep going, because we want to run as many crons as possible
|
||||
fmt.Fprintf(progress, "%v", errCronFailed.WithMessageF(drush.Slug, code))
|
||||
}
|
||||
|
|
@ -31,7 +32,7 @@ func (drush *Drush) Cron(ctx context.Context, progress io.Writer) error {
|
|||
|
||||
func (drush *Drush) LastCron(ctx context.Context, server *phpx.Server) (t time.Time, err error) {
|
||||
var timestamp int64
|
||||
err = drush.Dependencies.PHP.EvalCode(ctx, server, ×tamp, `$val = \Drupal::state()->get('system.cron_last'); return $val; `)
|
||||
err = drush.Dependencies.PHP.EvalCode(ctx, server, ×tamp, `return \Drupal::state()->get('system.cron_last');`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package drush
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Drush implements commands related to drush
|
||||
|
|
@ -16,3 +20,13 @@ type Drush struct {
|
|||
PHP *php.PHP
|
||||
}
|
||||
}
|
||||
|
||||
// Enable enables the given drush modules
|
||||
func (drush *Drush) Enable(ctx context.Context, progress io.Writer, modules ...string) error {
|
||||
return drush.Exec(ctx, progress, append([]string{"pm-enable", "--yes"}, modules...)...)
|
||||
}
|
||||
|
||||
func (drush *Drush) Exec(ctx context.Context, progress io.Writer, command ...string) error {
|
||||
script := append([]string{"drush"}, command...)
|
||||
return drush.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), script...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ import (
|
|||
)
|
||||
|
||||
var errBlindUpdateFailed = exit.Error{
|
||||
Message: "failed to run blind update script for instance %q: exited with code %d",
|
||||
Message: "failed to run blind update script for instance %q",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
// Update performs a blind drush update
|
||||
func (drush *Drush) Update(ctx context.Context, progress io.Writer) error {
|
||||
code := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/blind_update.sh")()
|
||||
if code != 0 {
|
||||
return errBlindUpdateFailed.WithMessageF(drush.Slug, code)
|
||||
err := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/blind_update.sh")
|
||||
if err != nil {
|
||||
return errBlindUpdateFailed.WithMessageF(drush.Slug).Wrap(err)
|
||||
}
|
||||
|
||||
return drush.setLastUpdate(ctx)
|
||||
|
|
|
|||
196
internal/wisski/ingredient/barrel/manager/apply.go
Normal file
196
internal/wisski/ingredient/barrel/manager/apply.go
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Apply applies the given profile to this existing instance.
|
||||
func (manager *Manager) Apply(ctx context.Context, progress io.Writer, flags Profile) error {
|
||||
// Update drupal
|
||||
if flags.Drupal != "" {
|
||||
err := manager.applyDrupal(ctx, progress, flags.Drupal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update WissKI
|
||||
if flags.WissKI != "" {
|
||||
err := manager.applyWissKI(ctx, progress, flags.WissKI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// install custom modules
|
||||
if len(flags.InstallModules) > 0 {
|
||||
err := manager.installModules(ctx, progress, flags.InstallModules, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// install + enable custom modules
|
||||
if len(flags.EnableModules) > 0 {
|
||||
err := manager.installModules(ctx, progress, flags.EnableModules, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *Manager) installModules(ctx context.Context, progress io.Writer, modules []string, enable bool) error {
|
||||
message := ""
|
||||
if enable {
|
||||
message = "Installing and enabling modules"
|
||||
} else {
|
||||
message = "Installing modules"
|
||||
}
|
||||
|
||||
// enable the module
|
||||
return logging.LogOperation(func() error {
|
||||
for _, spec := range modules {
|
||||
logging.LogMessage(progress, fmt.Sprintf("Installing %q", spec))
|
||||
err := manager.Dependencies.Composer.Install(ctx, progress, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if enable {
|
||||
name := composer.ModuleName(spec)
|
||||
logging.LogMessage(progress, fmt.Sprintf("Enabling %q (from spec %q)", name, spec))
|
||||
err := manager.Dependencies.Drush.Enable(ctx, progress, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, progress, "%s", message)
|
||||
|
||||
}
|
||||
|
||||
// applyDrupal applies a specific drupal version.
|
||||
// Assumes that drupal != "".
|
||||
func (manager *Manager) applyDrupal(ctx context.Context, progress io.Writer, drupal string) error {
|
||||
return logging.LogOperation(func() error {
|
||||
|
||||
logging.LogMessage(progress, "Clearing up permissions for update")
|
||||
{
|
||||
for _, script := range [][]string{
|
||||
{"chmod", "777", "web/sites/default"},
|
||||
{"chmod", "666", "web/sites/default/*settings.php"},
|
||||
{"chmod", "666", "web/sites/default/*services.php"},
|
||||
} {
|
||||
err := manager.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), script...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
logging.LogMessage(progress, "Resetting permissions")
|
||||
{
|
||||
for _, script := range [][]string{
|
||||
{"chmod", "755", "web/sites/default"},
|
||||
{"chmod", "644", "web/sites/default/*settings.php"},
|
||||
{"chmod", "644", "web/sites/default/*services.php"},
|
||||
} {
|
||||
manager.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), script...)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// write out a specific Drupal version
|
||||
logging.LogMessage(progress, "Performing Drupal update")
|
||||
{
|
||||
args := []string{
|
||||
"drupal/internal/core-recommended:", "drupal/internal/core-composer-scaffold:", "drupal/internal/core-project-message:",
|
||||
}
|
||||
for i, cm := range args {
|
||||
args[i] = cm + drupal
|
||||
}
|
||||
args = append(args, "--update-with-dependencies", "--no-update")
|
||||
|
||||
if err := manager.Dependencies.Composer.Install(ctx, progress, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Running composer update")
|
||||
{
|
||||
if err := manager.Dependencies.Composer.Exec(ctx, progress, "update"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Performing database updates (if any)")
|
||||
{
|
||||
if err := manager.Dependencies.Drush.Exec(ctx, progress, "updatedb", "--yes"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, progress, "%s", "Updating to Drupal %q", drupal)
|
||||
|
||||
}
|
||||
|
||||
// applyWissKI applies the WissKI version.
|
||||
func (manager *Manager) applyWissKI(ctx context.Context, progress io.Writer, wisski string) error {
|
||||
return logging.LogOperation(func() error {
|
||||
|
||||
logging.LogMessage(progress, "Installing WissKI Module")
|
||||
{
|
||||
spec := "drupal/wisski"
|
||||
if wisski != "" {
|
||||
spec += ":" + wisski
|
||||
}
|
||||
|
||||
err := manager.Dependencies.Composer.Install(ctx, progress, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// install dependencies in the WissKI directory
|
||||
logging.LogMessage(progress, "Installing WissKI Dependencies")
|
||||
{
|
||||
if err := manager.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), "composer", "--working-dir", barrel.WissKIDirectory, "install"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Enable Wisski modules")
|
||||
{
|
||||
if err := manager.Dependencies.Drush.Enable(ctx, progress,
|
||||
"wisski_core", "wisski_linkblock", "wisski_pathbuilder", "wisski_adapter_sparql11_pb", "wisski_salz",
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := manager.Dependencies.Composer.FixPermission(ctx, progress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Performing database updates (if any)")
|
||||
{
|
||||
if err := manager.Dependencies.Drush.Exec(ctx, progress, "updatedb", "--yes"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, progress, "Installing WissKI version %q", wisski)
|
||||
}
|
||||
64
internal/wisski/ingredient/barrel/manager/manager.go
Normal file
64
internal/wisski/ingredient/barrel/manager/manager.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package manager
|
||||
|
||||
import (
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/drush"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/system"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/bookkeeping"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras"
|
||||
)
|
||||
|
||||
// Manager manages a profile applied to specific WissKI instances.
|
||||
type Manager struct {
|
||||
ingredient.Base
|
||||
Dependencies struct {
|
||||
Barrel *barrel.Barrel
|
||||
Bookkeeping *bookkeeping.Bookkeeping
|
||||
|
||||
SystemManager *system.SystemManager
|
||||
|
||||
Composer *composer.Composer
|
||||
Drush *drush.Drush
|
||||
|
||||
Adapters *extras.Adapters
|
||||
Settings *extras.Settings
|
||||
}
|
||||
}
|
||||
|
||||
// Profile represents a profile applied to a WissKI instance of the Distillery.
|
||||
type Profile struct {
|
||||
Drupal string // Version of Drupal to use
|
||||
WissKI string // Version of WissKI to use
|
||||
|
||||
InstallModules []string // Modules to be installed (but not neccessarily enabled)
|
||||
EnableModules []string // Modules to be installed and enabled
|
||||
}
|
||||
|
||||
// DefaultDrupalVersion is the default drupal version
|
||||
const DefaultDrupalVersion = "^9.0.0"
|
||||
|
||||
// ApplyDefaults applies the default settings to missing profile settings.
|
||||
func (profile *Profile) ApplyDefaults() {
|
||||
if profile.Drupal == "" {
|
||||
profile.Drupal = DefaultDrupalVersion
|
||||
}
|
||||
if profile.InstallModules == nil {
|
||||
profile.InstallModules = []string{
|
||||
"drupal/inline_entity_form:^1.0@RC",
|
||||
"drupal/imagemagick",
|
||||
"drupal/image_effects",
|
||||
"drupal/colorbox",
|
||||
}
|
||||
}
|
||||
if profile.EnableModules == nil {
|
||||
profile.EnableModules = []string{
|
||||
"drupal/devel:^4.1",
|
||||
"drupal/geofield:^1.40",
|
||||
"drupal/geofield_map:^2.85",
|
||||
"drupal/imce:^2.4",
|
||||
"drupal/remove_generator:^2.0",
|
||||
}
|
||||
}
|
||||
}
|
||||
196
internal/wisski/ingredient/barrel/manager/provision.go
Normal file
196
internal/wisski/ingredient/barrel/manager/provision.go
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/composer"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||
"github.com/tkw1536/pkglib/contextx"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Provision provisions this instance with the given flags.
|
||||
//
|
||||
// Provision assumes that the instance does not yet exist, and may fail with an existing instance.
|
||||
//
|
||||
// Provision applies defaults to flags, to ensure some values are set
|
||||
func (manager *Manager) Provision(ctx context.Context, progress io.Writer, system models.System, flags Profile) error {
|
||||
// Force building and applying the system!
|
||||
if err := manager.Dependencies.SystemManager.Apply(ctx, progress, system, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the composer directory!
|
||||
logging.LogMessage(progress, "Creating required directories")
|
||||
{
|
||||
code, err := manager.Dependencies.Barrel.Stack().Run(ctx, stream.FromNil(), component.RunFlags{Detach: true, AutoRemove: true}, "barrel", "sudo", "-u", "www-data", "mkdir", "-p", barrel.ComposerDirectory)
|
||||
if code != 0 {
|
||||
err = barrel.ExitError(code)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// start the container, and have it do nothing!
|
||||
code, err := manager.Dependencies.Barrel.Stack().Run(ctx, stream.FromNil(), component.RunFlags{Detach: true, AutoRemove: true}, "barrel", "tail", "-f", "/dev/null")
|
||||
if code != 0 {
|
||||
err = barrel.ExitError(code)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// when we are done, shut it down!
|
||||
defer func() {
|
||||
anyways, cancel := contextx.Anyways(ctx, time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// stop the container (even if the context was cancelled)
|
||||
manager.Dependencies.Barrel.Stack().DownAll(anyways, progress)
|
||||
}()
|
||||
|
||||
// Apply the defaults to the flags
|
||||
flags.ApplyDefaults()
|
||||
return manager.bootstrap(ctx, progress, flags)
|
||||
}
|
||||
|
||||
// TODO: Move this to the flags
|
||||
var drushVariants = []string{
|
||||
"drush/drush", "drush/drush:^12", "drush/drush:^11",
|
||||
}
|
||||
|
||||
// bootstrap applies the initial flags induced by flags.
|
||||
// Applies defaults to the flags.
|
||||
func (provision *Manager) bootstrap(ctx context.Context, progress io.Writer, flags Profile) error {
|
||||
// TODO: Check if we can remove the easyrdf patch!
|
||||
|
||||
flags.ApplyDefaults()
|
||||
|
||||
logging.LogMessage(progress, "Creating Composer Project")
|
||||
{
|
||||
drupal := "drupal/recommended-project"
|
||||
if flags.Drupal != "" {
|
||||
drupal += ":" + flags.Drupal
|
||||
}
|
||||
err := provision.Dependencies.Composer.Exec(ctx, progress, "create-project", drupal, ".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Configuring Composer")
|
||||
{
|
||||
// needed for composer > 2.2
|
||||
err := provision.Dependencies.Composer.Exec(ctx, progress, "config", "allow-plugins", "true")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Installing drush")
|
||||
{
|
||||
for _, v := range drushVariants {
|
||||
err := provision.Dependencies.Composer.TryInstall(ctx, progress, v)
|
||||
if err == composer.ErrNotInstalled {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var sqlDBURL = "mysql://" + provision.SqlUsername + ":" + provision.SqlPassword + "@sql/" + provision.SqlDatabase
|
||||
|
||||
// Use 'drush' to run the site-installation.
|
||||
// Here we need to use the username, password and database creds we made above.
|
||||
logging.LogMessage(progress, "Running Drupal installation scripts")
|
||||
{
|
||||
if err := provision.Dependencies.Drush.Exec(
|
||||
ctx, progress,
|
||||
"site-install",
|
||||
"standard", "--yes", "--site-name="+provision.Domain(),
|
||||
"--account-name="+provision.DrupalUsername, "--account-pass="+provision.DrupalPassword,
|
||||
"--db-url="+sqlDBURL,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provision.Dependencies.Composer.FixPermission(ctx, progress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create directory for ontologies
|
||||
logging.LogMessage(progress, fmt.Sprintf("Creating %q", barrel.OntologyDirectory))
|
||||
{
|
||||
if err := provision.Dependencies.Barrel.ShellScript(ctx, stream.NonInteractive(progress), "mkdir", "-p", barrel.OntologyDirectory); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// make a set of flags to apply to the given instance
|
||||
flags := flags
|
||||
flags.Drupal = "" // Do not upgrade Drupal
|
||||
flags.WissKI = "" // Do not upgrade WissKI
|
||||
|
||||
// apply the rest of the flags
|
||||
if err := provision.Apply(ctx, progress, flags); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// install WissKI
|
||||
if err := provision.applyWissKI(ctx, progress, flags.WissKI); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the default adapter
|
||||
logging.LogMessage(progress, "Creating default adapter")
|
||||
{
|
||||
if err := provision.Dependencies.Adapters.CreateDistilleryAdapter(ctx, nil, extras.DistilleryAdapter{
|
||||
Label: "Default WissKI Distillery Adapter",
|
||||
MachineName: "default",
|
||||
Description: "Default Adapter for " + provision.Domain(),
|
||||
InstanceDomain: provision.Domain(),
|
||||
GraphDBRepository: provision.GraphDBRepository,
|
||||
GraphDBUsername: provision.GraphDBUsername,
|
||||
GraphDBPassword: provision.GraphDBPassword,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Updating TRUSTED_HOST_PATTERNS in settings.php")
|
||||
{
|
||||
if err := provision.Dependencies.Settings.SetTrustedDomain(ctx, nil, provision.Domain()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Running initial cron")
|
||||
{
|
||||
if err := provision.Dependencies.Drush.Exec(ctx, progress, "core-cron"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logging.LogMessage(progress, "Provisioning is now complete")
|
||||
{
|
||||
fmt.Fprintf(progress, "URL: %s\n", provision.URL())
|
||||
fmt.Fprintf(progress, "Username: %s\n", provision.DrupalUsername)
|
||||
fmt.Fprintf(progress, "Password: %s\n", provision.DrupalPassword)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
package provisioner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/bookkeeping"
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Provisioner provides provisioning for a barrel
|
||||
// NOTE(twiesing): This should be refactored to not use the provision script.
|
||||
// Instead, this should code directly defined in go.
|
||||
type Provisioner struct {
|
||||
ingredient.Base
|
||||
Dependencies struct {
|
||||
Barrel *barrel.Barrel
|
||||
Bookkeeping *bookkeeping.Bookkeeping
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyFlags applies flags to an already provisioned instance.
|
||||
func (provision *Provisioner) ApplyFlags(ctx context.Context, progress io.Writer, phpversion string) (err error) {
|
||||
// setup the new docker image
|
||||
provision.Instance.DockerBaseImage, err = models.GetBaseImage(phpversion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save in bookkeeping
|
||||
if err := provision.Dependencies.Bookkeeping.Save(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return provision.Dependencies.Barrel.Build(ctx, progress, true)
|
||||
}
|
||||
|
||||
// Provision provisions an instance, assuming that the required databases already exist.
|
||||
func (provision *Provisioner) Provision(ctx context.Context, progress io.Writer) error {
|
||||
|
||||
// build the container
|
||||
if err := provision.Dependencies.Barrel.Build(ctx, progress, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
provisionParams := []string{
|
||||
provision.Domain(),
|
||||
|
||||
provision.SqlDatabase,
|
||||
provision.SqlUsername,
|
||||
provision.SqlPassword,
|
||||
|
||||
provision.GraphDBRepository,
|
||||
provision.GraphDBUsername,
|
||||
provision.GraphDBPassword,
|
||||
|
||||
provision.DrupalUsername,
|
||||
provision.DrupalPassword,
|
||||
|
||||
"", // TODO: DrupalVersion
|
||||
"", // TODO: WissKIVersion
|
||||
}
|
||||
|
||||
// escape the parameter
|
||||
for i, param := range provisionParams {
|
||||
provisionParams[i] = shellescape.Quote(param)
|
||||
}
|
||||
|
||||
// figure out the provision script
|
||||
// TODO: Move the provision script into the control plane!
|
||||
provisionScript := "sudo PATH=$PATH -u www-data /bin/bash /provision_container.sh " + strings.Join(provisionParams, " ")
|
||||
|
||||
code, err := provision.Dependencies.Barrel.Stack().Run(ctx, stream.NonInteractive(progress), true, "barrel", "/bin/bash", "-c", provisionScript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if code != 0 {
|
||||
return errors.New("unable to run provision script")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -2,11 +2,35 @@ package barrel
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
// Shell executes a shell command inside the instance.
|
||||
func (barrel *Barrel) Shell(ctx context.Context, io stream.IOStream, argv ...string) func() int {
|
||||
return barrel.Stack().Exec(ctx, io, "barrel", "/bin/sh", append([]string{"/user_shell.sh"}, argv...)...)
|
||||
type ExitError int
|
||||
|
||||
func (ee ExitError) Error() string {
|
||||
return fmt.Sprintf("Exited with code %d", int(ee))
|
||||
}
|
||||
|
||||
func (ee ExitError) Code() exit.ExitCode {
|
||||
return exit.ExitCode(ee)
|
||||
}
|
||||
|
||||
// Shell executes a shell with the given command line arguments inside the container.
|
||||
// If an error occurs, it is of type ExitError.
|
||||
func (barrel *Barrel) Shell(ctx context.Context, io stream.IOStream, argv ...string) error {
|
||||
code := barrel.Stack().Exec(ctx, io, "barrel", "/bin/sh", append([]string{"/user_shell.sh"}, argv...)...)()
|
||||
if code != 0 {
|
||||
return ExitError(code)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellScript quotes the given command and executes it as a shell script inside the container.
|
||||
func (barrel *Barrel) ShellScript(ctx context.Context, io stream.IOStream, commands ...string) error {
|
||||
command := shellescape.QuoteCommand(commands)
|
||||
return barrel.Shell(ctx, io, "-c", command)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ func (barrel *Barrel) Stack() component.StackWithResources {
|
|||
"RUNTIME_DIR": barrel.Malt.Config.Paths.RuntimeDir(),
|
||||
|
||||
"BARREL_BASE_IMAGE": barrel.GetDockerBaseImage(),
|
||||
"OPCACHE_MODE": barrel.OpCacheMode(),
|
||||
},
|
||||
|
||||
MakeDirs: []string{"data", ".composer"},
|
||||
|
|
|
|||
37
internal/wisski/ingredient/barrel/system/system.go
Normal file
37
internal/wisski/ingredient/barrel/system/system.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/bookkeeping"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras"
|
||||
)
|
||||
|
||||
// SystemManager applies a specific system configuration
|
||||
type SystemManager struct {
|
||||
ingredient.Base
|
||||
Dependencies struct {
|
||||
Barrel *barrel.Barrel
|
||||
Bookkeeping *bookkeeping.Bookkeeping
|
||||
Settings *extras.Settings
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies a specific system version to this barrel.
|
||||
// If start is true, also starts the container.
|
||||
func (smanager *SystemManager) Apply(ctx context.Context, progress io.Writer, system models.System, start bool) (err error) {
|
||||
// setup the new docker image
|
||||
smanager.Instance.System = system
|
||||
|
||||
// save in bookkeeping
|
||||
if err := smanager.Dependencies.Bookkeeping.Save(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// and rebuild
|
||||
return smanager.Dependencies.Barrel.Build(ctx, progress, start)
|
||||
}
|
||||
41
internal/wisski/ingredient/php/extras/adapters.go
Normal file
41
internal/wisski/ingredient/php/extras/adapters.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package extras
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// Prefixes implements reading and writing prefix
|
||||
type Adapters struct {
|
||||
ingredient.Base
|
||||
Dependencies struct {
|
||||
PHP *php.PHP
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed adapters.php
|
||||
var adaptersPHP string
|
||||
|
||||
type DistilleryAdapter struct {
|
||||
Label string
|
||||
MachineName string
|
||||
Description string
|
||||
InstanceDomain string
|
||||
|
||||
GraphDBRepository string
|
||||
GraphDBUsername string
|
||||
GraphDBPassword string
|
||||
}
|
||||
|
||||
func (wisski *Adapters) CreateDistilleryAdapter(ctx context.Context, server *phpx.Server, adapter DistilleryAdapter) error {
|
||||
return wisski.Dependencies.PHP.ExecScript(
|
||||
ctx, server, nil, adaptersPHP,
|
||||
"create_distillery_adapter",
|
||||
adapter.Label, adapter.MachineName, adapter.Description, adapter.InstanceDomain, adapter.GraphDBRepository, adapter.GraphDBUsername, adapter.GraphDBPassword,
|
||||
)
|
||||
}
|
||||
57
internal/wisski/ingredient/php/extras/adapters.php
Normal file
57
internal/wisski/ingredient/php/extras/adapters.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Creates an adapter for the distillery
|
||||
*/
|
||||
function create_distillery_adapter(string $LABEL, string $MACHINE_NAME, string $DESCRIPTION, string $INSTANCE_DOMAIN, string $GRAPHDB_REPO, string $GRAPHDB_USER, string $GRAPHDB_PASSWORD) {
|
||||
|
||||
//
|
||||
// PROPERTIES FOR THE ADAPTER
|
||||
//
|
||||
$id = 'default'; // id
|
||||
$type = 'sparql11_with_pb'; // plugin
|
||||
$machine_name = $MACHINE_NAME; // machine-name
|
||||
$label = $LABEL;
|
||||
$description = $DESCRIPTION; // description
|
||||
$writable = TRUE; // writable
|
||||
$is_preferred_local_store = TRUE; // is_preferred_local_store
|
||||
$read_url = 'http://triplestore:7200/repositories/' . $GRAPHDB_REPO; // read_url
|
||||
$write_url = 'http://triplestore:7200/repositories/' . $GRAPHDB_REPO . '/statements'; // write_url
|
||||
$is_federatable = TRUE; // is_federatable
|
||||
$default_graph_uri = 'https://' . $INSTANCE_DOMAIN . '/';
|
||||
$same_as_properties = ['http://www.w3.org/2002/07/owl#sameAs']; // same_as_properties
|
||||
$ontology_graphs = []; // ontology_graphs
|
||||
|
||||
// header
|
||||
$header = "";
|
||||
if ($GRAPHDB_USER !== "" && $GRAPHDB_PASSWORD !== "") {
|
||||
$header = $GRAPHDB_USER . ":" . $GRAPHDB_PASSWORD;
|
||||
$header = base64_encode($header);
|
||||
}
|
||||
|
||||
//
|
||||
// Do the creation!
|
||||
//
|
||||
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('wisski_salz_adapter');
|
||||
$adapter = $storage->create([
|
||||
"id" => $id,
|
||||
"label" => $label,
|
||||
"description" => $description,
|
||||
]);
|
||||
$adapter->setEngineConfig([
|
||||
"id" => $type,
|
||||
"machine-name" => $machine_name,
|
||||
"header" => $header,
|
||||
"writeable" => $writable,
|
||||
"is_preferred_local_store" => $is_preferred_local_store,
|
||||
"read_url" => $read_url,
|
||||
"write_url" => $write_url,
|
||||
"is_federatable" => $is_federatable,
|
||||
"default_graph" => $default_graph_uri,
|
||||
"same_as_properties" => $same_as_properties,
|
||||
"ontology_graphs" => $ontology_graphs,
|
||||
]);
|
||||
$adapter->save();
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package extras
|
|||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
|
|
@ -24,6 +25,25 @@ func (settings *Settings) Get(ctx context.Context, server *phpx.Server, key stri
|
|||
return
|
||||
}
|
||||
|
||||
var errFailedToSetSetting = errors.New("failed to update setting")
|
||||
|
||||
func (settings *Settings) Set(ctx context.Context, server *phpx.Server, key string, value any) error {
|
||||
return settings.Dependencies.PHP.ExecScript(ctx, server, nil, settingsPHP, "set_setting", key, value)
|
||||
var ok bool
|
||||
err := settings.Dependencies.PHP.ExecScript(ctx, server, &ok, settingsPHP, "set_setting", key, value)
|
||||
if err == nil && !ok {
|
||||
err = errFailedToSetSetting
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var errFailedToSetTrustedDomain = errors.New("failed to set trusted domain")
|
||||
|
||||
func (settings *Settings) SetTrustedDomain(ctx context.Context, server *phpx.Server, domain string) error {
|
||||
var ok bool
|
||||
|
||||
err := settings.Dependencies.PHP.ExecScript(ctx, server, &ok, settingsPHP, "set_trusted_domain", domain)
|
||||
if err == nil && !ok {
|
||||
err = errFailedToSetTrustedDomain
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,28 @@
|
|||
<?php
|
||||
|
||||
use \Drupal\Core\Site\Settings;
|
||||
|
||||
/** gets a setting from 'settings.php' */
|
||||
function get_setting($name) {
|
||||
use \Drupal\Core\Site\Settings;
|
||||
return Settings::get($name);
|
||||
}
|
||||
|
||||
/** sets a setting in 'settings.php' */
|
||||
function set_setting($name, $value) {
|
||||
function set_setting(string $name, mixed $value): bool {
|
||||
// find settings.php
|
||||
$filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php";
|
||||
|
||||
// setup user write permissions for the file
|
||||
$old = fileperms($filename);
|
||||
if ($old === FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$new = 0777; // set all permissions
|
||||
if (!chmod($filename, $new)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// load install.inc
|
||||
if(is_file(DRUPAL_ROOT . "/internal/")) {
|
||||
include_once DRUPAL_ROOT . "/internal/core/includes/install.inc";
|
||||
|
|
@ -21,9 +36,20 @@ function set_setting($name, $value) {
|
|||
"required" => TRUE,
|
||||
];
|
||||
|
||||
// find the filename
|
||||
$filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php";
|
||||
drupal_rewrite_settings($settings, $filename);
|
||||
// do the rewrite
|
||||
try {
|
||||
drupal_rewrite_settings($settings, $filename);
|
||||
} catch(Throwable $t) {
|
||||
throw $t; // DEBUG
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return True;
|
||||
|
||||
// reset the file mode
|
||||
return chmod($filename, $old);
|
||||
}
|
||||
|
||||
/** Sets the trusted host to the specified domain */
|
||||
function set_trusted_domain(string $domain): bool {
|
||||
return set_setting("trusted_host_patterns", [preg_quote($domain)]);
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import (
|
|||
_ "embed"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/tkw1536/pkglib/stream"
|
||||
)
|
||||
|
||||
|
|
@ -21,6 +20,6 @@ func (php *PHP) NewServer() *phpx.Server {
|
|||
}
|
||||
|
||||
func (php *PHP) spawn(ctx context.Context, str stream.IOStream, code string) error {
|
||||
php.Dependencies.Barrel.Shell(ctx, str, "-c", shellescape.QuoteCommand([]string{"drush", "php:eval", code}))()
|
||||
php.Dependencies.Barrel.ShellScript(ctx, str, "drush", "php:eval", code)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue