mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-16 15:10:12 +01:00
add non-wasm version of tplprev
This commit is contained in:
parent
16883d21c0
commit
ecf1dbb6b1
6 changed files with 246 additions and 137 deletions
|
|
@ -60,6 +60,7 @@
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 0.77em 1.18em;
|
padding: 0.77em 1.18em;
|
||||||
margin:0;
|
margin:0;
|
||||||
|
height: 540px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -69,23 +70,24 @@
|
||||||
const form = document.querySelector('#tplprev');
|
const form = document.querySelector('#tplprev');
|
||||||
const input = form.template.value;
|
const input = form.template.value;
|
||||||
console.log('Input: %o', input);
|
console.log('Input: %o', input);
|
||||||
const actions = form.enablereport.checked ? [
|
const arrFromCount = (key) => Array.from(Array(form[key]?.valueAsNumber ?? 0), () => key);
|
||||||
[ form.skipped.valueAsNumber, "skipped" ],
|
const states = form.enablereport.checked ? [
|
||||||
[ form.scanned.valueAsNumber, "scanned" ],
|
...arrFromCount("skipped"),
|
||||||
[ form.updated.valueAsNumber, "updated" ],
|
...arrFromCount("scanned"),
|
||||||
[ form.failed.valueAsNumber, "failed" ],
|
...arrFromCount("updated"),
|
||||||
[ form.fresh.valueAsNumber, "fresh" ],
|
...arrFromCount("failed" ),
|
||||||
[ form.stale.valueAsNumber, "stale" ],
|
...arrFromCount("fresh" ),
|
||||||
|
...arrFromCount("stale" ),
|
||||||
] : [];
|
] : [];
|
||||||
console.log("Actions: %o", actions);
|
console.log("States: %o", states);
|
||||||
const logentries = form.enablelog.checked ? [
|
const levels = form.enablelog.checked ? [
|
||||||
form.logerrors.valueAsNumber,
|
...arrFromCount("error"),
|
||||||
form.logwarnings.valueAsNumber,
|
...arrFromCount("warning"),
|
||||||
form.loginfos.valueAsNumber,
|
...arrFromCount("info"),
|
||||||
form.logdebugs.valueAsNumber,
|
...arrFromCount("debug"),
|
||||||
] : [0, 0, 0, 0];
|
] : [];
|
||||||
console.log("LogLevel counts: %o", logentries);
|
console.log("Levels: %o", levels);
|
||||||
const output = WATCHTOWER.tplprev(input, actions, logentries);
|
const output = WATCHTOWER.tplprev(input, states, levels);
|
||||||
console.log('Output: \n%o', output);
|
console.log('Output: \n%o', output);
|
||||||
document.querySelector('#result').innerText = output;
|
document.querySelector('#result').innerText = output;
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +101,9 @@
|
||||||
if(debounce) clearTimeout(debounce);
|
if(debounce) clearTimeout(debounce);
|
||||||
debounce = setTimeout(() => updatePreview(), 400);
|
debounce = setTimeout(() => updatePreview(), 400);
|
||||||
}
|
}
|
||||||
|
const formChanged = (e) => {
|
||||||
|
console.log('form changed: %o', e);
|
||||||
|
}
|
||||||
const go = new Go();
|
const go = new Go();
|
||||||
WebAssembly.instantiateStreaming(fetch("../assets/tplprev.wasm"), go.importObject).then((result) => {
|
WebAssembly.instantiateStreaming(fetch("../assets/tplprev.wasm"), go.importObject).then((result) => {
|
||||||
document.querySelector('#loading').style.display = "none";
|
document.querySelector('#loading').style.display = "none";
|
||||||
|
|
@ -110,7 +115,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<form id="tplprev" onsubmit="formSubmitted(event)" style="margin: 0;display: flex; flex-direction: column; row-gap: 1rem; box-sizing: border-box; position: relative; margin-right: -13.3rem">
|
<form id="tplprev" onchange="inputUpdated()" onsubmit="formSubmitted(event)" style="margin: 0;display: flex; flex-direction: column; row-gap: 1rem; box-sizing: border-box; position: relative; margin-right: -13.3rem">
|
||||||
<pre id="loading" style="position: absolute; inset: 0; display: flex; padding: 1rem; box-sizing: border-box; background: var(--md-code-bg-color); margin-top: 0">loading wasm...</pre>
|
<pre id="loading" style="position: absolute; inset: 0; display: flex; padding: 1rem; box-sizing: border-box; background: var(--md-code-bg-color); margin-top: 0">loading wasm...</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -170,19 +175,19 @@ Logs:
|
||||||
<legend><label><input type="checkbox" name="enablelog" checked /> Log entries</label></legend>
|
<legend><label><input type="checkbox" name="enablelog" checked /> Log entries</label></legend>
|
||||||
<label class="numfield">
|
<label class="numfield">
|
||||||
Error:
|
Error:
|
||||||
<input type="number" name="logerrors" value="1" />
|
<input type="number" name="error" value="1" />
|
||||||
</label>
|
</label>
|
||||||
<label class="numfield">
|
<label class="numfield">
|
||||||
Warning:
|
Warning:
|
||||||
<input type="number" name="logwarnings" value="2" />
|
<input type="number" name="warning" value="2" />
|
||||||
</label>
|
</label>
|
||||||
<label class="numfield">
|
<label class="numfield">
|
||||||
Info:
|
Info:
|
||||||
<input type="number" name="loginfos" value="3" />
|
<input type="number" name="info" value="3" />
|
||||||
</label>
|
</label>
|
||||||
<label class="numfield">
|
<label class="numfield">
|
||||||
Debug:
|
Debug:
|
||||||
<input type="number" name="logdebugs" value="4" />
|
<input type="number" name="debug" value="4" />
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<button type="submit" style="flex:1; min-width: 12ch; padding: 0.5rem">Update preview</button>
|
<button type="submit" style="flex:1; min-width: 12ch; padding: 0.5rem">Update preview</button>
|
||||||
|
|
|
||||||
|
|
@ -24,34 +24,43 @@ type LogEntry struct {
|
||||||
Level LogLevel
|
Level LogLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogLevel int
|
type LogLevel string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PanicLevel LogLevel = iota
|
TraceLevel LogLevel = "trace"
|
||||||
FatalLevel
|
DebugLevel LogLevel = "debug"
|
||||||
ErrorLevel
|
InfoLevel LogLevel = "info"
|
||||||
WarnLevel
|
WarnLevel LogLevel = "warning"
|
||||||
InfoLevel
|
ErrorLevel LogLevel = "error"
|
||||||
DebugLevel
|
FatalLevel LogLevel = "fatal"
|
||||||
TraceLevel
|
PanicLevel LogLevel = "panic"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (level LogLevel) String() string {
|
func LevelsFromString(str string) []LogLevel {
|
||||||
switch level {
|
levels := make([]LogLevel, 0, len(str))
|
||||||
case TraceLevel:
|
for _, c := range str {
|
||||||
return "trace"
|
switch c {
|
||||||
case DebugLevel:
|
case 'p':
|
||||||
return "debug"
|
levels = append(levels, PanicLevel)
|
||||||
case InfoLevel:
|
case 'f':
|
||||||
return "info"
|
levels = append(levels, FatalLevel)
|
||||||
case WarnLevel:
|
case 'e':
|
||||||
return "warning"
|
levels = append(levels, ErrorLevel)
|
||||||
case ErrorLevel:
|
case 'w':
|
||||||
return "error"
|
levels = append(levels, WarnLevel)
|
||||||
case FatalLevel:
|
case 'i':
|
||||||
return "fatal"
|
levels = append(levels, InfoLevel)
|
||||||
case PanicLevel:
|
case 'd':
|
||||||
return "panic"
|
levels = append(levels, DebugLevel)
|
||||||
|
case 't':
|
||||||
|
levels = append(levels, TraceLevel)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return levels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (level LogLevel) String() string {
|
||||||
|
return string(level)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,89 +1,47 @@
|
||||||
|
//go:build !wasm
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"os"
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containrrr/watchtower/internal/meta"
|
"github.com/containrrr/watchtower/internal/meta"
|
||||||
"github.com/containrrr/watchtower/pkg/notifications/templates"
|
|
||||||
|
|
||||||
"syscall/js"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("watchtower/tplprev v" + meta.Version)
|
fmt.Fprintf(os.Stderr, "watchtower/tplprev v%v\n\n", meta.Version)
|
||||||
|
|
||||||
js.Global().Set("WATCHTOWER", js.ValueOf(map[string]any{
|
var states string
|
||||||
"tplprev": js.FuncOf(tplprev),
|
var entries string
|
||||||
}))
|
|
||||||
<-make(chan bool)
|
|
||||||
|
|
||||||
}
|
flag.StringVar(&states, "states", "cccuuueeekkktttfff", "sCanned, Updated, failEd, sKipped, sTale, Fresh")
|
||||||
|
flag.StringVar(&entries, "entries", "ewwiiidddd", "Fatal,Error,Warn,Info,Debug,Trace")
|
||||||
|
|
||||||
func tplprev(this js.Value, args []js.Value) any {
|
flag.Parse()
|
||||||
|
|
||||||
rb := ReportBuilder()
|
if len(flag.Args()) < 1 {
|
||||||
|
fmt.Fprintln(os.Stderr, "Missing required argument TEMPLATE")
|
||||||
if len(args) < 2 {
|
flag.Usage()
|
||||||
return "Requires 3 argument passed"
|
os.Exit(1)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
input := args[0].String()
|
input, err := os.ReadFile(flag.Arg(0))
|
||||||
tpl, err := template.New("").Funcs(templates.Funcs).Parse(input)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Failed to parse template: " + err.Error()
|
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to read template file %q: %v\n", flag.Arg(0), err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsArg := args[1]
|
result, err := TplPrev(string(input), StatesFromString(states), LevelsFromString(entries))
|
||||||
|
|
||||||
for i := 0; i < actionsArg.Length(); i++ {
|
|
||||||
action := actionsArg.Index(i)
|
|
||||||
if action.Length() != 2 {
|
|
||||||
return fmt.Sprintf("Invalid size of action tuple, expected 2, got %v", action.Length())
|
|
||||||
}
|
|
||||||
count := action.Index(0).Int()
|
|
||||||
state := State(action.Index(1).String())
|
|
||||||
rb.AddNContainers(count, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
entriesArg := args[2]
|
|
||||||
var entries []*LogEntry
|
|
||||||
for i := 0; i < entriesArg.Length(); i++ {
|
|
||||||
count := entriesArg.Index(i).Int()
|
|
||||||
level := ErrorLevel + LogLevel(i)
|
|
||||||
for m := 0; m < count; m++ {
|
|
||||||
var msg string
|
|
||||||
if level <= WarnLevel {
|
|
||||||
msg = rb.randomEntry(logErrors)
|
|
||||||
} else {
|
|
||||||
msg = rb.randomEntry(logMessages)
|
|
||||||
}
|
|
||||||
entries = append(entries, &LogEntry{
|
|
||||||
Message: msg,
|
|
||||||
Data: map[string]any{},
|
|
||||||
Time: time.Now(),
|
|
||||||
Level: level,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
report := rb.Build()
|
|
||||||
data := Data{
|
|
||||||
Entries: entries,
|
|
||||||
StaticData: StaticData{
|
|
||||||
Title: "Title",
|
|
||||||
Host: "Host",
|
|
||||||
},
|
|
||||||
Report: report,
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf strings.Builder
|
|
||||||
err = tpl.Execute(&buf, data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Failed to execute template: " + err.Error()
|
fmt.Fprintf(os.Stderr, "Failed to read template file %q: %v\n", flag.Arg(0), err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.String()
|
fmt.Println(result)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
tplprev/main_wasm.go
Normal file
60
tplprev/main_wasm.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
//go:build wasm
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containrrr/watchtower/internal/meta"
|
||||||
|
|
||||||
|
"syscall/js"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("watchtower/tplprev v" + meta.Version)
|
||||||
|
|
||||||
|
js.Global().Set("WATCHTOWER", js.ValueOf(map[string]any{
|
||||||
|
"tplprev": js.FuncOf(jsTplPrev),
|
||||||
|
}))
|
||||||
|
<-make(chan bool)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsTplPrev(this js.Value, args []js.Value) any {
|
||||||
|
|
||||||
|
if len(args) < 3 {
|
||||||
|
return "Requires 3 arguments passed"
|
||||||
|
}
|
||||||
|
|
||||||
|
input := args[0].String()
|
||||||
|
|
||||||
|
statesArg := args[1]
|
||||||
|
var states []State
|
||||||
|
|
||||||
|
if statesArg.Type() == js.TypeString {
|
||||||
|
states = StatesFromString(statesArg.String())
|
||||||
|
} else {
|
||||||
|
for i := 0; i < statesArg.Length(); i++ {
|
||||||
|
state := State(statesArg.Index(i).String())
|
||||||
|
states = append(states, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
levelsArg := args[2]
|
||||||
|
var levels []LogLevel
|
||||||
|
|
||||||
|
if levelsArg.Type() == js.TypeString {
|
||||||
|
levels = LevelsFromString(statesArg.String())
|
||||||
|
} else {
|
||||||
|
for i := 0; i < levelsArg.Length(); i++ {
|
||||||
|
level := LogLevel(levelsArg.Index(i).String())
|
||||||
|
levels = append(levels, level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := TplPrev(input, states, levels)
|
||||||
|
if err != nil {
|
||||||
|
return "Error: " + err.Error()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -30,30 +29,27 @@ func (rb *reportBuilder) Build() types.Report {
|
||||||
return &rb.report
|
return &rb.report
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rb *reportBuilder) AddNContainers(n int, state State) {
|
func (rb *reportBuilder) AddFromState(state State) {
|
||||||
fmt.Printf("Adding %v containers with state %v", n, state)
|
cid := types.ContainerID(rb.generateID())
|
||||||
for i := 0; i < n; i++ {
|
old := types.ImageID(rb.generateID())
|
||||||
cid := types.ContainerID(rb.generateID())
|
new := types.ImageID(rb.generateID())
|
||||||
old := types.ImageID(rb.generateID())
|
name := rb.generateName()
|
||||||
new := types.ImageID(rb.generateID())
|
image := rb.generateImageName(name)
|
||||||
name := rb.generateName()
|
var err error
|
||||||
image := rb.generateImageName(name)
|
if state == FailedState {
|
||||||
var err error
|
err = errors.New(rb.randomEntry(errorMessages))
|
||||||
if state == FailedState {
|
} else if state == SkippedState {
|
||||||
err = errors.New(rb.randomEntry(errorMessages))
|
err = errors.New(rb.randomEntry(skippedMessages))
|
||||||
} else if state == SkippedState {
|
|
||||||
err = errors.New(rb.randomEntry(skippedMessages))
|
|
||||||
}
|
|
||||||
rb.AddContainer(ContainerStatus{
|
|
||||||
containerID: cid,
|
|
||||||
oldImage: old,
|
|
||||||
newImage: new,
|
|
||||||
containerName: name,
|
|
||||||
imageName: image,
|
|
||||||
error: err,
|
|
||||||
state: state,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
rb.AddContainer(ContainerStatus{
|
||||||
|
containerID: cid,
|
||||||
|
oldImage: old,
|
||||||
|
newImage: new,
|
||||||
|
containerName: name,
|
||||||
|
imageName: image,
|
||||||
|
error: err,
|
||||||
|
state: state,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rb *reportBuilder) AddContainer(c ContainerStatus) {
|
func (rb *reportBuilder) AddContainer(c ContainerStatus) {
|
||||||
|
|
@ -127,6 +123,29 @@ type Report struct {
|
||||||
fresh []types.ContainerReport
|
fresh []types.ContainerReport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StatesFromString(str string) []State {
|
||||||
|
states := make([]State, 0, len(str))
|
||||||
|
for _, c := range str {
|
||||||
|
switch c {
|
||||||
|
case 'c':
|
||||||
|
states = append(states, ScannedState)
|
||||||
|
case 'u':
|
||||||
|
states = append(states, UpdatedState)
|
||||||
|
case 'e':
|
||||||
|
states = append(states, FailedState)
|
||||||
|
case 'k':
|
||||||
|
states = append(states, SkippedState)
|
||||||
|
case 't':
|
||||||
|
states = append(states, StaleState)
|
||||||
|
case 'f':
|
||||||
|
states = append(states, FreshState)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return states
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Report) Scanned() []types.ContainerReport {
|
func (r *Report) Scanned() []types.ContainerReport {
|
||||||
return r.scanned
|
return r.scanned
|
||||||
}
|
}
|
||||||
|
|
|
||||||
58
tplprev/tplprev.go
Normal file
58
tplprev/tplprev.go
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containrrr/watchtower/pkg/notifications/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TplPrev(input string, states []State, loglevels []LogLevel) (string, error) {
|
||||||
|
|
||||||
|
rb := ReportBuilder()
|
||||||
|
|
||||||
|
tpl, err := template.New("").Funcs(templates.Funcs).Parse(input)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse template: %e", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, state := range states {
|
||||||
|
rb.AddFromState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
var entries []*LogEntry
|
||||||
|
for _, level := range loglevels {
|
||||||
|
var msg string
|
||||||
|
if level <= WarnLevel {
|
||||||
|
msg = rb.randomEntry(logErrors)
|
||||||
|
} else {
|
||||||
|
msg = rb.randomEntry(logMessages)
|
||||||
|
}
|
||||||
|
entries = append(entries, &LogEntry{
|
||||||
|
Message: msg,
|
||||||
|
Data: map[string]any{},
|
||||||
|
Time: time.Now(),
|
||||||
|
Level: level,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
report := rb.Build()
|
||||||
|
data := Data{
|
||||||
|
Entries: entries,
|
||||||
|
StaticData: StaticData{
|
||||||
|
Title: "Title",
|
||||||
|
Host: "Host",
|
||||||
|
},
|
||||||
|
Report: report,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
err = tpl.Execute(&buf, data)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to execute template: %e", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue