From 1b3ff5a0e45fdc85424c994d1a01f6d4bfc61ca3 Mon Sep 17 00:00:00 2001 From: Ananth Date: Wed, 25 Feb 2026 08:34:44 +0000 Subject: [PATCH 1/2] Certificator renews certs in parallel Certificator renews five certificates in parallel. Can be configured by env vars. --- .envrc | 8 ++--- cmd/certificator/main.go | 71 ++++++++++++++++++++-------------------- pkg/config/config.go | 21 ++++++------ 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.envrc b/.envrc index 9f363e1..46c975f 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1,3 @@ -#!/usr/bin/env bash - -export DIRENV_WARN_TIMEOUT=20s - -use flake +if command -v nix >/dev/null 2>&1 && [ -f flake.nix ]; then + use flake +fi diff --git a/cmd/certificator/main.go b/cmd/certificator/main.go index 1a04885..64ef4c5 100644 --- a/cmd/certificator/main.go +++ b/cmd/certificator/main.go @@ -1,9 +1,11 @@ package main import ( + "context" "strings" legoLog "github.com/go-acme/lego/v4/log" + "github.com/sourcegraph/conc/pool" "github.com/vinted/certificator/pkg/acme" "github.com/vinted/certificator/pkg/certificate" "github.com/vinted/certificator/pkg/certmetrics" @@ -42,47 +44,46 @@ func main() { certmetrics.Up.WithLabelValues("certificator", version, cfg.Hostname, cfg.Environment).Set(1) defer certmetrics.Up.WithLabelValues("certificator", version, cfg.Hostname, cfg.Environment).Set(0) - var failedDomains []string + ctx := context.Background() + workerPool := pool.New().WithErrors().WithContext(ctx).WithMaxGoroutines(cfg.MaxConcurrentRenewals) for _, dom := range cfg.Domains { - allDomains := strings.Split(dom, ",") - mainDomain := allDomains[0] - cert, err := certificate.GetCertificate(mainDomain, vaultClient) - if err != nil { - failedDomains = append(failedDomains, mainDomain) - logger.Error(err) - continue - } - logger.Infof("checking certificate for %s", mainDomain) - - needsReissuing, err := certificate.NeedsReissuing(cert, allDomains, cfg.RenewBeforeDays, logger) - if err != nil { - failedDomains = append(failedDomains, mainDomain) - logger.Error(err) - continue - } + workerPool.Go(func(ctx context.Context) error { + allDomains := strings.Split(dom, ",") + mainDomain := allDomains[0] + cert, err := certificate.GetCertificate(mainDomain, vaultClient) + if err != nil { + return err + } + logger.Infof("checking certificate for %s", mainDomain) - if needsReissuing { - logger.Infof("obtaining certificate for %s", mainDomain) - err := certificate.ObtainCertificate(acmeClient, vaultClient, allDomains, - cfg.DNSAddress, cfg.Acme.DNSChallengeProvider, cfg.Acme.DNSPropagationRequirement) + needsReissuing, err := certificate.NeedsReissuing(cert, allDomains, cfg.RenewBeforeDays, logger) if err != nil { - failedDomains = append(failedDomains, mainDomain) - certmetrics.CertificatesRenewalFailures.WithLabelValues(mainDomain).Inc() - certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "failure").Inc() - logger.Error(err) - continue + return err } - certmetrics.CertificatesRenewed.WithLabelValues(mainDomain).Inc() - certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "renewed").Inc() - logger.Infof("certificate for %s renewed successfully", mainDomain) - } else { - certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "valid").Inc() - logger.Infof("certificate for %s is up to date, skipping renewal", mainDomain) - } + + if needsReissuing { + logger.Infof("obtaining certificate for %s", mainDomain) + err := certificate.ObtainCertificate(acmeClient, vaultClient, allDomains, + cfg.DNSAddress, cfg.Acme.DNSChallengeProvider, cfg.Acme.DNSPropagationRequirement) + if err != nil { + certmetrics.CertificatesRenewalFailures.WithLabelValues(mainDomain).Inc() + certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "failure").Inc() + return err + } + certmetrics.CertificatesRenewed.WithLabelValues(mainDomain).Inc() + certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "renewed").Inc() + logger.Infof("certificate for %s renewed successfully", mainDomain) + } else { + certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "valid").Inc() + logger.Infof("certificate for %s is up to date, skipping renewal", mainDomain) + } + + return nil + }) } - if len(failedDomains) > 0 { - logger.Fatalf("Failed to renew certificates for: %v", failedDomains) + if err := workerPool.Wait(); err != nil { + logger.Fatal(err) } } diff --git a/pkg/config/config.go b/pkg/config/config.go index e54b186..2f378a5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -41,16 +41,17 @@ type Metrics struct { // Config contains all configuration parameters type Config struct { - Hostname string - Acme Acme - Vault Vault - Log Log - Metrics Metrics - Certificatee Certificatee - DNSAddress string `envconfig:"DNS_ADDRESS" default:"127.0.0.1:53"` - Environment string `envconfig:"ENVIRONMENT" default:"prod"` - RenewBeforeDays int `envconfig:"CERTIFICATOR_RENEW_BEFORE_DAYS" default:"30"` - Domains []string `envconfig:"CERTIFICATOR_DOMAINS" default:""` + Hostname string + Acme Acme + Vault Vault + Log Log + Metrics Metrics + Certificatee Certificatee + DNSAddress string `envconfig:"DNS_ADDRESS" default:"127.0.0.1:53"` + Environment string `envconfig:"ENVIRONMENT" default:"prod"` + RenewBeforeDays int `envconfig:"CERTIFICATOR_RENEW_BEFORE_DAYS" default:"30"` + Domains []string `envconfig:"CERTIFICATOR_DOMAINS" default:""` + MaxConcurrentRenewals int `envconfig:"CERTIFICATOR_MAX_CONCURRENT_RENEWALS" default:"5"` } // Configuration values specific to the certificatee tool From 288e19761aabbf45a71a79fbb3cc30358318a045 Mon Sep 17 00:00:00 2001 From: Ananth Date: Wed, 25 Feb 2026 08:37:01 +0000 Subject: [PATCH 2/2] Clean up exit logic --- cmd/certificator/main.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/certificator/main.go b/cmd/certificator/main.go index 64ef4c5..8b06ae8 100644 --- a/cmd/certificator/main.go +++ b/cmd/certificator/main.go @@ -62,23 +62,23 @@ func main() { return err } - if needsReissuing { - logger.Infof("obtaining certificate for %s", mainDomain) - err := certificate.ObtainCertificate(acmeClient, vaultClient, allDomains, - cfg.DNSAddress, cfg.Acme.DNSChallengeProvider, cfg.Acme.DNSPropagationRequirement) - if err != nil { - certmetrics.CertificatesRenewalFailures.WithLabelValues(mainDomain).Inc() - certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "failure").Inc() - return err - } - certmetrics.CertificatesRenewed.WithLabelValues(mainDomain).Inc() - certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "renewed").Inc() - logger.Infof("certificate for %s renewed successfully", mainDomain) - } else { + if !needsReissuing { certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "valid").Inc() logger.Infof("certificate for %s is up to date, skipping renewal", mainDomain) + return nil } + logger.Infof("obtaining certificate for %s", mainDomain) + if err := certificate.ObtainCertificate(acmeClient, vaultClient, allDomains, + cfg.DNSAddress, cfg.Acme.DNSChallengeProvider, cfg.Acme.DNSPropagationRequirement); err != nil { + certmetrics.CertificatesRenewalFailures.WithLabelValues(mainDomain).Inc() + certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "failure").Inc() + return err + } + certmetrics.CertificatesRenewed.WithLabelValues(mainDomain).Inc() + certmetrics.CertificatesChecked.WithLabelValues(mainDomain, "renewed").Inc() + logger.Infof("certificate for %s renewed successfully", mainDomain) + return nil }) }