Verified Commit bd35dc4c authored by meskio's avatar meskio 🏔️
Browse files

Publish collector metrics every hour

Publish the bridges that has being tested in the last 24 hours with
their latest test result for each of them.

* Resolves #39 #24 #38
parent 7b482502
Loading
Loading
Loading
Loading
+14 −9
Original line number Diff line number Diff line
@@ -10,20 +10,21 @@ import (
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"sync"
	"time"
)

const (
	collectorResolution    = 24 * time.Hour
	collectorResolution    = time.Hour
	expireDuration         = 24 * time.Hour
	collectorTempExtension = ".tmp"
)

type bridgeTestResult struct {
	Functional      bool
	FingerprintHash string
	Time            time.Time
}

// CollectorMetrics stores the metrics to produce a metrics document for CollecTor
@@ -32,7 +33,7 @@ type CollectorMetrics struct {
	filename string

	CachedRequests uint
	BridgeTests    []bridgeTestResult
	BridgeTests    map[string]bridgeTestResult
	LastPublished  time.Time

	//synchronization for access to bridgestrap collector
@@ -45,6 +46,7 @@ var collectorMetrics *CollectorMetrics
// it must be called once before the variable is being used
func InitCollectorMetrics(metricsFilename string) error {
	collectorMetrics = new(CollectorMetrics)
	collectorMetrics.BridgeTests = make(map[string]bridgeTestResult)
	err := loadCollectorMetrics(metricsFilename + collectorTempExtension)
	if err != nil {
		return err
@@ -89,9 +91,10 @@ func (m *CollectorMetrics) IncCacheRequests() {

// AddBridgeTest adds the functional status of the bridge to the metrics
// fingerprint can be an empty string, and the fingerprint will be retrieved if possible from the bridgeLine
func (m *CollectorMetrics) AddBridgeTest(functional bool, fingerprint string, bridgeLine string) {
func (m *CollectorMetrics) AddBridgeTest(functional bool, fingerprint string, bridgeLine string, lastTested time.Time) {
	test := bridgeTestResult{
		Functional: functional,
		Time:       lastTested,
	}

	fp := ""
@@ -112,7 +115,7 @@ func (m *CollectorMetrics) AddBridgeTest(functional bool, fingerprint string, br

	m.lock.Lock()
	defer m.lock.Unlock()
	m.BridgeTests = append(m.BridgeTests, test)
	m.BridgeTests[fp] = test
}

func (m *CollectorMetrics) Save() {
@@ -154,10 +157,12 @@ func (m *CollectorMetrics) publishMetrics() {
	m.logger.Println("bridgestrap-stats-end", time.Now().UTC().Format("2006-01-02 15:04:05"), fmt.Sprintf("(%d s)", int(collectorResolution.Seconds())))
	m.logger.Println("bridgestrap-cached-requests", m.CachedRequests)

	sort.Slice(m.BridgeTests, func(i, j int) bool {
		return m.BridgeTests[i].FingerprintHash < m.BridgeTests[j].FingerprintHash
	})
	for _, test := range m.BridgeTests {
	expireDate := time.Now().UTC().Add(-expireDuration)
	for fp, test := range m.BridgeTests {
		if test.Time.Before(expireDate) {
			delete(m.BridgeTests, fp)
			continue
		}
		m.logger.Println("bridgestrap-test", test.Functional, test.FingerprintHash)
	}

+43 −3
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import (
	"strconv"
	"strings"
	"testing"
	"time"
)

func TestCachedRequests(t *testing.T) {
@@ -65,9 +66,9 @@ func TestBridgeTests(t *testing.T) {
		t.Fatalf("Failed to initialize the collector metrics: %s", err)
	}

	collectorMetrics.AddBridgeTest(true, fingerprint1, "1.2.3.4:443 "+fingerprint1)
	collectorMetrics.AddBridgeTest(false, "", "4.3.2.1:443 "+fingerprint2)
	collectorMetrics.AddBridgeTest(false, "", "8.7.6.4:443")
	collectorMetrics.AddBridgeTest(true, fingerprint1, "1.2.3.4:443 "+fingerprint1, time.Now().UTC())
	collectorMetrics.AddBridgeTest(false, "", "4.3.2.1:443 "+fingerprint2, time.Now().UTC())
	collectorMetrics.AddBridgeTest(false, "", "8.7.6.4:443", time.Now().UTC())

	collectorMetrics.publishMetrics()
	content, err := ioutil.ReadAll(tmpFh)
@@ -113,6 +114,45 @@ func TestBridgeTests(t *testing.T) {
	}
}

func TestCollectorTwiceBridge(t *testing.T) {
	const fingerprint1 = "0123456789ABCDEF0123456789ABCDEF01234567"

	tmpFh, err := ioutil.TempFile(os.TempDir(), "cache-file-")
	if err != nil {
		t.Fatalf("Could not create temporary file for test: %s", err)
	}
	defer os.Remove(tmpFh.Name())

	err = InitCollectorMetrics(tmpFh.Name())
	if err != nil {
		t.Fatalf("Failed to initialize the collector metrics: %s", err)
	}

	collectorMetrics.AddBridgeTest(true, fingerprint1, "1.2.3.4:443 "+fingerprint1, time.Now().UTC().Add(-1*time.Minute))
	collectorMetrics.AddBridgeTest(false, fingerprint1, "1.2.3.4:443 "+fingerprint1, time.Now().UTC())

	collectorMetrics.publishMetrics()
	content, err := ioutil.ReadAll(tmpFh)
	if err != nil {
		t.Fatalf("Failed to read the collected metrics: %s", err)
	}

	re := regexp.MustCompile(`bridgestrap-test ([a-z]*) (.+)\n`)
	fpTests := re.FindAll(content, -1)
	if len(fpTests) != 1 {
		t.Fatalf("Found %d tests with fingerprint", len(fpTests))
	}

	sp := strings.Split(string(fpTests[0]), " ")
	functional, err := strconv.ParseBool(sp[1])
	if err != nil {
		t.Errorf("Can't parse the status of the bridge to bool: %s", err)
	}
	if functional {
		t.Errorf("Not the expected functional %v for %s", functional, string(sp[2]))
	}
}

func TestHashFingerprint(t *testing.T) {
	fp := "C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C"
	expectedHash := "997B6FEB5A02B1F08A36CECCE9B5B3997C0A4680"
+1 −1
Original line number Diff line number Diff line
@@ -135,7 +135,7 @@ func testBridgeLines(req *TestRequest) *TestResult {
			} else {
				metrics.BridgeStatus.With(prometheus.Labels{"status": "dysfunctional"}).Inc()
			}
			collectorMetrics.AddBridgeTest(bridgeTest.Functional, bridgeTest.fingerprint, bridgeLine)
			collectorMetrics.AddBridgeTest(bridgeTest.Functional, bridgeTest.fingerprint, bridgeLine, bridgeTest.LastTested)
			result.Bridges[bridgeLine] = bridgeTest
		}
	} else {