Compare commits

..

No commits in common. "master" and "v0.0.40" have entirely different histories.

14 changed files with 471 additions and 557 deletions

View File

@ -7,27 +7,13 @@ BUILDTIME = $(shell date +%Y.%m.%d_%H%M)
# REDOMOD = $(shell if [ -e go.mod ]; then echo go.mod; else echo no go mod; fi) # REDOMOD = $(shell if [ -e go.mod ]; then echo go.mod; else echo no go mod; fi)
REDOMOD = $(shell if [ -e go.sum ]; then echo go.sum exists; else GO111MODULE= go mod init; GO111MODULE= go mod tidy; fi) REDOMOD = $(shell if [ -e go.sum ]; then echo go.sum exists; else GO111MODULE= go mod init; GO111MODULE= go mod tidy; fi)
all: build all: goimports build
./zookeeper list ./zookeeper
vet:
@GO111MODULE=off go vet
@echo this go binary package builds okay
nogui: nogui:
./zookeeper --gui nocui ./zookeeper --gui nocui
gocui: build build:
./zookeeper --gui gocui
gocui-5000: build
./zookeeper --gui gocui --port 5000
gocui-debugging: build
./zookeeper --gui gocui --gui-file ~/go/src/go.wit.com/toolkits/gocui/gocui.so
# ./zookeeper --gui gocui --gui-file ~/go/src/go.wit.com/toolkits/gocui/gocui.so >/tmp/forge.log 2>&1
build: goimports vet
GO111MODULE=off go build -v -x \ GO111MODULE=off go build -v -x \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
@ -55,7 +41,7 @@ redomod:
clean: clean:
rm -f go.* rm -f go.*
rm -f zookeeper rm -f zookeeper
go-mod-clean purge go-mod-clean --purge
# git clone the sources and all the golang dependancies into ~/go/src # git clone the sources and all the golang dependancies into ~/go/src
# if you don't have go-clone, you can get it from http://go.wit.com/ # if you don't have go-clone, you can get it from http://go.wit.com/
@ -68,5 +54,14 @@ http-toogle-ZOOD:
http-list-machines: http-list-machines:
curl --silent http://localhost:8080/list curl --silent http://localhost:8080/list
http-uptime: http-ConfigSave:
curl --silent http://localhost:8080/uptime curl --silent http://localhost:8080/save
http-set-zood-target:
curl --silent "http://localhost:8080/target?package=zood&version=v0.0.8"
http-upgrade-hpdev2.grid.wit.com:
curl --silent "http://localhost:8080/upgrade?hostname=hpdev2.grid.wit.com"
http-upgrade-mirrors.wit.com:
curl --silent "http://localhost:8080/upgrade?hostname=mirrors.wit.com"

50
argv.go
View File

