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
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue