Move wisski instance code to separate package
This commit is contained in:
parent
7c3c84e116
commit
063f3f9b7d
67 changed files with 533 additions and 409 deletions
|
|
@ -41,12 +41,12 @@ func (bk backup) Run(context wisski_distillery.Context) error {
|
||||||
// prune old backups
|
// prune old backups
|
||||||
if !bk.NoPrune {
|
if !bk.NoPrune {
|
||||||
defer logging.LogOperation(func() error {
|
defer logging.LogOperation(func() error {
|
||||||
return dis.SnapshotManager().PruneExports(context.IOStream)
|
return dis.ExportManager().PruneExports(context.IOStream)
|
||||||
}, context.IOStream, "Pruning old backups")
|
}, context.IOStream, "Pruning old backups")
|
||||||
}
|
}
|
||||||
|
|
||||||
// do the handling
|
// do the handling
|
||||||
err := dis.SnapshotManager().MakeExport(context.IOStream, snapshots.ExportTask{
|
err := dis.ExportManager().MakeExport(context.IOStream, snapshots.ExportTask{
|
||||||
Dest: bk.Positionals.Dest,
|
Dest: bk.Positionals.Dest,
|
||||||
StagingOnly: bk.StagingOnly,
|
StagingOnly: bk.StagingOnly,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/lib/collection"
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
|
|
@ -45,15 +45,15 @@ func (bu blindUpdate) Run(context wisski_distillery.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !bu.Force {
|
if !bu.Force {
|
||||||
wissKIs = collection.Filter(wissKIs, func(instance instances.WissKI) bool {
|
wissKIs = collection.Filter(wissKIs, func(instance *wisski.WissKI) bool {
|
||||||
return bool(instance.AutoBlindUpdateEnabled)
|
return bool(instance.AutoBlindUpdateEnabled)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// and do the actual blind_update!
|
// and do the actual blind_update!
|
||||||
return status.StreamGroup(context.IOStream, bu.Parallel, func(instance instances.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.BlindUpdate(str)
|
||||||
}, wissKIs, status.SmartMessage(func(item instances.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)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
@ -39,9 +39,9 @@ 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 instances.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.Cron(io)
|
||||||
}, wissKIs, status.SmartMessage(func(item instances.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("cron %q", item.Slug)
|
return fmt.Sprintf("cron %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,6 @@ func (p purge) Run(context wisski_distillery.Context) error {
|
||||||
context.EPrintln(err)
|
context.EPrintln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.LogMessage(context.IOStream, "Purging instance metadata")
|
|
||||||
if err := instance.Metadata().Purge(); err != nil {
|
|
||||||
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.Unlock() {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
|
|
@ -46,9 +46,9 @@ 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 instances.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.Build(io, true)
|
||||||
}, wissKIs, status.SmartMessage(func(item instances.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("rebuild %q", item.Slug)
|
return fmt.Sprintf("rebuild %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,11 @@ func (sn snapshot) Run(context wisski_distillery.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// do a snapshot of it!
|
// do a snapshot of it!
|
||||||
err = dis.SnapshotManager().MakeExport(context.IOStream, snapshots.ExportTask{
|
err = dis.ExportManager().MakeExport(context.IOStream, snapshots.ExportTask{
|
||||||
Dest: sn.Positionals.Dest,
|
Dest: sn.Positionals.Dest,
|
||||||
StagingOnly: sn.StagingOnly,
|
StagingOnly: sn.StagingOnly,
|
||||||
|
|
||||||
Instance: &instance,
|
Instance: instance,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,8 @@ func (si systemupdate) Run(context wisski_distillery.Context) error {
|
||||||
for _, d := range []string{
|
for _, d := range []string{
|
||||||
dis.Config.DeployRoot,
|
dis.Config.DeployRoot,
|
||||||
dis.Instances().Path(),
|
dis.Instances().Path(),
|
||||||
dis.SnapshotManager().StagingPath(),
|
dis.ExportManager().StagingPath(),
|
||||||
dis.SnapshotManager().ArchivePath(),
|
dis.ExportManager().ArchivePath(),
|
||||||
} {
|
} {
|
||||||
context.Println(d)
|
context.Println(d)
|
||||||
if err := dis.Core.Environment.MkdirAll(d, environment.DefaultDirPerm); err != nil {
|
if err := dis.Core.Environment.MkdirAll(d, environment.DefaultDirPerm); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
"github.com/FAU-CDI/wisski-distillery/internal/core"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
|
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
|
|
@ -42,14 +42,14 @@ func (upc updateprefixconfig) Run(context wisski_distillery.Context) error {
|
||||||
return errPrefixUpdateFailed.Wrap(err)
|
return errPrefixUpdateFailed.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return status.StreamGroup(context.IOStream, upc.Parallel, func(instance instances.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.UpdatePrefixes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errPrefixUpdateFailed.Wrap(err)
|
return errPrefixUpdateFailed.Wrap(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, wissKIs, status.SmartMessage(func(item instances.WissKI) string {
|
}, wissKIs, status.SmartMessage(func(item *wisski.WissKI) string {
|
||||||
return fmt.Sprintf("update_prefix %q", item.Slug)
|
return fmt.Sprintf("update_prefix %q", item.Slug)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"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([]instances.WissKIInfo, len(wissKIs))
|
context.Instances = make([]wisski.WissKIInfo, len(wissKIs))
|
||||||
|
|
||||||
// determine their infos
|
// determine their infos
|
||||||
var eg errgroup.Group
|
var eg errgroup.Group
|
||||||
|
|
@ -86,7 +86,7 @@ func (home *Home) homeRender() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type HomeContext struct {
|
type HomeContext struct {
|
||||||
Instances []instances.WissKIInfo
|
Instances []wisski.WissKIInfo
|
||||||
|
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ import (
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/config"
|
"github.com/FAU-CDI/wisski-distillery/internal/config"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ type indexPageContext struct {
|
||||||
|
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
|
|
||||||
Instances []instances.WissKIInfo
|
Instances []wisski.WissKIInfo
|
||||||
|
|
||||||
TotalCount int
|
TotalCount int
|
||||||
RunningCount int
|
RunningCount int
|
||||||
|
|
@ -42,7 +42,7 @@ func (info *Info) indexPageAPI(r *http.Request) (idx indexPageContext, err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all of their info!
|
// get all of their info!
|
||||||
idx.Instances = make([]instances.WissKIInfo, len(all))
|
idx.Instances = make([]wisski.WissKIInfo, len(all))
|
||||||
for i, instance := range all {
|
for i, instance := range all {
|
||||||
{
|
{
|
||||||
i := i
|
i := i
|
||||||
|
|
@ -61,7 +61,7 @@ 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.Instances.ExportLogFor("")
|
idx.Backups, err = info.SnapshotsLog.For("")
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshotslog"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
@ -16,6 +17,7 @@ type Info struct {
|
||||||
|
|
||||||
SnapshotManager *snapshots.Manager
|
SnapshotManager *snapshots.Manager
|
||||||
Instances *instances.Instances
|
Instances *instances.Instances
|
||||||
|
SnapshotsLog *snapshotslog.SnapshotsLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Info) Name() string { return "control-info" }
|
func (Info) Name() string { return "control-info" }
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/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/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ type instancePageContext struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
|
||||||
Instance models.Instance
|
Instance models.Instance
|
||||||
Info instances.WissKIInfo
|
Info wisski.WissKIInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *Info) instancePageAPI(r *http.Request) (is instancePageContext, err error) {
|
func (info *Info) instancePageAPI(r *http.Request) (is instancePageContext, err error) {
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
package info
|
package info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/tkw1536/goprogram/status"
|
"github.com/tkw1536/goprogram/status"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
type instanceActionFunc = func(info *Info, instance instances.WissKI, str stream.IOStream) error
|
type instanceActionFunc = func(info *Info, instance *wisski.WissKI, str stream.IOStream) error
|
||||||
|
|
||||||
var socketInstanceActions = map[string]instanceActionFunc{
|
var socketInstanceActions = map[string]instanceActionFunc{
|
||||||
"snapshot": func(info *Info, instance instances.WissKI, str stream.IOStream) error {
|
"snapshot": func(info *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return info.SnapshotManager.MakeExport(
|
return info.SnapshotManager.MakeExport(
|
||||||
str,
|
str,
|
||||||
snapshots.ExportTask{
|
snapshots.ExportTask{
|
||||||
Dest: "",
|
Dest: "",
|
||||||
Instance: &instance,
|
Instance: instance,
|
||||||
|
|
||||||
StagingOnly: false,
|
StagingOnly: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"rebuild": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
"rebuild": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.Build(str, true)
|
return instance.Build(str, true)
|
||||||
},
|
},
|
||||||
"update": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
"update": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.BlindUpdate(str)
|
return instance.BlindUpdate(str)
|
||||||
},
|
},
|
||||||
"cron": func(_ *Info, instance instances.WissKI, str stream.IOStream) error {
|
"cron": func(_ *Info, instance *wisski.WissKI, str stream.IOStream) error {
|
||||||
return instance.Cron(str)
|
return instance.Cron(str)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
internal/component/instances/create.go
Normal file
65
internal/component/instances/create.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/stringparser"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidSlug = errors.New("not a valid slug")
|
||||||
|
|
||||||
|
// Create fills the struct for a new WissKI instance.
|
||||||
|
// It validates that slug is a valid name for an instance.
|
||||||
|
//
|
||||||
|
// It does not perform any checks if the instance already exists, or does the creation in the database.
|
||||||
|
func (instances *Instances) Create(slug string) (wissKI *wisski.WissKI, err error) {
|
||||||
|
|
||||||
|
// make sure that the slug is valid!
|
||||||
|
slug, err = stringparser.ParseSlug(instances.Environment, slug)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errInvalidSlug
|
||||||
|
}
|
||||||
|
|
||||||
|
wissKI = new(wisski.WissKI)
|
||||||
|
instances.use(wissKI)
|
||||||
|
|
||||||
|
wissKI.Instance.Slug = slug
|
||||||
|
wissKI.Instance.FilesystemBase = filepath.Join(instances.Path(), wissKI.Domain())
|
||||||
|
|
||||||
|
wissKI.Instance.OwnerEmail = ""
|
||||||
|
wissKI.Instance.AutoBlindUpdateEnabled = true
|
||||||
|
|
||||||
|
// sql
|
||||||
|
|
||||||
|
wissKI.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + slug
|
||||||
|
wissKI.Instance.SqlUsername = instances.Config.MysqlUserPrefix + slug
|
||||||
|
|
||||||
|
wissKI.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.Instance.GraphDBPassword, err = instances.Config.NewPassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drupal
|
||||||
|
|
||||||
|
wissKI.DrupalUsername = "admin" // TODO: Change this!
|
||||||
|
|
||||||
|
wissKI.DrupalPassword, err = instances.Config.NewPassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the instance in the object and return it!
|
||||||
|
return wissKI, nil
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,12 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshotslog"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/triplestore"
|
"github.com/FAU-CDI/wisski-distillery/internal/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/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
|
|
@ -17,8 +20,10 @@ import (
|
||||||
type Instances struct {
|
type Instances struct {
|
||||||
component.ComponentBase
|
component.ComponentBase
|
||||||
|
|
||||||
TS *triplestore.Triplestore
|
TS *triplestore.Triplestore
|
||||||
SQL *sql.SQL
|
SQL *sql.SQL
|
||||||
|
Meta *meta.Meta
|
||||||
|
SnapshotsLog *snapshotslog.SnapshotsLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Instances) Name() string {
|
func (Instances) Name() string {
|
||||||
|
|
@ -37,40 +42,53 @@ var errSQL = exit.Error{
|
||||||
ExitCode: exit.ExitGeneric,
|
ExitCode: exit.ExitGeneric,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance is a convenience function to return an instance based on a model slug.
|
// use uses the non-nil wisski instance with this instances
|
||||||
// When the instance does not exist, returns nil.
|
func (instances *Instances) use(wisski *wisski.WissKI) {
|
||||||
func (instances *Instances) Instance(instance models.Instance) *WissKI {
|
wisski.Core = instances.Core
|
||||||
i, err := instances.WissKI(instance.Slug)
|
wisski.SQL = instances.SQL
|
||||||
if err != nil {
|
wisski.TS = instances.TS
|
||||||
return nil
|
wisski.Meta = instances.Meta
|
||||||
}
|
wisski.SnapshotsLog = instances.SnapshotsLog
|
||||||
return &i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WissKI returns the WissKI with the provided slug, if it exists.
|
// WissKI returns the WissKI with the provided slug, if it exists.
|
||||||
// It the WissKI does not exist, returns ErrWissKINotFound.
|
// It the WissKI does not exist, returns ErrWissKINotFound.
|
||||||
func (instances *Instances) WissKI(slug string) (i WissKI, err error) {
|
func (instances *Instances) WissKI(slug string) (wissKI *wisski.WissKI, err error) {
|
||||||
sql := instances.SQL
|
sql := instances.SQL
|
||||||
if err := sql.WaitQueryTable(); err != nil {
|
if err := sql.WaitQueryTable(); err != nil {
|
||||||
return i, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
table, err := sql.QueryTable(false, models.InstanceTable)
|
table, err := sql.QueryTable(false, models.InstanceTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a struct
|
||||||
|
wissKI = new(wisski.WissKI)
|
||||||
|
|
||||||
// find the instance by slug
|
// find the instance by slug
|
||||||
query := table.Where(&models.Instance{Slug: slug}).Find(&i.Instance)
|
query := table.Where(&models.Instance{Slug: slug}).Find(&wissKI.Instance)
|
||||||
switch {
|
switch {
|
||||||
case query.Error != nil:
|
case query.Error != nil:
|
||||||
return i, errSQL.WithMessageF(query.Error)
|
return nil, errSQL.WithMessageF(query.Error)
|
||||||
case query.RowsAffected == 0:
|
case query.RowsAffected == 0:
|
||||||
return i, ErrWissKINotFound
|
return nil, ErrWissKINotFound
|
||||||
default:
|
|
||||||
i.instances = instances
|
|
||||||
return i, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use the wissKI instance
|
||||||
|
instances.use(wissKI)
|
||||||
|
return wissKI, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance is a convenience function to return an instance based on a model slug.
|
||||||
|
// When the instance does not exist, returns nil.
|
||||||
|
func (instances *Instances) Instance(instance models.Instance) *wisski.WissKI {
|
||||||
|
wissKI, err := instances.WissKI(instance.Slug)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wissKI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has checks if a WissKI with the provided slug exists inside the database.
|
// Has checks if a WissKI with the provided slug exists inside the database.
|
||||||
|
|
@ -96,7 +114,7 @@ func (instances *Instances) Has(slug string) (ok bool, err error) {
|
||||||
// All returns all instances of the WissKI Distillery in consistent order.
|
// All returns all instances of the WissKI Distillery in consistent order.
|
||||||
//
|
//
|
||||||
// There is no guarantee that this order remains identical between different api releases; however subsequent invocations are guaranteed to return the same order.
|
// There is no guarantee that this order remains identical between different api releases; however subsequent invocations are guaranteed to return the same order.
|
||||||
func (instances *Instances) All() ([]WissKI, error) {
|
func (instances *Instances) All() ([]*wisski.WissKI, error) {
|
||||||
return instances.find(true, func(table *gorm.DB) *gorm.DB {
|
return instances.find(true, func(table *gorm.DB) *gorm.DB {
|
||||||
return table
|
return table
|
||||||
})
|
})
|
||||||
|
|
@ -104,14 +122,14 @@ func (instances *Instances) All() ([]WissKI, error) {
|
||||||
|
|
||||||
// WissKIs returns the WissKI instances with the provides slugs.
|
// WissKIs returns the WissKI instances with the provides slugs.
|
||||||
// If a slug does not exist, it is omitted from the result.
|
// If a slug does not exist, it is omitted from the result.
|
||||||
func (instances *Instances) WissKIs(slugs ...string) ([]WissKI, error) {
|
func (instances *Instances) WissKIs(slugs ...string) ([]*wisski.WissKI, error) {
|
||||||
return instances.find(true, func(table *gorm.DB) *gorm.DB {
|
return instances.find(true, func(table *gorm.DB) *gorm.DB {
|
||||||
return table.Where("slug IN ?", slugs)
|
return table.Where("slug IN ?", slugs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load is like All, except that when no slugs are provided, it calls All.
|
// Load is like All, except that when no slugs are provided, it calls All.
|
||||||
func (instances *Instances) Load(slugs ...string) ([]WissKI, error) {
|
func (instances *Instances) Load(slugs ...string) ([]*wisski.WissKI, error) {
|
||||||
if len(slugs) == 0 {
|
if len(slugs) == 0 {
|
||||||
return instances.All()
|
return instances.All()
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +137,7 @@ func (instances *Instances) Load(slugs ...string) ([]WissKI, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find finds instances based on the provided query
|
// find finds instances based on the provided query
|
||||||
func (instances *Instances) find(order bool, query func(table *gorm.DB) *gorm.DB) (results []WissKI, err error) {
|
func (instances *Instances) find(order bool, query func(table *gorm.DB) *gorm.DB) (results []*wisski.WissKI, err error) {
|
||||||
sql := instances.SQL
|
sql := instances.SQL
|
||||||
if err := sql.WaitQueryTable(); err != nil {
|
if err := sql.WaitQueryTable(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -148,10 +166,11 @@ func (instances *Instances) find(order bool, query func(table *gorm.DB) *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// make proper instances
|
// make proper instances
|
||||||
results = make([]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].Instance = bk
|
results[i].Instance = bk
|
||||||
results[i].instances = instances
|
instances.use(results[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
|
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
package instances
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/stringparser"
|
|
||||||
"github.com/alessio/shellescape"
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errInvalidSlug = errors.New("not a valid slug")
|
|
||||||
|
|
||||||
// Create fills the struct for a new WissKI instance.
|
|
||||||
// It validates that slug is a valid name for an instance.
|
|
||||||
//
|
|
||||||
// It does not perform any checks if the instance already exists, or does the creation in the database.
|
|
||||||
func (instances *Instances) Create(slug string) (wisski WissKI, err error) {
|
|
||||||
wisski.instances = instances
|
|
||||||
|
|
||||||
// make sure that the slug is valid!
|
|
||||||
slug, err = stringparser.ParseSlug(instances.Environment, slug)
|
|
||||||
if err != nil {
|
|
||||||
return wisski, errInvalidSlug
|
|
||||||
}
|
|
||||||
|
|
||||||
wisski.Instance.Slug = slug
|
|
||||||
wisski.Instance.FilesystemBase = filepath.Join(instances.Path(), wisski.Domain())
|
|
||||||
|
|
||||||
wisski.Instance.OwnerEmail = ""
|
|
||||||
wisski.Instance.AutoBlindUpdateEnabled = true
|
|
||||||
|
|
||||||
// sql
|
|
||||||
|
|
||||||
wisski.Instance.SqlDatabase = instances.Config.MysqlDatabasePrefix + slug
|
|
||||||
wisski.Instance.SqlUsername = instances.Config.MysqlUserPrefix + slug
|
|
||||||
|
|
||||||
wisski.Instance.SqlPassword, err = instances.Config.NewPassword()
|
|
||||||
if err != nil {
|
|
||||||
return WissKI{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// triplestore
|
|
||||||
|
|
||||||
wisski.Instance.GraphDBRepository = instances.Config.GraphDBRepoPrefix + slug
|
|
||||||
wisski.Instance.GraphDBUsername = instances.Config.GraphDBUserPrefix + slug
|
|
||||||
|
|
||||||
wisski.Instance.GraphDBPassword, err = instances.Config.NewPassword()
|
|
||||||
if err != nil {
|
|
||||||
return WissKI{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// drupal
|
|
||||||
|
|
||||||
wisski.DrupalUsername = "admin" // TODO: Change this!
|
|
||||||
|
|
||||||
wisski.DrupalPassword, err = instances.Config.NewPassword()
|
|
||||||
if err != nil {
|
|
||||||
return wisski, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// store the instance in the object and return it!
|
|
||||||
return wisski, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,48 +0,0 @@
|
||||||
package instances
|
|
||||||
|
|
||||||
import "github.com/FAU-CDI/wisski-distillery/internal/models"
|
|
||||||
|
|
||||||
// WissKI represents a single WissKI Instance
|
|
||||||
type WissKI struct {
|
|
||||||
// Whatever is stored inside the bookkeeping database
|
|
||||||
models.Instance
|
|
||||||
|
|
||||||
// Credentials to Drupal
|
|
||||||
DrupalUsername string
|
|
||||||
DrupalPassword string
|
|
||||||
|
|
||||||
// reference to the component!
|
|
||||||
instances *Instances
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves this instance in the bookkeeping table
|
|
||||||
func (wisski *WissKI) Save() error {
|
|
||||||
db, err := wisski.instances.SQL.QueryTable(false, models.InstanceTable)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// it has never been created => we need to create it in the database
|
|
||||||
if wisski.Instance.Created.IsZero() {
|
|
||||||
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) Delete() error {
|
|
||||||
db, err := wisski.instances.SQL.QueryTable(false, models.InstanceTable)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// doesn't exist => nothing to delete
|
|
||||||
if wisski.Instance.Created.IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete it directly
|
|
||||||
return db.Delete(&wisski.Instance).Error
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package instances
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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...)...)
|
|
||||||
}
|
|
||||||
44
internal/component/meta/meta.go
Normal file
44
internal/component/meta/meta.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Component meta is responsible for managing metadata per WissKI Instance
|
||||||
|
type Meta struct {
|
||||||
|
component.ComponentBase
|
||||||
|
|
||||||
|
SQL *sql.SQL
|
||||||
|
|
||||||
|
sl sync.Mutex
|
||||||
|
sc map[string]*Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Meta) Name() string { return "metadata" }
|
||||||
|
|
||||||
|
// Storage returns a Storage for the instance with the given slug.
|
||||||
|
// When slug is nil, returns a global storage.
|
||||||
|
func (meta *Meta) Storage(slug string) *Storage {
|
||||||
|
meta.sl.Lock()
|
||||||
|
defer meta.sl.Unlock()
|
||||||
|
|
||||||
|
// create the cache (unless it already exists)
|
||||||
|
if meta.sc == nil {
|
||||||
|
meta.sc = make(map[string]*Storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache hit
|
||||||
|
if storage, ok := meta.sc[slug]; ok {
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new storage
|
||||||
|
meta.sc[slug] = &Storage{
|
||||||
|
Slug: slug,
|
||||||
|
sql: meta.SQL,
|
||||||
|
}
|
||||||
|
return meta.sc[slug]
|
||||||
|
}
|
||||||
14
internal/component/meta/provision.go
Normal file
14
internal/component/meta/provision.go
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
import "github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
|
||||||
|
// Provision provisions new meta storage for this instance.
|
||||||
|
// NOTE(twiesing): This is a no-op, because we implement Purge.
|
||||||
|
func (meta *Meta) Provision(instance models.Instance, domain string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge purges the storage for the given instance.
|
||||||
|
func (meta *Meta) Purge(instance models.Instance, domain string) error {
|
||||||
|
return meta.Storage(instance.Slug).Purge()
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -6,68 +6,26 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetaKey represents a key for metadata.
|
// Key represents a key for metadata.
|
||||||
type MetaKey string
|
type Key string
|
||||||
|
|
||||||
// ErrMetadatumNotSet is returned by various [MetaStorage] functions when a metadatum is not set
|
// ErrMetadatumNotSet is returned by various [MetaStorage] functions when a metadatum is not set
|
||||||
var ErrMetadatumNotSet = errors.New("metadatum not set")
|
var ErrMetadatumNotSet = errors.New("metadatum not set")
|
||||||
|
|
||||||
// MetaStorage manages some metadata.
|
// Storage manages metadata for either the entire distillery, or a single slug
|
||||||
type MetaStorage interface {
|
type Storage struct {
|
||||||
// Get retrieves metadata with the provided key and deserializes the first one into target.
|
|
||||||
// If no metadatum exists, returns [ErrMetadatumNotSet].
|
|
||||||
Get(key MetaKey, target any) error
|
|
||||||
|
|
||||||
// GetAll receives all metadata with the provided keys.
|
|
||||||
// For each received value, the targets function is called with the current index, and total number of results.
|
|
||||||
// The function is intended to return a target for deserialization.
|
|
||||||
//
|
|
||||||
// When no metadatum exists, targets is not called, and nil error is returned.
|
|
||||||
GetAll(key MetaKey, targets func(index, total int) any) error
|
|
||||||
|
|
||||||
// Delete deletes all metadata with the provided key.
|
|
||||||
Delete(key MetaKey) error
|
|
||||||
|
|
||||||
// Set serializes value and stores it with the provided key.
|
|
||||||
// Any other metadata with the same key is deleted.
|
|
||||||
Set(key MetaKey, value any) error
|
|
||||||
|
|
||||||
// Set serializes values and stores them with the provided key.
|
|
||||||
// Any other metadata with the same key is deleted.
|
|
||||||
SetAll(key MetaKey, values ...any) error
|
|
||||||
|
|
||||||
// Purge removes all metadata, regardless of key.
|
|
||||||
Purge() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata returns a system-wide [MetaStorage].
|
|
||||||
func (instances *Instances) Metadata() MetaStorage {
|
|
||||||
return &storage{
|
|
||||||
SQL: instances.SQL,
|
|
||||||
Slug: "", // not associated to any slug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metadata returns a [MetaStorage] that manages metadata related to this WissKI instance.
|
|
||||||
// It will be automatically deleted once the instance is deleted.
|
|
||||||
func (wisski *WissKI) Metadata() MetaStorage {
|
|
||||||
return &storage{
|
|
||||||
SQL: wisski.instances.SQL,
|
|
||||||
Slug: wisski.Slug, // associated to this instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// storage implements MetaStorage
|
|
||||||
type storage struct {
|
|
||||||
SQL *sql.SQL
|
|
||||||
Slug string
|
Slug string
|
||||||
|
sql *sql.SQL
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) Get(key MetaKey, target any) error {
|
// Get retrieves metadata with the provided key and deserializes the first one into target.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
// If no metadatum exists, returns [ErrMetadatumNotSet].
|
||||||
|
func (s Storage) Get(key Key, target any) error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +48,13 @@ func (s *storage) Get(key MetaKey, target any) error {
|
||||||
return json.Unmarshal(datum.Value, target)
|
return json.Unmarshal(datum.Value, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) GetAll(key MetaKey, target func(index, total int) any) error {
|
// GetAll receives all metadata with the provided keys.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
// For each received value, the targets function is called with the current index, and total number of results.
|
||||||
|
// The function is intended to return a target for deserialization.
|
||||||
|
//
|
||||||
|
// When no metadatum exists, targets is not called, and nil error is returned.
|
||||||
|
func (s Storage) GetAll(key Key, target func(index, total int) any) error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -116,8 +79,9 @@ func (s *storage) GetAll(key MetaKey, target func(index, total int) any) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) Delete(key MetaKey) error {
|
// Delete deletes all metadata with the provided key.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
func (s Storage) Delete(key Key) error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -130,8 +94,10 @@ func (s *storage) Delete(key MetaKey) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) Set(key MetaKey, value any) error {
|
// Set serializes value and stores it with the provided key.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
// Any other metadata with the same key is deleted.
|
||||||
|
func (s Storage) Set(key Key, value any) error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -163,8 +129,10 @@ func (s *storage) Set(key MetaKey, value any) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) SetAll(key MetaKey, values ...any) error {
|
// Set serializes values and stores them with the provided key.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
// Any other metadata with the same key is deleted.
|
||||||
|
func (s Storage) SetAll(key Key, values ...any) error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -196,8 +164,9 @@ func (s *storage) SetAll(key MetaKey, values ...any) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *storage) Purge() error {
|
// Purge removes all metadata, regardless of key.
|
||||||
table, err := s.SQL.QueryTable(true, models.MetadataTable)
|
func (s Storage) Purge() error {
|
||||||
|
table, err := s.sql.QueryTable(true, models.MetadataTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -208,3 +177,42 @@ 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)
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
|
|
@ -131,13 +131,13 @@ func (backup *Backup) run(ios stream.IOStream, manager *Manager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a backup of the snapshots
|
// make a backup of the snapshots
|
||||||
backup.InstanceSnapshots = status.Group[instances.WissKI, Snapshot]{
|
backup.InstanceSnapshots = status.Group[*wisski.WissKI, Snapshot]{
|
||||||
PrefixString: func(item instances.WissKI, index int) string {
|
PrefixString: func(item *wisski.WissKI, index int) string {
|
||||||
return fmt.Sprintf("[snapshot %q]: ", item.Slug)
|
return fmt.Sprintf("[snapshot %q]: ", item.Slug)
|
||||||
},
|
},
|
||||||
PrefixAlign: true,
|
PrefixAlign: true,
|
||||||
|
|
||||||
Handler: func(instance instances.WissKI, index int, writer io.Writer) Snapshot {
|
Handler: func(instance *wisski.WissKI, index int, writer io.Writer) Snapshot {
|
||||||
dir := filepath.Join(instancesBackupDir, instance.Slug)
|
dir := filepath.Join(instancesBackupDir, instance.Slug)
|
||||||
if err := manager.Environment.Mkdir(dir, environment.DefaultDirPerm); err != nil {
|
if err := manager.Environment.Mkdir(dir, environment.DefaultDirPerm); err != nil {
|
||||||
return Snapshot{
|
return Snapshot{
|
||||||
|
|
@ -151,10 +151,10 @@ func (backup *Backup) run(ios stream.IOStream, manager *Manager) {
|
||||||
Dest: dir,
|
Dest: dir,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ResultString: func(res Snapshot, item instances.WissKI, index int) string {
|
ResultString: func(res Snapshot, item *wisski.WissKI, index int) string {
|
||||||
return "done"
|
return "done"
|
||||||
},
|
},
|
||||||
WaitString: status.DefaultWaitString[instances.WissKI],
|
WaitString: status.DefaultWaitString[*wisski.WissKI],
|
||||||
HandlerLimit: backup.Description.ConcurrentSnapshots,
|
HandlerLimit: backup.Description.ConcurrentSnapshots,
|
||||||
}.Use(st, wissKIs)
|
}.Use(st, wissKIs)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
"github.com/FAU-CDI/wisski-distillery/pkg/logging"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/targz"
|
"github.com/FAU-CDI/wisski-distillery/pkg/targz"
|
||||||
|
|
@ -26,7 +26,7 @@ type ExportTask struct {
|
||||||
|
|
||||||
// Instance is the instance to generate a snapshot of.
|
// Instance is the instance to generate a snapshot of.
|
||||||
// To generate a backup, leave this to be nil.
|
// To generate a backup, leave this to be nil.
|
||||||
Instance *instances.WissKI
|
Instance *wisski.WissKI
|
||||||
|
|
||||||
// BackupDescriptions and SnapshotDescriptions further specitfy options for the export.
|
// BackupDescriptions and SnapshotDescriptions further specitfy options for the export.
|
||||||
// The Dest parameter is ignored, and updated automatically.
|
// The Dest parameter is ignored, and updated automatically.
|
||||||
|
|
@ -99,7 +99,7 @@ func (manager *Manager) MakeExport(io stream.IOStream, task ExportTask) (err err
|
||||||
sl = &backup
|
sl = &backup
|
||||||
} else {
|
} else {
|
||||||
task.SnapshotDescription.Dest = stagingDir
|
task.SnapshotDescription.Dest = stagingDir
|
||||||
snapshot := manager.NewSnapshot(*task.Instance, io, task.SnapshotDescription)
|
snapshot := manager.NewSnapshot(task.Instance, io, task.SnapshotDescription)
|
||||||
sl = &snapshot
|
sl = &snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +131,7 @@ func (manager *Manager) MakeExport(io stream.IOStream, task ExportTask) (err err
|
||||||
// write out the log entry
|
// write out the log entry
|
||||||
entry.Path = stagingDir
|
entry.Path = stagingDir
|
||||||
entry.Packed = false
|
entry.Packed = false
|
||||||
manager.Instances.AddToExportLog(entry)
|
manager.SnapshotsLog.Add(entry)
|
||||||
|
|
||||||
io.Printf("Wrote %s\n", stagingDir)
|
io.Printf("Wrote %s\n", stagingDir)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -159,7 +159,7 @@ func (manager *Manager) MakeExport(io stream.IOStream, task ExportTask) (err err
|
||||||
logging.LogMessage(io, "Writing Log Entry")
|
logging.LogMessage(io, "Writing Log Entry")
|
||||||
entry.Path = archivePath
|
entry.Path = archivePath
|
||||||
entry.Packed = true
|
entry.Packed = true
|
||||||
manager.Instances.AddToExportLog(entry)
|
manager.SnapshotsLog.Add(entry)
|
||||||
|
|
||||||
// and we're done!
|
// and we're done!
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshotslog"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/fsx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/fsx"
|
||||||
|
|
@ -17,8 +18,9 @@ import (
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
component.ComponentBase
|
component.ComponentBase
|
||||||
|
|
||||||
SQL *sql.SQL
|
SQL *sql.SQL
|
||||||
Instances *instances.Instances
|
Instances *instances.Instances
|
||||||
|
SnapshotsLog *snapshotslog.SnapshotsLog
|
||||||
|
|
||||||
Snapshotable []component.Snapshotable
|
Snapshotable []component.Snapshotable
|
||||||
Backupable []component.Backupable
|
Backupable []component.Backupable
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,6 @@ func (manager *Manager) PruneExports(io stream.IOStream) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// prune the snapshot log!
|
// prune the snapshot log!
|
||||||
_, err = manager.Instances.ExportLog()
|
_, err = manager.SnapshotsLog.Log()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/FAU-CDI/wisski-distillery/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"
|
||||||
|
|
@ -43,7 +43,7 @@ type Snapshot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot creates a new snapshot of this instance into dest
|
// Snapshot creates a new snapshot of this instance into dest
|
||||||
func (snapshots *Manager) NewSnapshot(instance instances.WissKI, io stream.IOStream, desc SnapshotDescription) (snapshot Snapshot) {
|
func (snapshots *Manager) 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 err := instance.TryLock(); err != nil {
|
||||||
|
|
@ -83,7 +83,7 @@ func (snapshots *Manager) NewSnapshot(instance instances.WissKI, io stream.IOStr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (snapshot *Snapshot) makeParts(ios stream.IOStream, snapshots *Manager, instance instances.WissKI, needsRunning bool) map[string]error {
|
func (snapshot *Snapshot) makeParts(ios stream.IOStream, snapshots *Manager, instance *wisski.WissKI, needsRunning bool) map[string]error {
|
||||||
if !needsRunning && !snapshot.Description.Keepalive {
|
if !needsRunning && !snapshot.Description.Keepalive {
|
||||||
stack := instance.Barrel()
|
stack := instance.Barrel()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,27 @@
|
||||||
package instances
|
package snapshotslog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/tkw1536/goprogram/lib/collection"
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExportLogFor retrieves (and prunes) the ExportLog.
|
// SnapshotsLog is responsible for logging snapshots
|
||||||
|
type SnapshotsLog struct {
|
||||||
|
component.ComponentBase
|
||||||
|
|
||||||
|
SQL *sql.SQL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SnapshotsLog) Name() string { return "snapshots-log" }
|
||||||
|
|
||||||
|
// For retrieves (and prunes) the ExportLog.
|
||||||
// Slug determines if entries for Backups (empty slug)
|
// Slug determines if entries for Backups (empty slug)
|
||||||
// or a specific Instance (non-empty slug) are returned.
|
// or a specific Instance (non-empty slug) are returned.
|
||||||
func (instances *Instances) ExportLogFor(slug string) (exports []models.Export, err error) {
|
func (log *SnapshotsLog) For(slug string) (exports []models.Export, err error) {
|
||||||
exports, err = instances.ExportLog()
|
exports, err = log.Log()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -20,10 +31,10 @@ func (instances *Instances) ExportLogFor(slug string) (exports []models.Export,
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportLog retrieves (and prunes) all entries in the snapshot log.
|
// Log retrieves (and prunes) all entries in the snapshot log.
|
||||||
func (instances *Instances) ExportLog() ([]models.Export, error) {
|
func (log *SnapshotsLog) Log() ([]models.Export, error) {
|
||||||
// query the table!
|
// query the table!
|
||||||
table, err := instances.SQL.QueryTable(false, models.ExportTable)
|
table, err := log.SQL.QueryTable(false, models.ExportTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +48,7 @@ func (instances *Instances) ExportLog() ([]models.Export, error) {
|
||||||
|
|
||||||
// partition out the exports that have been deleted!
|
// partition out the exports that have been deleted!
|
||||||
parts := collection.Partition(exports, func(s models.Export) bool {
|
parts := collection.Partition(exports, func(s models.Export) bool {
|
||||||
_, err := instances.Core.Environment.Stat(s.Path)
|
_, err := log.Core.Environment.Stat(s.Path)
|
||||||
return !environment.IsNotExist(err)
|
return !environment.IsNotExist(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -52,15 +63,10 @@ func (instances *Instances) ExportLog() ([]models.Export, error) {
|
||||||
return parts[true], nil
|
return parts[true], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshots returns the list of snapshots of this WissKI
|
|
||||||
func (wisski *WissKI) Snapshots() (snapshots []models.Export, err error) {
|
|
||||||
return wisski.instances.ExportLogFor(wisski.Slug)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddToExportLog adds the provided export to the log.
|
// AddToExportLog adds the provided export to the log.
|
||||||
func (instances *Instances) AddToExportLog(export models.Export) error {
|
func (log *SnapshotsLog) Add(export models.Export) error {
|
||||||
// find the table
|
// find the table
|
||||||
table, err := instances.SQL.QueryTable(false, models.ExportTable)
|
table, err := log.SQL.QueryTable(false, models.ExportTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -8,61 +8,72 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/home"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/home"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/info"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/info"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/resolver"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/resolver"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshots"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshotslog"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/ssh"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/ssh"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/triplestore"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/triplestore"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component/web"
|
"github.com/FAU-CDI/wisski-distillery/internal/component/web"
|
||||||
|
"github.com/tkw1536/goprogram/lib/collection"
|
||||||
)
|
)
|
||||||
|
|
||||||
// register returns all components of the distillery
|
// register returns all components of the distillery
|
||||||
func (dis *Distillery) register(context component.ComponentPoolContext) []component.Component {
|
func (dis *Distillery) register(context component.ComponentPoolContext) []component.Component {
|
||||||
return []component.Component{
|
return collection.MapSlice([]initFunc{
|
||||||
ra[*web.Web](dis, context),
|
auto[*web.Web],
|
||||||
|
|
||||||
ra[*ssh.SSH](dis, context),
|
auto[*ssh.SSH],
|
||||||
|
|
||||||
r(dis, context, func(ts *triplestore.Triplestore) {
|
manual(func(ts *triplestore.Triplestore) {
|
||||||
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
||||||
ts.PollContext = dis.Context()
|
ts.PollContext = dis.Context()
|
||||||
ts.PollInterval = time.Second
|
ts.PollInterval = time.Second
|
||||||
}),
|
}),
|
||||||
r(dis, context, func(sql *sql.SQL) {
|
manual(func(sql *sql.SQL) {
|
||||||
sql.ServerURL = dis.Upstream.SQL
|
sql.ServerURL = dis.Upstream.SQL
|
||||||
sql.PollContext = dis.Context()
|
sql.PollContext = dis.Context()
|
||||||
sql.PollInterval = time.Second
|
sql.PollInterval = time.Second
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ra[*instances.Instances](dis, context),
|
auto[*instances.Instances],
|
||||||
|
auto[*meta.Meta],
|
||||||
|
|
||||||
// Snapshots
|
// Snapshots
|
||||||
ra[*snapshots.Manager](dis, context),
|
auto[*snapshots.Manager],
|
||||||
ra[*snapshots.Config](dis, context),
|
auto[*snapshotslog.SnapshotsLog],
|
||||||
ra[*snapshots.Bookkeeping](dis, context),
|
auto[*snapshots.Config],
|
||||||
ra[*snapshots.Filesystem](dis, context),
|
auto[*snapshots.Bookkeeping],
|
||||||
ra[*snapshots.Pathbuilders](dis, context),
|
auto[*snapshots.Filesystem],
|
||||||
|
auto[*snapshots.Pathbuilders],
|
||||||
|
|
||||||
// Control server
|
// Control server
|
||||||
ra[*control.Control](dis, context),
|
auto[*control.Control],
|
||||||
ra[*static.Static](dis, context),
|
auto[*static.Static],
|
||||||
r(dis, context, func(home *home.Home) {
|
manual(func(home *home.Home) {
|
||||||
home.RefreshInterval = time.Minute
|
home.RefreshInterval = time.Minute
|
||||||
}),
|
}),
|
||||||
r(dis, context, func(resolver *resolver.Resolver) {
|
manual(func(resolver *resolver.Resolver) {
|
||||||
resolver.RefreshInterval = time.Minute
|
resolver.RefreshInterval = time.Minute
|
||||||
}),
|
}),
|
||||||
ra[*info.Info](dis, context),
|
auto[*info.Info],
|
||||||
|
}, func(f initFunc) component.Component {
|
||||||
|
return f(dis, context)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type initFunc = func(dis *Distillery, context component.ComponentPoolContext) component.Component
|
||||||
|
|
||||||
|
// manual initializes a component from the provided distillery.
|
||||||
|
func manual[C component.Component](init func(component C)) initFunc {
|
||||||
|
return func(dis *Distillery, context component.ComponentPoolContext) component.Component {
|
||||||
|
return component.MakeComponent(context, dis.Core, init)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// r initializes a component from the provided distillery.
|
// use is like r, but does not provided additional initialization
|
||||||
func r[C component.Component](dis *Distillery, context component.ComponentPoolContext, init func(component C)) C {
|
func auto[C component.Component](dis *Distillery, context component.ComponentPoolContext) component.Component {
|
||||||
return component.MakeComponent(context, dis.Core, init)
|
return component.MakeComponent[C](context, dis.Core, nil)
|
||||||
}
|
|
||||||
|
|
||||||
// ra is like r, but does not provided additional initialization
|
|
||||||
func ra[C component.Component](dis *Distillery, context component.ComponentPoolContext) C {
|
|
||||||
return r[C](dis, context, nil)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func (dis *Distillery) Triplestore() *triplestore.Triplestore {
|
||||||
func (dis *Distillery) Instances() *instances.Instances {
|
func (dis *Distillery) Instances() *instances.Instances {
|
||||||
return e[*instances.Instances](dis)
|
return e[*instances.Instances](dis)
|
||||||
}
|
}
|
||||||
func (dis *Distillery) SnapshotManager() *snapshots.Manager {
|
func (dis *Distillery) ExportManager() *snapshots.Manager {
|
||||||
return e[*snapshots.Manager](dis)
|
return e[*snapshots.Manager](dis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -10,8 +10,8 @@ var ErrLocked = errors.New("instance is locked")
|
||||||
|
|
||||||
// TryLock attemps to lock this WissKI
|
// TryLock attemps to lock this WissKI
|
||||||
// If this is not possible, returns ErrLocked
|
// If this is not possible, returns ErrLocked
|
||||||
func (wisski WissKI) TryLock() error {
|
func (wisski *WissKI) TryLock() error {
|
||||||
table, err := wisski.instances.SQL.QueryTable(true, models.LockTable)
|
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrLocked
|
return ErrLocked
|
||||||
}
|
}
|
||||||
|
|
@ -25,8 +25,8 @@ func (wisski WissKI) TryLock() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski WissKI) IsLocked() (locked bool) {
|
func (wisski *WissKI) IsLocked() (locked bool) {
|
||||||
table, err := wisski.instances.SQL.QueryTable(true, models.LockTable)
|
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ func (wisski WissKI) IsLocked() (locked bool) {
|
||||||
|
|
||||||
// Unlock unlocks this WissKI instance and returns if it succeeded
|
// Unlock unlocks this WissKI instance and returns if it succeeded
|
||||||
func (wisski WissKI) Unlock() bool {
|
func (wisski WissKI) Unlock() bool {
|
||||||
table, err := wisski.instances.SQL.QueryTable(true, models.LockTable)
|
table, err := wisski.SQL.QueryTable(true, models.LockTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
7
internal/wisski/meta.go
Normal file
7
internal/wisski/meta.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package wisski
|
||||||
|
|
||||||
|
import "github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
|
|
||||||
|
func (wisski *WissKI) storage() *meta.Storage {
|
||||||
|
return wisski.Meta.Storage(wisski.Slug)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
"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"
|
||||||
|
|
||||||
|
|
@ -14,7 +15,7 @@ import (
|
||||||
// NoPrefix checks if this WissKI instance is excluded from generating prefixes.
|
// NoPrefix checks if this WissKI instance is excluded from generating prefixes.
|
||||||
// TODO: Move this to the database!
|
// TODO: Move this to the database!
|
||||||
func (wisski *WissKI) NoPrefix() bool {
|
func (wisski *WissKI) NoPrefix() bool {
|
||||||
return fsx.IsFile(wisski.instances.Environment, filepath.Join(wisski.FilesystemBase, "prefixes.skip"))
|
return fsx.IsFile(wisski.Core.Environment, filepath.Join(wisski.FilesystemBase, "prefixes.skip"))
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed php/list_uri_prefixes.php
|
//go:embed php/list_uri_prefixes.php
|
||||||
|
|
@ -51,7 +52,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.instances.blockedPrefixes()
|
blocks, err := wisski.blockedPrefixes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -60,9 +61,9 @@ 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 (instances *Instances) blockedPrefixes() ([]string, error) {
|
func (wisski *WissKI) blockedPrefixes() ([]string, error) {
|
||||||
// open the resolver block file
|
// open the resolver block file
|
||||||
file, err := instances.Environment.Open(instances.Config.SelfResolverBlockFile)
|
file, err := wisski.Core.Environment.Open(wisski.Core.Config.SelfResolverBlockFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -99,11 +100,11 @@ func hasAnyPrefix(candidate string, prefixes []string) bool {
|
||||||
|
|
||||||
func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
||||||
path := filepath.Join(wisski.FilesystemBase, "prefixes")
|
path := filepath.Join(wisski.FilesystemBase, "prefixes")
|
||||||
if !fsx.IsFile(wisski.instances.Environment, path) {
|
if !fsx.IsFile(wisski.Core.Environment, path) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := wisski.instances.Environment.Open(path)
|
file, err := wisski.Core.Environment.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -126,17 +127,11 @@ func (wisski *WissKI) filePrefixes() (prefixes []string, err error) {
|
||||||
|
|
||||||
// CACHING
|
// CACHING
|
||||||
|
|
||||||
var PrefixConfigKey MetaKey = "prefix"
|
var prefix = meta.StorageFor[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 *WissKI) PrefixesCached() (results []string, err error) {
|
||||||
err = wisski.Metadata().GetAll(PrefixConfigKey, func(index, total int) any {
|
return prefix(wisski.storage()).GetAll()
|
||||||
if results == nil {
|
|
||||||
results = make([]string, total)
|
|
||||||
}
|
|
||||||
return &results[index]
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePrefixes updates the cached prefixes of this instance
|
// UpdatePrefixes updates the cached prefixes of this instance
|
||||||
|
|
@ -145,6 +140,5 @@ func (wisski *WissKI) UpdatePrefixes() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return prefix(wisski.storage()).SetAll(prefixes...)
|
||||||
return wisski.Metadata().SetAll(PrefixConfigKey, collection.AsAny(prefixes)...)
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -7,7 +7,7 @@ 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 (wisski WissKI) Domain() string {
|
||||||
return fmt.Sprintf("%s.%s", wisski.Slug, wisski.instances.Config.DefaultDomain)
|
return fmt.Sprintf("%s.%s", wisski.Slug, wisski.Core.Config.DefaultDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL returns the public URL of this instance
|
// URL returns the public URL of this instance
|
||||||
|
|
@ -19,7 +19,7 @@ func (wisski WissKI) URL() *url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.instances.Config.HTTPSEnabled() {
|
if wisski.Core.Config.HTTPSEnabled() {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
} else {
|
} else {
|
||||||
url.Scheme = "http"
|
url.Scheme = "http"
|
||||||
55
internal/wisski/provision.go
Normal file
55
internal/wisski/provision.go
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
8
internal/wisski/snapshots.go
Normal file
8
internal/wisski/snapshots.go
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
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.SnapshotsLog.For(wisski.Slug)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ func (wisski *WissKI) Barrel() component.StackWithResources {
|
||||||
return component.StackWithResources{
|
return component.StackWithResources{
|
||||||
Stack: component.Stack{
|
Stack: component.Stack{
|
||||||
Dir: wisski.FilesystemBase,
|
Dir: wisski.FilesystemBase,
|
||||||
Env: wisski.instances.Environment,
|
Env: wisski.Core.Environment,
|
||||||
},
|
},
|
||||||
|
|
||||||
Resources: barrelResources,
|
Resources: barrelResources,
|
||||||
|
|
@ -25,15 +26,15 @@ func (wisski *WissKI) Barrel() component.StackWithResources {
|
||||||
EnvPath: filepath.Join("instances", "barrel.env"),
|
EnvPath: filepath.Join("instances", "barrel.env"),
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
EnvContext: map[string]string{
|
||||||
"DOCKER_NETWORK_NAME": wisski.instances.Config.DockerNetworkName,
|
"DOCKER_NETWORK_NAME": wisski.Core.Config.DockerNetworkName,
|
||||||
|
|
||||||
"SLUG": wisski.Slug,
|
"SLUG": wisski.Slug,
|
||||||
"VIRTUAL_HOST": wisski.Domain(),
|
"VIRTUAL_HOST": wisski.Domain(),
|
||||||
"HTTPS_ENABLED": wisski.instances.Config.HTTPSEnabledEnv(),
|
"HTTPS_ENABLED": wisski.Core.Config.HTTPSEnabledEnv(),
|
||||||
|
|
||||||
"DATA_PATH": filepath.Join(wisski.FilesystemBase, "data"),
|
"DATA_PATH": filepath.Join(wisski.FilesystemBase, "data"),
|
||||||
"RUNTIME_DIR": wisski.instances.Config.RuntimeDir(),
|
"RUNTIME_DIR": wisski.Core.Config.RuntimeDir(),
|
||||||
"GLOBAL_AUTHORIZED_KEYS_FILE": wisski.instances.Config.GlobalAuthorizedKeysFile,
|
"GLOBAL_AUTHORIZED_KEYS_FILE": wisski.Core.Config.GlobalAuthorizedKeysFile,
|
||||||
},
|
},
|
||||||
|
|
||||||
MakeDirs: []string{"data", ".composer"},
|
MakeDirs: []string{"data", ".composer"},
|
||||||
|
|
@ -44,14 +45,12 @@ func (wisski *WissKI) Barrel() component.StackWithResources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const KeyLastRebuild MetaKey = "lastRebuild"
|
// TODO: Move this to time.Time
|
||||||
|
var lastRebuild = meta.StorageFor[int64]("lastRebuild")
|
||||||
|
|
||||||
func (wisski *WissKI) LastRebuild() (t time.Time, err error) {
|
func (wisski *WissKI) LastRebuild() (t time.Time, err error) {
|
||||||
var epoch int64
|
epoch, err := lastRebuild(wisski.storage()).Get()
|
||||||
|
if err == meta.ErrMetadatumNotSet {
|
||||||
// read the epoch!
|
|
||||||
err = wisski.Metadata().Get(KeyLastRebuild, &epoch)
|
|
||||||
if err == ErrMetadatumNotSet {
|
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -63,7 +62,7 @@ func (wisski *WissKI) LastRebuild() (t time.Time, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) setLastRebuild() error {
|
func (wisski *WissKI) setLastRebuild() error {
|
||||||
return wisski.Metadata().Set(KeyLastRebuild, time.Now().Unix())
|
return lastRebuild(wisski.storage()).Set(time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds or rebuilds the barel connected to this instance.
|
// Build builds or rebuilds the barel connected to this instance.
|
||||||
|
|
@ -105,7 +104,7 @@ func (wisski *WissKI) Reserve() component.StackWithResources {
|
||||||
return component.StackWithResources{
|
return component.StackWithResources{
|
||||||
Stack: component.Stack{
|
Stack: component.Stack{
|
||||||
Dir: wisski.FilesystemBase,
|
Dir: wisski.FilesystemBase,
|
||||||
Env: wisski.instances.Environment,
|
Env: wisski.Core.Environment,
|
||||||
},
|
},
|
||||||
|
|
||||||
Resources: reserveResources,
|
Resources: reserveResources,
|
||||||
|
|
@ -113,11 +112,16 @@ func (wisski *WissKI) Reserve() component.StackWithResources {
|
||||||
EnvPath: filepath.Join("instances", "reserve.env"),
|
EnvPath: filepath.Join("instances", "reserve.env"),
|
||||||
|
|
||||||
EnvContext: map[string]string{
|
EnvContext: map[string]string{
|
||||||
"DOCKER_NETWORK_NAME": wisski.instances.Config.DockerNetworkName,
|
"DOCKER_NETWORK_NAME": wisski.Core.Config.DockerNetworkName,
|
||||||
|
|
||||||
"SLUG": wisski.Slug,
|
"SLUG": wisski.Slug,
|
||||||
"VIRTUAL_HOST": wisski.Domain(),
|
"VIRTUAL_HOST": wisski.Domain(),
|
||||||
"HTTPS_ENABLED": wisski.instances.Config.HTTPSEnabledEnv(),
|
"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,8 +1,9 @@
|
||||||
package instances
|
package wisski
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/tkw1536/goprogram/exit"
|
"github.com/tkw1536/goprogram/exit"
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
|
|
@ -26,14 +27,11 @@ func (wisski *WissKI) BlindUpdate(io stream.IOStream) error {
|
||||||
return wisski.setLastUpdate()
|
return wisski.setLastUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
const KeyLastUpdate MetaKey = "lastUpdate"
|
var lastUpdate = meta.StorageFor[int64]("lastUpdate")
|
||||||
|
|
||||||
func (wisski *WissKI) LastUpdate() (t time.Time, err error) {
|
func (wisski *WissKI) LastUpdate() (t time.Time, err error) {
|
||||||
var epoch int64
|
epoch, err := lastUpdate(wisski.storage()).Get()
|
||||||
|
if err == meta.ErrMetadatumNotSet {
|
||||||
// read the epoch!
|
|
||||||
err = wisski.Metadata().Get(KeyLastUpdate, &epoch)
|
|
||||||
if err == ErrMetadatumNotSet {
|
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -45,5 +43,5 @@ func (wisski *WissKI) LastUpdate() (t time.Time, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wisski *WissKI) setLastUpdate() error {
|
func (wisski *WissKI) setLastUpdate() error {
|
||||||
return wisski.Metadata().Set(KeyLastUpdate, time.Now().Unix())
|
return lastUpdate(wisski.storage()).Set(time.Now().Unix())
|
||||||
}
|
}
|
||||||
60
internal/wisski/wisski.go
Normal file
60
internal/wisski/wisski.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Package wisski provides WissKI
|
||||||
|
package wisski
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/meta"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/snapshotslog"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/sql"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/component/triplestore"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WissKI represents a single WissKI Instance
|
||||||
|
type WissKI struct {
|
||||||
|
models.Instance // whatever is stored inside the underlying instance
|
||||||
|
|
||||||
|
// Drupal credentials - not stored in the database
|
||||||
|
DrupalUsername string
|
||||||
|
DrupalPassword string
|
||||||
|
|
||||||
|
// references to components!
|
||||||
|
Core component.Core
|
||||||
|
Meta *meta.Meta
|
||||||
|
TS *triplestore.Triplestore
|
||||||
|
SQL *sql.SQL
|
||||||
|
|
||||||
|
SnapshotsLog *snapshotslog.SnapshotsLog
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// it has never been created => we need to create it in the database
|
||||||
|
if wisski.Instance.Created.IsZero() {
|
||||||
|
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) Delete() error {
|
||||||
|
db, err := wisski.SQL.QueryTable(false, models.InstanceTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// doesn't exist => nothing to delete
|
||||||
|
if wisski.Instance.Created.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete it directly
|
||||||
|
return db.Delete(&wisski.Instance).Error
|
||||||
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ type PoolContext[Component any] struct {
|
||||||
|
|
||||||
// function to return all components
|
// function to return all components
|
||||||
|
|
||||||
metaCache sync.Map
|
metaCache sync.Map // Map[string]meta[Component]
|
||||||
cache map[string]Component // cached components
|
cache map[string]Component // cached components
|
||||||
queue []delayedInit[Component] // init queue
|
queue []delayedInit[Component] // init queue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue