Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Metrics Fixes And Improvements #149

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"flag"
"fmt"
"os"
Expand Down Expand Up @@ -41,9 +42,6 @@ var (
)
var log = logf.Log.WithName("cmd")

// Set const to analyze to determine operator-sdk up local
const envSDK string = "OSDK_FORCE_RUN_MODE"

func printVersion() {
log.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
Expand Down Expand Up @@ -97,6 +95,8 @@ func main() {
// Set default manager options
options := manager.Options{
Namespace: namespace,
// Disable controller-runtime metrics serving
MetricsBindAddress: "0",
dustman9000 marked this conversation as resolved.
Show resolved Hide resolved
}

// Add support for MultiNamespace set in WATCH_NAMESPACE (e.g ns1,ns2)
Expand Down Expand Up @@ -149,17 +149,19 @@ func main() {
WithServiceName(operatorconfig.OperatorName).
GetConfig()

// detect if operator-sdk up local is run
detectSDKLocal := os.Getenv(envSDK)

// Configure metrics. If it errors, log the error and exit.
if detectSDKLocal == "local" {
log.Info("Skipping metrics configuration; not running in a cluster.")
// Get the namespace the operator is currently deployed in.
if _, err := k8sutil.GetOperatorNamespace(); err != nil {
fahlmant marked this conversation as resolved.
Show resolved Hide resolved
if errors.Is(err, k8sutil.ErrRunLocal) {
log.Info("Skipping CR metrics server creation; not running in a cluster.")
} else {
log.Error(err, "Failed to get operator namespace")
}
} else {
if err := metrics.ConfigureMetrics(context.TODO(), *metricsServer); err != nil {
log.Error(err, "Failed to configure Metrics")
os.Exit(1)
}
log.Info("Successfully configured Metrics")
dustman9000 marked this conversation as resolved.
Show resolved Hide resolved
}

// Invoke UpdateMetrics at a frequency defined as hours within a goroutine.
Expand Down
3 changes: 3 additions & 0 deletions pkg/controller/certificaterequest/issue_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (r *ReconcileCertificateRequest) IssueCertificate(reqLogger logr.Logger, cr

proceed, err := dnsClient.ValidateDNSWriteAccess(reqLogger, cr)
if err != nil {
reqLogger.Error(err, "failed to validate dns write access")
return err
}

Expand Down Expand Up @@ -79,10 +80,12 @@ func (r *ReconcileCertificateRequest) IssueCertificate(reqLogger logr.Logger, cr

err = leClient.CreateOrder(cr.Spec.DnsNames)
if err != nil {
reqLogger.Error(err, "failed to create order")
return err
}
URL, err := leClient.GetOrderURL()
if err != nil {
reqLogger.Error(err, "failed to get order url")
return err
}
reqLogger.Info("created a new order with Let's Encrypt.", "URL", URL)
Expand Down
4 changes: 4 additions & 0 deletions pkg/leclient/lets_encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@ func NewClient(kubeClient client.Client) (*ACMEClient, error) {
if err != nil {
return nil, err
}

if privateKey == nil {
return nil, errors.New("private key cannot be empty")
}
acmeClient.Account = acme.Account{PrivateKey: privateKey, URL: accountURL}

return acmeClient, nil
Expand Down
111 changes: 1 addition & 110 deletions pkg/localmetrics/crt_sh.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,115 +9,6 @@ import (
"github.com/prometheus/common/log"
)

// GetListOfCertsExpiringSoon returns a list of certs expiring within the specified number of days
func GetListOfCertsExpiringSoon(domain string, durationDays int) [][]string {

data := [][]string{}
db, err := sql.Open("postgres", getPsqlInfo())

if err != nil {
log.Error(err, "Failed to establish connection with crt.sh database")
}

defer db.Close()

today := time.Now().UTC()

futureDate := time.Now().UTC().AddDate(0, 0, durationDays)

certDuration := fmt.Sprintf("%d-%02d-%02d 00:00:00.000000", today.Year(), today.Month(), today.Day())

futureDuration := fmt.Sprintf("%d-%02d-%02d 00:00:00.000000", futureDate.Year(), futureDate.Month(), futureDate.Day())

rows, err := db.Query(GET_LIST_CERTS_ISSUED_BY_LE_SQL_EXPIRING_SOON, "%."+domain, certDuration, futureDuration)

if err != nil {
log.Error(err, "Failed to receive data from crt.sh database")
}

defer rows.Close()

for rows.Next() {
var name string
var notBefore string
var notAfter string
var row []string

err = rows.Scan(&name, &notBefore, &notAfter)

if err != nil {
log.Error(err, "Error reading table")
}

row = append(row, name)
row = append(row, notBefore)
row = append(row, notAfter)

data = append(data, row)
}

err = rows.Err()

if err != nil {
panic(err)
}

return data
}

// GetListOfCertsIssued returns a list of certs issued for a given domain since durationDays number of days ago
func GetListOfCertsIssued(domain string, durationDays int) [][]string {

data := [][]string{}

db, err := sql.Open("postgres", getPsqlInfo())

if err != nil {
log.Error(err, "Failed to establish connection with crt.sh database")
}

defer db.Close()

t := time.Now().UTC().AddDate(0, 0, durationDays*-1)

certDuration := fmt.Sprintf("%d-%02d-%02d 00:00:00.000000", t.Year(), t.Month(), t.Day())

rows, err := db.Query(GET_LIST_CERTS_ISSUED_BY_LE_SQL, "%."+domain, certDuration)

if err != nil {
log.Error(err, "Failed to receive data from crt.sh database")
}

defer rows.Close()

for rows.Next() {
var name string
var notBefore string
var notAfter string
var row []string

err = rows.Scan(&name, &notBefore, &notAfter)

if err != nil {
log.Error(err, "Error reading table")
}

row = append(row, name)
row = append(row, notBefore)
row = append(row, notAfter)

data = append(data, row)
}

err = rows.Err()

if err != nil {
panic(err)
}

return data
}

// GetCountOfCertsIssued returns the number of certs issued for a given domain in the last durationDays number of days
func GetCountOfCertsIssued(domain string, durationDays int) int {

Expand Down Expand Up @@ -147,5 +38,5 @@ func GetCountOfCertsIssued(domain string, durationDays int) int {
}

func getPsqlInfo() string {
return fmt.Sprintf("host=%s port=%d user=%s dbname=%s sslmode=disable", CRT_SH_PG_DB_HOSTNAME, CRT_SH_PG_DB_PORT, CRT_SH_PG_DB_USERNAME, CRT_SH_PG_DB_NAME)
return fmt.Sprintf("host=%s port=%d user=%s dbname=%s sslmode=disable binary_parameters=yes", CRT_SH_PG_DB_HOSTNAME, CRT_SH_PG_DB_PORT, CRT_SH_PG_DB_USERNAME, CRT_SH_PG_DB_NAME)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for PSQL error: unnamed prepared statement does not exist

}
31 changes: 12 additions & 19 deletions pkg/localmetrics/localmetrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ import (
)

var (
MetricCertsIssuedInLastDayOpenshiftCom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_day_openshift_com",
Help: "Report how many certs have been issued for Openshift.com in the last 24 hours",
MetricCertsIssuedInLastDayDevshiftOrg = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_day_devshift_org",
Help: "Report how many certs have been issued for Devshift.org in the last 24 hours",
}, []string{"name"})
MetricCertsIssuedInLastDayOpenshiftAppsCom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_day_openshift_apps_com",
Help: "Report how many certs have been issued for Openshiftapps.com in the last 24 hours",
}, []string{"name"})
MetricCertsIssuedInLastWeekOpenshiftCom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_week_openshift_com",
Help: "Report how many certs have been issued for Openshift.com in the last 7 days",
MetricCertsIssuedInLastWeekDevshiftOrg = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_week_devshift_org",
Help: "Report how many certs have been issued for Devshift.org in the last 7 days",
}, []string{"name"})
MetricCertsIssuedInLastWeekOpenshiftAppsCom = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "certman_operator_certs_in_last_week_openshift_apps_com",
Expand All @@ -48,9 +48,9 @@ var (
})

MetricsList = []prometheus.Collector{
MetricCertsIssuedInLastDayOpenshiftCom,
MetricCertsIssuedInLastDayDevshiftOrg,
MetricCertsIssuedInLastDayOpenshiftAppsCom,
MetricCertsIssuedInLastWeekOpenshiftCom,
MetricCertsIssuedInLastWeekDevshiftOrg,
MetricCertsIssuedInLastWeekOpenshiftAppsCom,
MetricDuplicateCertsIssuedInLastWeek,
MetricIssueCertificateDuration,
Expand All @@ -61,37 +61,30 @@ var (
func UpdateCertsIssuedInLastDayGauge() {

//Set these to certman calls
openshiftCertCount := GetCountOfCertsIssued("openshift.com", 1)
devshiftCertCount := GetCountOfCertsIssued("devshift.org", 1)
openshiftAppCertCount := GetCountOfCertsIssued("openshiftapps.com", 1)

MetricCertsIssuedInLastDayOpenshiftCom.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(openshiftCertCount))
MetricCertsIssuedInLastDayDevshiftOrg.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(devshiftCertCount))
MetricCertsIssuedInLastDayOpenshiftAppsCom.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(openshiftAppCertCount))
}

// UpdateCertsIssuedInLastWeekGuage sets the gauge metric with the number of certs issued in last week
func UpdateCertsIssuedInLastWeekGauge() {

//Set these to certman calls
openshiftCertCount := GetCountOfCertsIssued("openshift.com", 7)
devshiftCertCount := GetCountOfCertsIssued("devshift.org", 7)
openshiftAppCertCount := GetCountOfCertsIssued("openshiftapps.com", 7)

MetricCertsIssuedInLastWeekOpenshiftCom.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(openshiftCertCount))
MetricCertsIssuedInLastWeekDevshiftOrg.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(devshiftCertCount))
MetricCertsIssuedInLastWeekOpenshiftAppsCom.With(prometheus.Labels{"name": "certman-operator"}).Set(float64(openshiftAppCertCount))
}

// UpdateDuplicateCertsIssuedInLastWeek ...
func UpdateDuplicateCertsIssuedInLastWeek() {

}

// UpdateMetrics updates all the metrics every N hours
func UpdateMetrics(hour int) {

d := time.Duration(hour) * time.Hour
for range time.Tick(d) {
UpdateCertsIssuedInLastDayGauge()
UpdateCertsIssuedInLastWeekGauge()
UpdateDuplicateCertsIssuedInLastWeek()
}
}

Expand Down
102 changes: 42 additions & 60 deletions pkg/localmetrics/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,47 @@ const (
CRT_SH_PG_DB_NAME string = "certwatch"

GET_COUNT_CERTS_ISSUED_BY_LE_SQL string = `
select count(*) as certs_issued from (SELECT ci.ISSUER_CA_ID,
ca.NAME ISSUER_NAME,
ci.NAME_VALUE NAME_VALUE,
min(c.ID) MIN_CERT_ID,
min(ctle.ENTRY_TIMESTAMP) MIN_ENTRY_TIMESTAMP,
x509_notBefore(c.CERTIFICATE) NOT_BEFORE,
x509_notAfter(c.CERTIFICATE) NOT_AFTER
FROM ca,
ct_log_entry ctle,
certificate_identity ci,
certificate c
WHERE ca.ID in (SELECT id FROM ca WHERE lower(ca.NAME) LIKE lower('%Let''s Encrypt%'))
AND ci.ISSUER_CA_ID = ca.ID
AND c.ID = ctle.CERTIFICATE_ID
AND reverse(lower(ci.NAME_VALUE)) LIKE reverse(lower($1))
AND ci.CERTIFICATE_ID = c.ID
GROUP BY c.ID, ci.ISSUER_CA_ID, ISSUER_NAME, NAME_VALUE
) AS certs WHERE certs.MIN_ENTRY_TIMESTAMP >= $2
`

GET_LIST_CERTS_ISSUED_BY_LE_SQL string = `
select name_value, not_before, not_after from (SELECT ci.ISSUER_CA_ID,
ca.NAME ISSUER_NAME,
ci.NAME_VALUE NAME_VALUE,
min(c.ID) MIN_CERT_ID,
min(ctle.ENTRY_TIMESTAMP) MIN_ENTRY_TIMESTAMP,
x509_notBefore(c.CERTIFICATE) NOT_BEFORE,
x509_notAfter(c.CERTIFICATE) NOT_AFTER
FROM ca,
ct_log_entry ctle,
certificate_identity ci,
certificate c
WHERE ca.ID in (SELECT id FROM ca WHERE lower(ca.NAME) LIKE lower('%Let''s Encrypt%'))
AND ci.ISSUER_CA_ID = ca.ID
AND c.ID = ctle.CERTIFICATE_ID
AND reverse(lower(ci.NAME_VALUE)) LIKE reverse(lower($1))
AND ci.CERTIFICATE_ID = c.ID
GROUP BY c.ID, ci.ISSUER_CA_ID, ISSUER_NAME, NAME_VALUE
) AS certs WHERE certs.MIN_ENTRY_TIMESTAMP >= $2
`

GET_LIST_CERTS_ISSUED_BY_LE_SQL_EXPIRING_SOON string = `
select name_value, not_before, not_after from (SELECT ci.ISSUER_CA_ID,
ca.NAME ISSUER_NAME,
ci.NAME_VALUE NAME_VALUE,
min(c.ID) MIN_CERT_ID,
min(ctle.ENTRY_TIMESTAMP) MIN_ENTRY_TIMESTAMP,
x509_notBefore(c.CERTIFICATE) NOT_BEFORE,
x509_notAfter(c.CERTIFICATE) NOT_AFTER
FROM ca,
ct_log_entry ctle,
certificate_identity ci,
certificate c
WHERE ca.ID in (SELECT id FROM ca WHERE lower(ca.NAME) LIKE lower('%Let''s Encrypt%'))
AND ci.ISSUER_CA_ID = ca.ID
AND c.ID = ctle.CERTIFICATE_ID
AND reverse(lower(ci.NAME_VALUE)) LIKE reverse(lower($1))
AND ci.CERTIFICATE_ID = c.ID
GROUP BY c.ID, ci.ISSUER_CA_ID, ISSUER_NAME, NAME_VALUE
) AS certs WHERE certs.NOT_AFTER >= $2 AND certs.NOT_AFTER <= $3
SELECT COUNT(*) AS CERTS_ISSUED
FROM (
WITH ci AS (
SELECT min(sub.CERTIFICATE_ID) ID,
min(sub.ISSUER_CA_ID) ISSUER_CA_ID,
array_agg(DISTINCT sub.NAME_VALUE) NAME_VALUES,
x509_subjectName(sub.CERTIFICATE) SUBJECT_NAME,
x509_notBefore(sub.CERTIFICATE) NOT_BEFORE,
x509_notAfter(sub.CERTIFICATE) NOT_AFTER
FROM (SELECT *
FROM certificate_and_identities cai
WHERE plainto_tsquery('certwatch', $1) @@ identities(cai.CERTIFICATE)
AND cai.NAME_VALUE ILIKE ('%' || $1 || '%')
AND coalesce(x509_notAfter(cai.CERTIFICATE), 'infinity'::timestamp) >= date_trunc('year', now() AT TIME ZONE 'UTC')
AND x509_notAfter(cai.CERTIFICATE) >= now() AT TIME ZONE 'UTC'
AND NOT EXISTS (
SELECT 1
FROM certificate c2
WHERE x509_serialNumber(c2.CERTIFICATE) = x509_serialNumber(cai.CERTIFICATE)
AND c2.ISSUER_CA_ID = cai.ISSUER_CA_ID
AND c2.ID < cai.CERTIFICATE_ID
AND x509_tbscert_strip_ct_ext(c2.CERTIFICATE) = x509_tbscert_strip_ct_ext(cai.CERTIFICATE)
LIMIT 1
)
) sub
GROUP BY sub.CERTIFICATE
)
SELECT ci.ISSUER_CA_ID,
ca.NAME ISSUER_NAME,
array_to_string(ci.NAME_VALUES, chr(10)) NAME_VALUE,
ci.ID ID,
le.ENTRY_TIMESTAMP
FROM ci
LEFT JOIN LATERAL (
SELECT min(ctle.ENTRY_TIMESTAMP) ENTRY_TIMESTAMP
FROM ct_log_entry ctle
WHERE ctle.CERTIFICATE_ID = ci.ID
) le ON TRUE,
ca
WHERE ca.ID in (SELECT id FROM ca WHERE lower(ca.NAME) LIKE lower('%Let''s Encrypt%')) AND ci.ISSUER_CA_ID = ca.ID AND le.ENTRY_TIMESTAMP >= $2
GROUP BY ci.ID, ci.ISSUER_CA_ID, ca.NAME, le.ENTRY_TIMESTAMP, ci.NAME_VALUES
) AS Z;
`
)