Move WissKI Parts to new ingredients system
This commit is contained in:
parent
b5b1ce2340
commit
42b8cbd865
83 changed files with 1016 additions and 646 deletions
|
|
@ -52,7 +52,7 @@ func (bu blindUpdate) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// and do the actual blind_update!
|
// and do the actual blind_update!
|
||||||
return status.StreamGroup(context.IOStream, bu.Parallel, func(instance *wisski.WissKI, str stream.IOStream) error {
|
return status.StreamGroup(context.IOStream, bu.Parallel, func(instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.BlindUpdate(str)
|
return instance.Drush().Update(str)
|
||||||
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("blind_update %q", item.Slug)
|
return fmt.Sprintf("blind_update %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ func (cr cron) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// and do the actual blind_update!
|
// and do the actual blind_update!
|
||||||
return status.StreamGroup(context.IOStream, cr.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
return status.StreamGroup(context.IOStream, cr.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
||||||
return instance.Cron(io)
|
return instance.Drush().Cron(io)
|
||||||
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("cron %q", item.Slug)
|
return fmt.Sprintf("cron %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func (ds setting) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
if ds.Positionals.Value == "" {
|
if ds.Positionals.Value == "" {
|
||||||
// get the setting
|
// get the setting
|
||||||
value, err := instance.GetSettingsPHP(nil, ds.Positionals.Setting)
|
value, err := instance.Settings().Get(nil, ds.Positionals.Setting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errSettingGet.Wrap(err)
|
return errSettingGet.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +69,7 @@ func (ds setting) Run(context wisski_distillery.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the serialized value!
|
// set the serialized value!
|
||||||
if err := instance.SetSettingsPHP(nil, ds.Positionals.Setting, data); err != nil {
|
if err := instance.Settings().Set(nil, ds.Positionals.Setting, data); err != nil {
|
||||||
return errSettingSet.Wrap(err)
|
return errSettingSet.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func (i info) Run(context wisski_distillery.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := instance.Info(false)
|
info, err := instance.Info().Fetch(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package cmd
|
||||||
import (
|
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/locker"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -51,15 +52,15 @@ func (l instanceLock) Run(context wisski_distillery.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.Unlock {
|
if l.Unlock {
|
||||||
if !instance.Unlock() {
|
if !instance.Locker().TryUnlock() {
|
||||||
return errNotUnlock
|
return errNotUnlock
|
||||||
}
|
}
|
||||||
context.Println("unlocked")
|
context.Println("unlocked")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := instance.TryLock(); err != nil {
|
if !instance.Locker().TryLock() {
|
||||||
return err
|
return locker.Locked
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Println("locked")
|
context.Println("locked")
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func (pb pathbuilders) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// get all of the pathbuilders
|
// get all of the pathbuilders
|
||||||
if pb.Positionals.Name == "" {
|
if pb.Positionals.Name == "" {
|
||||||
names, err := instance.Pathbuilders(nil)
|
names, err := instance.Pathbuilder().All(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errPathbuilders.WithMessageF(err)
|
return errPathbuilders.WithMessageF(err)
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ func (pb pathbuilders) Run(context wisski_distillery.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all the pathbuilders
|
// get all the pathbuilders
|
||||||
xml, err := instance.Pathbuilder(nil, pb.Positionals.Name)
|
xml, err := instance.Pathbuilder().Get(nil, pb.Positionals.Name)
|
||||||
if xml == "" {
|
if xml == "" {
|
||||||
return errNoPathbuilder.WithMessageF(pb.Positionals.Name)
|
return errNoPathbuilder.WithMessageF(pb.Positionals.Name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func (p prefixes) Run(context wisski_distillery.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixes, err := instance.Prefixes(nil)
|
prefixes, err := instance.Prefixes().All(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errPrefixesGeneric.Wrap(err)
|
return errPrefixesGeneric.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// Store in the instances table!
|
// Store in the instances table!
|
||||||
if err := logging.LogOperation(func() error {
|
if err := logging.LogOperation(func() error {
|
||||||
if err := instance.Save(); err != nil {
|
if err := instance.Bookkeeping().Save(); err != nil {
|
||||||
return errProvisionGeneric.WithMessageF(slug, err)
|
return errProvisionGeneric.WithMessageF(slug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// run the provision script
|
// run the provision script
|
||||||
if err := logging.LogOperation(func() error {
|
if err := logging.LogOperation(func() error {
|
||||||
if err := instance.Provision(context.IOStream); err != nil {
|
if err := instance.Provisioner().Provision(context.IOStream); err != nil {
|
||||||
return errProvisionGeneric.WithMessageF(slug, err)
|
return errProvisionGeneric.WithMessageF(slug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// start the container!
|
// start the container!
|
||||||
logging.LogMessage(context.IOStream, "Starting Container")
|
logging.LogMessage(context.IOStream, "Starting Container")
|
||||||
if err := instance.Barrel().Up(context.IOStream); err != nil {
|
if err := instance.Barrel().Stack().Up(context.IOStream); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (p purge) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// remove docker stack
|
// remove docker stack
|
||||||
logging.LogMessage(context.IOStream, "Stopping and removing docker container")
|
logging.LogMessage(context.IOStream, "Stopping and removing docker container")
|
||||||
if err := instance.Barrel().Down(context.IOStream); err != nil {
|
if err := instance.Barrel().Stack().Down(context.IOStream); err != nil {
|
||||||
context.EPrintln(err)
|
context.EPrintln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,13 +93,13 @@ func (p purge) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// remove from bookkeeping
|
// remove from bookkeeping
|
||||||
logging.LogMessage(context.IOStream, "Removing instance from bookkeeping")
|
logging.LogMessage(context.IOStream, "Removing instance from bookkeeping")
|
||||||
if err := instance.Delete(); err != nil {
|
if err := instance.Bookkeeping().Delete(); err != nil {
|
||||||
context.EPrintln(err)
|
context.EPrintln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the filesystem
|
// remove the filesystem
|
||||||
logging.LogMessage(context.IOStream, "Remove lock data", instance.FilesystemBase)
|
logging.LogMessage(context.IOStream, "Remove lock data", instance.FilesystemBase)
|
||||||
if !instance.Unlock() {
|
if instance.Locker().TryUnlock() {
|
||||||
context.EPrintln("instance was not locked")
|
context.EPrintln("instance was not locked")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func (rb rebuild) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
// and do the actual rebuild
|
// and do the actual rebuild
|
||||||
return status.StreamGroup(context.IOStream, rb.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
return status.StreamGroup(context.IOStream, rb.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
||||||
return instance.Build(io, true)
|
return instance.Barrel().Build(io, 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)
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func (r reserve) Run(context wisski_distillery.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup docker stack
|
// setup docker stack
|
||||||
s := instance.Reserve()
|
s := instance.Reserve().Stack()
|
||||||
{
|
{
|
||||||
if err := logging.LogOperation(func() error {
|
if err := logging.LogOperation(func() error {
|
||||||
return s.Install(context.IOStream, component.InstallationContext{})
|
return s.Install(context.IOStream, component.InstallationContext{})
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func (sh shell) Run(context wisski_distillery.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
code, err := instance.Shell(context.IOStream, sh.Positionals.Args...)
|
code, err := instance.Barrel().Shell(context.IOStream, sh.Positionals.Args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errShell.WithMessageF(err)
|
return errShell.WithMessageF(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ func (upc updateprefixconfig) Run(context wisski_distillery.Context) error {
|
||||||
|
|
||||||
return status.StreamGroup(context.IOStream, upc.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
return status.StreamGroup(context.IOStream, upc.Parallel, func(instance *wisski.WissKI, io stream.IOStream) error {
|
||||||
io.Println("reading prefixes")
|
io.Println("reading prefixes")
|
||||||
err := instance.UpdatePrefixes()
|
err := instance.Prefixes().UpdatePrefixes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errPrefixUpdateFailed.Wrap(err)
|
return errPrefixUpdateFailed.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,6 @@ func auto[C component.Component](context ctx) component.Component {
|
||||||
|
|
||||||
// register returns all components of the distillery
|
// register returns all components of the distillery
|
||||||
func (dis *Distillery) register(context ctx) []component.Component {
|
func (dis *Distillery) register(context ctx) []component.Component {
|
||||||
dis.poolInit.Do(func() {
|
|
||||||
dis.pool.Init = component.Init
|
|
||||||
})
|
|
||||||
|
|
||||||
return collection.MapSlice(
|
return collection.MapSlice(
|
||||||
dis.allComponents(),
|
dis.allComponents(),
|
||||||
func(f initFunc) component.Component {
|
func(f initFunc) component.Component {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ func (Pathbuilders) SnapshotName() string { return "pathbuilders" }
|
||||||
|
|
||||||
func (pbs *Pathbuilders) Snapshot(wisski models.Instance, context component.StagingContext) error {
|
func (pbs *Pathbuilders) Snapshot(wisski models.Instance, context component.StagingContext) error {
|
||||||
return context.AddDirectory(".", func() error {
|
return context.AddDirectory(".", func() error {
|
||||||
builders, err := pbs.Instances.Instance(wisski).AllPathbuilders(nil)
|
builders, err := pbs.Instances.Instance(wisski).Pathbuilder().GetAll(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,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/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/locker"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/tkw1536/goprogram/lib/collection"
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
|
|
@ -46,7 +47,8 @@ type Snapshot struct {
|
||||||
func (snapshots *Exporter) NewSnapshot(instance *wisski.WissKI, io stream.IOStream, desc SnapshotDescription) (snapshot Snapshot) {
|
func (snapshots *Exporter) NewSnapshot(instance *wisski.WissKI, io stream.IOStream, desc SnapshotDescription) (snapshot Snapshot) {
|
||||||
|
|
||||||
logging.LogMessage(io, "Locking instance")
|
logging.LogMessage(io, "Locking instance")
|
||||||
if err := instance.TryLock(); err != nil {
|
if !instance.Locker().TryLock() {
|
||||||
|
err := locker.Locked
|
||||||
io.EPrintln(err)
|
io.EPrintln(err)
|
||||||
logging.LogMessage(io, "Aborting snapshot creation")
|
logging.LogMessage(io, "Aborting snapshot creation")
|
||||||
|
|
||||||
|
|
@ -56,7 +58,7 @@ func (snapshots *Exporter) NewSnapshot(instance *wisski.WissKI, io stream.IOStre
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
logging.LogMessage(io, "Unlocking instance")
|
logging.LogMessage(io, "Unlocking instance")
|
||||||
instance.Unlock()
|
instance.Locker().Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// setup the snapshot
|
// setup the snapshot
|
||||||
|
|
@ -85,7 +87,7 @@ func (snapshots *Exporter) NewSnapshot(instance *wisski.WissKI, io stream.IOStre
|
||||||
|
|
||||||
func (snapshot *Snapshot) makeParts(ios stream.IOStream, snapshots *Exporter, instance *wisski.WissKI, needsRunning bool) map[string]error {
|
func (snapshot *Snapshot) makeParts(ios stream.IOStream, snapshots *Exporter, instance *wisski.WissKI, needsRunning bool) map[string]error {
|
||||||
if !needsRunning && !snapshot.Description.Keepalive {
|
if !needsRunning && !snapshot.Description.Keepalive {
|
||||||
stack := instance.Barrel()
|
stack := instance.Barrel().Stack()
|
||||||
|
|
||||||
logging.LogMessage(ios, "Stopping instance")
|
logging.LogMessage(ios, "Stopping instance")
|
||||||
snapshot.ErrStop = stack.Down(ios)
|
snapshot.ErrStop = stack.Down(ios)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/info"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/timex"
|
"github.com/FAU-CDI/wisski-distillery/pkg/timex"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
@ -65,7 +65,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
context.Instances = make([]wisski.WissKIInfo, len(wissKIs))
|
context.Instances = make([]info.WissKIInfo, len(wissKIs))
|
||||||
|
|
||||||
// determine their infos
|
// determine their infos
|
||||||
var eg errgroup.Group
|
var eg errgroup.Group
|
||||||
|
|
@ -73,7 +73,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
||||||
i := i
|
i := i
|
||||||
wissKI := instance
|
wissKI := instance
|
||||||
eg.Go(func() (err error) {
|
eg.Go(func() (err error) {
|
||||||
context.Instances[i], err = wissKI.Info(true)
|
context.Instances[i], err = wissKI.Info().Fetch(true)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +86,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type HomeContext struct {
|
type HomeContext struct {
|
||||||
Instances []wisski.WissKIInfo
|
Instances []info.WissKIInfo
|
||||||
|
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/config"
|
"github.com/FAU-CDI/wisski-distillery/internal/config"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/static"
|
||||||
"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/ingredient/info"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ type indexPageContext struct {
|
||||||
|
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
|
|
||||||
Instances []wisski.WissKIInfo
|
Instances []info.WissKIInfo
|
||||||
|
|
||||||
TotalCount int
|
TotalCount int
|
||||||
RunningCount int
|
RunningCount int
|
||||||
|
|
@ -31,18 +31,18 @@ type indexPageContext struct {
|
||||||
Backups []models.Export
|
Backups []models.Export
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error) {
|
func (nfo *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error) {
|
||||||
var group errgroup.Group
|
var group errgroup.Group
|
||||||
|
|
||||||
group.Go(func() error {
|
group.Go(func() error {
|
||||||
// list all the instances
|
// list all the instances
|
||||||
all, err := info.Instances.All()
|
all, err := nfo.Instances.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all of their info!
|
// get all of their info!
|
||||||
idx.Instances = make([]wisski.WissKIInfo, len(all))
|
idx.Instances = make([]info.WissKIInfo, len(all))
|
||||||
for i, instance := range all {
|
for i, instance := range all {
|
||||||
{
|
{
|
||||||
i := i
|
i := i
|
||||||
|
|
@ -50,7 +50,7 @@ func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error
|
||||||
|
|
||||||
// store the info for this group!
|
// store the info for this group!
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
idx.Instances[i], err = instance.Info(true)
|
idx.Instances[i], err = instance.Info().Fetch(true)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -61,12 +61,12 @@ func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error
|
||||||
|
|
||||||
// get the log entries
|
// get the log entries
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
idx.Backups, err = info.SnapshotsLog.For("")
|
idx.Backups, err = nfo.SnapshotsLog.For("")
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
// get the static properties
|
// get the static properties
|
||||||
idx.Config = info.Config
|
idx.Config = nfo.Config
|
||||||
idx.Time = time.Now().UTC()
|
idx.Time = time.Now().UTC()
|
||||||
|
|
||||||
group.Wait()
|
group.Wait()
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"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/dis/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/static"
|
||||||
"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/ingredient/info"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ type instancePageContext struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
|
||||||
Instance models.Instance
|
Instance models.Instance
|
||||||
Info wisski.WissKIInfo
|
Info info.WissKIInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *Info) instancePageAPI(r *http.Request) (is instancePageContext, err error) {
|
func (info *Info) instancePageAPI(r *http.Request) (is instancePageContext, err error) {
|
||||||
|
|
@ -40,7 +40,7 @@ func (info *Info) instancePageAPI(r *http.Request) (is instancePageContext, err
|
||||||
is.Instance = instance.Instance
|
is.Instance = instance.Instance
|
||||||
|
|
||||||
// get some more info about the wisski
|
// get some more info about the wisski
|
||||||
is.Info, err = instance.Info(false)
|
is.Info, err = instance.Info().Fetch(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return is, err
|
return is, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,13 @@ var socketInstanceActions = map[string]instanceActionFunc{
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"rebuild": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
"rebuild": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.Build(str, true)
|
return instance.Barrel().Build(str, true)
|
||||||
},
|
},
|
||||||
"update": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
"update": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.BlindUpdate(str)
|
return instance.Drush().Update(str)
|
||||||
},
|
},
|
||||||
"cron": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
"cron": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.Cron(str)
|
return instance.Drush().Cron(str)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,37 +25,37 @@ func (instances *Instances) Create(slug string) (wissKI *wisski.WissKI, err erro
|
||||||
wissKI = new(wisski.WissKI)
|
wissKI = new(wisski.WissKI)
|
||||||
instances.use(wissKI)
|
instances.use(wissKI)
|
||||||
|
|
||||||
wissKI.Instance.Slug = slug
|
wissKI.Liquid.Instance.Slug = slug
|
||||||
wissKI.Instance.FilesystemBase = filepath.Join(instances.Path(), wissKI.Domain())
|
wissKI.Liquid.Instance.FilesystemBase = filepath.Join(instances.Path(), wissKI.Domain())
|
||||||
|
|
||||||
wissKI.Instance.OwnerEmail = ""
|
wissKI.Liquid.Instance.OwnerEmail = ""
|
||||||
wissKI.Instance.AutoBlindUpdateEnabled = true
|
wissKI.Liquid.Instance.AutoBlindUpdateEnabled = true
|
||||||
|
|
||||||
// sql
|
// sql
|
||||||
|
|
||||||
wissKI.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + slug
|
wissKI.Liquid.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + slug
|
||||||
wissKI.Instance.SqlUsername = instances.Config.MysqlUserPrefix + slug
|
wissKI.Liquid.Instance.SqlUsername = instances.Config.MysqlUserPrefix + slug
|
||||||
|
|
||||||
wissKI.Instance.SqlPassword, err = instances.Config.NewPassword()
|
wissKI.Liquid.Instance.SqlPassword, err = instances.Config.NewPassword()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// triplestore
|
// triplestore
|
||||||
|
|
||||||
wissKI.Instance.GraphDBRepository = instances.Config.GraphDBRepoPrefix + slug
|
wissKI.Liquid.Instance.GraphDBRepository = instances.Config.GraphDBRepoPrefix + slug
|
||||||
wissKI.Instance.GraphDBUsername = instances.Config.GraphDBUserPrefix + slug
|
wissKI.Liquid.Instance.GraphDBUsername = instances.Config.GraphDBUserPrefix + slug
|
||||||
|
|
||||||
wissKI.Instance.GraphDBPassword, err = instances.Config.NewPassword()
|
wissKI.Liquid.Instance.GraphDBPassword, err = instances.Config.NewPassword()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// drupal
|
// drupal
|
||||||
|
|
||||||
wissKI.DrupalUsername = "admin" // TODO: Change this!
|
wissKI.Liquid.DrupalUsername = "admin" // TODO: Change this!
|
||||||
|
|
||||||
wissKI.DrupalPassword, err = instances.Config.NewPassword()
|
wissKI.Liquid.DrupalPassword, err = instances.Config.NewPassword()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ 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/exporter/logger"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/malt"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore"
|
|
||||||
"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/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
|
|
@ -21,10 +19,8 @@ import (
|
||||||
type Instances struct {
|
type Instances struct {
|
||||||
component.Base
|
component.Base
|
||||||
|
|
||||||
TS *triplestore.Triplestore
|
Malt *malt.Malt
|
||||||
SQL *sql.SQL
|
SQL *sql.SQL
|
||||||
Meta *meta.Meta
|
|
||||||
ExporterLog *logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (instances *Instances) Path() string {
|
func (instances *Instances) Path() string {
|
||||||
|
|
@ -41,11 +37,7 @@ var errSQL = exit.Error{
|
||||||
|
|
||||||
// use uses the non-nil wisski instance with this instances
|
// use uses the non-nil wisski instance with this instances
|
||||||
func (instances *Instances) use(wisski *wisski.WissKI) {
|
func (instances *Instances) use(wisski *wisski.WissKI) {
|
||||||
wisski.Core = instances.Still
|
wisski.Liquid.Malt = instances.Malt
|
||||||
wisski.SQL = instances.SQL
|
|
||||||
wisski.TS = instances.TS
|
|
||||||
wisski.Meta = instances.Meta
|
|
||||||
wisski.ExporterLog = instances.ExporterLog
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WissKI returns the WissKI with the provided slug, if it exists.
|
// WissKI returns the WissKI with the provided slug, if it exists.
|
||||||
|
|
@ -65,7 +57,7 @@ func (instances *Instances) WissKI(slug string) (wissKI *wisski.WissKI, err erro
|
||||||
wissKI = new(wisski.WissKI)
|
wissKI = new(wisski.WissKI)
|
||||||
|
|
||||||
// find the instance by slug
|
// find the instance by slug
|
||||||
query := table.Where(&models.Instance{Slug: slug}).Find(&wissKI.Instance)
|
query := table.Where(&models.Instance{Slug: slug}).Find(&wissKI.Liquid.Instance)
|
||||||
switch {
|
switch {
|
||||||
case query.Error != nil:
|
case query.Error != nil:
|
||||||
return nil, errSQL.WithMessageF(query.Error)
|
return nil, errSQL.WithMessageF(query.Error)
|
||||||
|
|
@ -166,7 +158,7 @@ func (instances *Instances) find(order bool, query func(table *gorm.DB) *gorm.DB
|
||||||
results = make([]*wisski.WissKI, len(bks))
|
results = make([]*wisski.WissKI, len(bks))
|
||||||
for i, bk := range bks {
|
for i, bk := range bks {
|
||||||
results[i] = new(wisski.WissKI)
|
results[i] = new(wisski.WissKI)
|
||||||
results[i].Instance = bk
|
results[i].Liquid.Instance = bk
|
||||||
instances.use(results[i])
|
instances.use(results[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
19
internal/dis/component/instances/malt/malt.go
Normal file
19
internal/dis/component/instances/malt/malt.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package malt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter/logger"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Malt is a component passed to every WissKI ingredient
|
||||||
|
type Malt struct {
|
||||||
|
component.Base
|
||||||
|
|
||||||
|
TS *triplestore.Triplestore
|
||||||
|
SQL *sql.SQL
|
||||||
|
Meta *meta.Meta
|
||||||
|
ExporterLog *logger.Logger
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/tkw1536/goprogram/lib/collection"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -177,42 +176,3 @@ func (s Storage) Purge() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageFor returns a storage for the given key.
|
|
||||||
func StorageFor[Value any](key Key) func(storage *Storage) SpecifcStorage[Value] {
|
|
||||||
return func(storage *Storage) SpecifcStorage[Value] {
|
|
||||||
return SpecifcStorage[Value]{storage: storage, key: key}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SpecifcStorage[Value any] struct {
|
|
||||||
storage *Storage
|
|
||||||
key Key
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf SpecifcStorage[Value]) Get() (value Value, err error) {
|
|
||||||
err = sf.storage.Get(sf.key, &value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf SpecifcStorage[Value]) GetAll() (values []Value, err error) {
|
|
||||||
err = sf.storage.GetAll(sf.key, func(index, total int) any {
|
|
||||||
if values == nil {
|
|
||||||
values = make([]Value, total)
|
|
||||||
}
|
|
||||||
return &values[index]
|
|
||||||
})
|
|
||||||
return values, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf SpecifcStorage[Value]) Set(value Value) error {
|
|
||||||
return sf.storage.Set(sf.key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf SpecifcStorage[Value]) SetAll(values ...Value) error {
|
|
||||||
return sf.storage.SetAll(sf.key, collection.AsAny(values)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf SpecifcStorage[Value]) Delete() error {
|
|
||||||
return sf.storage.Delete(sf.key)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ func (resolver *Resolver) AllPrefixes() (map[string]string, error) {
|
||||||
gPrefixes := make(map[string]string)
|
gPrefixes := make(map[string]string)
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for _, instance := range instances {
|
for _, instance := range instances {
|
||||||
if instance.NoPrefix() {
|
if instance.Prefixes().NoPrefix() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
url := instance.URL().String()
|
url := instance.URL().String()
|
||||||
|
|
||||||
// failed to fetch prefixes for this particular instance
|
// failed to fetch prefixes for this particular instance
|
||||||
// => skip it!
|
// => skip it!
|
||||||
prefixes, err := instance.PrefixesCached()
|
prefixes, err := instance.Prefixes().PrefixesCached()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
lastErr = err
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/home"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/home"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/info"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/info"
|
||||||
"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/dis/component/instances/malt"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/resolver"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/resolver"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||||
|
|
@ -114,8 +115,10 @@ func (dis *Distillery) allComponents() []initFunc {
|
||||||
sql.PollInterval = time.Second
|
sql.PollInterval = time.Second
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// instainces
|
||||||
auto[*instances.Instances],
|
auto[*instances.Instances],
|
||||||
auto[*meta.Meta],
|
auto[*meta.Meta],
|
||||||
|
auto[*malt.Malt],
|
||||||
|
|
||||||
// Snapshots
|
// Snapshots
|
||||||
auto[*exporter.Exporter],
|
auto[*exporter.Exporter],
|
||||||
|
|
|
||||||
15
internal/wisski/ingredient/barrel/barrel.go
Normal file
15
internal/wisski/ingredient/barrel/barrel.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package barrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Barrel provides access to the underlying Barrel
|
||||||
|
type Barrel struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
Locker *locker.Locker
|
||||||
|
MStore *mstore.MStore
|
||||||
|
}
|
||||||
63
internal/wisski/ingredient/barrel/build.go
Normal file
63
internal/wisski/ingredient/barrel/build.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package barrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||||
|
"github.com/tkw1536/goprogram/stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build builds or rebuilds the barel connected to this instance.
|
||||||
|
//
|
||||||
|
// It also logs the current time into the metadata belonging to this instance.
|
||||||
|
func (barrel *Barrel) Build(stream stream.IOStream, start bool) error {
|
||||||
|
if !barrel.Locker.TryLock() {
|
||||||
|
err := locker.Locked
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer barrel.Locker.Unlock()
|
||||||
|
|
||||||
|
stack := barrel.Stack()
|
||||||
|
|
||||||
|
var context component.InstallationContext
|
||||||
|
|
||||||
|
{
|
||||||
|
err := stack.Install(stream, context)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
err := stack.Update(stream, start)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the current last rebuild
|
||||||
|
return barrel.setLastRebuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move this to time.Time
|
||||||
|
var lastRebuild = mstore.For[int64]("lastRebuild")
|
||||||
|
|
||||||
|
func (barrel Barrel) LastRebuild() (t time.Time, err error) {
|
||||||
|
epoch, err := lastRebuild.Get(barrel.MStore)
|
||||||
|
if err == meta.ErrMetadatumNotSet {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// and turn it into time!
|
||||||
|
return time.Unix(epoch, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (barrel *Barrel) setLastRebuild() error {
|
||||||
|
return lastRebuild.Set(barrel.MStore, time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package wisski
|
package drush
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
@ -12,23 +13,23 @@ var errCronFailed = exit.Error{
|
||||||
ExitCode: exit.ExitGeneric,
|
ExitCode: exit.ExitGeneric,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) Cron(io stream.IOStream) error {
|
func (drush *Drush) Cron(io stream.IOStream) error {
|
||||||
code, err := wisski.Shell(io, "/runtime/cron.sh")
|
code, err := drush.Barrel.Shell(io, "/runtime/cron.sh")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
io.EPrintln(err)
|
io.EPrintln(err)
|
||||||
}
|
}
|
||||||
if code != 0 {
|
if code != 0 {
|
||||||
// keep going, because we want to run as many crons as possible
|
// keep going, because we want to run as many crons as possible
|
||||||
err = errBlindUpdateFailed.WithMessageF(wisski.Slug, code)
|
err = errCronFailed.WithMessageF(drush.Slug, code)
|
||||||
io.EPrintln(err)
|
io.EPrintln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) LastCron(server *PHPServer) (t time.Time, err error) {
|
func (drush *Drush) LastCron(server *php.Server) (t time.Time, err error) {
|
||||||
var timestamp int64
|
var timestamp int64
|
||||||
err = wisski.EvalPHPCode(server, ×tamp, `$val = \Drupal::state()->get('system.cron_last'); return $val; `)
|
err = drush.PHP.EvalCode(server, ×tamp, `$val = \Drupal::state()->get('system.cron_last'); return $val; `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
17
internal/wisski/ingredient/barrel/drush/drush.go
Normal file
17
internal/wisski/ingredient/barrel/drush/drush.go
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
package drush
|
||||||
|
|
||||||
|
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/mstore"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Drush implements commands related to drush
|
||||||
|
type Drush struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
Barrel *barrel.Barrel
|
||||||
|
MStore *mstore.MStore
|
||||||
|
PHP *php.PHP
|
||||||
|
}
|
||||||
48
internal/wisski/ingredient/barrel/drush/update.go
Normal file
48
internal/wisski/ingredient/barrel/drush/update.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package drush
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
|
"github.com/tkw1536/goprogram/exit"
|
||||||
|
"github.com/tkw1536/goprogram/stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errBlindUpdateFailed = exit.Error{
|
||||||
|
Message: "Failed to run blind update script for instance %q: exited with code %s",
|
||||||
|
ExitCode: exit.ExitGeneric,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update performs a blind drush update
|
||||||
|
func (drush *Drush) Update(io stream.IOStream) error {
|
||||||
|
code, err := drush.Barrel.Shell(io, "/runtime/blind_update.sh")
|
||||||
|
if err != nil {
|
||||||
|
return errBlindUpdateFailed.WithMessageF(drush.Slug, environment.ExecCommandError)
|
||||||
|
}
|
||||||
|
if code != 0 {
|
||||||
|
return errBlindUpdateFailed.WithMessageF(drush.Slug, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return drush.setLastUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastUpdate = mstore.For[int64]("lastUpdate")
|
||||||
|
|
||||||
|
func (drush *Drush) LastUpdate() (t time.Time, err error) {
|
||||||
|
epoch, err := lastUpdate.Get(drush.MStore)
|
||||||
|
if err == meta.ErrMetadatumNotSet {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// and turn it into time!
|
||||||
|
return time.Unix(epoch, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (drush *Drush) setLastUpdate() error {
|
||||||
|
return lastUpdate.Set(drush.MStore, time.Now().Unix())
|
||||||
|
}
|
||||||
65
internal/wisski/ingredient/barrel/provisioner/provisioner.go
Normal file
65
internal/wisski/ingredient/barrel/provisioner/provisioner.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package provisioner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||||
|
"github.com/alessio/shellescape"
|
||||||
|
"github.com/tkw1536/goprogram/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
|
||||||
|
Barrel *barrel.Barrel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision provisions an instance, assuming that the required databases already exist.
|
||||||
|
func (provision *Provisioner) Provision(io stream.IOStream) error {
|
||||||
|
|
||||||
|
// build the container
|
||||||
|
if err := provision.Barrel.Build(io, 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.Barrel.Stack().Run(io, true, "barrel", "/bin/bash", "-c", provisionScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if code != 0 {
|
||||||
|
return errors.New("unable to run provision script")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
12
internal/wisski/ingredient/barrel/running.go
Normal file
12
internal/wisski/ingredient/barrel/running.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package barrel
|
||||||
|
|
||||||
|
import "github.com/tkw1536/goprogram/stream"
|
||||||
|
|
||||||
|
// Running checks if this WissKI is currently running.
|
||||||
|
func (barrel *Barrel) Running() (bool, error) {
|
||||||
|
ps, err := barrel.Stack().Ps(stream.FromNil())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return len(ps) > 0, nil
|
||||||
|
}
|
||||||
8
internal/wisski/ingredient/barrel/shell.go
Normal file
8
internal/wisski/ingredient/barrel/shell.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package barrel
|
||||||
|
|
||||||
|
import "github.com/tkw1536/goprogram/stream"
|
||||||
|
|
||||||
|
// Shell executes a shell command inside the instance.
|
||||||
|
func (barrel *Barrel) Shell(io stream.IOStream, argv ...string) (int, error) {
|
||||||
|
return barrel.Stack().Exec(io, "barrel", "/bin/sh", append([]string{"/user_shell.sh"}, argv...)...)
|
||||||
|
}
|
||||||
43
internal/wisski/ingredient/barrel/stack.go
Normal file
43
internal/wisski/ingredient/barrel/stack.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package barrel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed all:barrel barrel.env
|
||||||
|
var barrelResources embed.FS
|
||||||
|
|
||||||
|
// Barrel returns a stack representing the running WissKI Instance
|
||||||
|
func (barrel *Barrel) Stack() component.StackWithResources {
|
||||||
|
return component.StackWithResources{
|
||||||
|
Stack: component.Stack{
|
||||||
|
Dir: barrel.FilesystemBase,
|
||||||
|
Env: barrel.Malt.Environment,
|
||||||
|
},
|
||||||
|
|
||||||
|
Resources: barrelResources,
|
||||||
|
ContextPath: filepath.Join("barrel"),
|
||||||
|
EnvPath: filepath.Join("barrel.env"),
|
||||||
|
|
||||||
|
EnvContext: map[string]string{
|
||||||
|
"DOCKER_NETWORK_NAME": barrel.Malt.Config.DockerNetworkName,
|
||||||
|
|
||||||
|
"SLUG": barrel.Slug,
|
||||||
|
"VIRTUAL_HOST": barrel.Domain(),
|
||||||
|
"HTTPS_ENABLED": barrel.Malt.Config.HTTPSEnabledEnv(),
|
||||||
|
|
||||||
|
"DATA_PATH": filepath.Join(barrel.FilesystemBase, "data"),
|
||||||
|
"RUNTIME_DIR": barrel.Malt.Config.RuntimeDir(),
|
||||||
|
"GLOBAL_AUTHORIZED_KEYS_FILE": barrel.Malt.Config.GlobalAuthorizedKeysFile,
|
||||||
|
},
|
||||||
|
|
||||||
|
MakeDirs: []string{"data", ".composer"},
|
||||||
|
|
||||||
|
TouchFiles: []string{
|
||||||
|
filepath.Join("data", "authorized_keys"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
43
internal/wisski/ingredient/bookkeeping/bookkeeping.go
Normal file
43
internal/wisski/ingredient/bookkeeping/bookkeeping.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package bookkeeping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bookkeeping provides instance bookkeeping
|
||||||
|
type Bookkeeping struct {
|
||||||
|
ingredient.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save saves this instance in the bookkeeping table
|
||||||
|
func (bk *Bookkeeping) Save() error {
|
||||||
|
sdb, err := bk.Malt.SQL.QueryTable(false, models.InstanceTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// it has never been created => we need to create it in the database
|
||||||
|
if bk.Instance.Created.IsZero() {
|
||||||
|
return sdb.Create(&bk.Instance).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update based on the primary key!
|
||||||
|
return sdb.Where("pk = ?", bk.Instance.Pk).Updates(&bk.Instance).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes this instance from the bookkeeping table
|
||||||
|
func (bk *Bookkeeping) Delete() error {
|
||||||
|
sdb, err := bk.Malt.SQL.QueryTable(false, models.InstanceTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't exist => nothing to delete
|
||||||
|
if bk.Instance.Created.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete it directly
|
||||||
|
return sdb.Delete(&bk.Instance).Error
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,29 @@
|
||||||
package wisski
|
package info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"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/drush"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/extras"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
PHP *php.PHP
|
||||||
|
Barrel *barrel.Barrel
|
||||||
|
Locker *locker.Locker
|
||||||
|
Drush *drush.Drush
|
||||||
|
Prefixes *extras.Prefixes
|
||||||
|
Pathbuilder *extras.Pathbuilder
|
||||||
|
}
|
||||||
|
|
||||||
// WissKIInfo represents information about this WissKI Instance.
|
// WissKIInfo represents information about this WissKI Instance.
|
||||||
type WissKIInfo struct {
|
type WissKIInfo struct {
|
||||||
Time time.Time // Time this info was built
|
Time time.Time // Time this info was built
|
||||||
|
|
@ -33,14 +49,14 @@ type WissKIInfo struct {
|
||||||
Pathbuilders map[string]string // all the pathbuilders
|
Pathbuilders map[string]string // all the pathbuilders
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info fetches information about this WissKI.
|
// Fetch fetches information about this WissKI.
|
||||||
// TODO: Rework this to be able to determine what kind of information is available.
|
// TODO: Rework this to be able to determine what kind of information is available.
|
||||||
func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) {
|
func (wisski *Info) Fetch(quick bool) (info WissKIInfo, err error) {
|
||||||
var group errgroup.Group
|
var group errgroup.Group
|
||||||
wisski.infoQuick(&info, &group)
|
wisski.infoQuick(&info, &group)
|
||||||
|
|
||||||
if !quick {
|
if !quick {
|
||||||
server, err := wisski.NewPHPServer()
|
server, err := wisski.PHP.NewServer()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -51,45 +67,40 @@ func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) infoQuick(info *WissKIInfo, group *errgroup.Group) {
|
func (wisski *Info) infoQuick(info *WissKIInfo, group *errgroup.Group) {
|
||||||
info.Time = time.Now().UTC()
|
info.Time = time.Now().UTC()
|
||||||
info.Slug = wisski.Slug
|
info.Slug = wisski.Slug
|
||||||
info.URL = wisski.URL().String()
|
info.URL = wisski.URL().String()
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.Running, err = wisski.Running()
|
info.Running, err = wisski.Barrel.Running()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.Locked = wisski.IsLocked()
|
info.Locked = wisski.Locker.Locked()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.LastRebuild, _ = wisski.LastRebuild()
|
info.LastRebuild, _ = wisski.Barrel.LastRebuild()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.LastUpdate, _ = wisski.LastUpdate()
|
info.LastUpdate, _ = wisski.Drush.LastUpdate()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.LastRebuild, _ = wisski.LastRebuild()
|
info.NoPrefixes = wisski.Prefixes.NoPrefix()
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
|
||||||
info.NoPrefixes = wisski.NoPrefix()
|
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) infoSlow(info *WissKIInfo, server *PHPServer, group *errgroup.Group) {
|
func (wisski *Info) infoSlow(info *WissKIInfo, server *php.Server, group *errgroup.Group) {
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.Prefixes, _ = wisski.Prefixes(server)
|
info.Prefixes, _ = wisski.Prefixes.All(server)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -99,21 +110,12 @@ func (wisski *WissKI) infoSlow(info *WissKIInfo, server *PHPServer, group *errgr
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.Pathbuilders, _ = wisski.AllPathbuilders(server)
|
info.Pathbuilders, _ = wisski.Pathbuilder.GetAll(server)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
group.Go(func() (err error) {
|
group.Go(func() (err error) {
|
||||||
info.LastCron, _ = wisski.LastCron(server)
|
info.LastCron, _ = wisski.Drush.LastCron(server)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running checks if this WissKI is currently running.
|
|
||||||
func (wisski *WissKI) Running() (bool, error) {
|
|
||||||
ps, err := wisski.Barrel().Ps(stream.FromNil())
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return len(ps) > 0, nil
|
|
||||||
}
|
|
||||||
48
internal/wisski/ingredient/ingredient.go
Normal file
48
internal/wisski/ingredient/ingredient.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package ingredient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ingredients represent a part of a WissKI instance.
|
||||||
|
// An Ingredient should be implemented as a pointer to a struct.
|
||||||
|
// Every ingredient must embed [Base] and should be initialized using [Init] inside a [lazy.Pool].
|
||||||
|
//
|
||||||
|
// By convention these are defined within their corresponding subpackage.
|
||||||
|
// This subpackage also contains all required resources.
|
||||||
|
type Ingredient interface {
|
||||||
|
// Name returns the name of this ingredient
|
||||||
|
// Name should be implemented by the [Base] struct.
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// getBase returns the underlying Base object of this Ingredient.
|
||||||
|
// It is used internally during initialization
|
||||||
|
getBase() *Base
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base is embedded into every Ingredient
|
||||||
|
type Base struct {
|
||||||
|
name string // name is the name of this ingredient
|
||||||
|
*liquid.Liquid // the underlying liquid
|
||||||
|
}
|
||||||
|
|
||||||
|
//lint:ignore U1000 used to implement the private methods of [Component]
|
||||||
|
func (cb *Base) getBase() *Base {
|
||||||
|
return cb
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes a new Ingredient.
|
||||||
|
// Init is only intended to be used within a lazy.Pool[Ingredient,*Liquid].
|
||||||
|
func Init(ingredient Ingredient, liquid *liquid.Liquid) Ingredient {
|
||||||
|
base := ingredient.getBase() // pointer to a struct
|
||||||
|
base.Liquid = liquid
|
||||||
|
base.name = strings.ToLower(reflect.TypeOf(ingredient).Elem().Name())
|
||||||
|
return ingredient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cb Base) Name() string {
|
||||||
|
return cb.name
|
||||||
|
}
|
||||||
44
internal/wisski/ingredient/locker/lock.go
Normal file
44
internal/wisski/ingredient/locker/lock.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package locker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/tkw1536/goprogram/exit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locker provides facitilites for locking this WissKI instance
|
||||||
|
type Locker struct {
|
||||||
|
ingredient.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
var Locked = exit.Error{
|
||||||
|
Message: "WissKI Instance is locked for administrative operations",
|
||||||
|
ExitCode: exit.ExitGeneric,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryLock attemps to lock this WissKI and returns if it suceeded
|
||||||
|
func (lock *Locker) TryLock() bool {
|
||||||
|
table, err := lock.Malt.SQL.QueryTable(true, models.LockTable)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
result := table.FirstOrCreate(&models.Lock{}, models.Lock{Slug: lock.Slug})
|
||||||
|
return result.Error == nil && result.RowsAffected == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryUnlock attempts to unlock this WissKI and reports if it succeeded.
|
||||||
|
// An unlock can only
|
||||||
|
func (lock *Locker) TryUnlock() bool {
|
||||||
|
table, err := lock.Malt.SQL.QueryTable(true, models.LockTable)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
result := table.Where("slug = ?", lock.Slug).Delete(&models.Lock{})
|
||||||
|
return result.Error == nil && result.RowsAffected == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks this WissKI, ignoring any error.
|
||||||
|
func (lock *Locker) Unlock() {
|
||||||
|
lock.TryUnlock()
|
||||||
|
}
|
||||||
15
internal/wisski/ingredient/locker/locked.go
Normal file
15
internal/wisski/ingredient/locker/locked.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package locker
|
||||||
|
|
||||||
|
import "github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
|
||||||
|
// Locked checks if this WissKI is currently locked.
|
||||||
|
func (lock *Locker) Locked() (locked bool) {
|
||||||
|
table, err := lock.SQL.QueryTable(true, models.LockTable)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this instance is locked
|
||||||
|
table.Select("count(*) > 0").Where("slug = ?", lock.Slug).Find(&locked)
|
||||||
|
return
|
||||||
|
}
|
||||||
43
internal/wisski/ingredient/mstore/mstore.go
Normal file
43
internal/wisski/ingredient/mstore/mstore.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package mstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MStore implements metadata storage for this WissKI
|
||||||
|
type MStore struct {
|
||||||
|
ingredient.Base
|
||||||
|
*meta.Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// For is a Store for the provided value
|
||||||
|
type For[Value any] meta.Key
|
||||||
|
|
||||||
|
func (f For[Value]) Get(m *MStore) (value Value, err error) {
|
||||||
|
err = m.Storage.Get(meta.Key(f), &value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f For[Value]) GetAll(m *MStore) (values []Value, err error) {
|
||||||
|
err = m.Storage.GetAll(meta.Key(f), func(index, total int) any {
|
||||||
|
if values == nil {
|
||||||
|
values = make([]Value, total)
|
||||||
|
}
|
||||||
|
return &values[index]
|
||||||
|
})
|
||||||
|
return values, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f For[Value]) Set(m *MStore, value Value) error {
|
||||||
|
return m.Storage.Set(meta.Key(f), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f For[Value]) SetAll(m *MStore, values ...Value) error {
|
||||||
|
return m.Storage.SetAll(meta.Key(f), collection.AsAny(values)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f For[Value]) Delete(m *MStore) error {
|
||||||
|
return m.Storage.Delete(meta.Key(f))
|
||||||
|
}
|
||||||
44
internal/wisski/ingredient/php/extras/pathbuilder.go
Normal file
44
internal/wisski/ingredient/php/extras/pathbuilder.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package extras
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pathbuilder struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
PHP *php.PHP
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed pathbuilder.php
|
||||||
|
var pathbuilderPHP string
|
||||||
|
|
||||||
|
// All returns the ids of all pathbuilders in consistent order.
|
||||||
|
//
|
||||||
|
// server is the server to fetch the pathbuilders from, any may be nil.
|
||||||
|
func (pathbuilder *Pathbuilder) All(server *php.Server) (ids []string, err error) {
|
||||||
|
err = pathbuilder.PHP.ExecScript(server, &ids, pathbuilderPHP, "all_list")
|
||||||
|
slices.Sort(ids)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a single pathbuilder as xml.
|
||||||
|
// If it does not exist, it returns the empty string and nil error.
|
||||||
|
//
|
||||||
|
// server is the server to fetch the pathbuilders from, any may be nil.
|
||||||
|
func (pathbuilder *Pathbuilder) Get(server *php.Server, id string) (xml string, err error) {
|
||||||
|
err = pathbuilder.PHP.ExecScript(server, &xml, pathbuilderPHP, "one_xml", id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAll returns all pathbuilders serialized as xml
|
||||||
|
//
|
||||||
|
// server is the server to fetch the pathbuilders from, any may be nil.
|
||||||
|
func (pathbuilder *Pathbuilder) GetAll(server *php.Server) (pathbuilders map[string]string, err error) {
|
||||||
|
err = pathbuilder.PHP.ExecScript(server, &pathbuilders, pathbuilderPHP, "all_xml")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -1,47 +1,57 @@
|
||||||
package wisski
|
package extras
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"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/pkg/fsx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/fsx"
|
||||||
"github.com/tkw1536/goprogram/lib/collection"
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NoPrefix checks if this WissKI instance is excluded from generating prefixes.
|
// Prefixes implements reading and writing prefix
|
||||||
// TODO: Move this to the database!
|
type Prefixes struct {
|
||||||
func (wisski *WissKI) NoPrefix() bool {
|
ingredient.Base
|
||||||
return fsx.IsFile(wisski.Core.Environment, filepath.Join(wisski.FilesystemBase, "prefixes.skip"))
|
|
||||||
|
PHP *php.PHP
|
||||||
|
MStore *mstore.MStore
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed php/list_uri_prefixes.php
|
// NoPrefix checks if this WissKI instance is excluded from generating prefixes.
|
||||||
|
// TODO: Move this to the database!
|
||||||
|
func (prefixes *Prefixes) NoPrefix() bool {
|
||||||
|
return fsx.IsFile(prefixes.Malt.Environment, filepath.Join(prefixes.FilesystemBase, "prefixes.skip"))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed prefixes.php
|
||||||
var listURIPrefixesPHP string
|
var listURIPrefixesPHP string
|
||||||
|
|
||||||
// Prefixes returns the prefixes applying to this WissKI
|
// All returns the prefixes applying to this WissKI
|
||||||
//
|
//
|
||||||
// server is an optional server to fetch prefixes from.
|
// server is an optional server to fetch prefixes from.
|
||||||
// server may be nil.
|
// server may be nil.
|
||||||
func (wisski *WissKI) Prefixes(server *PHPServer) ([]string, error) {
|
func (prefixes *Prefixes) All(server *php.Server) ([]string, error) {
|
||||||
prefixes, err := wisski.dbPrefixes(server)
|
uris, err := prefixes.database(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixes2, err := wisski.filePrefixes()
|
uris2, err := prefixes.filePrefixes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(prefixes, prefixes2...), nil
|
return append(uris, uris2...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) dbPrefixes(server *PHPServer) (prefixes []string, err error) {
|
func (wisski *Prefixes) database(server *php.Server) (prefixes []string, err error) {
|
||||||
// get all the ugly prefixes
|
// get all the ugly prefixes
|
||||||
err = wisski.ExecPHPScript(server, &prefixes, listURIPrefixesPHP, "list_prefixes")
|
err = wisski.PHP.ExecScript(server, &prefixes, listURIPrefixesPHP, "list_prefixes")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +62,7 @@ func (wisski *WissKI) dbPrefixes(server *PHPServer) (prefixes []string, err erro
|
||||||
})
|
})
|
||||||
|
|
||||||
// load the list of blocked prefixes
|
// load the list of blocked prefixes
|
||||||
blocks, err := wisski.blockedPrefixes()
|
blocks, err := wisski.blocked()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -61,9 +71,10 @@ func (wisski *WissKI) dbPrefixes(server *PHPServer) (prefixes []string, err erro
|
||||||
return collection.Filter(prefixes, func(uri string) bool { return !hasAnyPrefix(uri, blocks) }), nil
|
return collection.Filter(prefixes, func(uri string) bool { return !hasAnyPrefix(uri, blocks) }), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) blockedPrefixes() ([]string, error) {
|
func (prefixes *Prefixes) blocked() ([]string, error) {
|
||||||
// open the resolver block file
|
// open the resolver block file
|
||||||
file, err := wisski.Core.Environment.Open(wisski.Core.Config.SelfResolverBlockFile)
|
// TODO: move this to the distillery
|
||||||
|
file, err := prefixes.Malt.Environment.Open(prefixes.Malt.Config.SelfResolverBlockFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -98,13 +109,13 @@ func hasAnyPrefix(candidate string, prefixes []string) bool {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
func (wisski *Prefixes) filePrefixes() (prefixes []string, err error) {
|
||||||
path := filepath.Join(wisski.FilesystemBase, "prefixes")
|
path := filepath.Join(wisski.FilesystemBase, "prefixes")
|
||||||
if !fsx.IsFile(wisski.Core.Environment, path) {
|
if !fsx.IsFile(wisski.Malt.Environment, path) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := wisski.Core.Environment.Open(path)
|
file, err := wisski.Malt.Environment.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -127,18 +138,18 @@ func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
||||||
|
|
||||||
// CACHING
|
// CACHING
|
||||||
|
|
||||||
var prefix = meta.StorageFor[string]("prefix")
|
var prefix = mstore.For[string]("prefix")
|
||||||
|
|
||||||
// Prefixes returns the cached prefixes from the given instance
|
// Prefixes returns the cached prefixes from the given instance
|
||||||
func (wisski *WissKI) PrefixesCached() (results []string, err error) {
|
func (wisski *Prefixes) PrefixesCached() (results []string, err error) {
|
||||||
return prefix(wisski.storage()).GetAll()
|
return prefix.GetAll(wisski.MStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePrefixes updates the cached prefixes of this instance
|
// UpdatePrefixes updates the cached prefixes of this instance
|
||||||
func (wisski *WissKI) UpdatePrefixes() error {
|
func (wisski *Prefixes) UpdatePrefixes() error {
|
||||||
prefixes, err := wisski.Prefixes(nil)
|
prefixes, err := wisski.All(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return prefix(wisski.storage()).SetAll(prefixes...)
|
return prefix.SetAll(wisski.MStore, prefixes...)
|
||||||
}
|
}
|
||||||
26
internal/wisski/ingredient/php/extras/settings.go
Normal file
26
internal/wisski/ingredient/php/extras/settings.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package extras
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
PHP *php.PHP
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed settings.php
|
||||||
|
var settingsPHP string
|
||||||
|
|
||||||
|
func (settings *Settings) Get(server *php.Server, key string) (value any, err error) {
|
||||||
|
err = settings.PHP.ExecScript(server, &value, settingsPHP, "get_setting", key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (settings *Settings) Set(server *php.Server, key string, value any) error {
|
||||||
|
return settings.PHP.ExecScript(server, nil, settingsPHP, "set_setting", key, value)
|
||||||
|
}
|
||||||
58
internal/wisski/ingredient/php/php.go
Normal file
58
internal/wisski/ingredient/php/php.go
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package php
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PHP struct {
|
||||||
|
ingredient.Base
|
||||||
|
|
||||||
|
Barrel *barrel.Barrel
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecScript executes the PHP code as a script on the given server.
|
||||||
|
// When server is nil, creates a new server and automatically closes it after execution.
|
||||||
|
// Calling this function repeatedly with server = nil is inefficient.
|
||||||
|
//
|
||||||
|
// The script should define a function called entrypoint, and may define additional functions.
|
||||||
|
//
|
||||||
|
// Code must start with "<?php" and may not contain a closing tag.
|
||||||
|
// Code is expected not to mess with PHPs output buffer.
|
||||||
|
// Code should not contain user input.
|
||||||
|
// Code breaking these conventions may or may not result in an error.
|
||||||
|
//
|
||||||
|
// It's arguments are encoded as json using [json.Marshal] and decoded within php.
|
||||||
|
//
|
||||||
|
// The return value of the function is again marshaled with json and returned to the caller.
|
||||||
|
func (php *PHP) ExecScript(server *Server, value any, code string, entrypoint string, args ...any) (err error) {
|
||||||
|
if server == nil {
|
||||||
|
server, err = php.NewServer()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if code != "" {
|
||||||
|
if err := server.MarshalEval(nil, strings.TrimPrefix(code, "<?php")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.MarshalCall(value, entrypoint, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (php *PHP) EvalCode(server *Server, value any, code string) (err error) {
|
||||||
|
if server == nil {
|
||||||
|
server, err = php.NewServer()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.MarshalEval(value, code)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package wisski
|
package php
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -21,50 +21,50 @@ import (
|
||||||
var (
|
var (
|
||||||
errPHPInit = "Unable to initialize"
|
errPHPInit = "Unable to initialize"
|
||||||
errPHPMarshal = "Marshal failed"
|
errPHPMarshal = "Marshal failed"
|
||||||
errPHPInvalid = PHPServerError{Message: "Invalid code to execute"}
|
errPHPInvalid = ServerError{Message: "Invalid code to execute"}
|
||||||
errPHPReceive = "Failed to receive response"
|
errPHPReceive = "Failed to receive response"
|
||||||
errPHPClosed = PHPServerError{Message: "Server closed"}
|
errPHPClosed = ServerError{Message: "Server closed"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// PHPError represents an error during PHPServer logic
|
// PHPError represents an error during PHPServer logic
|
||||||
type PHPServerError struct {
|
type ServerError struct {
|
||||||
Message string
|
Message string
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err PHPServerError) Unwrap() error {
|
func (err ServerError) Unwrap() error {
|
||||||
return err.Err
|
return err.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err PHPServerError) Error() string {
|
func (err ServerError) Error() string {
|
||||||
if err.Err == nil {
|
if err.Err == nil {
|
||||||
return fmt.Sprintf("PHPServer: %s", err.Message)
|
return fmt.Sprintf("PHPServer: %s", err.Message)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("PHPServer: %s: %s", err.Message, err.Err)
|
return fmt.Sprintf("PHPServer: %s: %s", err.Message, err.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHPThrowable represents an error during php code
|
// Throwable represents an error during php code
|
||||||
type PHPThrowable string
|
type Throwable string
|
||||||
|
|
||||||
func (throwable PHPThrowable) Error() string {
|
func (throwable Throwable) Error() string {
|
||||||
return string(throwable)
|
return string(throwable)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPHPServer returns a new server that can execute code within this distillery.
|
// NewServer returns a new server that can execute code within this distillery.
|
||||||
// When err == nil, the caller must call server.Close().
|
// When err == nil, the caller must call server.Close().
|
||||||
//
|
//
|
||||||
// See [PHPServer].
|
// See [PHPServer].
|
||||||
func (wisski *WissKI) NewPHPServer() (*PHPServer, error) {
|
func (php *PHP) NewServer() (*Server, error) {
|
||||||
// create input and output pipes
|
// create input and output pipes
|
||||||
ir, iw, err := os.Pipe()
|
ir, iw, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, PHPServerError{errPHPInit, err}
|
return nil, ServerError{errPHPInit, err}
|
||||||
}
|
}
|
||||||
or, ow, err := os.Pipe()
|
or, ow, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ir.Close()
|
ir.Close()
|
||||||
iw.Close()
|
iw.Close()
|
||||||
return nil, PHPServerError{errPHPInit, err}
|
return nil, ServerError{errPHPInit, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a context to close the server
|
// create a context to close the server
|
||||||
|
|
@ -83,22 +83,22 @@ func (wisski *WissKI) NewPHPServer() (*PHPServer, error) {
|
||||||
|
|
||||||
// start the server
|
// start the server
|
||||||
io := stream.NewIOStream(ow, nil, ir, 0)
|
io := stream.NewIOStream(ow, nil, ir, 0)
|
||||||
wisski.Shell(io, "-c", shellescape.QuoteCommand([]string{"drush", "php:eval", serverPHP}))
|
php.Barrel.Shell(io, "-c", shellescape.QuoteCommand([]string{"drush", "php:eval", serverPHP}))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// return the seerver
|
// return the seerver
|
||||||
return &PHPServer{
|
return &Server{
|
||||||
in: iw,
|
in: iw,
|
||||||
out: or,
|
out: or,
|
||||||
c: context,
|
c: context,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHPServer represents a server that executes code within a distillery.
|
// Server represents a server that executes code within a distillery.
|
||||||
// A typical use-case is to define functions using [MarshalEval], and then call those functions [MarshalCall].
|
// A typical use-case is to define functions using [MarshalEval], and then call those functions [MarshalCall].
|
||||||
//
|
//
|
||||||
// A nil PHPServer will return [ErrServerBroken] on every function call.
|
// A nil Server will return [ErrServerBroken] on every function call.
|
||||||
type PHPServer struct {
|
type Server struct {
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
|
|
||||||
in io.WriteCloser
|
in io.WriteCloser
|
||||||
|
|
@ -113,7 +113,7 @@ type PHPServer struct {
|
||||||
// as such any functions defined will remain in server memory.
|
// as such any functions defined will remain in server memory.
|
||||||
//
|
//
|
||||||
// When an exception is thrown by the PHP Code, error is not nil, and dest remains unchanged.
|
// When an exception is thrown by the PHP Code, error is not nil, and dest remains unchanged.
|
||||||
func (server *PHPServer) MarshalEval(value any, code string) error {
|
func (server *Server) MarshalEval(value any, code string) error {
|
||||||
server.m.Lock()
|
server.m.Lock()
|
||||||
defer server.m.Unlock()
|
defer server.m.Unlock()
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ func (server *PHPServer) MarshalEval(value any, code string) error {
|
||||||
// marshal the code, and send it to the server
|
// marshal the code, and send it to the server
|
||||||
bytes, err := json.Marshal(code)
|
bytes, err := json.Marshal(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
return ServerError{Message: errPHPMarshal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send it to the server
|
// send it to the server
|
||||||
|
|
@ -134,19 +134,19 @@ func (server *PHPServer) MarshalEval(value any, code string) error {
|
||||||
// read the next line (as a response)
|
// read the next line (as a response)
|
||||||
data, err := nobufio.ReadLine(server.out)
|
data, err := nobufio.ReadLine(server.out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PHPServerError{Message: errPHPReceive, Err: err}
|
return ServerError{Message: errPHPReceive, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read whatever we received
|
// read whatever we received
|
||||||
var received [2]json.RawMessage
|
var received [2]json.RawMessage
|
||||||
if err := json.Unmarshal([]byte(data), &received); err != nil {
|
if err := json.Unmarshal([]byte(data), &received); err != nil {
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
return ServerError{Message: errPHPMarshal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if there was an error
|
// check if there was an error
|
||||||
var errString string
|
var errString string
|
||||||
if err := json.Unmarshal(received[1], &errString); err == nil && errString != "" {
|
if err := json.Unmarshal(received[1], &errString); err == nil && errString != "" {
|
||||||
return PHPThrowable(errString)
|
return Throwable(errString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case: no return value => no unmarshaling needed
|
// special case: no return value => no unmarshaling needed
|
||||||
|
|
@ -159,7 +159,7 @@ func (server *PHPServer) MarshalEval(value any, code string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval is like [MarshalEval], but returns the value as an any
|
// Eval is like [MarshalEval], but returns the value as an any
|
||||||
func (server *PHPServer) Eval(code string) (value any, err error) {
|
func (server *Server) Eval(code string) (value any, err error) {
|
||||||
err = server.MarshalEval(&value, code)
|
err = server.MarshalEval(&value, code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -168,18 +168,18 @@ func (server *PHPServer) Eval(code string) (value any, err error) {
|
||||||
// Arguments are sent to php using json Marshal, and are 'json_decode'd on the php side.
|
// Arguments are sent to php using json Marshal, and are 'json_decode'd on the php side.
|
||||||
//
|
//
|
||||||
// Return values are received as in [MarshalEval].
|
// Return values are received as in [MarshalEval].
|
||||||
func (server *PHPServer) MarshalCall(value any, function string, args ...any) error {
|
func (server *Server) MarshalCall(value any, function string, args ...any) error {
|
||||||
// marshal a code for the call
|
// marshal a code for the call
|
||||||
userFunction, err := marshalPHP(function)
|
userFunction, err := marshalPHP(function)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
return ServerError{Message: errPHPMarshal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
userFunctionArgs := "[]"
|
userFunctionArgs := "[]"
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
userFunctionArgs, err = marshalPHP(args)
|
userFunctionArgs, err = marshalPHP(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
return ServerError{Message: errPHPMarshal, Err: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code := "return call_user_func_array(" + userFunction + "," + userFunctionArgs + ");"
|
code := "return call_user_func_array(" + userFunction + "," + userFunctionArgs + ");"
|
||||||
|
|
@ -189,7 +189,7 @@ func (server *PHPServer) MarshalCall(value any, function string, args ...any) er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call is like [MarshalCall] but returns the return value of the function as an any
|
// Call is like [MarshalCall] but returns the return value of the function as an any
|
||||||
func (server *PHPServer) Call(function string, args ...any) (value any, err error) {
|
func (server *Server) Call(function string, args ...any) (value any, err error) {
|
||||||
err = server.MarshalCall(&value, function, args...)
|
err = server.MarshalCall(&value, function, args...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@ func marshalPHP(data any) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes this server and prevents any further code from being run.
|
// Close closes this server and prevents any further code from being run.
|
||||||
func (server *PHPServer) Close() error {
|
func (server *Server) Close() error {
|
||||||
server.m.Lock()
|
server.m.Lock()
|
||||||
defer server.m.Unlock()
|
defer server.m.Unlock()
|
||||||
|
|
||||||
|
|
@ -248,51 +248,7 @@ func (server *PHPServer) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecPHPScript executes the PHP code as a script on the given server.
|
//go:embed server.php
|
||||||
// When server is nil, creates a new server and automatically closes it after execution.
|
|
||||||
// Calling this function repeatedly with server = nil is inefficient.
|
|
||||||
//
|
|
||||||
// The script should define a function called entrypoint, and may define additional functions.
|
|
||||||
//
|
|
||||||
// Code must start with "<?php" and may not contain a closing tag.
|
|
||||||
// Code is expected not to mess with PHPs output buffer.
|
|
||||||
// Code should not contain user input.
|
|
||||||
// Code breaking these conventions may or may not result in an error.
|
|
||||||
//
|
|
||||||
// It's arguments are encoded as json using [json.Marshal] and decoded within php.
|
|
||||||
//
|
|
||||||
// The return value of the function is again marshaled with json and returned to the caller.
|
|
||||||
func (wisski *WissKI) ExecPHPScript(server *PHPServer, value any, code string, entrypoint string, args ...any) (err error) {
|
|
||||||
if server == nil {
|
|
||||||
server, err = wisski.NewPHPServer()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if code != "" {
|
|
||||||
if err := server.MarshalEval(nil, strings.TrimPrefix(code, "<?php")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.MarshalCall(value, entrypoint, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wisski *WissKI) EvalPHPCode(server *PHPServer, value any, code string) (err error) {
|
|
||||||
if server == nil {
|
|
||||||
server, err = wisski.NewPHPServer()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer server.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.MarshalEval(value, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:embed php/server.php
|
|
||||||
var serverPHP string
|
var serverPHP string
|
||||||
|
|
||||||
// pre-process the server.php code to make it shorter
|
// pre-process the server.php code to make it shorter
|
||||||
40
internal/wisski/ingredient/reserve/reserve.go
Normal file
40
internal/wisski/ingredient/reserve/reserve.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package reserve
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reserve implements reserving a WissKI Instance
|
||||||
|
// TODO: This should be integrated into the bookkeeping table.
|
||||||
|
type Reserve struct {
|
||||||
|
ingredient.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed all:reserve reserve.env
|
||||||
|
var reserveResources embed.FS
|
||||||
|
|
||||||
|
// Stack returns a stack representing the reserve instance
|
||||||
|
func (reserve *Reserve) Stack() component.StackWithResources {
|
||||||
|
return component.StackWithResources{
|
||||||
|
Stack: component.Stack{
|
||||||
|
Dir: reserve.FilesystemBase,
|
||||||
|
Env: reserve.Malt.Environment,
|
||||||
|
},
|
||||||
|
|
||||||
|
Resources: reserveResources,
|
||||||
|
ContextPath: filepath.Join("reserve"),
|
||||||
|
EnvPath: filepath.Join("reserve.env"),
|
||||||
|
|
||||||
|
EnvContext: map[string]string{
|
||||||
|
"DOCKER_NETWORK_NAME": reserve.Malt.Config.DockerNetworkName,
|
||||||
|
|
||||||
|
"SLUG": reserve.Slug,
|
||||||
|
"VIRTUAL_HOST": reserve.Domain(),
|
||||||
|
"HTTPS_ENABLED": reserve.Malt.Config.HTTPSEnabledEnv(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
65
internal/wisski/ingredients.go
Normal file
65
internal/wisski/ingredients.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package wisski
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
||||||
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// ==== init ====
|
||||||
|
//
|
||||||
|
|
||||||
|
func (wisski *WissKI) init() {
|
||||||
|
wisski.poolInit.Do(func() {
|
||||||
|
wisski.pool.Init = ingredient.Init
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ==== registration ====
|
||||||
|
//
|
||||||
|
|
||||||
|
// manual initializes a component from the provided distillery.
|
||||||
|
func manual[I ingredient.Ingredient](init func(ingredient I)) initFunc {
|
||||||
|
return func(context ctx) ingredient.Ingredient {
|
||||||
|
return lazy.Make(context, init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use is like r, but does not provided additional initialization
|
||||||
|
func auto[I ingredient.Ingredient](context ctx) ingredient.Ingredient {
|
||||||
|
return lazy.Make[ingredient.Ingredient, I](context, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register returns all components of the distillery
|
||||||
|
func (wisski *WissKI) register(context ctx) []ingredient.Ingredient {
|
||||||
|
return collection.MapSlice(
|
||||||
|
wisski.allIngredients(),
|
||||||
|
func(f initFunc) ingredient.Ingredient {
|
||||||
|
return f(context)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctx is a context for component initialization
|
||||||
|
type ctx = *lazy.PoolContext[ingredient.Ingredient]
|
||||||
|
|
||||||
|
//
|
||||||
|
// ==== export ====
|
||||||
|
//
|
||||||
|
|
||||||
|
// export is a convenience function to export a single component
|
||||||
|
func export[I ingredient.Ingredient](wisski *WissKI) I {
|
||||||
|
wisski.init()
|
||||||
|
return lazy.ExportComponent[ingredient.Ingredient, *liquid.Liquid, I](&wisski.pool, &wisski.Liquid, wisski.register)
|
||||||
|
}
|
||||||
|
|
||||||
|
//lint:ignore U1000 for future use
|
||||||
|
func exportAll[I ingredient.Ingredient](wisski *WissKI) []I {
|
||||||
|
wisski.init()
|
||||||
|
return lazy.ExportComponents[ingredient.Ingredient, *liquid.Liquid, I](&wisski.pool, &wisski.Liquid, wisski.register)
|
||||||
|
}
|
||||||
|
|
||||||
|
type initFunc = func(context ctx) ingredient.Ingredient
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package wisski
|
package liquid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -6,20 +6,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Domain returns the full domain name of this WissKI
|
// Domain returns the full domain name of this WissKI
|
||||||
func (wisski WissKI) Domain() string {
|
func (liquid *Liquid) Domain() string {
|
||||||
return fmt.Sprintf("%s.%s", wisski.Slug, wisski.Core.Config.DefaultDomain)
|
return fmt.Sprintf("%s.%s", liquid.Slug, liquid.Malt.Config.DefaultDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL returns the public URL of this instance
|
// URL returns the public URL of this instance
|
||||||
func (wisski WissKI) URL() *url.URL {
|
func (liquid *Liquid) URL() *url.URL {
|
||||||
// setup domain and path
|
// setup domain and path
|
||||||
url := &url.URL{
|
url := &url.URL{
|
||||||
Host: wisski.Domain(),
|
Host: liquid.Domain(),
|
||||||
Path: "/",
|
Path: "/",
|
||||||
}
|
}
|
||||||
|
|
||||||
// use http or https scheme depending on if the distillery has it enabled
|
// use http or https scheme depending on if the distillery has it enabled
|
||||||
if wisski.Core.Config.HTTPSEnabled() {
|
if liquid.Malt.Config.HTTPSEnabled() {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
} else {
|
} else {
|
||||||
url.Scheme = "http"
|
url.Scheme = "http"
|
||||||
16
internal/wisski/liquid/liquid.go
Normal file
16
internal/wisski/liquid/liquid.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Package liquid provides Liquid
|
||||||
|
package liquid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/malt"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Liquid is the core of a WissKI Instance and used in every ingredient.
|
||||||
|
type Liquid struct {
|
||||||
|
*malt.Malt
|
||||||
|
models.Instance
|
||||||
|
|
||||||
|
DrupalUsername string
|
||||||
|
DrupalPassword string
|
||||||
|
}
|
||||||
10
internal/wisski/liquid/snapshots.go
Normal file
10
internal/wisski/liquid/snapshots.go
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
package liquid
|
||||||
|
|
||||||
|
import "github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
|
||||||
|
// Snapshots returns the list of snapshots of this WissKI
|
||||||
|
// NOTE(twiesing): Not entirely sure where this should go.
|
||||||
|
// It's not that this is
|
||||||
|
func (liquid *Liquid) Snapshots() (snapshots []models.Export, err error) {
|
||||||
|
return liquid.Malt.ExporterLog.For(liquid.Slug)
|
||||||
|
}
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrLocked = errors.New("instance is locked")
|
|
||||||
|
|
||||||
// TryLock attemps to lock this WissKI
|
|
||||||
// If this is not possible, returns ErrLocked
|
|
||||||
func (wisski *WissKI) TryLock() error {
|
|
||||||
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
|
||||||
if err != nil {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
|
|
||||||
result := table.FirstOrCreate(&models.Lock{}, models.Lock{Slug: wisski.Slug})
|
|
||||||
locked := result.Error == nil && result.RowsAffected == 1
|
|
||||||
|
|
||||||
if !locked {
|
|
||||||
return ErrLocked
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wisski *WissKI) IsLocked() (locked bool) {
|
|
||||||
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this instance is locked
|
|
||||||
table.Select("count(*) > 0").Where("slug = ?", wisski.Slug).Find(&locked)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unlock unlocks this WissKI instance and returns if it succeeded
|
|
||||||
func (wisski WissKI) Unlock() bool {
|
|
||||||
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
result := table.Where("slug = ?", wisski.Slug).Delete(&models.Lock{})
|
|
||||||
return result.Error == nil && result.RowsAffected == 1
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import "github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
|
||||||
|
|
||||||
func (wisski *WissKI) storage() *meta.Storage {
|
|
||||||
return wisski.Meta.Storage(wisski.Slug)
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed php/export_pathbuilder.php
|
|
||||||
var exportPathbuilderPHP string
|
|
||||||
|
|
||||||
// Pathbuilders returns the ids of all pathbuilders in consistent order.
|
|
||||||
//
|
|
||||||
// server is the server to fetch the pathbuilders from, any may be nil.
|
|
||||||
func (wisski *WissKI) Pathbuilders(server *PHPServer) (ids []string, err error) {
|
|
||||||
err = wisski.ExecPHPScript(server, &ids, exportPathbuilderPHP, "all_list")
|
|
||||||
slices.Sort(ids)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pathbuilder returns a single pathbuilder as xml.
|
|
||||||
// If it does not exist, it returns the empty string and nil error.
|
|
||||||
//
|
|
||||||
// server is the server to fetch the pathbuilders from, any may be nil.
|
|
||||||
func (wisski *WissKI) Pathbuilder(server *PHPServer, id string) (xml string, err error) {
|
|
||||||
err = wisski.ExecPHPScript(server, &xml, exportPathbuilderPHP, "one_xml", id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllPathbuilders returns all pathbuilders serialized as xml
|
|
||||||
//
|
|
||||||
// server is the server to fetch the pathbuilders from, any may be nil.
|
|
||||||
func (wisski *WissKI) AllPathbuilders(server *PHPServer) (pathbuilders map[string]string, err error) {
|
|
||||||
err = wisski.ExecPHPScript(server, &pathbuilders, exportPathbuilderPHP, "all_xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed php/settings.php
|
|
||||||
var settingsPHP string
|
|
||||||
|
|
||||||
func (wisski *WissKI) GetSettingsPHP(server *PHPServer, key string) (value any, err error) {
|
|
||||||
err = wisski.ExecPHPScript(server, &value, settingsPHP, "get_setting", key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wisski *WissKI) SetSettingsPHP(server *PHPServer, key string, value any) error {
|
|
||||||
return wisski.ExecPHPScript(server, nil, settingsPHP, "set_setting", key, value)
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alessio/shellescape"
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Provision provisions an instance, assuming that the required databases already exist.
|
|
||||||
func (wisski *WissKI) Provision(io stream.IOStream) error {
|
|
||||||
|
|
||||||
// build the container
|
|
||||||
if err := wisski.Build(io, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
provisionParams := []string{
|
|
||||||
wisski.Domain(),
|
|
||||||
|
|
||||||
wisski.SqlDatabase,
|
|
||||||
wisski.SqlUsername,
|
|
||||||
wisski.SqlPassword,
|
|
||||||
|
|
||||||
wisski.GraphDBRepository,
|
|
||||||
wisski.GraphDBUsername,
|
|
||||||
wisski.GraphDBPassword,
|
|
||||||
|
|
||||||
wisski.DrupalUsername,
|
|
||||||
wisski.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 := wisski.Barrel().Run(io, true, "barrel", "/bin/bash", "-c", provisionScript)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if code != 0 {
|
|
||||||
return errors.New("unable to run provision script")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import "github.com/FAU-CDI/wisski-distillery/internal/models"
|
|
||||||
|
|
||||||
// Snapshots returns the list of snapshots of this WissKI
|
|
||||||
func (wisski *WissKI) Snapshots() (snapshots []models.Export, err error) {
|
|
||||||
return wisski.ExporterLog.For(wisski.Slug)
|
|
||||||
}
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"path/filepath"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed all:instances/barrel instances/barrel.env
|
|
||||||
var barrelResources embed.FS
|
|
||||||
|
|
||||||
// Barrel returns a stack representing the running WissKI Instance
|
|
||||||
func (wisski *WissKI) Barrel() component.StackWithResources {
|
|
||||||
return component.StackWithResources{
|
|
||||||
Stack: component.Stack{
|
|
||||||
Dir: wisski.FilesystemBase,
|
|
||||||
Env: wisski.Core.Environment,
|
|
||||||
},
|
|
||||||
|
|
||||||
Resources: barrelResources,
|
|
||||||
ContextPath: filepath.Join("instances", "barrel"),
|
|
||||||
EnvPath: filepath.Join("instances", "barrel.env"),
|
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
|
||||||
"DOCKER_NETWORK_NAME": wisski.Core.Config.DockerNetworkName,
|
|
||||||
|
|
||||||
"SLUG": wisski.Slug,
|
|
||||||
"VIRTUAL_HOST": wisski.Domain(),
|
|
||||||
"HTTPS_ENABLED": wisski.Core.Config.HTTPSEnabledEnv(),
|
|
||||||
|
|
||||||
"DATA_PATH": filepath.Join(wisski.FilesystemBase, "data"),
|
|
||||||
"RUNTIME_DIR": wisski.Core.Config.RuntimeDir(),
|
|
||||||
"GLOBAL_AUTHORIZED_KEYS_FILE": wisski.Core.Config.GlobalAuthorizedKeysFile,
|
|
||||||
},
|
|
||||||
|
|
||||||
MakeDirs: []string{"data", ".composer"},
|
|
||||||
|
|
||||||
TouchFiles: []string{
|
|
||||||
filepath.Join("data", "authorized_keys"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move this to time.Time
|
|
||||||
var lastRebuild = meta.StorageFor[int64]("lastRebuild")
|
|
||||||
|
|
||||||
func (wisski *WissKI) LastRebuild() (t time.Time, err error) {
|
|
||||||
epoch, err := lastRebuild(wisski.storage()).Get()
|
|
||||||
if err == meta.ErrMetadatumNotSet {
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// and turn it into time!
|
|
||||||
return time.Unix(epoch, 0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wisski *WissKI) setLastRebuild() error {
|
|
||||||
return lastRebuild(wisski.storage()).Set(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build builds or rebuilds the barel connected to this instance.
|
|
||||||
//
|
|
||||||
// It also logs the current time into the metadata belonging to this instance.
|
|
||||||
func (wisski *WissKI) Build(stream stream.IOStream, start bool) error {
|
|
||||||
if err := wisski.TryLock(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer wisski.Unlock()
|
|
||||||
|
|
||||||
barrel := wisski.Barrel()
|
|
||||||
|
|
||||||
var context component.InstallationContext
|
|
||||||
|
|
||||||
{
|
|
||||||
err := barrel.Install(stream, context)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
err := barrel.Update(stream, start)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the current last rebuild
|
|
||||||
return wisski.setLastRebuild()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:embed all:instances/reserve instances/reserve.env
|
|
||||||
var reserveResources embed.FS
|
|
||||||
|
|
||||||
// Reserve returns a stack representing the reserve instance
|
|
||||||
func (wisski *WissKI) Reserve() component.StackWithResources {
|
|
||||||
return component.StackWithResources{
|
|
||||||
Stack: component.Stack{
|
|
||||||
Dir: wisski.FilesystemBase,
|
|
||||||
Env: wisski.Core.Environment,
|
|
||||||
},
|
|
||||||
|
|
||||||
Resources: reserveResources,
|
|
||||||
ContextPath: filepath.Join("instances", "reserve"),
|
|
||||||
EnvPath: filepath.Join("instances", "reserve.env"),
|
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
|
||||||
"DOCKER_NETWORK_NAME": wisski.Core.Config.DockerNetworkName,
|
|
||||||
|
|
||||||
"SLUG": wisski.Slug,
|
|
||||||
"VIRTUAL_HOST": wisski.Domain(),
|
|
||||||
"HTTPS_ENABLED": wisski.Core.Config.HTTPSEnabledEnv(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shell executes a shell command inside the instance.
|
|
||||||
func (wisski *WissKI) Shell(io stream.IOStream, argv ...string) (int, error) {
|
|
||||||
return wisski.Barrel().Exec(io, "barrel", "/bin/sh", append([]string{"/user_shell.sh"}, argv...)...)
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package wisski
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
|
||||||
"github.com/tkw1536/goprogram/exit"
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errBlindUpdateFailed = exit.Error{
|
|
||||||
Message: "Failed to run blind update script for instance %q: exited with code %s",
|
|
||||||
ExitCode: exit.ExitGeneric,
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlinUpdate performs a blind update of the given instance
|
|
||||||
func (wisski *WissKI) BlindUpdate(io stream.IOStream) error {
|
|
||||||
code, err := wisski.Shell(io, "/runtime/blind_update.sh")
|
|
||||||
if err != nil {
|
|
||||||
return errBlindUpdateFailed.WithMessageF(wisski.Slug, environment.ExecCommandError)
|
|
||||||
}
|
|
||||||
if code != 0 {
|
|
||||||
return errBlindUpdateFailed.WithMessageF(wisski.Slug, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
return wisski.setLastUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastUpdate = meta.StorageFor[int64]("lastUpdate")
|
|
||||||
|
|
||||||
func (wisski *WissKI) LastUpdate() (t time.Time, err error) {
|
|
||||||
epoch, err := lastUpdate(wisski.storage()).Get()
|
|
||||||
if err == meta.ErrMetadatumNotSet {
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// and turn it into time!
|
|
||||||
return time.Unix(epoch, 0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wisski *WissKI) setLastUpdate() error {
|
|
||||||
return lastUpdate(wisski.storage()).Set(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
@ -2,60 +2,108 @@
|
||||||
package wisski
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"sync"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter/logger"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/drush"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/provisioner"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"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/locker"
|
||||||
|
"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/extras"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/reserve"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WissKI represents a single WissKI Instance
|
// WissKI represents a single WissKI Instance.
|
||||||
|
// A WissKI may not be copied
|
||||||
type WissKI struct {
|
type WissKI struct {
|
||||||
models.Instance // whatever is stored inside the underlying instance
|
liquid.Liquid
|
||||||
|
|
||||||
// Drupal credentials - not stored in the database
|
poolInit sync.Once
|
||||||
DrupalUsername string
|
pool lazy.Pool[ingredient.Ingredient, *liquid.Liquid]
|
||||||
DrupalPassword string
|
|
||||||
|
|
||||||
// references to components!
|
|
||||||
Core component.Still
|
|
||||||
Meta *meta.Meta
|
|
||||||
TS *triplestore.Triplestore
|
|
||||||
SQL *sql.SQL
|
|
||||||
|
|
||||||
ExporterLog *logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves this instance in the bookkeeping table
|
//
|
||||||
func (wisski *WissKI) Save() error {
|
// PUBLIC INGREDIENT GETTERS
|
||||||
db, err := wisski.SQL.QueryTable(false, models.InstanceTable)
|
//
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// it has never been created => we need to create it in the database
|
func (wisski *WissKI) Locker() *locker.Locker {
|
||||||
if wisski.Instance.Created.IsZero() {
|
return export[*locker.Locker](wisski)
|
||||||
return db.Create(&wisski.Instance).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update based on the primary key!
|
|
||||||
return db.Where("pk = ?", wisski.Instance.Pk).Updates(&wisski.Instance).Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes this instance from the bookkeeping table
|
func (wisski *WissKI) Reserve() *reserve.Reserve {
|
||||||
func (wisski *WissKI) Delete() error {
|
return export[*reserve.Reserve](wisski)
|
||||||
db, err := wisski.SQL.QueryTable(false, models.InstanceTable)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func (wisski *WissKI) Barrel() *barrel.Barrel {
|
||||||
}
|
return export[*barrel.Barrel](wisski)
|
||||||
|
}
|
||||||
// doesn't exist => nothing to delete
|
|
||||||
if wisski.Instance.Created.IsZero() {
|
func (wisski *WissKI) Provisioner() *provisioner.Provisioner {
|
||||||
return nil
|
return export[*provisioner.Provisioner](wisski)
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete it directly
|
func (wisski *WissKI) PHP() *php.PHP {
|
||||||
return db.Delete(&wisski.Instance).Error
|
return export[*php.PHP](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Bookkeeping() *bookkeeping.Bookkeeping {
|
||||||
|
return export[*bookkeeping.Bookkeeping](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Drush() *drush.Drush {
|
||||||
|
return export[*drush.Drush](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Prefixes() *extras.Prefixes {
|
||||||
|
return export[*extras.Prefixes](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Settings() *extras.Settings {
|
||||||
|
return export[*extras.Settings](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Pathbuilder() *extras.Pathbuilder {
|
||||||
|
return export[*extras.Pathbuilder](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wisski *WissKI) Info() *info.Info {
|
||||||
|
return export[*info.Info](wisski)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// All components
|
||||||
|
// THESE SHOULD NEVER BE CALLED DIRECTLY
|
||||||
|
//
|
||||||
|
|
||||||
|
func (wisski *WissKI) allIngredients() []initFunc {
|
||||||
|
return []initFunc{
|
||||||
|
// core bits
|
||||||
|
auto[*locker.Locker],
|
||||||
|
manual(func(m *mstore.MStore) {
|
||||||
|
m.Storage = wisski.Malt.Meta.Storage(wisski.Slug)
|
||||||
|
}),
|
||||||
|
|
||||||
|
// php
|
||||||
|
auto[*php.PHP],
|
||||||
|
auto[*extras.Prefixes],
|
||||||
|
auto[*extras.Settings],
|
||||||
|
auto[*extras.Pathbuilder],
|
||||||
|
|
||||||
|
// info
|
||||||
|
auto[*info.Info],
|
||||||
|
|
||||||
|
// stacks
|
||||||
|
auto[*barrel.Barrel],
|
||||||
|
auto[*bookkeeping.Bookkeeping],
|
||||||
|
auto[*provisioner.Provisioner],
|
||||||
|
auto[*drush.Drush],
|
||||||
|
|
||||||
|
auto[*reserve.Reserve],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue