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!
|
||||
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 {
|
||||
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!
|
||||
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 {
|
||||
return fmt.Sprintf("cron %q", item.Slug)
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (ds setting) Run(context wisski_distillery.Context) error {
|
|||
|
||||
if ds.Positionals.Value == "" {
|
||||
// get the setting
|
||||
value, err := instance.GetSettingsPHP(nil, ds.Positionals.Setting)
|
||||
value, err := instance.Settings().Get(nil, ds.Positionals.Setting)
|
||||
if err != nil {
|
||||
return errSettingGet.Wrap(err)
|
||||
}
|
||||
|
|
@ -69,7 +69,7 @@ func (ds setting) Run(context wisski_distillery.Context) error {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func (i info) Run(context wisski_distillery.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
info, err := instance.Info(false)
|
||||
info, err := instance.Info().Fetch(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package cmd
|
|||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/cli"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
|
|
@ -51,15 +52,15 @@ func (l instanceLock) Run(context wisski_distillery.Context) error {
|
|||
}
|
||||
|
||||
if l.Unlock {
|
||||
if !instance.Unlock() {
|
||||
if !instance.Locker().TryUnlock() {
|
||||
return errNotUnlock
|
||||
}
|
||||
context.Println("unlocked")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := instance.TryLock(); err != nil {
|
||||
return err
|
||||
if !instance.Locker().TryLock() {
|
||||
return locker.Locked
|
||||
}
|
||||
|
||||
context.Println("locked")
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func (pb pathbuilders) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// get all of the pathbuilders
|
||||
if pb.Positionals.Name == "" {
|
||||
names, err := instance.Pathbuilders(nil)
|
||||
names, err := instance.Pathbuilder().All(nil)
|
||||
if err != nil {
|
||||
return errPathbuilders.WithMessageF(err)
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ func (pb pathbuilders) Run(context wisski_distillery.Context) error {
|
|||
}
|
||||
|
||||
// get all the pathbuilders
|
||||
xml, err := instance.Pathbuilder(nil, pb.Positionals.Name)
|
||||
xml, err := instance.Pathbuilder().Get(nil, pb.Positionals.Name)
|
||||
if xml == "" {
|
||||
return errNoPathbuilder.WithMessageF(pb.Positionals.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func (p prefixes) Run(context wisski_distillery.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
prefixes, err := instance.Prefixes(nil)
|
||||
prefixes, err := instance.Prefixes().All(nil)
|
||||
if err != nil {
|
||||
return errPrefixesGeneric.Wrap(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// Store in the instances table!
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := instance.Save(); err != nil {
|
||||
if err := instance.Bookkeeping().Save(); err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// run the provision script
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ func (p provision) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// start the 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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func (p purge) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// remove docker stack
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -93,13 +93,13 @@ func (p purge) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// remove 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)
|
||||
}
|
||||
|
||||
// remove the filesystem
|
||||
logging.LogMessage(context.IOStream, "Remove lock data", instance.FilesystemBase)
|
||||
if !instance.Unlock() {
|
||||
if instance.Locker().TryUnlock() {
|
||||
context.EPrintln("instance was not locked")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (rb rebuild) Run(context wisski_distillery.Context) error {
|
|||
|
||||
// and do the actual rebuild
|
||||
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 {
|
||||
return fmt.Sprintf("rebuild %q", item.Slug)
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func (r reserve) Run(context wisski_distillery.Context) error {
|
|||
}
|
||||
|
||||
// setup docker stack
|
||||
s := instance.Reserve()
|
||||
s := instance.Reserve().Stack()
|
||||
{
|
||||
if err := logging.LogOperation(func() error {
|
||||
return s.Install(context.IOStream, component.InstallationContext{})
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func (sh shell) Run(context wisski_distillery.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
code, err := instance.Shell(context.IOStream, sh.Positionals.Args...)
|
||||
code, err := instance.Barrel().Shell(context.IOStream, sh.Positionals.Args...)
|
||||
if err != nil {
|
||||
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 {
|
||||
io.Println("reading prefixes")
|
||||
err := instance.UpdatePrefixes()
|
||||
err := instance.Prefixes().UpdatePrefixes()
|
||||
if err != nil {
|
||||
return errPrefixUpdateFailed.Wrap(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,6 @@ func auto[C component.Component](context ctx) component.Component {
|
|||
|
||||
// register returns all components of the distillery
|
||||
func (dis *Distillery) register(context ctx) []component.Component {
|
||||
dis.poolInit.Do(func() {
|
||||
dis.pool.Init = component.Init
|
||||
})
|
||||
|
||||
return collection.MapSlice(
|
||||
dis.allComponents(),
|
||||
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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||
"github.com/tkw1536/goprogram/lib/collection"
|
||||
"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) {
|
||||
|
||||
logging.LogMessage(io, "Locking instance")
|
||||
if err := instance.TryLock(); err != nil {
|
||||
if !instance.Locker().TryLock() {
|
||||
err := locker.Locked
|
||||
io.EPrintln(err)
|
||||
logging.LogMessage(io, "Aborting snapshot creation")
|
||||
|
||||
|
|
@ -56,7 +58,7 @@ func (snapshots *Exporter) NewSnapshot(instance *wisski.WissKI, io stream.IOStre
|
|||
}
|
||||
defer func() {
|
||||
logging.LogMessage(io, "Unlocking instance")
|
||||
instance.Unlock()
|
||||
instance.Locker().Unlock()
|
||||
}()
|
||||
|
||||
// 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 {
|
||||
if !needsRunning && !snapshot.Description.Keepalive {
|
||||
stack := instance.Barrel()
|
||||
stack := instance.Barrel().Stack()
|
||||
|
||||
logging.LogMessage(ios, "Stopping instance")
|
||||
snapshot.ErrStop = stack.Down(ios)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
_ "embed"
|
||||
|
||||
"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/tkw1536/goprogram/stream"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
|
@ -65,7 +65,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
context.Instances = make([]wisski.WissKIInfo, len(wissKIs))
|
||||
context.Instances = make([]info.WissKIInfo, len(wissKIs))
|
||||
|
||||
// determine their infos
|
||||
var eg errgroup.Group
|
||||
|
|
@ -73,7 +73,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
|||
i := i
|
||||
wissKI := instance
|
||||
eg.Go(func() (err error) {
|
||||
context.Instances[i], err = wissKI.Info(true)
|
||||
context.Instances[i], err = wissKI.Info().Fetch(true)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
|||
}
|
||||
|
||||
type HomeContext struct {
|
||||
Instances []wisski.WissKIInfo
|
||||
Instances []info.WissKIInfo
|
||||
|
||||
Time time.Time
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"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/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/info"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ type indexPageContext struct {
|
|||
|
||||
Config *config.Config
|
||||
|
||||
Instances []wisski.WissKIInfo
|
||||
Instances []info.WissKIInfo
|
||||
|
||||
TotalCount int
|
||||
RunningCount int
|
||||
|
|
@ -31,18 +31,18 @@ type indexPageContext struct {
|
|||
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
|
||||
|
||||
group.Go(func() error {
|
||||
// list all the instances
|
||||
all, err := info.Instances.All()
|
||||
all, err := nfo.Instances.All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get all of their info!
|
||||
idx.Instances = make([]wisski.WissKIInfo, len(all))
|
||||
idx.Instances = make([]info.WissKIInfo, len(all))
|
||||
for i, instance := range all {
|
||||
{
|
||||
i := i
|
||||
|
|
@ -50,7 +50,7 @@ func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error
|
|||
|
||||
// store the info for this group!
|
||||
group.Go(func() (err error) {
|
||||
idx.Instances[i], err = instance.Info(true)
|
||||
idx.Instances[i], err = instance.Info().Fetch(true)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
@ -61,12 +61,12 @@ func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error
|
|||
|
||||
// get the log entries
|
||||
group.Go(func() (err error) {
|
||||
idx.Backups, err = info.SnapshotsLog.For("")
|
||||
idx.Backups, err = nfo.SnapshotsLog.For("")
|
||||
return
|
||||
})
|
||||
|
||||
// get the static properties
|
||||
idx.Config = info.Config
|
||||
idx.Config = nfo.Config
|
||||
idx.Time = time.Now().UTC()
|
||||
|
||||
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/static"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ type instancePageContext struct {
|
|||
Time time.Time
|
||||
|
||||
Instance models.Instance
|
||||
Info wisski.WissKIInfo
|
||||
Info info.WissKIInfo
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// get some more info about the wisski
|
||||
is.Info, err = instance.Info(false)
|
||||
is.Info, err = instance.Info().Fetch(false)
|
||||
if err != nil {
|
||||
return is, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ var socketInstanceActions = map[string]instanceActionFunc{
|
|||
)
|
||||
},
|
||||
"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 {
|
||||
return instance.BlindUpdate(str)
|
||||
return instance.Drush().Update(str)
|
||||
},
|
||||
"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)
|
||||
instances.use(wissKI)
|
||||
|
||||
wissKI.Instance.Slug = slug
|
||||
wissKI.Instance.FilesystemBase = filepath.Join(instances.Path(), wissKI.Domain())
|
||||
wissKI.Liquid.Instance.Slug = slug
|
||||
wissKI.Liquid.Instance.FilesystemBase = filepath.Join(instances.Path(), wissKI.Domain())
|
||||
|
||||
wissKI.Instance.OwnerEmail = ""
|
||||
wissKI.Instance.AutoBlindUpdateEnabled = true
|
||||
wissKI.Liquid.Instance.OwnerEmail = ""
|
||||
wissKI.Liquid.Instance.AutoBlindUpdateEnabled = true
|
||||
|
||||
// sql
|
||||
|
||||
wissKI.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + slug
|
||||
wissKI.Instance.SqlUsername = instances.Config.MysqlUserPrefix + slug
|
||||
wissKI.Liquid.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// triplestore
|
||||
|
||||
wissKI.Instance.GraphDBRepository = instances.Config.GraphDBRepoPrefix + slug
|
||||
wissKI.Instance.GraphDBUsername = instances.Config.GraphDBUserPrefix + slug
|
||||
wissKI.Liquid.Instance.GraphDBRepository = instances.Config.GraphDBRepoPrefix + 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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/exporter/logger"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/meta"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/malt"
|
||||
"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/wisski"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
|
|
@ -21,10 +19,8 @@ import (
|
|||
type Instances struct {
|
||||
component.Base
|
||||
|
||||
TS *triplestore.Triplestore
|
||||
Malt *malt.Malt
|
||||
SQL *sql.SQL
|
||||
Meta *meta.Meta
|
||||
ExporterLog *logger.Logger
|
||||
}
|
||||
|
||||
func (instances *Instances) Path() string {
|
||||
|
|
@ -41,11 +37,7 @@ var errSQL = exit.Error{
|
|||
|
||||
// use uses the non-nil wisski instance with this instances
|
||||
func (instances *Instances) use(wisski *wisski.WissKI) {
|
||||
wisski.Core = instances.Still
|
||||
wisski.SQL = instances.SQL
|
||||
wisski.TS = instances.TS
|
||||
wisski.Meta = instances.Meta
|
||||
wisski.ExporterLog = instances.ExporterLog
|
||||
wisski.Liquid.Malt = instances.Malt
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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 {
|
||||
case query.Error != nil:
|
||||
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))
|
||||
for i, bk := range bks {
|
||||
results[i] = new(wisski.WissKI)
|
||||
results[i].Instance = bk
|
||||
results[i].Liquid.Instance = bk
|
||||
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/models"
|
||||
"github.com/tkw1536/goprogram/lib/collection"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
|
@ -177,42 +176,3 @@ func (s Storage) Purge() error {
|
|||
}
|
||||
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)
|
||||
var lastErr error
|
||||
for _, instance := range instances {
|
||||
if instance.NoPrefix() {
|
||||
if instance.Prefixes().NoPrefix() {
|
||||
continue
|
||||
}
|
||||
url := instance.URL().String()
|
||||
|
||||
// failed to fetch prefixes for this particular instance
|
||||
// => skip it!
|
||||
prefixes, err := instance.PrefixesCached()
|
||||
prefixes, err := instance.Prefixes().PrefixesCached()
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
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/info"
|
||||
"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/resolver"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||
|
|
@ -114,8 +115,10 @@ func (dis *Distillery) allComponents() []initFunc {
|
|||
sql.PollInterval = time.Second
|
||||
}),
|
||||
|
||||
// instainces
|
||||
auto[*instances.Instances],
|
||||
auto[*meta.Meta],
|
||||
auto[*malt.Malt],
|
||||
|
||||
// Snapshots
|
||||
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 (
|
||||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
)
|
||||
|
|
@ -12,23 +13,23 @@ var errCronFailed = exit.Error{
|
|||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (wisski *WissKI) Cron(io stream.IOStream) error {
|
||||
code, err := wisski.Shell(io, "/runtime/cron.sh")
|
||||
func (drush *Drush) Cron(io stream.IOStream) error {
|
||||
code, err := drush.Barrel.Shell(io, "/runtime/cron.sh")
|
||||
if err != nil {
|
||||
io.EPrintln(err)
|
||||
}
|
||||
if code != 0 {
|
||||
// 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)
|
||||
}
|
||||
|
||||
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
|
||||
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 {
|
||||
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 (
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
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.
|
||||
type WissKIInfo struct {
|
||||
Time time.Time // Time this info was built
|
||||
|
|
@ -33,14 +49,14 @@ type WissKIInfo struct {
|
|||
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.
|
||||
func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) {
|
||||
func (wisski *Info) Fetch(quick bool) (info WissKIInfo, err error) {
|
||||
var group errgroup.Group
|
||||
wisski.infoQuick(&info, &group)
|
||||
|
||||
if !quick {
|
||||
server, err := wisski.NewPHPServer()
|
||||
server, err := wisski.PHP.NewServer()
|
||||
if err == nil {
|
||||
defer server.Close()
|
||||
}
|
||||
|
|
@ -51,45 +67,40 @@ func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) {
|
|||
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.Slug = wisski.Slug
|
||||
info.URL = wisski.URL().String()
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.Running, err = wisski.Running()
|
||||
info.Running, err = wisski.Barrel.Running()
|
||||
return
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.Locked = wisski.IsLocked()
|
||||
info.Locked = wisski.Locker.Locked()
|
||||
return
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.LastRebuild, _ = wisski.LastRebuild()
|
||||
info.LastRebuild, _ = wisski.Barrel.LastRebuild()
|
||||
return
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.LastUpdate, _ = wisski.LastUpdate()
|
||||
info.LastUpdate, _ = wisski.Drush.LastUpdate()
|
||||
return
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.LastRebuild, _ = wisski.LastRebuild()
|
||||
return
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.NoPrefixes = wisski.NoPrefix()
|
||||
info.NoPrefixes = wisski.Prefixes.NoPrefix()
|
||||
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) {
|
||||
info.Prefixes, _ = wisski.Prefixes(server)
|
||||
info.Prefixes, _ = wisski.Prefixes.All(server)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
@ -99,21 +110,12 @@ func (wisski *WissKI) infoSlow(info *WissKIInfo, server *PHPServer, group *errgr
|
|||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.Pathbuilders, _ = wisski.AllPathbuilders(server)
|
||||
info.Pathbuilders, _ = wisski.Pathbuilder.GetAll(server)
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Go(func() (err error) {
|
||||
info.LastCron, _ = wisski.LastCron(server)
|
||||
info.LastCron, _ = wisski.Drush.LastCron(server)
|
||||
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 (
|
||||
"bufio"
|
||||
"path/filepath"
|
||||
"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/tkw1536/goprogram/lib/collection"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// NoPrefix checks if this WissKI instance is excluded from generating prefixes.
|
||||
// TODO: Move this to the database!
|
||||
func (wisski *WissKI) NoPrefix() bool {
|
||||
return fsx.IsFile(wisski.Core.Environment, filepath.Join(wisski.FilesystemBase, "prefixes.skip"))
|
||||
// Prefixes implements reading and writing prefix
|
||||
type Prefixes struct {
|
||||
ingredient.Base
|
||||
|
||||
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
|
||||
|
||||
// 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 may be nil.
|
||||
func (wisski *WissKI) Prefixes(server *PHPServer) ([]string, error) {
|
||||
prefixes, err := wisski.dbPrefixes(server)
|
||||
func (prefixes *Prefixes) All(server *php.Server) ([]string, error) {
|
||||
uris, err := prefixes.database(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prefixes2, err := wisski.filePrefixes()
|
||||
uris2, err := prefixes.filePrefixes()
|
||||
if err != nil {
|
||||
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
|
||||
err = wisski.ExecPHPScript(server, &prefixes, listURIPrefixesPHP, "list_prefixes")
|
||||
err = wisski.PHP.ExecScript(server, &prefixes, listURIPrefixesPHP, "list_prefixes")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -52,7 +62,7 @@ func (wisski *WissKI) dbPrefixes(server *PHPServer) (prefixes []string, err erro
|
|||
})
|
||||
|
||||
// load the list of blocked prefixes
|
||||
blocks, err := wisski.blockedPrefixes()
|
||||
blocks, err := wisski.blocked()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (wisski *WissKI) blockedPrefixes() ([]string, error) {
|
||||
func (prefixes *Prefixes) blocked() ([]string, error) {
|
||||
// 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 {
|
||||
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")
|
||||
if !fsx.IsFile(wisski.Core.Environment, path) {
|
||||
if !fsx.IsFile(wisski.Malt.Environment, path) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
file, err := wisski.Core.Environment.Open(path)
|
||||
file, err := wisski.Malt.Environment.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -127,18 +138,18 @@ func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
|||
|
||||
// CACHING
|
||||
|
||||
var prefix = meta.StorageFor[string]("prefix")
|
||||
var prefix = mstore.For[string]("prefix")
|
||||
|
||||
// Prefixes returns the cached prefixes from the given instance
|
||||
func (wisski *WissKI) PrefixesCached() (results []string, err error) {
|
||||
return prefix(wisski.storage()).GetAll()
|
||||
func (wisski *Prefixes) PrefixesCached() (results []string, err error) {
|
||||
return prefix.GetAll(wisski.MStore)
|
||||
}
|
||||
|
||||
// UpdatePrefixes updates the cached prefixes of this instance
|
||||
func (wisski *WissKI) UpdatePrefixes() error {
|
||||
prefixes, err := wisski.Prefixes(nil)
|
||||
func (wisski *Prefixes) UpdatePrefixes() error {
|
||||
prefixes, err := wisski.All(nil)
|
||||
if err != nil {
|
||||
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 (
|
||||
"context"
|
||||
|
|
@ -21,50 +21,50 @@ import (
|
|||
var (
|
||||
errPHPInit = "Unable to initialize"
|
||||
errPHPMarshal = "Marshal failed"
|
||||
errPHPInvalid = PHPServerError{Message: "Invalid code to execute"}
|
||||
errPHPInvalid = ServerError{Message: "Invalid code to execute"}
|
||||
errPHPReceive = "Failed to receive response"
|
||||
errPHPClosed = PHPServerError{Message: "Server closed"}
|
||||
errPHPClosed = ServerError{Message: "Server closed"}
|
||||
)
|
||||
|
||||
// PHPError represents an error during PHPServer logic
|
||||
type PHPServerError struct {
|
||||
type ServerError struct {
|
||||
Message string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err PHPServerError) Unwrap() error {
|
||||
func (err ServerError) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
func (err PHPServerError) Error() string {
|
||||
func (err ServerError) Error() string {
|
||||
if err.Err == nil {
|
||||
return fmt.Sprintf("PHPServer: %s", err.Message)
|
||||
}
|
||||
return fmt.Sprintf("PHPServer: %s: %s", err.Message, err.Err)
|
||||
}
|
||||
|
||||
// PHPThrowable represents an error during php code
|
||||
type PHPThrowable string
|
||||
// Throwable represents an error during php code
|
||||
type Throwable string
|
||||
|
||||
func (throwable PHPThrowable) Error() string {
|
||||
func (throwable Throwable) Error() string {
|
||||
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().
|
||||
//
|
||||
// See [PHPServer].
|
||||
func (wisski *WissKI) NewPHPServer() (*PHPServer, error) {
|
||||
func (php *PHP) NewServer() (*Server, error) {
|
||||
// create input and output pipes
|
||||
ir, iw, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, PHPServerError{errPHPInit, err}
|
||||
return nil, ServerError{errPHPInit, err}
|
||||
}
|
||||
or, ow, err := os.Pipe()
|
||||
if err != nil {
|
||||
ir.Close()
|
||||
iw.Close()
|
||||
return nil, PHPServerError{errPHPInit, err}
|
||||
return nil, ServerError{errPHPInit, err}
|
||||
}
|
||||
|
||||
// create a context to close the server
|
||||
|
|
@ -83,22 +83,22 @@ func (wisski *WissKI) NewPHPServer() (*PHPServer, error) {
|
|||
|
||||
// start the server
|
||||
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 &PHPServer{
|
||||
return &Server{
|
||||
in: iw,
|
||||
out: or,
|
||||
c: context,
|
||||
}, 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 nil PHPServer will return [ErrServerBroken] on every function call.
|
||||
type PHPServer struct {
|
||||
// A nil Server will return [ErrServerBroken] on every function call.
|
||||
type Server struct {
|
||||
m sync.Mutex
|
||||
|
||||
in io.WriteCloser
|
||||
|
|
@ -113,7 +113,7 @@ type PHPServer struct {
|
|||
// 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.
|
||||
func (server *PHPServer) MarshalEval(value any, code string) error {
|
||||
func (server *Server) MarshalEval(value any, code string) error {
|
||||
server.m.Lock()
|
||||
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
|
||||
bytes, err := json.Marshal(code)
|
||||
if err != nil {
|
||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
||||
return ServerError{Message: errPHPMarshal, Err: err}
|
||||
}
|
||||
|
||||
// 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)
|
||||
data, err := nobufio.ReadLine(server.out)
|
||||
if err != nil {
|
||||
return PHPServerError{Message: errPHPReceive, Err: err}
|
||||
return ServerError{Message: errPHPReceive, Err: err}
|
||||
}
|
||||
|
||||
// read whatever we received
|
||||
var received [2]json.RawMessage
|
||||
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
|
||||
var errString string
|
||||
if err := json.Unmarshal(received[1], &errString); err == nil && errString != "" {
|
||||
return PHPThrowable(errString)
|
||||
return Throwable(errString)
|
||||
}
|
||||
|
||||
// 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
|
||||
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)
|
||||
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.
|
||||
//
|
||||
// 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
|
||||
userFunction, err := marshalPHP(function)
|
||||
if err != nil {
|
||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
||||
return ServerError{Message: errPHPMarshal, Err: err}
|
||||
}
|
||||
|
||||
userFunctionArgs := "[]"
|
||||
if len(args) > 0 {
|
||||
userFunctionArgs, err = marshalPHP(args)
|
||||
if err != nil {
|
||||
return PHPServerError{Message: errPHPMarshal, Err: err}
|
||||
return ServerError{Message: errPHPMarshal, Err: err}
|
||||
}
|
||||
}
|
||||
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
|
||||
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...)
|
||||
return
|
||||
}
|
||||
|
|
@ -233,7 +233,7 @@ func marshalPHP(data any) (string, error) {
|
|||
}
|
||||
|
||||
// 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()
|
||||
defer server.m.Unlock()
|
||||
|
||||
|
|
@ -248,51 +248,7 @@ func (server *PHPServer) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ExecPHPScript 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 (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
|
||||
//go:embed server.php
|
||||
var serverPHP string
|
||||
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
|
@ -6,20 +6,20 @@ import (
|
|||
)
|
||||
|
||||
// Domain returns the full domain name of this WissKI
|
||||
func (wisski WissKI) Domain() string {
|
||||
return fmt.Sprintf("%s.%s", wisski.Slug, wisski.Core.Config.DefaultDomain)
|
||||
func (liquid *Liquid) Domain() string {
|
||||
return fmt.Sprintf("%s.%s", liquid.Slug, liquid.Malt.Config.DefaultDomain)
|
||||
}
|
||||
|
||||
// URL returns the public URL of this instance
|
||||
func (wisski WissKI) URL() *url.URL {
|
||||
func (liquid *Liquid) URL() *url.URL {
|
||||
// setup domain and path
|
||||
url := &url.URL{
|
||||
Host: wisski.Domain(),
|
||||
Host: liquid.Domain(),
|
||||
Path: "/",
|
||||
}
|
||||
|
||||
// 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"
|
||||
} else {
|
||||
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
|
||||
|
||||
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/dis/component/meta"
|
||||
"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/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/barrel/provisioner"
|
||||
"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 {
|
||||
models.Instance // whatever is stored inside the underlying instance
|
||||
liquid.Liquid
|
||||
|
||||
// Drupal credentials - not stored in the database
|
||||
DrupalUsername string
|
||||
DrupalPassword string
|
||||
|
||||
// references to components!
|
||||
Core component.Still
|
||||
Meta *meta.Meta
|
||||
TS *triplestore.Triplestore
|
||||
SQL *sql.SQL
|
||||
|
||||
ExporterLog *logger.Logger
|
||||
poolInit sync.Once
|
||||
pool lazy.Pool[ingredient.Ingredient, *liquid.Liquid]
|
||||
}
|
||||
|
||||
// Save saves this instance in the bookkeeping table
|
||||
func (wisski *WissKI) Save() error {
|
||||
db, err := wisski.SQL.QueryTable(false, models.InstanceTable)
|
||||
if err != nil {
|
||||
return err
|
||||
//
|
||||
// PUBLIC INGREDIENT GETTERS
|
||||
//
|
||||
|
||||
func (wisski *WissKI) Locker() *locker.Locker {
|
||||
return export[*locker.Locker](wisski)
|
||||
}
|
||||
|
||||
// it has never been created => we need to create it in the database
|
||||
if wisski.Instance.Created.IsZero() {
|
||||
return db.Create(&wisski.Instance).Error
|
||||
func (wisski *WissKI) Reserve() *reserve.Reserve {
|
||||
return export[*reserve.Reserve](wisski)
|
||||
}
|
||||
|
||||
// Update based on the primary key!
|
||||
return db.Where("pk = ?", wisski.Instance.Pk).Updates(&wisski.Instance).Error
|
||||
func (wisski *WissKI) Barrel() *barrel.Barrel {
|
||||
return export[*barrel.Barrel](wisski)
|
||||
}
|
||||
|
||||
// Delete deletes this instance from the bookkeeping table
|
||||
func (wisski *WissKI) Delete() error {
|
||||
db, err := wisski.SQL.QueryTable(false, models.InstanceTable)
|
||||
if err != nil {
|
||||
return err
|
||||
func (wisski *WissKI) Provisioner() *provisioner.Provisioner {
|
||||
return export[*provisioner.Provisioner](wisski)
|
||||
}
|
||||
|
||||
// doesn't exist => nothing to delete
|
||||
if wisski.Instance.Created.IsZero() {
|
||||
return nil
|
||||
func (wisski *WissKI) PHP() *php.PHP {
|
||||
return export[*php.PHP](wisski)
|
||||
}
|
||||
|
||||
// delete it directly
|
||||
return db.Delete(&wisski.Instance).Error
|
||||
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