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
2
API.md
2
API.md
|
|
@ -25,6 +25,8 @@ All routes can be found under `/api/v1/http/`
|
||||||
|
|
||||||
## Interactive Websocket API
|
## Interactive Websocket API
|
||||||
|
|
||||||
|
** This is not yet implemented in it's entirety **
|
||||||
|
|
||||||
Some API calls require interactivity or provide streaming content to clients.
|
Some API calls require interactivity or provide streaming content to clients.
|
||||||
An example of such an action is creating a new instance.
|
An example of such an action is creating a new instance.
|
||||||
The protocol is based on [Websockets](https://websockets.spec.whatwg.org/).
|
The protocol is based on [Websockets](https://websockets.spec.whatwg.org/).
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
)
|
)
|
||||||
|
|
@ -13,6 +14,7 @@ var Provision wisski_distillery.Command = pv{}
|
||||||
|
|
||||||
type pv struct {
|
type pv struct {
|
||||||
PHPVersion string `short:"p" long:"php" description:"specific php version to use for instance. Should be one of '8.0', '8.1'."`
|
PHPVersion string `short:"p" long:"php" description:"specific php version to use for instance. Should be one of '8.0', '8.1'."`
|
||||||
|
OPCacheDevelopment bool `short:"o" long:"opcache-devel" description:"Include opcache development configuration"`
|
||||||
Positionals struct {
|
Positionals struct {
|
||||||
Slug string `positional-arg-name:"slug" required:"1-1" description:"slug of instance to create"`
|
Slug string `positional-arg-name:"slug" required:"1-1" description:"slug of instance to create"`
|
||||||
} `positional-args:"true"`
|
} `positional-args:"true"`
|
||||||
|
|
@ -36,9 +38,12 @@ var errProvisionGeneric = exit.Error{
|
||||||
// TODO: AfterParse to check instance!
|
// TODO: AfterParse to check instance!
|
||||||
|
|
||||||
func (p pv) Run(context wisski_distillery.Context) error {
|
func (p pv) Run(context wisski_distillery.Context) error {
|
||||||
instance, err := context.Environment.Provision().Provision(context.Stderr, context.Context, provision.ProvisionFlags{
|
instance, err := context.Environment.Provision().Provision(context.Stderr, context.Context, provision.Flags{
|
||||||
Slug: p.Positionals.Slug,
|
Slug: p.Positionals.Slug,
|
||||||
PHPVersion: p.PHPVersion,
|
System: models.System{
|
||||||
|
PHP: p.PHPVersion,
|
||||||
|
OpCacheDevelopment: p.OPCacheDevelopment,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errProvisionGeneric.WithMessageF(p.Positionals.Slug).Wrap(err)
|
return errProvisionGeneric.WithMessageF(p.Positionals.Slug).Wrap(err)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/pkglib/status"
|
"github.com/tkw1536/pkglib/status"
|
||||||
|
|
@ -15,8 +16,10 @@ import (
|
||||||
var Rebuild wisski_distillery.Command = rebuild{}
|
var Rebuild wisski_distillery.Command = rebuild{}
|
||||||
|
|
||||||
type rebuild struct {
|
type rebuild struct {
|
||||||
Parallel int `short:"p" long:"parallel" description:"run on (at most) this many instances in parallel. 0 for no limit." default:"1"`
|
Parallel int `short:"a" long:"parallel" description:"run on (at most) this many instances in parallel. 0 for no limit." default:"1"`
|
||||||
PHPVersion string `short:"u" long:"php" description:"update to specific php version to use for instance. Should be one of '8.0', '8.1'."`
|
|
||||||
|
PHPVersion string `short:"p" long:"php" description:"update to specific php version to use for instance. Should be one of '8.0', '8.1'."`
|
||||||
|
OPCacheDevelopment bool `short:"o" long:"opcache-devel" description:"Include opcache development configuration"`
|
||||||
Positionals struct {
|
Positionals struct {
|
||||||
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug of instance or instances to run rebuild"`
|
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug of instance or instances to run rebuild"`
|
||||||
} `positional-args:"true"`
|
} `positional-args:"true"`
|
||||||
|
|
@ -50,13 +53,10 @@ func (rb rebuild) Run(context wisski_distillery.Context) (err error) {
|
||||||
|
|
||||||
// and do the actual rebuild
|
// and do the actual rebuild
|
||||||
return status.WriterGroup(context.Stderr, rb.Parallel, func(instance *wisski.WissKI, writer io.Writer) error {
|
return status.WriterGroup(context.Stderr, rb.Parallel, func(instance *wisski.WissKI, writer io.Writer) error {
|
||||||
if rb.PHPVersion != "" {
|
return instance.SystemManager().Apply(context.Context, writer, models.System{
|
||||||
if err := instance.Provisioner().ApplyFlags(context.Context, writer, rb.PHPVersion); err != nil {
|
PHP: rb.PHPVersion,
|
||||||
return err
|
OpCacheDevelopment: rb.OPCacheDevelopment,
|
||||||
}
|
}, true)
|
||||||
}
|
|
||||||
|
|
||||||
return instance.Barrel().Build(context.Context, writer, true)
|
|
||||||
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("rebuild %q", item.Slug)
|
return fmt.Sprintf("rebuild %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/pkglib/fsx"
|
"github.com/tkw1536/pkglib/fsx"
|
||||||
|
|
@ -53,7 +54,7 @@ func (r reserve) Run(context wisski_distillery.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// make it in-memory
|
// make it in-memory
|
||||||
instance, err := dis.Instances().Create(slug, "")
|
instance, err := dis.Instances().Create(slug, models.System{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errProvisionGeneric.WithMessageF(slug, err)
|
return errProvisionGeneric.WithMessageF(slug, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
cmd/shell.go
11
cmd/shell.go
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/parser"
|
"github.com/tkw1536/goprogram/parser"
|
||||||
)
|
)
|
||||||
|
|
@ -43,12 +44,16 @@ func (sh shell) Run(context wisski_distillery.Context) error {
|
||||||
return errShellWissKI.Wrap(err)
|
return errShellWissKI.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
code := instance.Barrel().Shell(context.Context, context.IOStream, sh.Positionals.Args...)()
|
{
|
||||||
if code != 0 {
|
err := instance.Barrel().Shell(context.Context, context.IOStream, sh.Positionals.Args...)
|
||||||
|
if err != nil {
|
||||||
|
code := err.(barrel.ExitError).Code()
|
||||||
return exit.Error{
|
return exit.Error{
|
||||||
ExitCode: exit.ExitCode(uint8(code)),
|
ExitCode: code,
|
||||||
Message: fmt.Sprintf("Exit code %d", code),
|
Message: fmt.Sprintf("Exit code %d", code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
// This file was generated automatically at 10-07-2023 22:16:29 using gogenlicense.
|
// This file was generated automatically at 13-07-2023 07:44:29 using gogenlicense.
|
||||||
// Do not edit manually, as changes may be overwritten.
|
// Do not edit manually, as changes may be overwritten.
|
||||||
// ===========================================================================================================
|
// ===========================================================================================================
|
||||||
|
|
||||||
|
|
@ -4913,7 +4913,7 @@ package cli
|
||||||
// # Generation
|
// # Generation
|
||||||
//
|
//
|
||||||
// This variable and the associated documentation have been automatically generated using the 'gogenlicense' tool.
|
// This variable and the associated documentation have been automatically generated using the 'gogenlicense' tool.
|
||||||
// It was last updated at 10-07-2023 22:16:29.
|
// It was last updated at 13-07-2023 07:44:29.
|
||||||
var LegalNotices string
|
var LegalNotices string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ var (
|
||||||
// It validates that slug is a valid name for an instance.
|
// It validates that slug is a valid name for an instance.
|
||||||
//
|
//
|
||||||
// It does not perform any checks if the instance already exists, or does the creation in the database.
|
// It does not perform any checks if the instance already exists, or does the creation in the database.
|
||||||
func (instances *Instances) Create(slug string, phpversion string) (wissKI *wisski.WissKI, err error) {
|
func (instances *Instances) Create(slug string, system models.System) (wissKI *wisski.WissKI, err error) {
|
||||||
|
|
||||||
// make sure that the slug is valid!
|
// make sure that the slug is valid!
|
||||||
slug, err = instances.IsValidSlug(slug)
|
slug, err = instances.IsValidSlug(slug)
|
||||||
|
|
@ -66,7 +66,7 @@ func (instances *Instances) Create(slug string, phpversion string) (wissKI *wiss
|
||||||
}
|
}
|
||||||
|
|
||||||
// docker image
|
// docker image
|
||||||
wissKI.Liquid.Instance.DockerBaseImage, err = models.GetBaseImage(phpversion)
|
wissKI.Liquid.Instance.System = system
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
)
|
)
|
||||||
|
|
@ -37,7 +38,7 @@ func (purger *Purger) Purge(ctx context.Context, out io.Writer, slug string) err
|
||||||
instance, err := purger.Dependencies.Instances.WissKI(ctx, slug)
|
instance, err := purger.Dependencies.Instances.WissKI(ctx, slug)
|
||||||
if err == instances.ErrWissKINotFound {
|
if err == instances.ErrWissKINotFound {
|
||||||
fmt.Fprintln(out, "Not found in bookkeeping table, assuming defaults")
|
fmt.Fprintln(out, "Not found in bookkeeping table, assuming defaults")
|
||||||
instance, err = purger.Dependencies.Instances.Create(slug, "")
|
instance, err = purger.Dependencies.Instances.Create(slug, models.System{})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errPurgeNoDetails.WithMessageF(err)
|
return errPurgeNoDetails.WithMessageF(err)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This utility script can be used to run all cron tasks.
|
|
||||||
|
|
||||||
cd /var/www/data/project || exit 1
|
|
||||||
export PATH=/var/www/data/project/vendor/bin:$PATH
|
|
||||||
|
|
||||||
drush core-cron
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script can be used to repatch EasyRDF when needed.
|
|
||||||
cd /var/www/data/project/web/modules/contrib/wisski || exit 1
|
|
||||||
EASYRDF_RESPONSE="./vendor/easyrdf/easyrdf/lib/EasyRdf/Http/Response.php"
|
|
||||||
patch -N "$EASYRDF_RESPONSE" < "/patch/easyrdf.patch"
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script can be used to repatch EasyRDF when needed.
|
|
||||||
cd /var/www/data/project/web/modules/contrib/wisski/ || exit 1
|
|
||||||
TRIPLESTABCONTROLLER="./wisski_adapter_sparql11_pb/src/Controller/Sparql11TriplesTabController.php"
|
|
||||||
patch -N "$TRIPLESTABCONTROLLER" < "/patch/triples.patch"
|
|
||||||
|
|
@ -18,7 +18,6 @@ popd || exit 1
|
||||||
|
|
||||||
# run the update and clear the cache!
|
# run the update and clear the cache!
|
||||||
drush updatedb --yes
|
drush updatedb --yes
|
||||||
# drush cc
|
|
||||||
|
|
||||||
# and reset everything back to normal
|
# and reset everything back to normal
|
||||||
chmod 755 web/sites/default
|
chmod 755 web/sites/default
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,14 @@ package provision
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/manager"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/tkw1536/pkglib/fsx"
|
"github.com/tkw1536/pkglib/fsx"
|
||||||
)
|
)
|
||||||
|
|
@ -21,39 +23,46 @@ type Provision struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvisionFlags are flags for a new instance
|
// Flags are flags for a new instance.
|
||||||
type ProvisionFlags struct {
|
type Flags struct {
|
||||||
|
// NOTE(twiesing): Any changes here should be reflected in instance_provision.html and remote/api.ts.
|
||||||
|
|
||||||
// Slug is the slug of the wisski instance
|
// Slug is the slug of the wisski instance
|
||||||
Slug string
|
Slug string
|
||||||
|
|
||||||
// PHP Version to use
|
// System is information about the system
|
||||||
PHPVersion string
|
System models.System
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profile returns the profile belonging to this provision flags.
|
||||||
|
func (flags Flags) Profile() manager.Profile {
|
||||||
|
// TODO: Actually do something here
|
||||||
|
return manager.Profile{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrInstanceAlreadyExists = errors.New("instance with provided slug already exists")
|
var ErrInstanceAlreadyExists = errors.New("instance with provided slug already exists")
|
||||||
|
|
||||||
func (pv *Provision) ValidateFlags(flags ProvisionFlags) error {
|
func (pv *Provision) Validate(flags Flags) error {
|
||||||
// check the slug
|
// check the slug
|
||||||
if _, err := pv.Dependencies.Instances.IsValidSlug(flags.Slug); err != nil {
|
if _, err := pv.Dependencies.Instances.IsValidSlug(flags.Slug); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// check for known php versions
|
|
||||||
if _, err := models.GetBaseImage(flags.PHPVersion); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision provisions a new docker compose instance.
|
// Provision provisions a new docker compose instance.
|
||||||
func (pv *Provision) Provision(progress io.Writer, ctx context.Context, flags ProvisionFlags) (*wisski.WissKI, error) {
|
func (pv *Provision) Provision(progress io.Writer, ctx context.Context, flags Flags) (*wisski.WissKI, error) {
|
||||||
// check that it doesn't already exist
|
// check that it doesn't already exist
|
||||||
logging.LogMessage(progress, "Provisioning new WissKI instance %s", flags.Slug)
|
logging.LogMessage(progress, "Provisioning new WissKI instance %s", flags.Slug)
|
||||||
if exists, err := pv.Dependencies.Instances.Has(ctx, flags.Slug); err != nil || exists {
|
if exists, err := pv.Dependencies.Instances.Has(ctx, flags.Slug); err != nil || exists {
|
||||||
return nil, ErrInstanceAlreadyExists
|
return nil, ErrInstanceAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log out what we're doing!
|
||||||
|
fmt.Fprintf(progress, "%#v", flags)
|
||||||
|
|
||||||
// make it in-memory
|
// make it in-memory
|
||||||
instance, err := pv.Dependencies.Instances.Create(flags.Slug, flags.PHPVersion)
|
instance, err := pv.Dependencies.Instances.Create(flags.Slug, flags.System)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +108,7 @@ func (pv *Provision) Provision(progress io.Writer, ctx context.Context, flags Pr
|
||||||
|
|
||||||
// run the provision script
|
// run the provision script
|
||||||
if err := logging.LogOperation(func() error {
|
if err := logging.LogOperation(func() error {
|
||||||
return instance.Provisioner().Provision(ctx, progress)
|
return instance.Manager().Provision(ctx, progress, flags.System, flags.Profile())
|
||||||
}, progress, "Running setup scripts"); err != nil {
|
}, progress, "Running setup scripts"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ var (
|
||||||
|
|
||||||
menuInstances = component.MenuItem{Title: "Instances", Path: "/admin/instances/"}
|
menuInstances = component.MenuItem{Title: "Instances", Path: "/admin/instances/"}
|
||||||
menuInstance = component.DummyMenuItem()
|
menuInstance = component.DummyMenuItem()
|
||||||
|
menuRebuild = component.DummyMenuItem()
|
||||||
menuGrants = component.DummyMenuItem()
|
menuGrants = component.DummyMenuItem()
|
||||||
menuIngredients = component.DummyMenuItem()
|
menuIngredients = component.DummyMenuItem()
|
||||||
)
|
)
|
||||||
|
|
@ -146,6 +147,11 @@ func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http
|
||||||
router.Handler(http.MethodGet, route+"instance/:slug", instance)
|
router.Handler(http.MethodGet, route+"instance/:slug", instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
rebuild := admin.instanceRebuild(ctx)
|
||||||
|
router.Handler(http.MethodGet, route+"rebuild/:slug", rebuild)
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
grants := admin.grants(ctx)
|
grants := admin.grants(ctx)
|
||||||
router.Handler(http.MethodGet, route+"grants/:slug", grants)
|
router.Handler(http.MethodGet, route+"grants/:slug", grants)
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (funcs
|
||||||
// replace the functions
|
// replace the functions
|
||||||
funcs = []templating.FlagFunc{
|
funcs = []templating.FlagFunc{
|
||||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}),
|
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}),
|
||||||
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")}),
|
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)}),
|
||||||
templating.Title(gc.Instance.Slug + " - Grants"),
|
templating.Title(gc.Instance.Slug + " - Grants"),
|
||||||
}
|
}
|
||||||
return funcs, nil
|
return funcs, nil
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,48 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-xl-2-5">
|
||||||
|
<div class="padding">
|
||||||
|
<div class="overflow">
|
||||||
|
<table class="pure-table pure-table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">
|
||||||
|
System
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
PHP Version
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.System.PHP }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Docker Base Image
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.System.GetDockerBaseImage }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
OPCache Development Config
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<code>{{ .Instance.System.OpCacheDevelopment }}</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1 pure-u-xl-2-5">
|
<div class="pure-u-1 pure-u-xl-2-5">
|
||||||
<div class="padding">
|
<div class="padding">
|
||||||
<div class="overflow">
|
<div class="overflow">
|
||||||
|
|
@ -182,7 +224,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Last Rebuild <br>
|
Last Rebuild <br>
|
||||||
<button class="remote-action pure-button pure-button-action" data-action="rebuild" data-param="{{ .Instance.Slug }}" data-buffer="1000" data-force-reload>Rebuild</button>
|
<a class="pure-button" href="/admin/rebuild/{{ .Info.Slug }}">Rebuild</button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code class="date">{{ .Info.LastRebuild.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
<code class="date">{{ .Info.LastRebuild.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,38 @@
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<form class="pure-form pure-form-aligned" id="provision">
|
<form class="pure-form pure-form-aligned" id="provision">
|
||||||
<fieldset disabled="disabled">
|
<fieldset>
|
||||||
|
<legend>Main Parameters</legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="slug">Slug</label>
|
<label for="slug">Slug</label>
|
||||||
<input name="slug" id="slug" placeholder="" autocomplete="slug">
|
<input name="slug" id="slug" placeholder="" autocomplete="slug">
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" value="Provision" class="pure-button">
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>System Parameters</legend>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="php">PHP Version</label>
|
||||||
|
<select id="php">
|
||||||
|
<option value="" selected>Default ({{ .DefaultPHPVersion }})</option>
|
||||||
|
{{ range .PHPVersions }}
|
||||||
|
<option value="{{ . }}">{{ . }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label for="opcacheDevelopment" class="pure-checkbox">
|
||||||
|
<input type="checkbox" id="opcacheDevelopment" />
|
||||||
|
Opache Development Configuration
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Profile</legend>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
Not yet available
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<input type="submit" value="Provision" class="pure-button">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<div class="pure-u-1-1">
|
||||||
|
<form class="pure-form pure-form-aligned" id="provision">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Main Parameters</legend>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="slug">Slug</label>
|
||||||
|
<input name="slug" id="slug" placeholder="" autocomplete="slug" readonly="readonly" value="{{ .Slug }}">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>System Parameters</legend>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
<label for="php">PHP Version</label>
|
||||||
|
<select id="php">
|
||||||
|
{{ $PHP := .System.PHP }}
|
||||||
|
<option {{ if eq $PHP "" }}selected{{ end }}>Default ({{ .DefaultPHPVersion }})</option>
|
||||||
|
{{ range .PHPVersions }}
|
||||||
|
<option {{ if eq $PHP . }}selected{{ end }} value="{{ . }}">{{ . }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label for="opcacheDevelopment" class="pure-checkbox">
|
||||||
|
<input {{ if .System.OpCacheDevelopment }}checked{{end}} type="checkbox" id="opcacheDevelopment" check/>
|
||||||
|
Opache Development Configuration
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Profile</legend>
|
||||||
|
|
||||||
|
<div class="pure-control-group">
|
||||||
|
Not yet available
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<input type="submit" value="Rebuild" class="pure-button">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
@ -22,7 +23,18 @@ var instanceProvisionTemplate = templating.Parse[instanceProvisionContext](
|
||||||
type instanceProvisionContext struct {
|
type instanceProvisionContext struct {
|
||||||
templating.RuntimeFlags
|
templating.RuntimeFlags
|
||||||
|
|
||||||
// nothing for the moment
|
systemParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type systemParams struct {
|
||||||
|
PHPVersions []string
|
||||||
|
DefaultPHPVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSystemParams() (sp systemParams) {
|
||||||
|
sp.PHPVersions = models.KnownPHPVersions()
|
||||||
|
sp.DefaultPHPVersion = models.DefaultPHPVersion
|
||||||
|
return sp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
||||||
|
|
@ -37,6 +49,7 @@ func (admin *Admin) instanceProvision(ctx context.Context) http.Handler {
|
||||||
)
|
)
|
||||||
|
|
||||||
return tpl.HTMLHandler(func(r *http.Request) (ipc instanceProvisionContext, err error) {
|
return tpl.HTMLHandler(func(r *http.Request) (ipc instanceProvisionContext, err error) {
|
||||||
|
ipc.systemParams = newSystemParams()
|
||||||
return ipc, nil
|
return ipc, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
74
internal/dis/component/server/admin/instance_rebuild.go
Normal file
74
internal/dis/component/server/admin/instance_rebuild.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/tkw1536/pkglib/httpx"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed "html/instance_rebuild.html"
|
||||||
|
var instanceRebuildHTML []byte
|
||||||
|
var instanceRebuildTemplate = templating.Parse[instanceRebuildContext](
|
||||||
|
"instance_rebuild.html", instanceRebuildHTML, nil,
|
||||||
|
|
||||||
|
templating.Title("Rebuild Instance"),
|
||||||
|
templating.Assets(assets.AssetsAdminRebuild),
|
||||||
|
)
|
||||||
|
|
||||||
|
type instanceRebuildContext struct {
|
||||||
|
templating.RuntimeFlags
|
||||||
|
|
||||||
|
Slug string
|
||||||
|
System models.System
|
||||||
|
|
||||||
|
systemParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (admin *Admin) instanceRebuild(ctx context.Context) http.Handler {
|
||||||
|
tpl := instanceRebuildTemplate.Prepare(
|
||||||
|
admin.Dependencies.Templating,
|
||||||
|
templating.Crumbs(
|
||||||
|
menuAdmin,
|
||||||
|
menuInstances,
|
||||||
|
menuInstance,
|
||||||
|
menuRebuild,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ib instanceRebuildContext, funcs []templating.FlagFunc, err error) {
|
||||||
|
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||||
|
|
||||||
|
var instance *wisski.WissKI
|
||||||
|
instance, err = admin.Dependencies.Instances.WissKI(r.Context(), slug)
|
||||||
|
if err == instances.ErrWissKINotFound {
|
||||||
|
return ib, nil, httpx.ErrNotFound
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ib, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ib.Slug = instance.Slug
|
||||||
|
ib.System = instance.System
|
||||||
|
|
||||||
|
// replace the menu item
|
||||||
|
funcs = []templating.FlagFunc{
|
||||||
|
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + instance.Slug)}),
|
||||||
|
templating.ReplaceCrumb(menuRebuild, component.MenuItem{Title: "Rebuild", Path: template.URL("/admin/rebuild/" + instance.Slug)}),
|
||||||
|
templating.Title(instance.Slug + " - Rebuild"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ib.systemParams = newSystemParams()
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -28,7 +29,7 @@ func (sockets *Sockets) Actions() ActionMap {
|
||||||
}),
|
}),
|
||||||
"provision": sockets.Generic(1, func(ctx context.Context, sockets *Sockets, in io.Reader, out io.Writer, params ...string) error {
|
"provision": sockets.Generic(1, func(ctx context.Context, sockets *Sockets, in io.Reader, out io.Writer, params ...string) error {
|
||||||
// read the flags of the instance to be provisioned
|
// read the flags of the instance to be provisioned
|
||||||
var flags provision.ProvisionFlags
|
var flags provision.Flags
|
||||||
if err := json.Unmarshal([]byte(params[0]), &flags); err != nil {
|
if err := json.Unmarshal([]byte(params[0]), &flags); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -63,8 +64,13 @@ func (sockets *Sockets) Actions() ActionMap {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
"rebuild": sockets.Instance(0, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
"rebuild": sockets.Instance(1, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
||||||
return instance.Barrel().Build(ctx, out, true)
|
// read the flags of the instance to be provisioned
|
||||||
|
var system models.System
|
||||||
|
if err := json.Unmarshal([]byte(params[0]), &system); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return instance.SystemManager().Apply(ctx, out, system, true)
|
||||||
}),
|
}),
|
||||||
"update": sockets.Instance(0, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
"update": sockets.Instance(0, func(ctx context.Context, _ *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error {
|
||||||
return instance.Drush().Update(ctx, out)
|
return instance.Drush().Update(ctx, out)
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,4 @@ type Assets struct {
|
||||||
Styles template.HTML // <link> tags inserted by the asset
|
Styles template.HTML // <link> tags inserted by the asset
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate node build.mjs Default User Admin AdminProvision
|
//go:generate node build.mjs Default User Admin AdminProvision AdminRebuild
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,18 @@ var AssetsUser = Assets{
|
||||||
|
|
||||||
// AssetsAdmin contains assets for the 'Admin' entrypoint.
|
// AssetsAdmin contains assets for the 'Admin' entrypoint.
|
||||||
var AssetsAdmin = Assets{
|
var AssetsAdmin = Assets{
|
||||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/Admin.205f0180.js"></script><script src="/⛰/Admin.59fb2e50.js" nomodule="" defer></script>`,
|
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script src="/⛰/Admin.6daf9fdd.js" nomodule="" defer></script>`,
|
||||||
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css">`,
|
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css">`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetsAdminProvision contains assets for the 'AdminProvision' entrypoint.
|
// AssetsAdminProvision contains assets for the 'AdminProvision' entrypoint.
|
||||||
var AssetsAdminProvision = Assets{
|
var AssetsAdminProvision = Assets{
|
||||||
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.59fb2e50.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.205f0180.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminProvision.3cf9e19e.js"></script><script src="/⛰/AdminProvision.d195fd59.js" nomodule="" defer></script>`,
|
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.6daf9fdd.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminProvision.b7679968.js"></script><script src="/⛰/AdminProvision.12a47f22.js" nomodule="" defer></script>`,
|
||||||
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css"><link rel="stylesheet" href="/⛰/AdminProvision.38d394c2.css">`,
|
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css"><link rel="stylesheet" href="/⛰/AdminProvision.38d394c2.css">`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssetsAdminRebuild contains assets for the 'AdminRebuild' entrypoint.
|
||||||
|
var AssetsAdminRebuild = Assets{
|
||||||
|
Scripts: `<script nomodule="" defer src="/⛰/User.924f7900.js"></script><script nomodule="" defer src="/⛰/Admin.6daf9fdd.js"></script><script type="module" src="/⛰/User.47a3b7e3.js"></script><script type="module" src="/⛰/Admin.ad1b495b.js"></script><script type="module" src="/⛰/Default.38d394c2.js"></script><script src="/⛰/Default.38d394c2.js" nomodule="" defer></script><script type="module" src="/⛰/AdminRebuild.330247d9.js"></script><script src="/⛰/AdminRebuild.527a9616.js" nomodule="" defer></script>`,
|
||||||
|
Styles: `<link rel="stylesheet" href="/⛰/Default.938b4407.css"><link rel="stylesheet" href="/⛰/Admin.a1e05c23.css"><link rel="stylesheet" href="/⛰/User.840de3b4.css"><link rel="stylesheet" href="/⛰/User.68febbf8.css"><link rel="stylesheet" href="/⛰/Admin.d79c7b30.css"><link rel="stylesheet" href="/⛰/AdminRebuild.38d394c2.css">`,
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/Admin.6daf9fdd.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/Admin.6daf9fdd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/Admin.ad1b495b.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/Admin.ad1b495b.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
internal/dis/component/server/assets/dist/AdminProvision.12a47f22.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminProvision.12a47f22.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var r={id:e,exports:{}};return n[e]=r,t.call(r.exports,r,r.exports),r.exports}var l=new Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("dK5Bi");var r,l=t("8vh0V");async function i(e){return new Promise(((n,o)=>{(0,l.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(t,r)=>{t?n(e.Slug):o(new Error(null!=r?r:"unspecified error"))}})}))}const d=document.getElementById("provision"),a=document.getElementById("slug"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment");d.addEventListener("submit",(e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:u.value,OpCacheDevelopment:c.checked}}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),null===(r=d.querySelector("fieldset"))||void 0===r||r.removeAttribute("disabled")}();
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var r={id:e,exports:{}};return n[e]=r,t.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("8xGhL");var r=t("12vpF");const i=document.getElementById("provision"),l=document.getElementById("slug");i.addEventListener("submit",(e=>{e.preventDefault();const n={Slug:l.value};(0,r.createModal)("provision",[JSON.stringify(n)],{bufferSize:0,onClose:e=>{e?location.href="/admin/instance/"+n.Slug:location.reload()}})})),i.querySelector("fieldset")?.removeAttribute("disabled");
|
|
||||||
1
internal/dis/component/server/assets/dist/AdminProvision.b7679968.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminProvision.b7679968.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function i(e){return new Promise(((n,t)=>{(0,r.createModal)("provision",[JSON.stringify(e)],{bufferSize:0,onClose:(o,r)=>{o?n(e.Slug):t(new Error(r??"unspecified error"))}})}))}const l=document.getElementById("provision"),a=document.getElementById("slug"),d=document.getElementById("php"),u=document.getElementById("opcacheDevelopment");l.addEventListener("submit",(e=>{e.preventDefault(),i({Slug:a.value,System:{PHP:d.value,OpCacheDevelopment:u.checked}}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),l.querySelector("fieldset")?.removeAttribute("disabled");
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},o={},t=e.parcelRequireafa4;null==t&&((t=function(e){if(e in n)return n[e].exports;if(e in o){var t=o[e];delete o[e];var i={id:e,exports:{}};return n[e]=i,t.call(i.exports,i,i.exports),i.exports}var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}).register=function(e,n){o[e]=n},e.parcelRequireafa4=t),t("dK5Bi");var i,r=t("8vh0V");const l=document.getElementById("provision"),d=document.getElementById("slug");l.addEventListener("submit",(e=>{e.preventDefault();const n={Slug:d.value};(0,r.createModal)("provision",[JSON.stringify(n)],{bufferSize:0,onClose:e=>{e?location.href="/admin/instance/"+n.Slug:location.reload()}})})),null===(i=l.querySelector("fieldset"))||void 0===i||i.removeAttribute("disabled")}();
|
|
||||||
1
internal/dis/component/server/assets/dist/AdminRebuild.330247d9.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.330247d9.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var i=new Error("Cannot find module '"+e+"'");throw i.code="MODULE_NOT_FOUND",i}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("8xGhL");var r=o("12vpF");async function i(e,n){return new Promise(((t,o)=>{(0,r.createModal)("rebuild",[e,JSON.stringify(n)],{bufferSize:0,onClose:(n,r)=>{n?t(e):o(new Error(r??"unspecified error"))}})}))}const l=document.getElementById("slug"),d=document.getElementById("provision"),a=document.getElementById("php"),u=document.getElementById("opcacheDevelopment");d.addEventListener("submit",(e=>{e.preventDefault(),i(l.value,{PHP:a.value,OpCacheDevelopment:u.checked}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),d.querySelector("fieldset")?.removeAttribute("disabled");
|
||||||
0
internal/dis/component/server/assets/dist/AdminRebuild.38d394c2.css
vendored
Normal file
0
internal/dis/component/server/assets/dist/AdminRebuild.38d394c2.css
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.527a9616.js
vendored
Normal file
1
internal/dis/component/server/assets/dist/AdminRebuild.527a9616.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},n={},t={},o=e.parcelRequireafa4;null==o&&((o=function(e){if(e in n)return n[e].exports;if(e in t){var o=t[e];delete t[e];var r={id:e,exports:{}};return n[e]=r,o.call(r.exports,r,r.exports),r.exports}var l=new Error("Cannot find module '"+e+"'");throw l.code="MODULE_NOT_FOUND",l}).register=function(e,n){t[e]=n},e.parcelRequireafa4=o),o("dK5Bi");var r,l=o("8vh0V");async function i(e,n){return new Promise(((t,o)=>{(0,l.createModal)("rebuild",[e,JSON.stringify(n)],{bufferSize:0,onClose:(n,r)=>{n?t(e):o(new Error(null!=r?r:"unspecified error"))}})}))}const d=document.getElementById("slug"),a=document.getElementById("provision"),u=document.getElementById("php"),c=document.getElementById("opcacheDevelopment");a.addEventListener("submit",(e=>{e.preventDefault(),i(d.value,{PHP:u.value,OpCacheDevelopment:c.checked}).then((e=>{location.href="/admin/instance/"+e})).catch((e=>{console.error(e),location.reload()}))})),null===(r=a.querySelector("fieldset"))||void 0===r||r.removeAttribute("disabled")}();
|
||||||
|
|
@ -1,30 +1,22 @@
|
||||||
import "../Admin/index.ts"
|
import "../Admin/index.ts"
|
||||||
import "../Admin/index.css"
|
import "../Admin/index.css"
|
||||||
|
|
||||||
import { createModal } from "~/src/lib/remote"
|
import { Provision } from "~/src/lib/remote/api"
|
||||||
|
|
||||||
const provision = document.getElementById("provision") as HTMLFormElement;
|
const provision = document.getElementById("provision") as HTMLFormElement;
|
||||||
const slug = document.getElementById("slug") as HTMLInputElement;
|
const slug = document.getElementById("slug") as HTMLInputElement;
|
||||||
|
const php = document.getElementById("php") as HTMLSelectElement;
|
||||||
|
const opcacheDevelopment = document.getElementById("opcacheDevelopment") as HTMLInputElement;
|
||||||
|
|
||||||
// add an event handler to open the modal form!
|
// add an event handler to open the modal form!
|
||||||
provision.addEventListener('submit', (evt) => {
|
provision.addEventListener('submit', (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
// flags used to create the server
|
Provision({ Slug: slug.value, System: { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked } })
|
||||||
const flags = { Slug: slug.value };
|
.then(slug => {
|
||||||
|
location.href = "/admin/instance/" + slug;
|
||||||
// open a modal to provision a new instance
|
|
||||||
createModal("provision", [JSON.stringify(flags)], {
|
|
||||||
bufferSize: 0,
|
|
||||||
onClose: (success: boolean) => {
|
|
||||||
if (success) {
|
|
||||||
location.href = "/admin/instance/" + flags.Slug
|
|
||||||
} else {
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
.catch((e) => {console.error(e); location.reload()});
|
||||||
})
|
})
|
||||||
|
|
||||||
// enable the form!
|
// enable the form!
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import "../Admin/index.ts"
|
||||||
|
import "../Admin/index.css"
|
||||||
|
|
||||||
|
import { Rebuild } from "~/src/lib/remote/api"
|
||||||
|
|
||||||
|
const slug = document.getElementById("slug") as HTMLInputElement
|
||||||
|
const provision = document.getElementById("provision") as HTMLFormElement;
|
||||||
|
const php = document.getElementById("php") as HTMLSelectElement;
|
||||||
|
const opcacheDevelopment = document.getElementById("opcacheDevelopment") as HTMLInputElement;
|
||||||
|
|
||||||
|
// add an event handler to open the modal form!
|
||||||
|
provision.addEventListener('submit', (evt) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
Rebuild(slug.value, { PHP: php.value, OpCacheDevelopment: opcacheDevelopment.checked })
|
||||||
|
.then(slug => {
|
||||||
|
location.href = "/admin/instance/" + slug;
|
||||||
|
})
|
||||||
|
.catch((e) => {console.error(e); location.reload()});
|
||||||
|
})
|
||||||
|
|
||||||
|
// enable the form!
|
||||||
|
provision.querySelector('fieldset')?.removeAttribute('disabled');
|
||||||
|
|
||||||
50
internal/dis/component/server/assets/src/lib/remote/api.ts
Normal file
50
internal/dis/component/server/assets/src/lib/remote/api.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { createModal } from "~/src/lib/remote"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags to provision a new system.
|
||||||
|
* Should mirror "provision".Flags.
|
||||||
|
*/
|
||||||
|
interface ProvisionFlags {
|
||||||
|
Slug: string
|
||||||
|
System: System
|
||||||
|
}
|
||||||
|
|
||||||
|
interface System {
|
||||||
|
PHP: string;
|
||||||
|
OpCacheDevelopment: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rebuild the specified instance */
|
||||||
|
export async function Rebuild(slug: string, system: System): Promise<string> {
|
||||||
|
return new Promise((rs, rj) => {
|
||||||
|
createModal("rebuild", [slug, JSON.stringify(system)], {
|
||||||
|
bufferSize: 0,
|
||||||
|
onClose: (success: boolean, message?: string) => {
|
||||||
|
if (!success) {
|
||||||
|
rj(new Error(message ?? "unspecified error"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs(slug);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provision provisions a new instance */
|
||||||
|
export async function Provision(flags: ProvisionFlags): Promise<string> {
|
||||||
|
// open a modal to provision a new instance
|
||||||
|
return new Promise((rs, rj) => {
|
||||||
|
createModal("provision", [JSON.stringify(flags)], {
|
||||||
|
bufferSize: 0,
|
||||||
|
onClose: (success: boolean, message?: string) => {
|
||||||
|
if (!success) {
|
||||||
|
rj(new Error(message ?? "unspecified error"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs(flags.Slug);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ export default function setup() {
|
||||||
|
|
||||||
type ModalOptions = {
|
type ModalOptions = {
|
||||||
bufferSize: number;
|
bufferSize: number;
|
||||||
onClose: (success: boolean) => void
|
onClose: (success: boolean, message?: string) => void
|
||||||
}
|
}
|
||||||
export function createModal(action: string, params: string[], opts: Partial<ModalOptions>) {
|
export function createModal(action: string, params: string[], opts: Partial<ModalOptions>) {
|
||||||
// create a modal dialog and append it to the body
|
// create a modal dialog and append it to the body
|
||||||
|
|
@ -123,14 +123,14 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
||||||
finishButton.className = "pure-button pure-button-success"
|
finishButton.className = "pure-button pure-button-success"
|
||||||
finishButton.append(typeof opts?.onClose === 'function' ? "Close & Finish" : "Close")
|
finishButton.append(typeof opts?.onClose === 'function' ? "Close & Finish" : "Close")
|
||||||
|
|
||||||
let result = {success: false, error: "unknown error"};
|
let result: ResultMessage = {success: false};
|
||||||
finishButton.addEventListener('click', (event) => {
|
finishButton.addEventListener('click', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (typeof opts?.onClose === 'function') {
|
if (typeof opts?.onClose === 'function') {
|
||||||
finishButton.setAttribute('disabled', 'disabled')
|
finishButton.setAttribute('disabled', 'disabled')
|
||||||
target.innerHTML = 'Finishing up ...'
|
target.innerHTML = 'Finishing up ...'
|
||||||
opts.onClose(result.success)
|
opts.onClose(result.success, result.message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +147,9 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
||||||
window.onbeforeunload = () => "A remote session is in progress. Are you sure you want to leave?";
|
window.onbeforeunload = () => "A remote session is in progress. Are you sure you want to leave?";
|
||||||
|
|
||||||
// when closing, add a button to the modal!
|
// when closing, add a button to the modal!
|
||||||
const close = (result: ResultMessage) => {
|
const close = (message: ResultMessage) => {
|
||||||
|
result = message
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
println('Process completed successfully. ', true);
|
println('Process completed successfully. ', true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -168,6 +170,7 @@ export function createModal(action: string, params: string[], opts: Partial<Moda
|
||||||
|
|
||||||
// connect to the socket and send the action
|
// connect to the socket and send the action
|
||||||
callServerAction(
|
callServerAction(
|
||||||
|
location.href.replace('http', 'ws'),
|
||||||
{
|
{
|
||||||
'name': action,
|
'name': action,
|
||||||
'params': params,
|
'params': params,
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ function isResultMessage(value: any): value is ResultMessage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a WebSocket connection and calls a server action
|
* Opens a WebSocket connection and calls a server action
|
||||||
|
* @param endpoint Endpoint to call
|
||||||
* @param call Function to call
|
* @param call Function to call
|
||||||
* @param onOpen callback for once the connection is opened. The send function can be used to send additional text to the server.
|
* @param onOpen callback for once the connection is opened. The send function can be used to send additional text to the server.
|
||||||
* @param onText called when the connection receives some text
|
* @param onText called when the connection receives some text
|
||||||
* @returns a promise that is resolved once the conneciton is closed. Rejected if the connection errors.
|
* @returns a promise that is resolved once the conneciton is closed. Rejected if the connection errors.
|
||||||
*/
|
*/
|
||||||
export default async function callServerAction(
|
export default async function callServerAction(
|
||||||
|
endpoint: string,
|
||||||
call: CallMessage,
|
call: CallMessage,
|
||||||
onOpen: (send: (text: string) => void, cancel: () => void) => void,
|
onOpen: (send: (text: string) => void, cancel: () => void) => void,
|
||||||
onText: (text: string) => void,
|
onText: (text: string) => void,
|
||||||
|
|
@ -24,7 +26,7 @@ export default async function callServerAction(
|
||||||
return new Promise((rs, rj) => {
|
return new Promise((rs, rj) => {
|
||||||
const mutex = new Mutex();
|
const mutex = new Mutex();
|
||||||
|
|
||||||
const socket = new WebSocket(location.href.replace('http', 'ws'));
|
const socket = new WebSocket(endpoint);
|
||||||
|
|
||||||
let result: ResultMessage;
|
let result: ResultMessage;
|
||||||
socket.onmessage = (msg) => {
|
socket.onmessage = (msg) => {
|
||||||
|
|
|
||||||
|
|
@ -88,18 +88,27 @@ func (ds Stack) Exec(ctx context.Context, io stream.IOStream, service, executabl
|
||||||
return ds.compose(ctx, io, compose...)
|
return ds.compose(ctx, io, compose...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RunFlags struct {
|
||||||
|
AutoRemove bool
|
||||||
|
Detach bool
|
||||||
|
}
|
||||||
|
|
||||||
// Run runs a command in a running container with the given executable.
|
// Run runs a command in a running container with the given executable.
|
||||||
// It is equivalent to 'docker compose run [--rm] $service $executable $args...'.
|
// It is equivalent to 'docker compose run [--rm] $service $executable $args...'.
|
||||||
//
|
//
|
||||||
// It returns the exit code of the process.
|
// It returns the exit code of the process.
|
||||||
func (ds Stack) Run(ctx context.Context, io stream.IOStream, autoRemove bool, service, command string, args ...string) (int, error) {
|
func (ds Stack) Run(ctx context.Context, io stream.IOStream, flags RunFlags, service, command string, args ...string) (int, error) {
|
||||||
compose := []string{"run"}
|
compose := []string{"run"}
|
||||||
if autoRemove {
|
if flags.AutoRemove {
|
||||||
compose = append(compose, "--rm")
|
compose = append(compose, "--rm")
|
||||||
}
|
}
|
||||||
if !io.StdinIsATerminal() {
|
if !io.StdinIsATerminal() {
|
||||||
compose = append(compose, "-T")
|
compose = append(compose, "--no-TTY")
|
||||||
}
|
}
|
||||||
|
if flags.Detach {
|
||||||
|
compose = append(compose, "--detach")
|
||||||
|
}
|
||||||
|
|
||||||
compose = append(compose, service, command)
|
compose = append(compose, service, command)
|
||||||
compose = append(compose, args...)
|
compose = append(compose, args...)
|
||||||
|
|
||||||
|
|
@ -131,6 +140,16 @@ func (ds Stack) Down(ctx context.Context, progress io.Writer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DownAll stops and removes all containers in this Stack, and those not defined in the compose file.
|
||||||
|
// It is equivalent to 'docker compose down -v --remove-orphans' on the shell.
|
||||||
|
func (ds Stack) DownAll(ctx context.Context, progress io.Writer) error {
|
||||||
|
code := ds.compose(ctx, stream.NonInteractive(progress), "down", "-v", "--remove-orphans")()
|
||||||
|
if code != 0 {
|
||||||
|
return errStackDown
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// compose executes a 'docker compose' command on this stack.
|
// compose executes a 'docker compose' command on this stack.
|
||||||
//
|
//
|
||||||
// NOTE(twiesing): Check if this can be replaced by an internal call to libcompose.
|
// NOTE(twiesing): Check if this can be replaced by an internal call to libcompose.
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,8 @@ type Instance struct {
|
||||||
// The filesystem path the system can be found under
|
// The filesystem path the system can be found under
|
||||||
FilesystemBase string `gorm:"column:filesystem_base;not null"`
|
FilesystemBase string `gorm:"column:filesystem_base;not null"`
|
||||||
|
|
||||||
// DockerBaseImage is the php base image to use
|
// information about the system being used
|
||||||
DockerBaseImage string `gorm:"column:docker_base;not_null"`
|
System `gorm:"embed"`
|
||||||
|
|
||||||
// SQL Database credentials for the system
|
// SQL Database credentials for the system
|
||||||
SqlDatabase string `gorm:"column:sql_database;not null"`
|
SqlDatabase string `gorm:"column:sql_database;not null"`
|
||||||
|
|
@ -48,38 +48,6 @@ type Instance struct {
|
||||||
GraphDBPassword string `gorm:"column:graphdb_password;not null"`
|
GraphDBPassword string `gorm:"column:graphdb_password;not null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Cleanup this stuff
|
|
||||||
const (
|
|
||||||
PHP_DEFAULT_IMAGE = PHP8_1_IMAGE
|
|
||||||
PHP8 = "8.0"
|
|
||||||
PHP8_IMAGE = "docker.io/library/php:8.0-apache-bullseye"
|
|
||||||
PHP8_1 = "8.1"
|
|
||||||
PHP8_1_IMAGE = "docker.io/library/php:8.1-apache-bullseye"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errUnknownPHPVersion = errors.New("unknown php version")
|
|
||||||
|
|
||||||
// GetBaseImage returns the php base image to use
|
|
||||||
func GetBaseImage(php string) (string, error) {
|
|
||||||
switch php {
|
|
||||||
case "":
|
|
||||||
return PHP_DEFAULT_IMAGE, nil
|
|
||||||
case PHP8:
|
|
||||||
return PHP8_IMAGE, nil
|
|
||||||
case PHP8_1:
|
|
||||||
return PHP8_1_IMAGE, nil
|
|
||||||
default:
|
|
||||||
return "", errUnknownPHPVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i Instance) GetDockerBaseImage() string {
|
|
||||||
if i.DockerBaseImage == "" {
|
|
||||||
return PHP8_IMAGE
|
|
||||||
}
|
|
||||||
return i.DockerBaseImage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i Instance) IsBlindUpdateEnabled() bool {
|
func (i Instance) IsBlindUpdateEnabled() bool {
|
||||||
return bool(i.AutoBlindUpdateEnabled)
|
return bool(i.AutoBlindUpdateEnabled)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
internal/models/instances_system.go
Normal file
50
internal/models/instances_system.go
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
// System represents system information.
|
||||||
|
// It is embedded into the instances struct by gorm.
|
||||||
|
type System struct {
|
||||||
|
// NOTE(twiesing): Any changes here should be reflected in instance_{provision,rebuild}.html and remote/api.ts.
|
||||||
|
PHP string `gorm:"column:php;not null"`
|
||||||
|
OpCacheDevelopment bool `gorm:"column:opcache_devel;not null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
imagePrefix = "docker.io/library/php:"
|
||||||
|
imageSuffix = "-apache-bullseye"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpCacheMode returns the name of the `opcache-*.ini` configuration being included in the docker image
|
||||||
|
func (system System) OpCacheMode() string {
|
||||||
|
if system.OpCacheDevelopment {
|
||||||
|
return "devel"
|
||||||
|
}
|
||||||
|
return "prod"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
phpVersions = []string{"8.0", "8.1", "8.2"}
|
||||||
|
phpVersionMap = (func() map[string]struct{} {
|
||||||
|
m := make(map[string]struct{}, len(phpVersions))
|
||||||
|
for _, v := range phpVersions {
|
||||||
|
m[v] = struct{}{}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
})()
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultPHPVersion is the default php version
|
||||||
|
const DefaultPHPVersion = "8.1"
|
||||||
|
|
||||||
|
// KnownPHPVersions returns a slice of php versions.
|
||||||
|
func KnownPHPVersions() []string {
|
||||||
|
return append([]string(nil), phpVersions...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDockerBaseImage returns the docker base image used by the given system.
|
||||||
|
func (system System) GetDockerBaseImage() string {
|
||||||
|
version := DefaultPHPVersion
|
||||||
|
if _, ok := phpVersionMap[system.PHP]; ok {
|
||||||
|
version = system.PHP
|
||||||
|
}
|
||||||
|
return imagePrefix + version + imageSuffix
|
||||||
|
}
|
||||||
|
|
@ -7,4 +7,6 @@ HOST_RULE=${HOST_RULE}
|
||||||
DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME}
|
DOCKER_NETWORK_NAME=${DOCKER_NETWORK_NAME}
|
||||||
|
|
||||||
HTTPS_ENABLED=${HTTPS_ENABLED}
|
HTTPS_ENABLED=${HTTPS_ENABLED}
|
||||||
|
|
||||||
BARREL_BASE_IMAGE=${BARREL_BASE_IMAGE}
|
BARREL_BASE_IMAGE=${BARREL_BASE_IMAGE}
|
||||||
|
OPCACHE_MODE=${OPCACHE_MODE}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package barrel
|
package barrel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
"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/locker"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||||
|
|
@ -17,6 +15,11 @@ type Barrel struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (barrel *Barrel) DataPath() string {
|
const (
|
||||||
return filepath.Join(barrel.FilesystemBase, "data")
|
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:
|
# allow the following files:
|
||||||
!conf/*
|
!conf/*
|
||||||
!scripts/*
|
!scripts/*
|
||||||
!patch/*
|
|
||||||
!ssh/*
|
!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
|
FROM $BARREL_BASE_IMAGE
|
||||||
ARG COMPOSER_VERSION=2.3.8
|
|
||||||
|
# Setup in /var/www
|
||||||
WORKDIR /var/www
|
WORKDIR /var/www
|
||||||
|
|
||||||
# install and enable the various required php extensions and dropbear ssh server
|
# 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
|
# enable the apache rewrite mod
|
||||||
RUN a2enmod rewrite
|
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 && \
|
RUN curl -sS https://getcomposer.org/installer | php -- --version=$COMPOSER_VERSION && \
|
||||||
mv composer.phar /usr/local/bin/composer
|
mv composer.phar /usr/local/bin/composer
|
||||||
|
|
||||||
|
# Add it to the path
|
||||||
ENV PATH "/usr/local/bin:/var/www/data/project/vendor/bin:$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 && \
|
RUN rm /etc/apache2/sites-available/*.conf && \
|
||||||
rm /etc/apache2/sites-enabled/*.conf
|
rm /etc/apache2/sites-enabled/*.conf
|
||||||
|
|
||||||
ADD patch/easyrdf.patch /patch/easyrdf.patch
|
# Then add the WissKI site
|
||||||
ADD patch/triples.patch /patch/triples.patch
|
|
||||||
|
|
||||||
# Add wisski configuration
|
|
||||||
ADD conf/ports.conf /etc/apache2/ports.conf
|
ADD conf/ports.conf /etc/apache2/ports.conf
|
||||||
ADD conf/wisski.conf /etc/apache2/sites-available/wisski.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
|
RUN a2ensite wisski
|
||||||
|
|
||||||
# volumes for composer
|
# volumes for composer
|
||||||
VOLUME /var/www/.composer
|
VOLUME /var/www/.composer
|
||||||
VOLUME /var/www/data
|
VOLUME /var/www/data
|
||||||
|
|
||||||
|
|
||||||
# Add and configure the entrypoint
|
# Add and configure the entrypoint
|
||||||
ADD scripts/entrypoint.sh /entrypoint.sh
|
ADD scripts/entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/bash", "/entrypoint.sh" ]
|
ENTRYPOINT [ "/bin/bash", "/entrypoint.sh" ]
|
||||||
CMD ["apache2-foreground"]
|
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 the user_shell.sh
|
||||||
ADD scripts/user_shell.sh /user_shell.sh
|
ADD scripts/user_shell.sh /user_shell.sh
|
||||||
ADD ssh/ /ssh/
|
ADD ssh/ /ssh/
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ services:
|
||||||
context: .
|
context: .
|
||||||
args:
|
args:
|
||||||
BARREL_BASE_IMAGE: ${BARREL_BASE_IMAGE}
|
BARREL_BASE_IMAGE: ${BARREL_BASE_IMAGE}
|
||||||
|
OPCACHE_MODE: ${OPCACHE_MODE}
|
||||||
|
logging:
|
||||||
|
driver: none
|
||||||
|
|
||||||
restart: always
|
restart: always
|
||||||
hostname: ${WISSKI_HOSTNAME}
|
hostname: ${WISSKI_HOSTNAME}
|
||||||
|
|
@ -32,11 +35,10 @@ services:
|
||||||
|
|
||||||
# volumes that are mounted
|
# volumes that are mounted
|
||||||
volumes:
|
volumes:
|
||||||
- ${DATA_PATH}/.composer:/var/www/.composer
|
- ${DATA_PATH}/.composer:/var/www/.composer:rw
|
||||||
- ${DATA_PATH}/data:/var/www/data
|
- ${DATA_PATH}/data:/var/www/data:rw
|
||||||
- ${DATA_PATH}/home:/var/www/
|
- ${DATA_PATH}/home:/var/www:rw
|
||||||
- ${DATA_PATH}/hostkeys:/ssh/hostkeys:rw
|
- ${DATA_PATH}/hostkeys:/ssh/hostkeys:rw
|
||||||
- ${RUNTIME_DIR}:/runtime:ro
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
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 up to 1GB
|
||||||
file_uploads = On
|
file_uploads = On
|
||||||
upload_max_filesize = 1000M
|
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/phpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
"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"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/pkglib/stream"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errCronFailed = exit.Error{
|
var errCronFailed = exit.Error{
|
||||||
|
|
@ -20,8 +20,9 @@ var errCronFailed = exit.Error{
|
||||||
}
|
}
|
||||||
|
|
||||||
func (drush *Drush) Cron(ctx context.Context, progress io.Writer) error {
|
func (drush *Drush) Cron(ctx context.Context, progress io.Writer) error {
|
||||||
code := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/cron.sh")()
|
err := drush.Exec(ctx, progress, "core-cron")
|
||||||
if code != 0 {
|
if err != nil {
|
||||||
|
code := err.(barrel.ExitError).Code
|
||||||
// keep going, because we want to run as many crons as possible
|
// keep going, because we want to run as many crons as possible
|
||||||
fmt.Fprintf(progress, "%v", errCronFailed.WithMessageF(drush.Slug, code))
|
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) {
|
func (drush *Drush) LastCron(ctx context.Context, server *phpx.Server) (t time.Time, err error) {
|
||||||
var timestamp int64
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package drush
|
package drush
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
"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"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
|
"github.com/tkw1536/pkglib/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Drush implements commands related to drush
|
// Drush implements commands related to drush
|
||||||
|
|
@ -16,3 +20,13 @@ type Drush struct {
|
||||||
PHP *php.PHP
|
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{
|
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,
|
ExitCode: exit.ExitGeneric,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update performs a blind drush update
|
// Update performs a blind drush update
|
||||||
func (drush *Drush) Update(ctx context.Context, progress io.Writer) error {
|
func (drush *Drush) Update(ctx context.Context, progress io.Writer) error {
|
||||||
code := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/blind_update.sh")()
|
err := drush.Dependencies.Barrel.Shell(ctx, stream.NonInteractive(progress), "/runtime/blind_update.sh")
|
||||||
if code != 0 {
|
if err != nil {
|
||||||
return errBlindUpdateFailed.WithMessageF(drush.Slug, code)
|
return errBlindUpdateFailed.WithMessageF(drush.Slug).Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return drush.setLastUpdate(ctx)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alessio/shellescape"
|
||||||
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/pkglib/stream"
|
"github.com/tkw1536/pkglib/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Shell executes a shell command inside the instance.
|
type ExitError int
|
||||||
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...)...)
|
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(),
|
"RUNTIME_DIR": barrel.Malt.Config.Paths.RuntimeDir(),
|
||||||
|
|
||||||
"BARREL_BASE_IMAGE": barrel.GetDockerBaseImage(),
|
"BARREL_BASE_IMAGE": barrel.GetDockerBaseImage(),
|
||||||
|
"OPCACHE_MODE": barrel.OpCacheMode(),
|
||||||
},
|
},
|
||||||
|
|
||||||
MakeDirs: []string{"data", ".composer"},
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
"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"
|
||||||
|
|
@ -24,6 +25,25 @@ func (settings *Settings) Get(ctx context.Context, server *phpx.Server, key stri
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errFailedToSetSetting = errors.New("failed to update setting")
|
||||||
|
|
||||||
func (settings *Settings) Set(ctx context.Context, server *phpx.Server, key string, value any) error {
|
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
|
<?php
|
||||||
|
|
||||||
|
use \Drupal\Core\Site\Settings;
|
||||||
|
|
||||||
/** gets a setting from 'settings.php' */
|
/** gets a setting from 'settings.php' */
|
||||||
function get_setting($name) {
|
function get_setting($name) {
|
||||||
use \Drupal\Core\Site\Settings;
|
|
||||||
return Settings::get($name);
|
return Settings::get($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** sets a setting in 'settings.php' */
|
/** 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
|
// load install.inc
|
||||||
if(is_file(DRUPAL_ROOT . "/internal/")) {
|
if(is_file(DRUPAL_ROOT . "/internal/")) {
|
||||||
include_once DRUPAL_ROOT . "/internal/core/includes/install.inc";
|
include_once DRUPAL_ROOT . "/internal/core/includes/install.inc";
|
||||||
|
|
@ -21,9 +36,20 @@ function set_setting($name, $value) {
|
||||||
"required" => TRUE,
|
"required" => TRUE,
|
||||||
];
|
];
|
||||||
|
|
||||||
// find the filename
|
// do the rewrite
|
||||||
$filename = DRUPAL_ROOT . "/" . \Drupal::service("site.path") . "/settings.php";
|
try {
|
||||||
drupal_rewrite_settings($settings, $filename);
|
drupal_rewrite_settings($settings, $filename);
|
||||||
|
} catch(Throwable $t) {
|
||||||
return True;
|
throw $t; // DEBUG
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
"github.com/FAU-CDI/wisski-distillery/internal/phpx"
|
||||||
"github.com/alessio/shellescape"
|
|
||||||
"github.com/tkw1536/pkglib/stream"
|
"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 {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
"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"
|
||||||
|
"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/drush"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/provisioner"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/manager"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/ssh"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/ssh"
|
||||||
|
"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/bookkeeping"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/info"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/info"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||||
|
|
@ -46,8 +48,12 @@ func (wisski *WissKI) Barrel() *barrel.Barrel {
|
||||||
return export[*barrel.Barrel](wisski)
|
return export[*barrel.Barrel](wisski)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) Provisioner() *provisioner.Provisioner {
|
func (wisski *WissKI) Manager() *manager.Manager {
|
||||||
return export[*provisioner.Provisioner](wisski)
|
return export[*manager.Manager](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) SystemManager() *system.SystemManager {
|
||||||
|
return export[*system.SystemManager](wisski)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) PHP() *php.PHP {
|
func (wisski *WissKI) PHP() *php.PHP {
|
||||||
|
|
@ -111,6 +117,7 @@ func (wisski *WissKI) allIngredients() []initFunc {
|
||||||
auto[*extras.Stats],
|
auto[*extras.Stats],
|
||||||
auto[*extras.Blocks],
|
auto[*extras.Blocks],
|
||||||
auto[*extras.Requirements],
|
auto[*extras.Requirements],
|
||||||
|
auto[*extras.Adapters],
|
||||||
auto[*users.Users],
|
auto[*users.Users],
|
||||||
auto[*users.UserPolicy],
|
auto[*users.UserPolicy],
|
||||||
|
|
||||||
|
|
@ -127,7 +134,9 @@ func (wisski *WissKI) allIngredients() []initFunc {
|
||||||
// stacks
|
// stacks
|
||||||
auto[*barrel.Barrel],
|
auto[*barrel.Barrel],
|
||||||
auto[*bookkeeping.Bookkeeping],
|
auto[*bookkeeping.Bookkeeping],
|
||||||
auto[*provisioner.Provisioner],
|
auto[*manager.Manager],
|
||||||
|
auto[*system.SystemManager],
|
||||||
|
auto[*composer.Composer],
|
||||||
auto[*drush.Drush],
|
auto[*drush.Drush],
|
||||||
|
|
||||||
auto[*reserve.Reserve],
|
auto[*reserve.Reserve],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue