Skip to content

Commit 2303da2

Browse files
author
Ricky Cousins
committed
Minor refactor & Implement Rocky support
1 parent e1b56de commit 2303da2

File tree

7 files changed

+187
-137
lines changed

7 files changed

+187
-137
lines changed

distros/distros.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package distros
22

33
type Distro interface {
4-
GetSecurityUpdates() int
5-
GetTotalUpdates() int
6-
GetRebootRequired() bool
4+
GetSecurityUpdates() int
5+
GetTotalUpdates() int
6+
GetRebootRequired() bool
77
}

distros/rhel/rocky.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package rhel
2+
3+
import (
4+
"log"
5+
"os/exec"
6+
7+
utils "github.com/Puppet-Finland/updates-exporter/distros"
8+
)
9+
10+
type Rocky struct{}
11+
12+
func (Rocky) GetSecurityUpdates() int {
13+
cmd := exec.Command("sh", "-c", "dnf updateinfo list --sec-severity=Critical --sec-severity=Important --all | wc -l")
14+
out, err := cmd.Output()
15+
if err != nil {
16+
log.Printf("Error running dnf: %v", err)
17+
return -1
18+
}
19+
return utils.ParseUpdateCount(string(out))
20+
}
21+
22+
func (Rocky) GetTotalUpdates() int {
23+
cmd := exec.Command("sh", "-c", "dnf updateinfo list --all | wc -l")
24+
out, err := cmd.Output()
25+
if err != nil {
26+
log.Printf("Error running dnf: %v", err)
27+
return -1
28+
}
29+
return utils.ParseUpdateCount(string(out))
30+
}
31+
32+
func (Rocky) GetRebootRequired() bool {
33+
cmd := exec.Command("needs-restarting", "-r")
34+
err := cmd.Run()
35+
return err != nil
36+
}

distros/ubuntu/ubuntu.go

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,40 @@
11
package ubuntu
22

33
import (
4-
"log"
5-
"os"
6-
"os/exec"
7-
"strconv"
8-
"strings"
4+
"log"
5+
"os"
6+
"os/exec"
7+
8+
utils "github.com/Puppet-Finland/updates-exporter/distros"
99
)
1010

1111
type Ubuntu struct{}
1212

1313
var rebootFile = "/var/run/reboot-required"
1414

15-
func parseUpdateCount(out string) int {
16-
count, _ := strconv.Atoi(strings.TrimSpace(out))
17-
return count
18-
}
19-
2015
func (Ubuntu) GetSecurityUpdates() int {
21-
cmd := exec.Command("sh", "-c", `apt-get -s dist-upgrade | grep "^Inst" | grep security | wc -l`)
22-
output, err := cmd.Output()
23-
if err != nil {
24-
log.Printf("Error running apt-get: %v", err)
25-
return -1
26-
}
27-
return parseUpdateCount(string(output))
16+
cmd := exec.Command("sh", "-c", `apt-get -s dist-upgrade | grep "^Inst" | grep security | wc -l`)
17+
output, err := cmd.Output()
18+
if err != nil {
19+
log.Printf("Error running apt-get: %v", err)
20+
return -1
21+
}
22+
return utils.ParseUpdateCount(string(output))
2823
}
2924

3025
func (Ubuntu) GetTotalUpdates() int {
31-
cmd := exec.Command("sh", "-c", `apt-get -s dist-upgrade | grep "^Inst" | wc -l`)
32-
output, err := cmd.Output()
33-
if err != nil {
34-
log.Printf("Error running apt-get: %v", err)
35-
return -1
36-
}
37-
return parseUpdateCount(string(output))
26+
cmd := exec.Command("sh", "-c", `apt-get -s dist-upgrade | grep "^Inst" | wc -l`)
27+
output, err := cmd.Output()
28+
if err != nil {
29+
log.Printf("Error running apt-get: %v", err)
30+
return -1
31+
}
32+
return utils.ParseUpdateCount(string(output))
3833
}
3934

4035
func (Ubuntu) GetRebootRequired() bool {
41-
if _, err := os.Stat(rebootFile); err == nil {
42-
return true
43-
}
44-
return false
36+
if _, err := os.Stat(rebootFile); err == nil {
37+
return true
38+
}
39+
return false
4540
}
46-

distros/ubuntu/ubuntu_test.go

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,27 @@
11
package ubuntu
22

33
import (
4-
"os"
5-
"testing"
4+
"os"
5+
"testing"
66
)
77

88
var ubuntu = Ubuntu{}
99