@ -9,27 +9,14 @@ package main
*/ */
import ( import (
"fmt"
"os"
"go.wit.com/log" "go.wit.com/log"
) )
var argv args var argv args
type args struct { type args struct {
Gui *EmptyCmd `arg:"subcommand:gui" help:"open the gui"` Daemon bool `arg:"--daemon" default:"false" help:"run in daemon mode"`
List *EmptyCmd `arg:"subcommand:list" help:"list the machines in your zoo"` Port int `arg:"--port" default:"8080" help:"port to run on"`
Upgrade *EmptyCmd `arg:"subcommand:upgrade" help:"upgrade the machines"`
Verbose bool `arg:"--verbose" default:"false" help:"talk more"`
Daemon bool `arg:"--daemon" default:"false" help:"run in daemon mode"`
Port int `arg:"--port" default:"8080" help:"port to run on"`
NoPort bool `arg:"--no-port" help:"don't open socket"`
Bash bool `arg:"--bash" help:"generate bash completion"`
BashAuto []string `arg:"--auto-complete" help:"todo: move this to go-arg"`
}
type EmptyCmd struct {
} }
func (args) Version() string { func (args) Version() string {
@ -62,36 +49,3 @@ func init() {
ZOOD = log.NewFlag("ZOOD", false, full, short, "show reporting from zood") ZOOD = log.NewFlag("ZOOD", false, full, short, "show reporting from zood")
WARN = log.NewFlag("WARN", true, full, short, "bad things") WARN = log.NewFlag("WARN", true, full, short, "bad things")
} }
// prints help to STDERR // TODO: move everything below this to go-args
func (args) doBashHelp() {
if argv.BashAuto[1] != "''" {
// if this is not blank, then the user has typed something
return
}
if argv.BashAuto[0] != ARGNAME {
// if this is not the name of the command, the user already started doing something
return
}
if argv.BashAuto[0] == ARGNAME {
me.pp.WriteHelp(os.Stderr)
return
}
}
func (args) DoAutoComplete(argv []string) {
switch argv[0] {
case "list":
fmt.Println("")
case "verify":
fmt.Println("on")
case "upgrade":
fmt.Println("")
default:
if argv[0] == ARGNAME {
// list the subcommands here
fmt.Println("help list")
}
}
os.Exit(0)
}

View File

@ -8,7 +8,7 @@ Maintainer: Jeff Carr <jcarr@wit.com>
Architecture: amd64 Architecture: amd64
Depends: Depends:
URL: https://go.wit.com/apps/zookeeper URL: https://go.wit.com/apps/zookeeper
Description: manage your homelab cluster Description: zookeeper for homelab grids
keeps track of things in a grid. Maybe keeps track of things in a grid. Maybe
this is similar to the apache project by this is similar to the apache project by
the same name, but in any case, this is the same name, but in any case, this is

115
doGui.go
View File

@ -6,43 +6,44 @@ package main
// An app to submit patches for the 30 GO GUI repos // An app to submit patches for the 30 GO GUI repos
import ( import (
"fmt"
"os" "os"
"time"
"go.wit.com/gui"
"go.wit.com/lib/gadgets" "go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/zoopb" "go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log" "go.wit.com/log"
) )
// refresh the windows & tables the user has open func debug() {
func refresh() { for {
if argv.Verbose { time.Sleep(90 * time.Second)
log.Info("zookeeper scan here") log.Info("TODO: zookeeper scan here. repo count =")
}
if me.zood != nil {
// me.zood.doMachinesUpgradeTable(me.machines)
all := me.machines.All()
for all.Scan() {
m := all.Next()
if me.hostname == m.Hostname {
// this is me! This is the version of zood that should be installed everywhere
v := m.FindVersion("zood")
me.zood.version = v
me.zood.versionL.SetText(v)
}
}
me.zood.refresh()
} }
} }
func doGui() { func doGui() {
win := gadgets.RawBasicWindow("Zookeeper: (inventory your cluster)") me.myGui = gui.New()
win.Make() me.myGui.InitEmbed(resources)
win.Show() me.myGui.Default()
win.Custom = func() {
mainWindow := gadgets.RawBasicWindow("Zookeeper: (inventory your cluster)")
mainWindow.Make()
mainWindow.Show()
mainWindow.Custom = func() {
log.Warn("Main window close") log.Warn("Main window close")
os.Exit(0) os.Exit(0)
} }
drawWindow(mainWindow)
// sits here forever
debug()
}
func drawWindow(win *gadgets.BasicWindow) {
box := win.Box() box := win.Box()
vbox := box.NewVerticalBox("BOX2") vbox := box.NewVerticalBox("BOX2")
@ -50,32 +51,62 @@ func doGui() {
group1 := vbox.NewGroup("Zookeeper Settings") group1 := vbox.NewGroup("Zookeeper Settings")
grid := group1.NewGrid("buildOptions", 0, 0) grid := group1.NewGrid("buildOptions", 0, 0)
grid.NewButton("zood versions", func() { var testWin *genericWindow
// if the window exists, just toggle it open or closed grid.NewButton("machine list", func() {
if me.zood != nil { if testWin != nil {
me.zood.Toggle() testWin.Toggle()
return return
} }
me.zood = makeZoodWin() testWin = makeMachineWindow(me.machines)
}) })
grid.NewButton("Cluster Events", func() { var test2 *genericWindow
log.Info("todo: start a list here!") grid.NewButton("test2", func() {
}) if test2 != nil {
test2.Toggle()
grid.NewButton("ConfigSave()", func() { return
saveMachineState() }
test2 = makeMachineWindow(me.machines)
}) })
} }
func saveMachineState() { func findVersion(m *zoopb.Machine, pkgname string) string {
cur := zoopb.NewMachines() zood := m.Packages.FindByName(pkgname)
if zood == nil {
all := me.machines.SortByHostname() return "n/a"
for all.Scan() {
m := all.Next()
log.Info("have machine:", m.Hostname)
cur.Append(m)
} }
cur.ConfigSave() return zood.Version
}
func makeMachineWindow(pb *zoopb.Machines) *genericWindow {
win := initGenericWindow("Machines registered with Zookeeper", "Buttons of things")
grid := win.group.RawGrid()
grid.NewButton("List", func() {
log.Info("list...")
})
grid.NewButton("more", func() {
log.Info("?")
})
grid.NextRow()
grid.NewButton("smore", func() {
log.Info("smore")
})
tbox := win.win.Box().Vertical() // a vertical box (like a stack of books)
t := pb.NewTable("test 2")
t.SetParent(tbox)
t.AddHostname()
t.AddMemory()
t.AddCpus()
t.AddStringFunc("sMB", func(m *zoopb.Machine) string {
return fmt.Sprintf("%d mb", m.Memory/(1024*1024))
})
t.AddStringFunc("zood", func(m *zoopb.Machine) string {
return findVersion(m, "zood")
})
t.AddTimeFunc("age", func(m *zoopb.Machine) time.Time {
return m.Laststamp.AsTime()
})
t.ShowTable()
return win
} }

