diff --git a/cmd/blind_update.go b/cmd/blind_update.go index c48a5fe..62e7695 100644 --- a/cmd/blind_update.go +++ b/cmd/blind_update.go @@ -6,7 +6,6 @@ import ( 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/pkg/environment" "github.com/tkw1536/goprogram/exit" "github.com/tkw1536/goprogram/lib/collection" "github.com/tkw1536/goprogram/status" @@ -52,15 +51,8 @@ func (bu blindUpdate) Run(context wisski_distillery.Context) error { } // and do the actual blind_update! - return status.StreamGroup(context.IOStream, bu.Parallel, func(instance instances.WissKI, io stream.IOStream) error { - code, err := instance.Shell(io, "/runtime/blind_update.sh") - if err != nil { - return errBlindUpdateFailed.WithMessageF(instance.Slug, environment.ExecCommandError) - } - if code != 0 { - return errBlindUpdateFailed.WithMessageF(instance.Slug, code) - } - return nil + return status.StreamGroup(context.IOStream, bu.Parallel, func(instance instances.WissKI, str stream.IOStream) error { + return instance.BlindUpdate(str) }, wissKIs, status.SmartMessage(func(item instances.WissKI) string { return fmt.Sprintf("blind_update %q", item.Slug) })) diff --git a/cmd/info.go b/cmd/info.go index 6df1d7c..6084165 100644 --- a/cmd/info.go +++ b/cmd/info.go @@ -60,6 +60,7 @@ func (i info) Run(context wisski_distillery.Context) error { context.Printf("Running: %v\n", info.Running) context.Printf("Locked: %v\n", info.Locked) context.Printf("Last Rebuild: %v\n", info.LastRebuild.String()) + context.Printf("Last Update: %v\n", info.LastUpdate.String()) context.Printf("Skip Prefixes: %v\n", info.NoPrefixes) context.Printf("Prefixes: (count %d)\n", len(info.Prefixes)) diff --git a/internal/component/info/html/instance.html b/internal/component/info/html/instance.html index 4fce2ca..e2bd884 100644 --- a/internal/component/info/html/instance.html +++ b/internal/component/info/html/instance.html @@ -78,7 +78,6 @@ Build - @@ -93,7 +92,8 @@ - Last Rebuild + Last Rebuild
+ {{ .Info.LastRebuild.Format "2006-01-02T15:04:05Z07:00" }} @@ -107,6 +107,15 @@ {{ .Instance.AutoBlindUpdateEnabled }} + + + Last Update
+ + + + {{ .Info.LastUpdate.Format "2006-01-02T15:04:05Z07:00" }} + + diff --git a/internal/component/info/socket.go b/internal/component/info/socket.go index 9b04897..a1912fe 100644 --- a/internal/component/info/socket.go +++ b/internal/component/info/socket.go @@ -1,12 +1,35 @@ package info 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/pkg/httpx" "github.com/tkw1536/goprogram/status" "github.com/tkw1536/goprogram/stream" ) +type instanceActionFunc = func(info *Info, instance instances.WissKI, str stream.IOStream) error + +var socketInstanceActions = map[string]instanceActionFunc{ + "snapshot": func(info *Info, instance instances.WissKI, str stream.IOStream) error { + return info.SnapshotManager.MakeExport( + str, + snapshots.ExportTask{ + Dest: "", + Instance: &instance, + + StagingOnly: false, + }, + ) + }, + "rebuild": func(_ *Info, instance instances.WissKI, str stream.IOStream) error { + return instance.Build(str, true) + }, + "update": func(_ *Info, instance instances.WissKI, str stream.IOStream) error { + return instance.BlindUpdate(str) + }, +} + func (info *Info) serveSocket(conn httpx.WebSocketConnection) { // read the next message to act on message, ok := <-conn.Read() @@ -14,77 +37,47 @@ func (info *Info) serveSocket(conn httpx.WebSocketConnection) { return } - switch string(message.Bytes) { - case "snapshot": - slug, ok := <-conn.Read() - if !ok { - return - } - info.serverSocketSnapshot(string(slug.Bytes), info.socketWriter(conn)) - case "rebuild": - slug, ok := <-conn.Read() - if !ok { - return - } - info.serverSocketRebuild(string(slug.Bytes), info.socketWriter(conn)) + // perform an action if it exists! + if action, ok := socketInstanceActions[string(message.Bytes)]; ok { + info.handleInstanceAction(conn, action) + return } } -func (*Info) socketWriter(conn httpx.WebSocketConnection) *status.LineBuffer { - return &status.LineBuffer{ +func (info *Info) handleInstanceAction(conn httpx.WebSocketConnection, action instanceActionFunc) { + + // read the slug + slug, ok := <-conn.Read() + if !ok { + conn.WriteText("Error reading slug") + return + } + + // resolve the instance + instance, err := info.Instances.WissKI(string(slug.Bytes)) + if err != nil { + conn.WriteText("Instance not found") + return + } + + // build a stream + writer := &status.LineBuffer{ Line: func(line string) { <-conn.WriteText(line) }, FlushLineOnClose: true, } -} + defer writer.Close() -func (info *Info) serverSocketSnapshot(slug string, writer *status.LineBuffer) { - stream := stream.NewIOStream(writer, writer, nil, 0) - - // get the wisski - wissKI, err := info.Instances.WissKI(slug) - if err != nil { - stream.EPrintln(err) - return - } + str := stream.NewIOStream(writer, writer, nil, 0) + // and perform the action { - err := info.SnapshotManager.MakeExport( - stream, - snapshots.ExportTask{ - Dest: "", - Instance: &wissKI, - - StagingOnly: false, - }, - ) + err := action(info, instance, str) if err != nil { - stream.EPrintln(err) + str.EPrintln(err) return } + str.Println("done") } - stream.Println("Done") - -} - -func (info *Info) serverSocketRebuild(slug string, writer *status.LineBuffer) { - stream := stream.NewIOStream(writer, writer, nil, 0) - - // get the wisski - wissKI, err := info.Instances.WissKI(slug) - if err != nil { - stream.EPrintln(err) - return - } - - { - err := wissKI.Build(stream, true) - if err != nil { - stream.EPrintln(err) - return - } - } - stream.Println("Done") - } diff --git a/internal/component/instances/wisski_status.go b/internal/component/instances/wisski_status.go index 19a2521..5fa7886 100644 --- a/internal/component/instances/wisski_status.go +++ b/internal/component/instances/wisski_status.go @@ -21,6 +21,7 @@ type WissKIInfo struct { // Information about the running instance Running bool LastRebuild time.Time + LastUpdate time.Time // List of backups made Snapshots []models.Export @@ -63,6 +64,10 @@ func (wisski *WissKI) Info(quick bool) (info WissKIInfo, err error) { info.LastRebuild, _ = wisski.LastRebuild() return nil }) + group.Go(func() (err error) { + info.LastUpdate, _ = wisski.LastUpdate() + return nil + }) group.Go(func() error { info.Pathbuilders, _ = wisski.AllPathbuilders() return nil diff --git a/internal/component/instances/wisski_update.go b/internal/component/instances/wisski_update.go new file mode 100644 index 0000000..cc8c15f --- /dev/null +++ b/internal/component/instances/wisski_update.go @@ -0,0 +1,49 @@ +package instances + +import ( + "time" + + "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() +} + +const KeyLastUpdate MetaKey = "lastUpdate" + +func (wisski *WissKI) LastUpdate() (t time.Time, err error) { + var epoch int64 + + // read the epoch! + err = wisski.Metadata().Get(KeyLastUpdate, &epoch) + if err == 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 wisski.Metadata().Set(KeyLastUpdate, time.Now().Unix()) +}