10-
func TestParseUpdateCount(t *testing.T) {
11-
output := "3\n"
12-
expected := 3
13-
got := parseUpdateCount(output)
14-
if got != expected {
15-
t.Errorf("Expected %d, got %d", expected, got)
16-
}
17-
}
18-
19-
func TestRebootRequired(t *testing.T) {
20-
// Create a fake reboot-required file
21-
tmpfile := "/tmp/reboot-required"
22-
err := os.WriteFile(tmpfile, []byte("reboot"), 0644)
23-
if err != nil {
24-
t.Fatalf("failed to create temp file: %v", err)
25-
}
10+
func TestUbuntuRebootRequired(t *testing.T) {
11+
// Create a fake reboot-required file
12+
tmpfile := "/tmp/reboot-required"
13+
err := os.WriteFile(tmpfile, []byte("reboot"), 0644)
14+
if err != nil {
15+
t.Fatalf("failed to create temp file: %v", err)
16+
}
2617

27-
oldFile := rebootFile
28-
rebootFile = tmpfile
29-
defer func() {
30-
rebootFile = oldFile
31-
}()
18+
oldFile := rebootFile
19+
rebootFile = tmpfile
20+
defer func() {
21+
rebootFile = oldFile
22+
}()
3223

33-
if !ubuntu.GetRebootRequired() {
34-
t.Errorf("Expected reboot required = true")
35-
}
24+
if !ubuntu.GetRebootRequired() {
25+
t.Errorf("Expected reboot required = true")
26+
}
3627
}

distros/utils.go

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,41 @@
11
package distros
22

33
import (
4-
"runtime"
5-
"strings"
6-
"os/exec"
4+
"os"
5+
"os/exec"
6+
"runtime"
7+
"strconv"
8+
"strings"
79
)
810

9-
// DetectLinuxDistro returns "ubuntu", "rhel", or "unknown"
11+
var rockyReleaseFile = "/etc/rocky-release"
12+
13+
func ParseUpdateCount(out string) int {
14+
count, _ := strconv.Atoi(strings.TrimSpace(out))
15+
return count
16+
}
17+
1018
func GetLinuxDistro() string {
11-
if runtime.GOOS != "linux" {
12-
return "unknown"
13-
}
19+
if runtime.GOOS != "linux" {
20+
return "unknown"
21+
}
22+
23+
if _, err := os.Stat(rockyReleaseFile); err == nil {
24+
return "rocky"
25+
}
1426

15-
out, err := exec.Command("sh", "-c", "cat /etc/os-release").Output()
16-
if err != nil {
17-
return "unknown"
18-
}
27+
out, err := exec.Command("sh", "-c", "cat /etc/os-release").Output()
28+
if err != nil {
29+
return "unknown"
30+
}
1931

20-
s := strings.ToLower(string(out))
21-
switch {
22-
case strings.Contains(s, "ubuntu"):
23-
return "ubuntu"
24-
case strings.Contains(s, "rhel"), strings.Contains(s, "centos"), strings.Contains(s, "fedora"):
25-
return "rhel"
26-
default:
27-
return "unknown"
28-
}
32+
s := strings.ToLower(string(out))
33+
switch {
34+
case strings.Contains(s, "ubuntu"):
35+
return "ubuntu"
36+
case strings.Contains(s, "rhel"), strings.Contains(s, "centos"), strings.Contains(s, "fedora"):
37+
return "rhel"
38+
default:
39+
return "unknown"
40+
}
2941
}

distros/utils_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package distros
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestParseUpdateCount(t *testing.T) {
8+
output := "3\n"
9+
expected := 3
10+
got := ParseUpdateCount(output)
11+
if got != expected {
12+
t.Errorf("Expected %d, got %d", expected, got)
13+
}
14+
}

main.go

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,89 @@
11
package main
22

33
import (
4-
"flag"
5-
"fmt"
6-
"log"
7-
"net/http"
8-
"time"
4+
"flag"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"time"
99

10-
"github.com/prometheus/client_golang/prometheus"
11-
"github.com/prometheus/client_golang/prometheus/promhttp"
10+
"github.com/prometheus/client_golang/prometheus"
11+
"github.com/prometheus/client_golang/prometheus/promhttp"
1212

13-
"github.com/Puppet-Finland/updates-exporter/distros"
14-
"github.com/Puppet-Finland/updates-exporter/distros/ubuntu"
13+
"github.com/Puppet-Finland/updates-exporter/distros"
14+
"github.com/Puppet-Finland/updates-exporter/distros/rhel"
15+
"github.com/Puppet-Finland/updates-exporter/distros/ubuntu"
1516
)
1617

1718
const (
18-
DEFAULT_INTERVAL = 3600
19-
DEFAULT_PORT = 9101
19+
DEFAULT_INTERVAL = 3600
20+
DEFAULT_PORT = 9101
2021
)
22+
2123
var (
22-
securityUpdates = prometheus.NewGauge(prometheus.GaugeOpts{
23-
Name: "pending_security_updates",
24-
Help: "Number of pending security updates",
25-
})
26-
totalUpdates = prometheus.NewGauge(prometheus.GaugeOpts{
27-
Name: "pending_updates",
28-
Help: "Total number of pending updates",
29-
})
30-
rebootRequired = prometheus.NewGauge(prometheus.GaugeOpts{
31-
Name: "reboot_required",
32-
Help: "1 if a reboot is required, 0 otherwise",
33-
})
24+
securityUpdates = prometheus.NewGauge(prometheus.GaugeOpts{
25+
Name: "pending_security_updates",
26+
Help: "Number of pending security updates",
27+
})
28+
totalUpdates = prometheus.NewGauge(prometheus.GaugeOpts{
29+
Name: "pending_updates",
30+
Help: "Total number of pending updates",
31+
})
32+
rebootRequired = prometheus.NewGauge(prometheus.GaugeOpts{
33+
Name: "reboot_required",
34+
Help: "1 if a reboot is required, 0 otherwise",
35+
})
3436
)
3537

3638
func getDistro() distros.Distro {
37-
switch distros.GetLinuxDistro() {
38-
case "ubuntu":
39-
return ubuntu.Ubuntu{}
40-
default:
41-
return nil
42-
}
39+
switch distros.GetLinuxDistro() {
40+
case "ubuntu":
41+
return ubuntu.Ubuntu{}
42+
case "rocky":
43+
return rhel.Rocky{}
44+
default:
45+
return nil
46+
}
4347
}
4448

4549
func updateMetrics(d distros.Distro) {
46-
if d == nil {
47-
return
48-
}
49-
securityUpdates.Set(float64(d.GetSecurityUpdates()))
50-
totalUpdates.Set(float64(d.GetTotalUpdates()))
50+
if d == nil {
51+
return
52+
}
53+
securityUpdates.Set(float64(d.GetSecurityUpdates()))
54+
totalUpdates.Set(float64(d.GetTotalUpdates()))
5155

52-
if d.GetRebootRequired() {
53-
rebootRequired.Set(1)
54-
} else {
55-
rebootRequired.Set(0)
56-
}
56+
if d.GetRebootRequired() {
57+
rebootRequired.Set(1)
58+
} else {
59+
rebootRequired.Set(0)
60+
}
5761

5862
}
5963

6064
func main() {
61-
port := flag.Int("port", DEFAULT_PORT, "HTTP port")
62-
interval := flag.Int("interval", DEFAULT_INTERVAL, "Metrics refresh interval (seconds)")
65+
port := flag.Int("port", DEFAULT_PORT, "HTTP port")
66+
interval := flag.Int("interval", DEFAULT_INTERVAL, "Metrics refresh interval (seconds)")
6367

64-
flag.Parse()
68+
flag.Parse()
6569

66-
prometheus.MustRegister(securityUpdates)
67-
prometheus.MustRegister(totalUpdates)
68-
prometheus.MustRegister(rebootRequired)
70+
prometheus.MustRegister(securityUpdates)
71+
prometheus.MustRegister(totalUpdates)
72+
prometheus.MustRegister(rebootRequired)
6973

70-
distro := getDistro()
71-
if distro == nil {
72-
log.Fatal("Error: Distro not detected")
73-
}
74+
distro := getDistro()
75+
if distro == nil {
76+
log.Fatal("Error: Distro not detected")
77+
}
7478

75-
go func() {
76-
for {
77-
updateMetrics(distro)
78-
time.Sleep(time.Duration(*interval) * time.Second)
79-
}
80-
}()
79+
go func() {
80+
for {
81+
updateMetrics(distro)
82+
time.Sleep(time.Duration(*interval) * time.Second)
83+
}
84+
}()
8185

82-
http.Handle("/metrics", promhttp.Handler())
83-
fmt.Printf("Starting updates_exporter on :%d, updating every %d seconds\n", *port, *interval)
84-
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
86+
http.Handle("/metrics", promhttp.Handler())
87+
fmt.Printf("Starting updates_exporter on :%d, updating every %d seconds\n", *port, *interval)
88+
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
8589
}
86-

0 commit comments

Comments
 (0)