22
exit.go
View File

@ -1,22 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"os"
"go.wit.com/log"
)
func okExit(thing string) {
if thing != "" {
log.Info("zookeeper exit:", thing, "ok")
}
os.Exit(0)
}
func badExit(err error) {
log.Info("zookeeper failed: ", err)
os.Exit(-1)
}

198
http.go
View File

@ -5,90 +5,162 @@ package main
import ( import (
"fmt" "fmt"
"io" "io/ioutil"
"net/http" "net/http"
"time" "strings"
"go.wit.com/lib/protobuf/httppb" "go.wit.com/lib/protobuf/forgepb"
"go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log" "go.wit.com/log"
) )
/* // remove '?' part and trailing '/'
if strings.HasPrefix(route, "/repos/") { func cleanURL(url string) string {
pb := gitpb.NewRepos() url = "/" + strings.Trim(url, "/")
if err := pb.Unmarshal(reqPB.ClientData); err == nil { return url
reqPB.Log("Repos Unmarshal() len=%d", pb.Len()) }
} else {
reqPB.Logf("Repos Unmarshal() err=%v", err) func okHandler(w http.ResponseWriter, r *http.Request) {
} // log.Info("Got URL Path: ", r.URL.Path)
result := gitpb.NewRepos() route := cleanURL(r.URL.Path)
switch route {
case "/repos/check": hostname := r.URL.Query().Get("hostname")
result = addRequest(pb, reqPB) flag := r.URL.Query().Get("flag")
reqPB.Logf("repos check result.Len()=%d pb.Len()=%d\n", result.Len(), pb.Len()) packname := r.URL.Query().Get("package")
case "/repos/pull": version := r.URL.Query().Get("version")
result = pullRequest(pb, reqPB)
case "/repos/add": msg, err := ioutil.ReadAll(r.Body) // Read the body as []byte
result = addRequest(pb, reqPB) if err != nil {
default: log.Info("ReadAll() error =", err)
reqPB.Logf("repos check result.Len()=%d pb.Len()=%d\n", result.Len(), pb.Len())
log.Info("repos", route, "unknown")
}
if err := result.SendReply(w, reqPB); err != nil {
reqPB.Logf("Oh well, Send to client failed. err=%v", err)
}
// todo: logReq(reqPB)
logReqPB(reqPB)
return return
} }
*/
func okHandler(w http.ResponseWriter, r *http.Request) {
reqPB, err := httppb.ReqToPB(r)
reqPB.Logf("START: Got %d bytes from the client", len(reqPB.ClientData))
if err != nil {
reqPB.Logf("httppb err %v", err)
}
route := reqPB.Route
if route == "/" { if route == "/" {
return return
} }
if route == "/machine" { if route == "/machine" {
handleMachine(w, reqPB) handleMachine(r, w, hostname, msg)
return
var m *zoopb.Machine
m = new(zoopb.Machine)
if err := m.Unmarshal(msg); err != nil {
log.Info("zoo host sent unknown machine protobuf len", len(msg))
forgepb.IdentifyProtobuf(msg)
log.Info("error =", err)
return
}
log.Log(INFO, "proto.Unmarshal() worked on wire message len", len(msg), "from", m.Hostname)
b := me.upgrade[m.Hostname]
switch updateMachine(m) {
case "upgrade":
if b {
fmt.Fprintln(w, "apt update")
me.upgrade[m.Hostname] = false
} else {
fmt.Fprintln(w, "upgrade")
}
default:
fmt.Fprintln(w, "notsure")
}
return return
} }
if route == "/uptime" { if route == "/status" {
doUptime(w) var packs *zoopb.Packages
packs = new(zoopb.Packages)
if err := packs.Unmarshal(msg); err != nil {
log.Info("/status proto.Unmarshal() failed on wire message len", len(msg), "from", hostname)
return
}
log.Info("/status Unmarshal worked with msg len", len(msg), "from", hostname)
log.Info("/status hostname", hostname, "has", packs.Len(), "packages installed")
fmt.Fprintln(w, "upgrade")
return
}
// list out the machines and thier version of zood
if route == "/list" {
log.HttpMode(w)
defer log.HttpMode(nil)
loop := me.machines.SortByHostname()
for loop.Scan() {
m := loop.Next()
zood := m.Packages.FindByName("zood")
v := me.targets["zood"] // this is the target version
if zood == nil {
log.Info("machine", m.Hostname, "does not have zood installed")
} else {
log.Info(fmt.Sprintf("zood version %s vs target version %s on machine %s", zood.Version, v, m.Hostname))
}
}
return
}
// save the config file
if route == "/save" {
log.HttpMode(w)
defer log.HttpMode(nil)
if err := me.machines2.ConfigSave(); err == nil {
log.Log(NOW, "ConfigSave() ok")
} else {
log.Log(NOW, "ConfigSave() failed", err)
}
return
}
// flag a package to attempt to upgrade
if route == "/upgrade" {
log.HttpMode(w)
defer log.HttpMode(nil)
me.upgrade[hostname] = true
log.Log(NOW, "setting package ", packname, " to upgrade")
return
}
// set the target version for a package
if route == "/target" {
log.HttpMode(w)
defer log.HttpMode(nil)
// me.targets[packname] = version
log.Log(NOW, "setting package/version to ", packname, " ", version)
return
}
// toggle logging flags
if route == "/flag" {
log.HttpMode(w)
defer log.HttpMode(nil)
log.Info("going to toggle flag:", flag)
switch flag {
case "ZOOD":
if ZOOD.Enabled() {
log.Log(NOW, "toogle ZOOD false")
ZOOD.SetBool(false)
} else {
log.Log(NOW, "toogle ZOOD true")
ZOOD.SetBool(true)
}
default:
log.Info("unknown looging flag:", flag)
}
return return
} }
log.Warn("BAD URL =", route) log.Warn("BAD URL =", route)
} }
func doUptime(w io.Writer) { // starts and sits waiting for HTTP requests
if me.zood == nil { func startHTTP() {
fmt.Fprintf(w, "BAD zood == nil\n") http.HandleFunc("/", okHandler)
return
} p := fmt.Sprintf(":%d", argv.Port)
var count int log.Println("Running on port", p)
var bad int
for m := range me.machines.IterAll() { err := http.ListenAndServe(p, nil)
count += 1 if err != nil {
if m.FindVersion("zood") != me.zood.version { log.Println("Error starting server:", err)
if m.SinceLastUpdate() > 10*time.Minute {
// skip machines that have not been updated in the last 10 minutes
log.Info("ignoring old machine", m.Hostname)
continue
}
bad += 1
}
}
if bad == 0 {
fmt.Fprintf(w, "GOOD machine count=(%d) all machines are version %s\n", count, me.zood.version)
} else {
fmt.Fprintf(w, "BAD machine count=(%d) upgrade=(%d) to %s\n", count, bad, me.zood.version)
} }
} }

83
httpDump.go Normal file
View File

@ -0,0 +1,83 @@
package main
import (
"fmt"
"net/http"
)
func dumpRemoteAddr(r *http.Request) string {
return r.RemoteAddr
}
func dumpUserAgent(r *http.Request) string {
var all string
for param, values := range r.URL.Query() {
for _, value := range values {
all += fmt.Sprint(" Query:", param, value)
}
}
// hostname := r.URL.Query().Get("hostname")
return r.UserAgent() + all
}
func dumpClient(r *http.Request) {
/*
var host, url, proto, addr, agent string
host = r.Host
url = r.URL.String()
proto = r.Proto
addr = r.RemoteAddr
agent = r.UserAgent()
log.Warn(host, proto, addr, url, agent)
fmt.Fprintln(accessf, time.Now(), host, proto, addr, url, agent)
// return
fmt.Fprintln(clientf)
fmt.Fprintln(clientf, time.Now())
// Basic request information
fmt.Fprintln(clientf, "Method:", r.Method)
fmt.Fprintln(clientf, "URL:", r.URL)
fmt.Fprintln(clientf, "Protocol:", r.Proto)
fmt.Fprintln(clientf, "Host:", r.Host)
fmt.Fprintln(clientf, "Remote Address:", r.RemoteAddr)
// Headers
fmt.Fprintln(clientf, "Headers:")
for name, values := range r.Header {
for _, value := range values {
fmt.Fprintln(clientf, "Headers:", name, value)
}
}
// Query parameters
fmt.Fprintln(clientf, "Query Parameters:")
for param, values := range r.URL.Query() {
for _, value := range values {
fmt.Fprintln(clientf, "Query:", param, value)
}
}
// User-Agent
fmt.Fprintln(clientf, "User-Agent:", r.UserAgent())
// Content Length
fmt.Fprintln(clientf, "Content Length:", r.ContentLength)
// Cookies
fmt.Fprintln(clientf, "Cookies:")
for _, cookie := range r.Cookies() {
fmt.Fprintln(clientf, cookie.Name, cookie.Value)
}
// Request Body (if applicable)
if r.Body != nil {
body, err := ioutil.ReadAll(r.Body)
if err == nil {
fmt.Fprintln(clientf, "Body:", string(body))
}
}
*/
}

View File

@ -4,11 +4,10 @@
package main package main
import ( import (
"fmt"
"net/http" "net/http"
"strings"
"time" "time"
"go.wit.com/lib/protobuf/httppb"
"go.wit.com/lib/protobuf/zoopb" "go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log" "go.wit.com/log"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
@ -20,54 +19,41 @@ func rawGetHostname(data []byte) *zoopb.Machine {
return newm return newm
} }
func handleMachine(w http.ResponseWriter, reqPB *httppb.HttpRequest) { func handleMachine(r *http.Request, w http.ResponseWriter, hostname string, data []byte) {
// hostname := strings.TrimSpace(reqPB.Hostname) hostname = strings.TrimSpace(hostname)
newm := rawGetHostname(reqPB.ClientData) newm := rawGetHostname(data)
if newm == nil { if newm == nil {
log.Info("unmarshal failed on data len =", len(reqPB.ClientData)) log.Info("unmarshal failed on data len =", len(data))
}
if hostname != newm.Hostname {
// log.Info("hostname mismatch", hostname, "vs", newm.Hostname)
hostname = newm.Hostname
} }
/*
if reqPB.Hostname != newm.Hostname {
// log.Info("hostname mismatch", hostname, "vs", newm.Hostname)
hostname = newm.Hostname
}
*/
if reqPB.Hostname == "" { if hostname == "" {
ua := reqPB.DumpUserAgent() ua := dumpUserAgent(r)
ra := reqPB.DumpRemoteAddr() ra := dumpRemoteAddr(r)
log.Info("hostname is blank even after unmarshal. data len =", len(reqPB.ClientData), ra, ua, newm.Cpus, newm.Hostname) log.Info("hostname is blank even after unmarshal. data len =", len(data), ra, ua, newm.Cpus, newm.Hostname)
return return
} }
// log.Info("lookoing for", hostname) // log.Info("lookoing for", hostname)
m := me.machines.FindByHostname(reqPB.Hostname) m := me.machines.FindByHostname(hostname)
if m == nil { if m == nil {
am := new(zoopb.Machine) am := new(zoopb.Machine)
am.Hostname = newm.Hostname am.Hostname = newm.Hostname
am.Memory = newm.Memory am.Memory = newm.Memory
me.machines2.Append(am)
me.machines.Append(newm) me.machines.Append(newm)
log.Info("new machine", am.Hostname, am.Memory) log.Info("new machine", am.Hostname, am.Memory)
return return
} }
ua := reqPB.DumpUserAgent() ua := dumpUserAgent(r)
ra := reqPB.DumpRemoteAddr() ra := dumpRemoteAddr(r)
if m.UserAgent != ua { if m.UserAgent != ua {
log.Info("hostname ua changed len =", len(reqPB.ClientData), ra, reqPB.Hostname, ua) log.Info("hostname ua changed len =", len(data), ra, hostname, ua)
m.UserAgent = ua m.UserAgent = ua
} }
if m.Upgrade { // log.Info("update machine protobuf", hostname)
log.Info(m.Hostname, "was told to upgrade zood")
if m.UpgradeCmd == "" {
fmt.Fprintln(w, "apt update")
} else {
fmt.Fprintln(w, m.UpgradeCmd)
}
m.UpgradeCmd = ""
m.Upgrade = false
} else {
fmt.Fprintln(w, "good")
}
// log.Info("update machine protobuf", reqPB.hostname)
updateMachine(newm) updateMachine(newm)
} }
@ -81,12 +67,8 @@ func updateMachine(u *zoopb.Machine) string {
if m == nil { if m == nil {
log.Info("adding new machine", u.Hostname) log.Info("adding new machine", u.Hostname)
me.machines.Append(u) me.machines.Append(u)
if me.zood == nil { log.Info("save machines pb file here...")
// do nothing. window has not been opened me.machines2.ConfigSave()
} else {
me.zood.doMachinesUpgradeTable(me.machines)
}
saveMachineState()
return "new" return "new"
} }
// log.Info("updating machine", m.Hostname) // log.Info("updating machine", m.Hostname)
@ -115,9 +97,7 @@ func updateMachine(u *zoopb.Machine) string {
} }
m.Laststamp = timestamppb.New(time.Now()) m.Laststamp = timestamppb.New(time.Now())
if updatePackages(m, u.Packages) { updatePackages(m, u.Packages)
// trigger save pb
}
return "upgrade" return "upgrade"
} }
@ -132,36 +112,20 @@ func updatePackages(m *zoopb.Machine, newp *zoopb.Packages) bool {
for loop.Scan() { for loop.Scan() {
p := loop.Next() p := loop.Next()
if p.Name == "zood" { if p.Name == "zood" {
if updatePackageVersion(m, p) { if pold := m.Packages.FindByName("zood"); pold == nil {
changed = true
}
}
if p.Name == "virtigod" {
if updatePackageVersion(m, p) {
changed = true
}
}
if p.Name == "forge" {
if updatePackageVersion(m, p) {
changed = true changed = true
log.Log(ZOOD, "updatePackages() new package", p.Name, "version", p.Version, "machine", m.Hostname)
m.Packages.Append(p)
} else {
if p.Version == pold.Version {
log.Log(ZOOD, "updatePackages() unchanged", p.Version, "machine", m.Hostname)
} else {
changed = true
log.Log(NOW, "updatePackages() package", p.Name, "version changed", pold.Version, "to", p.Version, "machine", m.Hostname)
pold.Version = p.Version
}
} }
} }
} }
return changed return changed
} }
func updatePackageVersion(m *zoopb.Machine, pnew *zoopb.Package) bool {
pold := m.Packages.FindByName(pnew.Name)
if pold == nil {
log.Log(NOW, "updatePackages() new package", pnew.Name, "version", pnew.Version, "machine", m.Hostname)
m.Packages.Append(pnew)
return true
}
if pold.Version == pnew.Version {
log.Log(ZOOD, "updatePackages() unchanged", pold.Version, "machine", m.Hostname)
return false
}
log.Log(NOW, "updatePackages() package", pnew.Name, "version changed", pold.Version, "to", pnew.Version, "machine", m.Hostname)
pold.Version = pnew.Version
return true
}

