@@ -8,14 +8,17 @@ import (
88 "crypto/x509/pkix"
99 "errors"
1010 "fmt"
11+ "math"
1112 "math/big"
1213 "net"
1314 "net/url"
15+ "strconv"
1416 "strings"
1517 "testing"
1618 "time"
1719
1820 "github.com/prometheus/client_golang/prometheus/testutil"
21+ "github.com/prometheus/common/expfmt"
1922 "github.com/stretchr/testify/require"
2023 "github.com/strongswan/govici/vici"
2124)
@@ -28,6 +31,11 @@ type fakeViciClient struct {
2831 closeTriggered int
2932}
3033
34+ const (
35+ crtExpireTimeMetricName = "strongswan_crt_expire_secs"
36+ crtExpireAllowedDiffFromExpected = 60.0
37+ )
38+
3139func (fvc * fakeViciClient ) StreamedCommandRequest (cmd string , event string , _ * vici.Message ) ([]* vici.Message , error ) {
3240 if cmd == "list-sas" && event == "list-sa" {
3341 return fvc .sasMsgs , fvc .sasErr
@@ -410,27 +418,16 @@ func TestCollector_Metrics(t *testing.T) {
410418 wantMetricsHelp : "Number of X509 certificates" ,
411419 wantMetricsType : "gauge" ,
412420 wantMetricsValue : 1 ,
413- wantMetricsCount : 3 ,
421+ wantMetricsCount : 4 ,
414422 },
415423 {
416- name : "one cert count" ,
417- crtsMsgsModifierFn : func () []* vici.Message {
418- crtMsg := vici .NewMessage ()
419- crtMsg .Set ("type" , "X509" )
420-
421- crt , err := createSingleX509Crt ()
422- if err != nil {
423- return []* vici.Message {}
424- }
425-
426- crtMsg .Set ("data" , string (crt ))
427- return []* vici.Message {crtMsg }
428- },
429- metricName : "strongswan_crt_count" ,
430- wantMetricsHelp : "Number of X509 certificates" ,
431- wantMetricsType : "gauge" ,
432- wantMetricsValue : 1 ,
433- wantMetricsCount : 3 ,
424+ name : "one cert count" ,
425+ crtsMsgsModifierFn : singleCertViciMessages (createSingleX509Crt ),
426+ metricName : "strongswan_crt_count" ,
427+ wantMetricsHelp : "Number of X509 certificates" ,
428+ wantMetricsType : "gauge" ,
429+ wantMetricsValue : 1 ,
430+ wantMetricsCount : 4 ,
434431 },
435432 {
436433 name : "two cert count" ,
@@ -454,28 +451,27 @@ func TestCollector_Metrics(t *testing.T) {
454451 wantMetricsHelp : "Number of X509 certificates" ,
455452 wantMetricsType : "gauge" ,
456453 wantMetricsValue : 2 ,
457- wantMetricsCount : 4 ,
454+ wantMetricsCount : 6 ,
458455 },
459456 {
460- name : "cert valid" ,
461- crtsMsgsModifierFn : func () []* vici.Message {
462- crtMsg := vici .NewMessage ()
463- crtMsg .Set ("type" , "X509" )
464-
465- crt , err := createSingleX509Crt ()
466- if err != nil {
467- return []* vici.Message {}
468- }
469-
470- crtMsg .Set ("data" , string (crt ))
471- return []* vici.Message {crtMsg }
472- },
473- metricName : "strongswan_crt_valid" ,
474- wantMetricsHelp : "X509 certificate validity" ,
475- wantMetricsType : "gauge" ,
476- wantMetricsLabels : `alternate_names="",not_after="2124-01-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
477- wantMetricsValue : 1 ,
478- wantMetricsCount : 3 ,
457+ name : "cert valid" ,
458+ crtsMsgsModifierFn : singleCertViciMessages (createSingleX509Crt ),
459+ metricName : "strongswan_crt_valid" ,
460+ wantMetricsHelp : "X509 certificate validity" ,
461+ wantMetricsType : "gauge" ,
462+ wantMetricsLabels : `alternate_names="",not_after="2124-01-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
463+ wantMetricsValue : 1 ,
464+ wantMetricsCount : 4 ,
465+ },
466+ {
467+ name : "cert expired" ,
468+ crtsMsgsModifierFn : singleCertViciMessages (createExpiredX509Crt ),
469+ metricName : "strongswan_crt_valid" ,
470+ wantMetricsHelp : "X509 certificate validity" ,
471+ wantMetricsType : "gauge" ,
472+ wantMetricsLabels : `alternate_names="",not_after="2024-12-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
473+ wantMetricsValue : 0 ,
474+ wantMetricsCount : 4 ,
479475 },
480476 {
481477 name : "cert alternate names" ,
@@ -510,7 +506,7 @@ func TestCollector_Metrics(t *testing.T) {
510506 wantMetricsType : "gauge" ,
511507 wantMetricsLabels : `alternate_names="DNS=test.org+DNS=test2.org,EM=Name1+EM=Name2,IP=192.168.0.1+IP=192.168.1.1,URI=https://test.org/foobar+URI=https://test2.org/foobar2",not_after="2124-01-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
512508 wantMetricsValue : 1 ,
513- wantMetricsCount : 3 ,
509+ wantMetricsCount : 4 ,
514510 },
515511 }
516512 for _ , tt := range tests {
@@ -750,6 +746,100 @@ func TestCollector_MetricsSaChild(t *testing.T) {
750746 }
751747}
752748
749+ func TestCollector_MetricsCrtExpireTime (t * testing.T ) {
750+ tz , err := time .LoadLocation ("UTC" )
751+ if err != nil {
752+ t .Errorf ("failed to load timezone: %s" , err )
753+ }
754+
755+ tests := []struct {
756+ name string
757+ crtsMsgsModifierFn func () []* vici.Message
758+ wantMetricsLabels string
759+ wantMetricsValue float64
760+ }{
761+ {
762+ name : "cert expire time for valid cert" ,
763+ crtsMsgsModifierFn : singleCertViciMessages (createSingleX509Crt ),
764+ wantMetricsLabels : `alternate_names="",not_after="2124-01-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
765+ wantMetricsValue : time .Until (time .Date (2124 , 1 , 1 , 12 , 0 , 0 , 0 , tz )).Seconds (),
766+ },
767+ {
768+ name : "cert expire time for expired cert" ,
769+ crtsMsgsModifierFn : singleCertViciMessages (createExpiredX509Crt ),
770+ wantMetricsLabels : `alternate_names="",not_after="2024-12-01T12:00:00Z",not_before="2024-01-01T12:00:00Z",serial_number="1",subject="CN=Test,O=Org1"` ,
771+ wantMetricsValue : time .Until (time .Date (2024 , 12 , 1 , 12 , 0 , 0 , 0 , tz )).Seconds (),
772+ },
773+ }
774+ for _ , tt := range tests {
775+ t .Run (tt .name , func (t * testing.T ) {
776+ crtsMmsgs := []* vici.Message {}
777+ if tt .crtsMsgsModifierFn != nil {
778+ crtsMmsgs = tt .crtsMsgsModifierFn ()
779+ }
780+ c := NewCollector (func () (ViciClient , error ) {
781+ return & fakeViciClient {
782+ crtsMsgs : crtsMmsgs ,
783+ },
784+ nil
785+ })
786+
787+ cnt := testutil .CollectAndCount (c )
788+ const wantMetricsCount = 4
789+ require .Equal (t , wantMetricsCount , cnt , "metrics count" )
790+
791+ metricBytes , err := testutil .CollectAndFormat (c , expfmt .TypeTextPlain , crtExpireTimeMetricName )
792+ if err != nil {
793+ t .Fatalf ("unexpected collecting result of '%s':\n %s" , crtExpireTimeMetricName , err )
794+ }
795+
796+ metricStr := strings .TrimSpace (string (metricBytes ))
797+ validateMetricsCrtExpireTimeValue (t , metricStr , tt .wantMetricsValue )
798+ validateMetricsCrtExpireTimeLabels (t , metricStr , tt .wantMetricsLabels )
799+ })
800+ }
801+ }
802+
803+ func validateMetricsCrtExpireTimeValue (t * testing.T , metricStr string , wantMetricsValue float64 ) {
804+ metricFields := strings .Split (metricStr , " " )
805+ if len (metricFields ) == 0 {
806+ t .Fatalf ("unexpected format of metric '%s': %s" , crtExpireTimeMetricName , metricStr )
807+ }
808+
809+ metricValStr := metricFields [len (metricFields )- 1 ]
810+ metricVal , err := strconv .ParseFloat (metricValStr , 64 )
811+ if err != nil {
812+ t .Fatalf ("failure in parsing of metric's value '%s': %s\n %s" , crtExpireTimeMetricName , metricValStr , err )
813+ }
814+
815+ require .GreaterOrEqual (t , crtExpireAllowedDiffFromExpected , math .Abs (metricVal - wantMetricsValue ), "seconds till cert expires value" )
816+ }
817+
818+ func validateMetricsCrtExpireTimeLabels (t * testing.T , metricStr , wantMetricsLabels string ) {
819+ labels := strings .Split (metricStr , "{" )
820+ if len (labels ) < 2 {
821+ t .Fatalf ("unexpected format of metric '%s': %s" , crtExpireTimeMetricName , metricStr )
822+ }
823+
824+ labels = strings .Split (labels [1 ], "}" )
825+ require .Equal (t , labels [0 ], wantMetricsLabels , "seconds till cert expires labels" )
826+ }
827+
828+ func singleCertViciMessages (createX509CrtFn func () ([]byte , error )) func () []* vici.Message {
829+ return func () []* vici.Message {
830+ crtMsg := vici .NewMessage ()
831+ crtMsg .Set ("type" , "X509" )
832+
833+ crt , err := createX509CrtFn ()
834+ if err != nil {
835+ return []* vici.Message {}
836+ }
837+
838+ crtMsg .Set ("data" , string (crt ))
839+ return []* vici.Message {crtMsg }
840+ }
841+ }
842+
753843func createSingleX509Crt () ([]byte , error ) {
754844 return createSingleX509CrtWithAlternateNames ([]string {}, []string {}, []net.IP {}, []* url.URL {})
755845}
@@ -808,6 +898,22 @@ func createDoubleX509Crt() ([]byte, []byte, error) {
808898 return crt1 , crt2 , nil
809899}
810900
901+ func createExpiredX509Crt () ([]byte , error ) {
902+ tz , err := time .LoadLocation ("UTC" )
903+ if err != nil {
904+ return nil , err
905+ }
906+
907+ return createX509Crt (
908+ pkix.Name {
909+ Organization : []string {"Org1" },
910+ CommonName : "Test" ,
911+ },
912+ time .Date (2024 , 1 , 1 , 12 , 0 , 0 , 0 , tz ),
913+ time .Date (2024 , 12 , 1 , 12 , 0 , 0 , 0 , tz ),
914+ )
915+ }
916+
811917func createX509Crt (subject pkix.Name , notBefore , notAfter time.Time ) ([]byte , error ) {
812918 return createX509CrtWithAlternateNames (subject , notBefore , notAfter , []string {}, []string {}, []net.IP {}, []* url.URL {})
813919}
0 commit comments