summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apt.go72
-rw-r--r--apt_linux.go72
-rw-r--r--config.go71
-rw-r--r--distro.go61
-rw-r--r--machine.proto3
-rw-r--r--package.proto8
6 files changed, 284 insertions, 3 deletions
diff --git a/apt.go b/apt.go
new file mode 100644
index 0000000..cf8a4fd
--- /dev/null
+++ b/apt.go
@@ -0,0 +1,72 @@
+package zoopb
+
+import (
+ "fmt"
+
+ "go.wit.com/log"
+)
+
+// init the installed package list
+func (me *Machine) initPackages() {
+ // Get the list of installed packages for the detected distro
+ newP, err := getPackageList(me.Distro)
+ if err != nil {
+ fmt.Println("Error:", err)
+ return
+ }
+
+ // Print the installed packages and their versions
+ for pkg, version := range newP {
+ new1 := new(Package)
+ new1.Name = pkg
+ new1.Version = version
+ if me.Packages.Append(new1) {
+ // log.Info("added", new1.Name, "ok")
+ } else {
+ log.Info("added", new1.Name, "failed")
+ }
+ }
+
+ log.Info(me.Hostname, "has distro", me.Distro, "with", me.Packages.Len(), "packages installed.")
+}
+
+func (me *Machine) addNew(name string, version string) bool {
+ new1 := new(Package)
+ new1.Name = name
+ new1.Version = version
+ return me.Packages.Append(new1)
+}
+
+func (me *Machine) updatePackages() string {
+ // Get the list of installed packages for the detected distro
+ newP, err := getPackageList(me.Distro)
+ if err != nil {
+ fmt.Println("Error:", err)
+ return fmt.Sprintln("getPackageList()", err)
+ }
+
+ var newCounter, changeCounter int
+ // Print the installed packages and their versions
+ for pkg, version := range newP {
+ found := me.Packages.FindByName(pkg)
+ if found == nil {
+ log.Info("adding new", pkg, version)
+ me.addNew(pkg, version)
+ newCounter += 1
+ } else {
+ found.Version = version
+ if me.Packages.Update(found) {
+ changeCounter += 1
+ }
+ }
+ }
+
+ footer := fmt.Sprintf("%s has distro %s with %d packages installed", me.Hostname, me.Distro, me.Packages.Len())
+ if changeCounter != 0 {
+ footer += fmt.Sprintf(" (%d changed)", changeCounter)
+ }
+ if newCounter != 0 {
+ footer += fmt.Sprintf(" (%d new)", newCounter)
+ }
+ return footer
+}
diff --git a/apt_linux.go b/apt_linux.go
new file mode 100644
index 0000000..3e71d1a
--- /dev/null
+++ b/apt_linux.go
@@ -0,0 +1,72 @@
+package zoopb
+
+import (
+ "bufio"
+ "fmt"
+ "os/exec"
+ "strings"
+
+ "go.wit.com/log"
+)
+
+// getPackageList returns the list of installed packages based on the distro
+func getPackageList(distro string) (map[string]string, error) {
+ var cmd *exec.Cmd
+
+ // Run the appropriate command based on the detected distribution
+ switch distro {
+ case "ubuntu", "debian":
+ return dpkgQuery()
+ case "fedora", "centos", "rhel":
+ cmd = exec.Command("rpm", "-qa")
+ case "arch", "manjaro":
+ cmd = exec.Command("pacman", "-Q")
+ default:
+ return nil, fmt.Errorf("unsupported distribution: %s", distro)
+ }
+
+ // Capture the command's output
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return nil, fmt.Errorf("error running command: %v", err)
+ }
+
+ // todo: Split the output into lines and return
+ lines := strings.Split(string(output), "\n")
+ log.Info("output had", len(lines), "lines")
+ return nil, nil
+}
+
+func dpkgQuery() (map[string]string, error) {
+ // Run the dpkg-query command to list installed packages and versions
+ cmd := exec.Command("dpkg-query", "-W", "-f=${Package} ${Version}\n")
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ // Start the command execution
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ defer cmd.Wait()
+
+ // Create a map to store package names and versions
+ installedPackages := make(map[string]string)
+
+ // Use a scanner to read the output of the command line by line
+ scanner := bufio.NewScanner(stdout)
+ for scanner.Scan() {
+ line := scanner.Text()
+ // Split each line into package name and version
+ parts := strings.SplitN(line, " ", 2)
+ if len(parts) == 2 {
+ packageName := parts[0]
+ version := parts[1]
+ installedPackages[packageName] = version
+ }
+ }
+
+ // Return the map with package names and versions
+ return installedPackages, scanner.Err()
+}
diff --git a/config.go b/config.go
index affe013..03c81c5 100644
--- a/config.go
+++ b/config.go
@@ -24,6 +24,29 @@ func (m *Machines) ConfigSave() error {
configWrite(data)
return nil
}
+
+// when running on a single machine, save the file in forge/
+// as <hostname>.pb
+// write to ~/.config/forge/ unless ENV{FORGE_HOME} is set
+func (m *Machine) ConfigSave() error {
+ if os.Getenv("FORGE_HOME") == "" {
+ homeDir, _ := os.UserHomeDir()
+ fullpath := filepath.Join(homeDir, ".config/forge")
+ os.Setenv("FORGE_HOME", fullpath)
+ }
+ data, err := m.Marshal()
+ if err != nil {
+ log.Info("proto.Marshal() failed len", len(data), err)
+ return err
+ }
+
+ log.Info("ConfigSave() proto.Marshal() worked len", len(data))
+
+ hostname, _ := os.Hostname()
+ fname := hostname + ".pb"
+ return m.configWrite(fname, data)
+}
+
func ConfigSaveRaw(data []byte) error {
configWrite(data)
return nil
@@ -45,6 +68,30 @@ func (m *Machines) ConfigLoad() error {
return nil
}
+func (m *Machine) ConfigLoad() error {
+ if m == nil {
+ return errors.New("It's not safe to run ConfigLoad() on a nil ?")
+ }
+ if os.Getenv("FORGE_HOME") == "" {
+ homeDir, _ := os.UserHomeDir()
+ fullpath := filepath.Join(homeDir, ".config/forge")
+ os.Setenv("FORGE_HOME", fullpath)
+ }
+
+ hostname, _ := os.Hostname()
+ fname := hostname + ".pb"
+
+ if data, err := m.loadFile(fname); err == nil {
+ if err = proto.Unmarshal(data, m); err != nil {
+ log.Warn("broken zookeeper.pb config file", fname)
+ return err
+ }
+ } else {
+ return err
+ }
+ return nil
+}
+
func loadFile(filename string) ([]byte, error) {
homeDir, err := os.UserHomeDir()
p := filepath.Join(homeDir, ".config/zookeeper")
@@ -57,6 +104,17 @@ func loadFile(filename string) ([]byte, error) {
return data, nil
}
+func (m *Machine) loadFile(fname string) ([]byte, error) {
+ fullname := filepath.Join(os.Getenv("FORGE_HOME"), fname)
+
+ data, err := os.ReadFile(fullname)
+ if err != nil {
+ // log.Info("open config file :", err)
+ return nil, err
+ }
+ return data, nil
+}
+
func configWrite(data []byte) error {
homeDir, err := os.UserHomeDir()
p := filepath.Join(homeDir, ".config/zookeeper")
@@ -70,3 +128,16 @@ func configWrite(data []byte) error {
cfgfile.Write(data)
return nil
}
+
+func (m *Machine) configWrite(fname string, data []byte) error {
+ fullname := filepath.Join(os.Getenv("FORGE_HOME"), fname)
+
+ cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666)
+ defer cfgfile.Close()
+ if err != nil {
+ log.Warn("open config file :", err)
+ return err
+ }
+ cfgfile.Write(data)
+ return nil
+}
diff --git a/distro.go b/distro.go
new file mode 100644
index 0000000..bbbdda3
--- /dev/null
+++ b/distro.go
@@ -0,0 +1,61 @@
+// Copyright 2024 WIT.COM Inc.
+
+package zoopb
+
+// simple stab at making a human readable distro name
+// this is for displaying in a table in the zookeeper app
+// it's just so you can easily see what machines in your grid are
+// doing what
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "runtime"
+ "strings"
+)
+
+func initDistro() string {
+ switch runtime.GOOS {
+ case "windows":
+ return "windows"
+ case "macos":
+ return "macos"
+ case "linux":
+ // Detect the Linux distribution
+ distro := detectDistro()
+ if distro == "" {
+ fmt.Println("Unable to detect Linux distribution.")
+ distro = "fixme"
+ }
+
+ fmt.Printf("Detected distribution: %s\n", distro)
+ return distro
+ default:
+ return runtime.GOOS
+ }
+}
+
+// detectDistro returns the Linux distribution name (if possible)
+func detectDistro() string {
+ // Check if we're on Linux
+
+ // Try to read /etc/os-release to determine the distro
+ file, err := os.Open("/etc/os-release")
+ if err != nil {
+ return ""
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "ID=") {
+ parts := strings.SplitN(line, "=", 2)
+ if len(parts) == 2 {
+ return strings.Trim(parts[1], `"`)
+ }
+ }
+ }
+ return ""
+}
diff --git a/machine.proto b/machine.proto
index 0926fa5..76c7aa2 100644
--- a/machine.proto
+++ b/machine.proto
@@ -12,6 +12,9 @@ message Machine {
string distro = 4;
Packages packages = 5;
google.protobuf.Timestamp laststamp = 6; // the last time we heard anything from this machine
+ Packages installed = 7; // packages that are installed
+ Packages available = 8; // packages that are available
+ Packages wit = 9; // packages that are available from mirrors.wit.com
}
message Machines {
diff --git a/package.proto b/package.proto
index 53859a8..500db12 100644
--- a/package.proto
+++ b/package.proto
@@ -6,9 +6,11 @@ package gitpb;
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
message Package {
- string name = 1;
- string version = 2;
- google.protobuf.Timestamp laststamp = 4; // the last time we heard anything from this droplet
+ string name = 1; // name: zookeeper-go
+ string version = 2; // version: 0.0.3
+ google.protobuf.Timestamp laststamp = 3; // the last time this package was seen (used to timeout entries)
+ string srcPath = 4; // path to the sources (go.wit.com/apps/zookeeper)
+ bool installed = 5; // installed: true
}
message Packages {