43
main.go
View File

@ -4,12 +4,12 @@
package main package main
import ( import (
"embed"
"os" "os"
"time" "time"
"go.wit.com/dev/alexflint/arg" "go.wit.com/dev/alexflint/arg"
"go.wit.com/lib/gui/prep" "go.wit.com/gui"
"go.wit.com/lib/protobuf/httppb"
"go.wit.com/lib/protobuf/zoopb" "go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log" "go.wit.com/log"
) )
@ -17,42 +17,43 @@ import (
var VERSION string var VERSION string
var BUILDTIME string var BUILDTIME string
var ARGNAME string = "zookeeper" //go:embed resources/*
var resources embed.FS
func main() { func main() {
me = new(mainType) var pp *arg.Parser
prep.Bash(ARGNAME, argv.DoAutoComplete) // this line should be: prep.Bash(argv) gui.InitArg()
me.myGui = prep.Gui() // prepares the GUI package for go-args pp = arg.MustParse(&argv)
me.pp = arg.MustParse(&argv)
if pp == nil {
pp.WriteHelp(os.Stdout)
os.Exit(0)
}
if argv.Daemon { if argv.Daemon {
// turn off timestamps for STDOUT (systemd adds them) // turn off timestamps for STDOUT (systemd adds them)
log.DaemonMode(true) log.DaemonMode(true)
} }
me = new(stuff)
me.hostname, _ = os.Hostname() me.hostname, _ = os.Hostname()
me.pollDelay = time.Hour me.pollDelay = 10 * time.Second
me.machines = zoopb.NewMachines() me.machines = zoopb.NewMachines()
me.machines2 = zoopb.NewMachines()
if err := me.machines.ConfigLoad(); err != nil { if err := me.machines.ConfigLoad(); err != nil {
log.Warn("load config failed", err) log.Warn("load config failed", err)
os.Exit(-1) os.Exit(-1)
} }
if argv.List != nil { if err := me.machines2.ConfigLoad(); err != nil {
log.Info("do list here") log.Warn("load config failed", err)
okExit("") os.Exit(-1)
} }
// me.targets = make(map[string]string) // keep track of what versions the machines should be running
me.upgrade = make(map[string]bool) // used to trigger upgrade attempts
go NewWatchdog() go NewWatchdog()
if !argv.NoPort {
go httppb.StartHTTP(okHandler, argv.Port)
}
me.myGui.Start() // loads the GUI toolkit go startHTTP()
doGui() // start making our forge GUI
// sit here forever refreshing the GUI doGui()
for {
refresh()
time.Sleep(90 * time.Second)
}
} }

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -6,30 +6,23 @@ package main
import ( import (
"time" "time"
"go.wit.com/dev/alexflint/arg"
"go.wit.com/gui" "go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/gui/prep"
"go.wit.com/lib/protobuf/zoopb" "go.wit.com/lib/protobuf/zoopb"
) )
var me *mainType var me *stuff
// this app's variables // this app's variables
type mainType struct { type stuff struct {
pp *arg.Parser // for parsing the command line args. Yay to alexf lint! hostname string // my fqdn dns zookeeper hostname
hostname string // my fqdn dns zookeeper hostname pollDelay time.Duration // how often to report our status
pollDelay time.Duration // how often to report our status dog *time.Ticker // the watchdog timer
dog *time.Ticker // the watchdog timer dogchan chan bool // can kill the watchdog
dogchan chan bool // can kill the watchdog distro string // debian,redhat,gentoo,macos,wincrap
distro string // debian,redhat,gentoo,macos,wincrap packages *zoopb.Packages // installed packages and versions
packages *zoopb.Packages // installed packages and versions machines *zoopb.Machines // every machine that has reported itself to the zookeeper
machines *zoopb.Machines // every machine that has reported itself to the zookeeper machines2 *zoopb.Machines // every machine that has reported itself to the zookeeper
targets map[string]string // what versions the machines should be running targets map[string]string // what versions the machines should be running
upgrade map[string]bool // use this to trigger builds upgrade map[string]bool // use this to trigger builds
myGui *prep.GuiPrep // the gui toolkit handle myGui *gui.Node // the gui toolkit handle
machinesWin *gadgets.GenericWindow // the machines gui window
machinesBox *gui.Node // the machines gui parent box widget
machinesTB *zoopb.MachinesTable // the machines gui table buffer
zood *stdTableWin // the zood version window
} }

View File

@ -55,7 +55,7 @@ func NewWatchdog() {
// log.Info("know about machine", m.Hostname, "zood version", zood.Version) // log.Info("know about machine", m.Hostname, "zood version", zood.Version)
} }
} }
log.Info("hour watchdog:", counter, "machines. Current time:", t) log.Info("zookeeper has", counter, "machines. Current time:", t)
// h.pollHypervisor() // h.pollHypervisor()
// h.Scan() // h.Scan()

