diff --git a/cmd/godplugin/main.go b/cmd/godplugin/main.go index 64573aca7e..3b9fa749d8 100644 --- a/cmd/godplugin/main.go +++ b/cmd/godplugin/main.go @@ -30,6 +30,7 @@ import ( _ "github.com/netdata/go.d.plugin/modules/solr" _ "github.com/netdata/go.d.plugin/modules/springboot2" _ "github.com/netdata/go.d.plugin/modules/weblog" + _ "github.com/netdata/go.d.plugin/modules/x509check" ) var ( diff --git a/config/go.d.conf b/config/go.d.conf index 33128bfb65..b9b2dc7e3c 100644 --- a/config/go.d.conf +++ b/config/go.d.conf @@ -31,3 +31,4 @@ modules: # solr: yes # springboot2: yes # web_log: yes +# x509check: yes diff --git a/config/go.d/x509check.conf b/config/go.d/x509check.conf new file mode 100644 index 0000000000..596ad16744 --- /dev/null +++ b/config/go.d/x509check.conf @@ -0,0 +1,116 @@ +# netdata go.d.plugin configuration for x509check +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - GLOBAL +# - JOBS +# +# +# [ GLOBAL ] +# These variables set the defaults for all JOBs, however each JOB may define its own, overriding the defaults. +# +# The GLOBAL section format: +# param1: value1 +# param2: value2 +# +# Currently supported global parameters: +# - update_every +# Data collection frequency in seconds. Default: 1. +# +# - autodetection_retry +# Re-check interval in seconds. Attempts to start the job are made once every interval. +# Zero means not to schedule re-check. Default: 0. +# +# +# [ JOBS ] +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# IMPORTANT: +# - Parameter 'name' is mandatory. +# - Jobs with the same name are mutually exclusive. Only one of them will be allowed running at any time. +# +# This allows autodetection to try several alternatives and pick the one that works. +# Any number of jobs is supported. +# +# The JOBS section format: +# +# jobs: +# - name: job1 +# param1: value1 +# param2: value2 +# +# - name: job2 +# param1: value1 +# param2: value2 +# +# - name: job2 +# param1: value1 +# +# +# [ List of JOB specific parameters ]: +# - source +# Certificate source. Allowed schemes: https, tcp, tcp4, tcp6, udp, udp4, udp6. +# Syntax: +# source: https://example.org:443 +# +# - days_until_expiration_warning +# Number of days before the alarm status is warning. +# Syntax: +# days_until_expiration_warning: 30 +# +# - days_until_expiration_critical +# Number of days before the alarm status is critical. +# Syntax: +# days_until_expiration_critical: 15 +# +# - timeout +# SSL connection timeout. +# Syntax: +# timeout: 3 +# +# - tls_skip_verify +# Whether to skip verifying server's certificate chain and hostname. +# Syntax: +# tls_skip_verify: yes/no +# +# - tls_ca +# Certificate authority that client use when verifying server certificates. +# Syntax: +# tls_ca: path/to/ca.pem +# +# - tls_cert +# Client tls certificate. +# Syntax: +# tls_cert: path/to/cert.pem +# +# - tls_key +# Client tls key. +# Syntax: +# tls_key: path/to/key.pem +# +# +# [ JOB defaults ]: +# days_until_expiration_warning: 14 +# days_until_expiration_critical: 7 +# timeout: 2 +# tls_skip_verify: no +# +# +# [ JOB mandatory parameters ]: +# - name +# - source +# +# ------------------------------------------------MODULE-CONFIGURATION-------------------------------------------------- +# [ GLOBAL ] +# update_every: 60 +# autodetection_retry: 0 +# +# +# [ JOBS ] +# jobs: +# - name: example_org +# source: https://example.org:443 diff --git a/go.mod b/go.mod index 9f60e7eae8..68738a1e6f 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,20 @@ module github.com/netdata/go.d.plugin +go 1.12 + require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect github.com/go-sql-driver/mysql v1.4.1 github.com/hpcloud/tail v1.0.0 - github.com/miekg/dns v1.1.4 - github.com/netdata/go-orchestrator v0.0.0-20190312061432-547df3f9ece6 + github.com/miekg/dns v1.1.6 + github.com/netdata/go-orchestrator v0.0.0-20190313225439-42e20dc9010d github.com/prometheus/common v0.2.0 // indirect github.com/prometheus/prometheus v2.5.0+incompatible github.com/stretchr/testify v1.3.0 - golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 // indirect - golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect + golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect + golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect + golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect google.golang.org/appengine v1.4.0 // indirect gopkg.in/fsnotify.v1 v1.4.7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 8284b41e32..b950b4c6b8 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -25,17 +26,17 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0= -github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.6 h1:jVwb4GDwD65q/gtItR/lIZHjNH93QfeGxZUkzJcW9mc= +github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/netdata/go-orchestrator v0.0.0-20190312061432-547df3f9ece6 h1:fS3/R+LVSdfIK5R+0aI0lsCMszGBsElfq7IZqnuWzWk= -github.com/netdata/go-orchestrator v0.0.0-20190312061432-547df3f9ece6/go.mod h1:GkRx+XaReZcgCkm8nlSM+90ctTww5l0G9Qtxx8ZkvYY= +github.com/netdata/go-orchestrator v0.0.0-20190313225439-42e20dc9010d h1:efRP4qspNR5uLzxcj/xxjBYGPqOnPGL3dq9HXfCFzPE= +github.com/netdata/go-orchestrator v0.0.0-20190313225439-42e20dc9010d/go.mod h1:ErI8v7oo3A0a5PMhPg/sxyV9FmB8Md+QlNn0p5qLvEs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -56,19 +57,21 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2 h1:NwxKRvbkH5MsNkvOtPZi3/3kmI8CAzs3mtv+GLQMkNo= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/modules/x509check/README.md b/modules/x509check/README.md new file mode 100644 index 0000000000..a84cbf6484 --- /dev/null +++ b/modules/x509check/README.md @@ -0,0 +1,24 @@ +# x509 certificate expiry check + +Checks the time until a x509 certificate expires. + +It produces the following charts: + +1. **Time Until Certificate Expiration** in seconds + * time + +### configuration + +For all available options and defaults please see module [configuration file](https://github.com/netdata/go.d.plugin/blob/master/config/go.d/x509.conf). +___ + +```yaml +update_every : 60 + +jobs: + - name: example_org + source: https://example.org:443 + + - name: my_site_org + source: https://my_site_org:443 +``` diff --git a/modules/x509check/cert_getter.go b/modules/x509check/cert_getter.go new file mode 100644 index 0000000000..63cde015f0 --- /dev/null +++ b/modules/x509check/cert_getter.go @@ -0,0 +1,129 @@ +package x509check + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io/ioutil" + "net" + "net/url" + "time" + + "github.com/netdata/go.d.plugin/pkg/web" +) + +var supportedSchemes = []string{ + //"file", + "https", + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", +} + +func newCertGetter(config Config) (certGetter, error) { + if config.Source == "" { + return nil, errors.New("'source' parameter is mandatory, but it's not set") + } + + u, err := url.Parse(config.Source) + + if err != nil { + return nil, fmt.Errorf("error on parsing source : %v", err) + } + + switch u.Scheme { + // TODO: not tested + //case "file": + // return newFileCertGetter(u.Path), nil + case "https": + u.Scheme = "tcp" + fallthrough + case "udp", "udp4", "udp6", "tcp", "tcp4", "tcp6": + tlsCfg, err := web.NewTLSConfig(config.ClientTLSConfig) + + if err != nil { + return nil, fmt.Errorf("error on creating tls config : %v", err) + } + + if tlsCfg == nil { + tlsCfg = &tls.Config{} + } + + tlsCfg.ServerName = u.Hostname() + + return newURLCertGetter(u, tlsCfg, config.Timeout.Duration), nil + } + + return nil, fmt.Errorf("unsupported scheme in '%s', supported schemes : %v", u, supportedSchemes) +} + +type certGetter interface { + getCert() ([]*x509.Certificate, error) +} + +func newFileCertGetter(path string) *fileCertGetter { + return &fileCertGetter{path: path} +} + +type fileCertGetter struct { + path string +} + +func (fg fileCertGetter) getCert() ([]*x509.Certificate, error) { + content, err := ioutil.ReadFile(fg.path) + if err != nil { + return nil, fmt.Errorf("error on reading '%s' : %v", fg.path, err) + } + + block, _ := pem.Decode(content) + if block == nil { + return nil, fmt.Errorf("error on decoding '%s' : %v", fg.path, err) + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("error on parsing certigicate '%s' : %v", fg.path, err) + } + + return []*x509.Certificate{cert}, nil +} + +func newURLCertGetter(url *url.URL, tlsCfg *tls.Config, timeout time.Duration) *urlCertGetter { + return &urlCertGetter{ + url: url, + tlsCfg: tlsCfg, + timeout: timeout, + } +} + +type urlCertGetter struct { + url *url.URL + tlsCfg *tls.Config + timeout time.Duration +} + +func (ug urlCertGetter) getCert() ([]*x509.Certificate, error) { + ipConn, err := net.DialTimeout(ug.url.Scheme, ug.url.Host, ug.timeout) + if err != nil { + return nil, fmt.Errorf("error on dial to '%s' : %v", ug.url, err) + } + + defer ipConn.Close() + + conn := tls.Client(ipConn, ug.tlsCfg.Clone()) + + defer conn.Close() + + if err := conn.Handshake(); err != nil { + return nil, fmt.Errorf("error on ssl handshake with '%s' : %v", ug.url, err) + } + + certs := conn.ConnectionState().PeerCertificates + + return certs, nil +} diff --git a/modules/x509check/charts.go b/modules/x509check/charts.go new file mode 100644 index 0000000000..f04593a1f2 --- /dev/null +++ b/modules/x509check/charts.go @@ -0,0 +1,29 @@ +package x509check + +import "github.com/netdata/go-orchestrator/module" + +type ( + // Charts is an alias for module.Charts + Charts = module.Charts + // Dims is an alias for module.Dims + Dims = module.Dims + // Vars is an alias for module.Vars + Vars = module.Vars +) + +var charts = Charts{ + { + ID: "time_until_expiration", + Title: "Time Until Certificate Expiration", + Units: "seconds", + Fam: "expiration time", + Ctx: "x509check.time_until_expiration", + Dims: Dims{ + {ID: "expiry"}, + }, + Vars: Vars{ + {ID: "days_until_expiration_warning"}, + {ID: "days_until_expiration_critical"}, + }, + }, +} diff --git a/modules/x509check/x509check.go b/modules/x509check/x509check.go new file mode 100644 index 0000000000..baa7a090eb --- /dev/null +++ b/modules/x509check/x509check.go @@ -0,0 +1,101 @@ +package x509check + +import ( + "time" + + "github.com/netdata/go.d.plugin/pkg/web" + + "github.com/netdata/go-orchestrator/module" +) + +const ( + defaultConnTimeout = time.Second * 2 + defaultDaysUntilWarn = 14 + defaultDaysUntilCrit = 7 +) + +func init() { + creator := module.Creator{ + Create: func() module.Module { return New() }, + } + + module.Register("x509check", creator) +} + +// New creates X509Check with default values +func New() *X509Check { + return &X509Check{ + Config: Config{ + Timeout: web.Duration{Duration: defaultConnTimeout}, + DaysUntilWarn: defaultDaysUntilWarn, + DaysUntilCrit: defaultDaysUntilCrit, + }, + } +} + +// Config is the x509Check module configuration. +type Config struct { + web.ClientTLSConfig `yaml:",inline"` + Timeout web.Duration + Source string + DaysUntilWarn int `yaml:"days_until_expiration_warning"` + DaysUntilCrit int `yaml:"days_until_expiration_critical"` +} + +// X509Check X509Check module. +type X509Check struct { + module.Base + Config `yaml:",inline"` + certGetter +} + +// Cleanup makes cleanup. +func (X509Check) Cleanup() {} + +// Init makes initialization. +func (x *X509Check) Init() bool { + getter, err := newCertGetter(x.Config) + + if err != nil { + x.Error(err) + return false + } + + x.certGetter = getter + + return true +} + +// Check makes check. +func (x *X509Check) Check() bool { + return len(x.Collect()) > 0 +} + +// Charts creates Charts. +func (X509Check) Charts() *Charts { + return charts.Copy() +} + +// Collect collects metrics. +func (x *X509Check) Collect() map[string]int64 { + certs, err := x.getCert() + + if err != nil { + x.Error(err) + return nil + } + + if len(certs) == 0 { + x.Error("no certificate was provided by '%s'", x.Config.Source) + return nil + } + + now := time.Now() + notAfter := certs[0].NotAfter + + return map[string]int64{ + "expiry": int64(notAfter.Sub(now).Seconds()), + "days_until_expiration_warning": int64(x.DaysUntilWarn), + "days_until_expiration_critical": int64(x.DaysUntilCrit), + } +} diff --git a/modules/x509check/x509check_test.go b/modules/x509check/x509check_test.go new file mode 100644 index 0000000000..51c7755445 --- /dev/null +++ b/modules/x509check/x509check_test.go @@ -0,0 +1,44 @@ +package x509check + +import ( + "github.com/netdata/go-orchestrator/module" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNew(t *testing.T) { + job := New() + + assert.Implements(t, (*module.Module)(nil), job) + assert.Equal(t, defaultConnTimeout, job.Config.Timeout.Duration) + assert.Equal(t, defaultDaysUntilWarn, job.Config.DaysUntilWarn) + assert.Equal(t, defaultDaysUntilCrit, job.Config.DaysUntilCrit) +} + +func TestX509Check_Cleanup(t *testing.T) { New().Cleanup() } + +func TestX509Check_Charts(t *testing.T) { + job := New() + + assert.NotNil(t, job.Charts()) + +} + +func TestX509Check_Init(t *testing.T) { + job := New() + assert.False(t, job.Init()) + + job = New() + job.Source = "wrong" + assert.False(t, job.Init()) + + job = New() + job.Source = "https://example.org:443" + assert.True(t, job.Init()) +} + +// TODO: +func TestX509Check_Check(t *testing.T) {} + +func TestX509Check_Collect(t *testing.T) {}