92
windowGeneric.go Normal file
View File

@ -0,0 +1,92 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"go.wit.com/lib/gadgets"
"go.wit.com/log"
"go.wit.com/gui"
)
type genericWindow struct {
win *gadgets.BasicWindow // the window widget itself
box *gui.Node // the top box of the repolist window
group *gui.Node // the default group
}
func (r *genericWindow) Hidden() bool {
if r == nil {
return true
}
if r.win == nil {
return true
}
return r.win.Hidden()
}
func (r *genericWindow) Toggle() {
if r.Hidden() {
r.Show()
} else {
r.Hide()
}
}
func (r *genericWindow) Show() {
if r == nil {
return
}
if r.win == nil {
return
}
r.win.Show()
}
func (r *genericWindow) Hide() {
if r == nil {
return
}
if r.win == nil {
return
}
r.win.Hide()
}
func (r *genericWindow) Disable() {
if r == nil {
return
}
if r.box == nil {
return
}
r.box.Disable()
}
func (r *genericWindow) Enable() {
if r == nil {
return
}
if r.box == nil {
return
}
r.box.Enable()
}
func initGenericWindow(title string, grouptxt string) *genericWindow {
gw := new(genericWindow)
gw.win = gadgets.RawBasicWindow(title)
gw.win.Make()
gw.box = gw.win.Box().Vertical() // a vertical box (like a stack of books)
gw.win.Custom = func() {
log.Warn("Found Window close. setting hidden=true")
// sets the hidden flag to false so Toggle() works
gw.win.Hide()
}
gw.group = gw.box.NewGroup(grouptxt)
gw.Show()
return gw
}

View File

@ -1,250 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
import (
"fmt"
"sync"
"time"
"go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log"
)
type stdTableWin struct {
sync.Mutex
win *gadgets.GenericWindow // the machines gui window
box *gui.Node // the machines gui parent box widget
TB *zoopb.MachinesTable // the machines gui table buffer
version string // the current zood version
versionL *gui.Node // label widget to display the current zood version
outOfDate *gui.Node // checkbox to only show out of date droplets
showAll *gui.Node // show everything in the zoo
update bool // if the window should be updated
}
func (w *stdTableWin) Toggle() {
if w == nil {
return
}
if w.win == nil {
return
}
w.win.Toggle()
}
func makeZoodWin() *stdTableWin {
stdw := new(stdTableWin)
stdw.win = gadgets.NewGenericWindow("zood daemon versions", "todo: add global controls here")
stdw.win.Custom = func() {
log.Info("test delete window here")
}
grid := stdw.win.Group.RawGrid()
grid.NewButton("save machines.pb", func() {
saveMachineState()
})
grid.NewButton("show active", func() {
stdw.doMachinesUpgradeTable(me.machines)
})
grid.NewButton("refresh", func() {
stdw.refresh()
})
stdw.versionL = grid.NewLabel("scan")
stdw.outOfDate = grid.NewCheckbox("out of date")
stdw.showAll = grid.NewCheckbox("all")
grid.NewButton("upgrade 10", func() {
sendUpgrade(10)
})
grid.NewButton("upgrade all", func() {
sendUpgrade(-1)
})
// make a box at the bottom of the window for the protobuf table
stdw.box = stdw.win.Bottom.Box().SetProgName("TBOX")
stdw.doMachinesUpgradeTable(me.machines)
return stdw
}
func sendUpgrade(i int) {
var count int
all := me.machines.All()
for all.Scan() {
m := all.Next()
mtime := m.Laststamp.AsTime()
if time.Since(mtime) > 10*time.Hour {
continue
}
if m.FindVersion("zood") != me.zood.version {
count += 1
m.Upgrade = true
log.Info("upgrade", m.Hostname, count)
}
if i == -1 || count > i {
return
}
}
}
func (stdw *stdTableWin) refresh() {
if stdw.outOfDate.Checked() {
log.Info("refresh() showing out of date zoo")
found := zoopb.NewMachines()
all := me.machines.All()
for all.Scan() {
m := all.Next()
if !stdw.showAll.Checked() {
// skip non-active zoo members
mtime := m.Laststamp.AsTime()
if time.Since(mtime) > 10*time.Hour {
continue
}
}
if m.FindVersion("zood") != me.zood.version {
found.Append(m)
}
}
stdw.doMachinesUpgradeTable(found)
return
}
if stdw.showAll.Checked() {
log.Info("refresh() showing everything in zoo")
stdw.doMachinesUpgradeTable(me.machines)
return
}
log.Info("refresh() only show active zoo")
found := zoopb.NewMachines()
all := me.machines.All()
for all.Scan() {
m := all.Next()
mtime := m.Laststamp.AsTime()
// now := time.Now()
if time.Since(mtime) > 10*time.Hour {
continue
}
found.Append(m)
}
stdw.doMachinesUpgradeTable(found)
}
func (zood *stdTableWin) doMachinesUpgradeTable(pb *zoopb.Machines) {
zood.Lock()
defer zood.Unlock()
if zood.TB != nil {
zood.TB.Delete()
zood.TB = nil
}
/*
found := zoopb.NewMachines()
all := pb.SortByHostname()
for all.Scan() {
m := all.Next()
found.Append(m)
}
*/
// display the protobuf
zood.TB = AddMachinesPB(zood.box, pb)
f := func(m *zoopb.Machine) {
log.Info("Triggering machine", m.Hostname, "to upgrade zood")
m.Upgrade = true
}
zood.TB.Custom(f)
log.Info("table has uuid", zood.TB.GetUuid())
}
func AddMachinesPB(tbox *gui.Node, pb *zoopb.Machines) *zoopb.MachinesTable {
t := pb.NewTable("MachinesPB")
t.NewUuid()
t.SetParent(tbox)
upbut := t.AddButtonFunc("upgrade", func(m *zoopb.Machine) string {
if me.zood != nil {
mver := m.FindVersion("zood")
if mver == me.zood.version {
return ""
} else {
// log.Info("machine mismatch", m.Hostname, mver, me.zood.version)
}
}
// log.Info("machine =", m.Hostname)
return "now"
})
upbut.Custom = func(m *zoopb.Machine) {
log.Info("Triggering machine", m.Hostname, "to upgrade zood")
m.Upgrade = true
}
t.AddHostname()
t.AddMemory()
t.AddCpus()
t.AddStringFunc("sMB", func(m *zoopb.Machine) string {
if m.Memory/(1024*1024) > 10000 {
return fmt.Sprintf("%4d G", m.Memory/(1024*1024*1024))
}
return fmt.Sprintf("%4d M", m.Memory/(1024*1024))
})
t.AddStringFunc("zood", func(m *zoopb.Machine) string {
return m.FindVersion("zood")
})
virtbut := t.AddButtonFunc("virtigod", func(m *zoopb.Machine) string {
ver := m.FindVersion("virtigod")
if ver == "n/a" {
return ""
}
return ver
})
virtbut.Custom = func(m *zoopb.Machine) {
log.Info("Triggering machine", m.Hostname, "to upgrade virtigod")
m.Upgrade = true
m.UpgradeCmd = "apt install virtigod"
}
forgebut := t.AddButtonFunc("forge", func(m *zoopb.Machine) string {
ver := m.FindVersion("forge")
if ver == "n/a" {
return ""
}
return ver
})
forgebut.Custom = func(m *zoopb.Machine) {
log.Info("Triggering machine", m.Hostname, "to upgrade forge")
m.Upgrade = true
m.UpgradeCmd = "apt install forge"
}
delf := func(m *zoopb.Machine) string {
return "delete"
}
delbut := t.AddButtonFunc("delete", delf)
delbut.Custom = func(m *zoopb.Machine) {
log.Info("Need to delete the protobuf record here", m.Hostname)
}
/*
// show if the machine needs to be upgraded
t.AddStringFunc("triggered?", func(m *zoopb.Machine) string {
if m.Upgrade {
return "yes"
}
return ""
})
*/
t.AddTimeFunc("age", func(m *zoopb.Machine) time.Time {
return m.Laststamp.AsTime()
})
t.ShowTable()
return t
}