refactor(cmd): replace Fatal with error

This commit is contained in:
Fernandez Ludovic 2026-01-24 19:37:51 +01:00
commit 44bc03ee08
10 changed files with 211 additions and 177 deletions

View file

@ -56,20 +56,26 @@ func createRenew() *cli.Command {
func renew(ctx context.Context, cmd *cli.Command) error {
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
if err != nil {
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
return fmt.Errorf("accounts storage initialization: %w", err)
}
keyType := getKeyType(cmd)
keyType, err := getKeyType(cmd.String(flgKeyType))
if err != nil {
return fmt.Errorf("get the key type: %w", err)
}
account := setupAccount(ctx, keyType, accountsStorage)
account, err := setupAccount(ctx, keyType, accountsStorage)
if err != nil {
return fmt.Errorf("set up account: %w", err)
}
if account.Registration == nil {
log.Fatal("The account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
return fmt.Errorf("the account %s is not registered", account.Email)
}
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
if err != nil {
log.Fatal("Certificates storage", log.ErrorAttr(err))
return fmt.Errorf("certificates storage initialization: %w", err)
}
meta := map[string]string{
@ -94,10 +100,7 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, storage.ExtCert)
if err != nil {
log.Fatal("Error while loading the certificate.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("error while reading the certificate for domain %q: %w", domain, err)
}
cert := certificates[0]
@ -110,7 +113,10 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
var client *lego.Client
if !cmd.Bool(flgARIDisable) {
client = setupClient(cmd, account, keyType)
client, err = setupClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("set up client: %w", err)
}
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
@ -125,13 +131,14 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
slog.Duration("sleep", ariRenewalTime.Sub(now)),
slog.Time("renewalTime", *ariRenewalTime),
)
time.Sleep(ariRenewalTime.Sub(now))
}
}
replacesCertID, err = certificate.MakeARICertID(cert)
if err != nil {
log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err))
return fmt.Errorf("error while constructing the ARI CertID for domain %q: %w", domain, err)
}
}
@ -145,11 +152,15 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
}
if client == nil {
client = setupClient(cmd, account, keyType)
client, err = setupClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("set up client: %w", err)
}
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Info("acme: Trying renewal.",
log.DomainAttr(domain),
slog.Int("hoursRemaining", int(timeLeft.Hours())),
@ -160,15 +171,12 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
if cmd.Bool(flgReuseKey) {
keyBytes, errR := certsStorage.ReadFile(domain, storage.ExtKey)
if errR != nil {
log.Fatal("Error while loading the private key.",
log.DomainAttr(domain),
log.ErrorAttr(errR),
)
return fmt.Errorf("error while reading the private key for domain %q: %w", domain, errR)
}
privateKey, errR = certcrypto.ParsePEMPrivateKey(keyBytes)
if errR != nil {
return errR
return fmt.Errorf("error while parsing the private key for domain %q: %w", domain, errR)
}
}
@ -200,12 +208,15 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
certRes, err := client.Certificate.Obtain(ctx, request)
if err != nil {
log.Fatal("Could not obtain the certificate.", log.ErrorAttr(err))
return fmt.Errorf("could not obtain the certificate for domain %q: %w", domain, err)
}
certRes.Domain = domain
certsStorage.SaveResource(certRes)
err = certsStorage.SaveResource(certRes)
if err != nil {
return fmt.Errorf("could not save the resource: %w", err)
}
hook.AddPathToMetadata(meta, certRes.Domain, certRes, certsStorage)
@ -215,15 +226,12 @@ func renewForDomains(ctx context.Context, cmd *cli.Command, account *storage.Acc
func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType, certsStorage *storage.CertificatesStorage, meta map[string]string) error {
csr, err := readCSRFile(cmd.String(flgCSR))
if err != nil {
log.Fatal("Could not read CSR file.",
slog.String(flgCSR, cmd.String(flgCSR)),
log.ErrorAttr(err),
)
return fmt.Errorf("could not read CSR file %q: %w", cmd.String(flgCSR), err)
}
domain, err := certcrypto.GetCSRMainDomain(csr)
if err != nil {
log.Fatal("Could not get CSR main domain.", log.ErrorAttr(err))
return fmt.Errorf("could not get CSR main domain: %w", err)
}
// load the cert resource from files.
@ -231,10 +239,7 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
// as web servers would not be able to work with a combined file.
certificates, err := certsStorage.ReadCertificate(domain, storage.ExtCert)
if err != nil {
log.Fatal("Error while loading the certificate.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("error while reading the certificate for domain %q: %w", domain, err)
}
cert := certificates[0]
@ -247,7 +252,10 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
var client *lego.Client
if !cmd.Bool(flgARIDisable) {
client = setupClient(cmd, account, keyType)
client, err = setupClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("set up client: %w", err)
}
willingToSleep := cmd.Duration(flgARIWaitToRenewDuration)
@ -262,13 +270,14 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
slog.Duration("sleep", ariRenewalTime.Sub(now)),
slog.Time("renewalTime", *ariRenewalTime),
)
time.Sleep(ariRenewalTime.Sub(now))
}
}
replacesCertID, err = certificate.MakeARICertID(cert)
if err != nil {
log.Fatal("Error while construction the ARI CertID.", log.DomainAttr(domain), log.ErrorAttr(err))
return fmt.Errorf("error while constructing the ARI CertID for domain %q: %w", domain, err)
}
}
@ -277,11 +286,15 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
}
if client == nil {
client = setupClient(cmd, account, keyType)
client, err = setupClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("set up client: %w", err)
}
}
// This is just meant to be informal for the user.
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
log.Info("acme: Trying renewal.",
log.DomainAttr(domain),
slog.Int("hoursRemaining", int(timeLeft.Hours())),
@ -295,10 +308,13 @@ func renewForCSR(ctx context.Context, cmd *cli.Command, account *storage.Account
certRes, err := client.Certificate.ObtainForCSR(ctx, request)
if err != nil {
log.Fatal("Could not obtain the certificate for CSR.", log.ErrorAttr(err))
return fmt.Errorf("could not obtain the certificate for CSR: %w", err)
}
certsStorage.SaveResource(certRes)
err = certsStorage.SaveResource(certRes)
if err != nil {
return fmt.Errorf("could not save the resource: %w", err)
}
hook.AddPathToMetadata(meta, domain, certRes, certsStorage)

View file

@ -2,9 +2,10 @@ package cmd
import (
"context"
"log/slog"
"fmt"
"github.com/go-acme/lego/v5/cmd/internal/storage"
"github.com/go-acme/lego/v5/lego"
"github.com/go-acme/lego/v5/log"
"github.com/urfave/cli/v3"
)
@ -21,56 +22,81 @@ func createRevoke() *cli.Command {
func revoke(ctx context.Context, cmd *cli.Command) error {
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
if err != nil {
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
return fmt.Errorf("accounts storage initialization: %w", err)
}
keyType := getKeyType(cmd)
keyType, err := getKeyType(cmd.String(flgKeyType))
if err != nil {
return fmt.Errorf("get the key type: %w", err)
}
account := setupAccount(ctx, keyType, accountsStorage)
account, err := setupAccount(ctx, keyType, accountsStorage)
if err != nil {
return fmt.Errorf("set up account: %w", err)
}
if account.Registration == nil {
log.Fatal("Account is not registered. Use 'run' to register a new account.", slog.String("email", account.Email))
return fmt.Errorf("the account %s is not registered", account.Email)
}
client := newClient(cmd, account, keyType)
client, err := newClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("new client: %w", err)
}
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
if err != nil {
log.Fatal("Certificates storage", log.ErrorAttr(err))
return fmt.Errorf("certificates storage initialization: %w", err)
}
certsStorage.CreateRootFolder()
err = certsStorage.CreateRootFolder()
if err != nil {
return fmt.Errorf("root folder creation: %w", err)
}
reason := cmd.Uint(flgReason)
keep := cmd.Bool(flgKeep)
for _, domain := range cmd.StringSlice(flgDomains) {
log.Info("Trying to revoke the certificate.", log.DomainAttr(domain))
certBytes, err := certsStorage.ReadFile(domain, storage.ExtCert)
if err != nil {
log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err))
}
reason := cmd.Uint(flgReason)
err = client.Certificate.RevokeWithReason(ctx, certBytes, &reason)
if err != nil {
log.Fatal("Error while revoking the certificate.", log.DomainAttr(domain), log.ErrorAttr(err))
}
log.Info("Certificate was revoked.", log.DomainAttr(domain))
if cmd.Bool(flgKeep) {
return nil
}
certsStorage.CreateArchiveFolder()
err = certsStorage.MoveToArchive(domain)
err := revokeCertificate(ctx, client, certsStorage, domain, reason, keep)
if err != nil {
return err
}
log.Info("Certificate was archived", log.DomainAttr(domain))
}
return nil
}
func revokeCertificate(ctx context.Context, client *lego.Client, certsStorage *storage.CertificatesStorage, domain string, reason uint, keep bool) error {
log.Info("Trying to revoke the certificate.", log.DomainAttr(domain))
certBytes, err := certsStorage.ReadFile(domain, storage.ExtCert)
if err != nil {
return fmt.Errorf("certificate reading for domain %s: %w", domain, err)
}
err = client.Certificate.RevokeWithReason(ctx, certBytes, &reason)
if err != nil {
return fmt.Errorf("certificate revocation for domain %s: %w", domain, err)
}
log.Info("The certificate has been revoked.", log.DomainAttr(domain))
if keep {
return nil
}
err = certsStorage.CreateArchiveFolder()
if err != nil {
return fmt.Errorf("archive folder creation: %w", err)
}
err = certsStorage.MoveToArchive(domain)
if err != nil {
return err
}
log.Info("The certificate has been archived.", log.DomainAttr(domain))
return nil
}

View file

@ -41,26 +41,35 @@ func createRun() *cli.Command {
func run(ctx context.Context, cmd *cli.Command) error {
accountsStorage, err := storage.NewAccountsStorage(newAccountsStorageConfig(cmd))
if err != nil {
log.Fatal("Accounts storage initialization", log.ErrorAttr(err))
return fmt.Errorf("accounts storage initialization: %w", err)
}
keyType := getKeyType(cmd)
keyType, err := getKeyType(cmd.String(flgKeyType))
if err != nil {
return fmt.Errorf("get the key type: %w", err)
}
account := setupAccount(ctx, keyType, accountsStorage)
account, err := setupAccount(ctx, keyType, accountsStorage)
if err != nil {
return fmt.Errorf("set up account: %w", err)
}
client := setupClient(cmd, account, keyType)
client, err := setupClient(cmd, account, keyType)
if err != nil {
return fmt.Errorf("set up client: %w", err)
}
if account.Registration == nil {
var reg *registration.Resource
reg, err = registerAccount(ctx, cmd, client)
if err != nil {
log.Fatal("Could not complete registration.", log.ErrorAttr(err))
return fmt.Errorf("could not complete registration: %w", err)
}
account.Registration = reg
if err = accountsStorage.Save(account); err != nil {
log.Fatal("Could not save the account file.", log.ErrorAttr(err))
return fmt.Errorf("could not save the account file: %w", err)
}
fmt.Printf(rootPathWarningMessage, accountsStorage.GetRootPath())
@ -68,19 +77,25 @@ func run(ctx context.Context, cmd *cli.Command) error {
certsStorage, err := storage.NewCertificatesStorage(newCertificatesWriterConfig(cmd))
if err != nil {
log.Fatal("Certificates storage", log.ErrorAttr(err))
return fmt.Errorf("certificates storage initialization: %w", err)
}
certsStorage.CreateRootFolder()
err = certsStorage.CreateRootFolder()
if err != nil {
return fmt.Errorf("root folder creation: %w", err)
}
cert, err := obtainCertificate(ctx, cmd, client)
if err != nil {
// Make sure to return a non-zero exit code if ObtainSANCertificate returned at least one error.
// Due to us not returning partial certificate we can just exit here instead of at the end.
log.Fatal("Could not obtain certificates", log.ErrorAttr(err))
return fmt.Errorf("obtain certificate: %w", err)
}
certsStorage.SaveResource(cert)
err = certsStorage.SaveResource(cert)
if err != nil {
return fmt.Errorf("could not save the resource: %w", err)
}
meta := map[string]string{
hook.EnvAccountEmail: account.Email,

View file

@ -142,23 +142,17 @@ func (s *AccountsStorage) Save(account *Account) error {
return os.WriteFile(s.accountFilePath, jsonBytes, filePerm)
}
func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.PrivateKey) *Account {
func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.PrivateKey) (*Account, error) {
fileBytes, err := os.ReadFile(s.accountFilePath)
if err != nil {
log.Fatal("Could not load the account file.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not read the account file (userID: %s): %w", s.GetUserID(), err)
}
var account Account
err = json.Unmarshal(fileBytes, &account)
if err != nil {
log.Fatal("Could not parse the account file.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not parse the account file (userID: %s): %w", s.GetUserID(), err)
}
account.key = privateKey
@ -166,67 +160,52 @@ func (s *AccountsStorage) LoadAccount(ctx context.Context, privateKey crypto.Pri
if account.Registration == nil || account.Registration.Body.Status == "" {
reg, err := s.tryRecoverRegistration(ctx, privateKey)
if err != nil {
log.Fatal("Could not load the account file. Registration is nil.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not load the account file, registration is nil (userID: %s): %w", s.GetUserID(), err)
}
account.Registration = reg
err = s.Save(&account)
if err != nil {
log.Fatal("Could not save the account file. Registration is nil.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not save the account file, registration is nil (userID: %s): %w", s.GetUserID(), err)
}
}
return &account
return &account, nil
}
func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) crypto.PrivateKey {
func (s *AccountsStorage) GetPrivateKey(keyType certcrypto.KeyType) (crypto.PrivateKey, error) {
accKeyPath := filepath.Join(s.keysPath, s.GetUserID()+".key")
if _, err := os.Stat(accKeyPath); os.IsNotExist(err) {
// TODO(ldez): debug level?
log.Info("No key found for the account. Generating a new private key.",
slog.String("userID", s.GetUserID()),
slog.Any("keyType", keyType),
)
s.createKeysFolder()
err := CreateNonExistingFolder(s.keysPath)
if err != nil {
return nil, fmt.Errorf("could not check/create the directory %q for the account (userID: %s): %w", s.keysPath, s.GetUserID(), err)
}
privateKey, err := generatePrivateKey(accKeyPath, keyType)
if err != nil {
log.Fatal("Could not generate the RSA private account key.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not generate the private account key (userID: %s): %w", s.GetUserID(), err)
}
// TODO(ldez): debug level?
log.Info("Saved key.", slog.String("filepath", accKeyPath))
return privateKey
return privateKey, nil
}
privateKey, err := LoadPrivateKey(accKeyPath)
if err != nil {
log.Fatal("Could not load an RSA private key from the file.",
slog.String("filepath", accKeyPath),
log.ErrorAttr(err),
)
return nil, fmt.Errorf("could not load the private key from the file %q: %w", accKeyPath, err)
}
return privateKey
}
func (s *AccountsStorage) createKeysFolder() {
if err := CreateNonExistingFolder(s.keysPath); err != nil {
log.Fatal("Could not check/create the directory for the account.",
slog.String("userID", s.GetUserID()),
log.ErrorAttr(err),
)
}
return privateKey, nil
}
func (s *AccountsStorage) tryRecoverRegistration(ctx context.Context, privateKey crypto.PrivateKey) (*registration.Resource, error) {

View file

@ -164,7 +164,8 @@ func TestAccountsStorage_LoadAccount(t *testing.T) {
storage.accountFilePath = filepath.Join("testdata", accountFileName)
account := storage.LoadAccount(t.Context(), "")
account, err := storage.LoadAccount(t.Context(), "")
require.NoError(t, err)
expected := &Account{
Email: "account@example.com",
@ -213,7 +214,8 @@ func TestAccountsStorage_GetPrivateKey(t *testing.T) {
expectedPath := filepath.Join(test.basePath, baseAccountsRootFolderName, "test@example.com", baseKeysFolderName, "test@example.com.key")
privateKey := storage.GetPrivateKey(certcrypto.RSA4096)
privateKey, err := storage.GetPrivateKey(certcrypto.RSA4096)
require.NoError(t, err)
assert.FileExists(t, expectedPath)

View file

@ -3,6 +3,7 @@ package storage
import (
"crypto/x509"
"encoding/json"
"fmt"
"log/slog"
"os"
"path/filepath"
@ -22,24 +23,18 @@ func NewCertificatesReader(basePath string) *CertificatesReader {
}
}
func (s *CertificatesReader) ReadResource(domain string) certificate.Resource {
func (s *CertificatesReader) ReadResource(domain string) (certificate.Resource, error) {
raw, err := s.ReadFile(domain, ExtResource)
if err != nil {
log.Fatal("Error while loading the metadata.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return certificate.Resource{}, fmt.Errorf("unable to load resource for domain %q: %w", domain, err)
}
var resource certificate.Resource
if err = json.Unmarshal(raw, &resource); err != nil {
log.Fatal("Error while marshaling the metadata.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return certificate.Resource{}, fmt.Errorf("unable to unmarshal resource for domain %q: %w", domain, err)
}
return resource
return resource, nil
}
func (s *CertificatesReader) ExistsFile(domain, extension string) bool {

View file

@ -6,12 +6,14 @@ import (
"github.com/go-acme/lego/v5/certificate"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCertificatesWriter_ReadResource(t *testing.T) {
reader := NewCertificatesReader("testdata")
resource := reader.ReadResource("example.com")
resource, err := reader.ReadResource("example.com")
require.NoError(t, err)
expected := certificate.Resource{
Domain: "example.com",

View file

@ -75,46 +75,38 @@ func NewCertificatesWriter(config CertificatesWriterConfig) (*CertificatesWriter
}, nil
}
func (s *CertificatesWriter) CreateRootFolder() {
func (s *CertificatesWriter) CreateRootFolder() error {
err := CreateNonExistingFolder(s.rootPath)
if err != nil {
log.Fatal("Could not check/create the root folder",
slog.String("filepath", s.rootPath),
log.ErrorAttr(err),
)
return fmt.Errorf("could not check/create the root folder %q: %w", s.rootPath, err)
}
return nil
}
func (s *CertificatesWriter) CreateArchiveFolder() {
func (s *CertificatesWriter) CreateArchiveFolder() error {
err := CreateNonExistingFolder(s.archivePath)
if err != nil {
log.Fatal("Could not check/create the archive folder.",
slog.String("filepath", s.archivePath),
log.ErrorAttr(err),
)
return fmt.Errorf("could not check/create the archive folder %q: %w", s.archivePath, err)
}
return nil
}
func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) {
func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) error {
domain := certRes.Domain
// We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file.
err := s.writeFile(domain, ExtCert, certRes.Certificate)
if err != nil {
log.Fatal("Unable to save Certificate.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("unable to save the certificate for the domain %q: %w", domain, err)
}
if certRes.IssuerCertificate != nil {
err = s.writeFile(domain, ExtIssuer, certRes.IssuerCertificate)
if err != nil {
log.Fatal("Unable to save IssuerCertificate.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("unable to save the issuer certificate for the domain %q: %w", domain, err)
}
}
@ -122,28 +114,24 @@ func (s *CertificatesWriter) SaveResource(certRes *certificate.Resource) {
if certRes.PrivateKey != nil {
err = s.writeCertificateFiles(domain, certRes)
if err != nil {
log.Fatal("Unable to save PrivateKey.", log.DomainAttr(domain), log.ErrorAttr(err))
return fmt.Errorf("unable to save the private key for the domain %q: %w", domain, err)
}
} else if s.pem || s.pfx {
// we don't have the private key; can't write the .pem or .pfx file
log.Fatal("Unable to save PEM or PFX without the private key. Are you using a CSR?", log.DomainAttr(domain))
return fmt.Errorf("unable to save PEM or PFX without the private key for the domain %q: probable usage of a CSR", domain)
}
jsonBytes, err := json.MarshalIndent(certRes, "", "\t")
if err != nil {
log.Fatal("Unable to marshal CertResource.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("unable to marshal the resource for domain %q: %w", domain, err)
}
err = s.writeFile(domain, ExtResource, jsonBytes)
if err != nil {
log.Fatal("Unable to save CertResource.",
log.DomainAttr(domain),
log.ErrorAttr(err),
)
return fmt.Errorf("unable to save the resource for the domain %q: %w", domain, err)
}
return nil
}
func (s *CertificatesWriter) MoveToArchive(domain string) error {

View file

@ -19,7 +19,8 @@ func TestCertificatesWriter_CreateRootFolder(t *testing.T) {
require.NoDirExists(t, writer.rootPath)
writer.CreateRootFolder()
err = writer.CreateRootFolder()
require.NoError(t, err)
require.DirExists(t, writer.rootPath)
}
@ -32,7 +33,8 @@ func TestCertificatesWriter_CreateArchiveFolder(t *testing.T) {
require.NoDirExists(t, writer.GetArchivePath())
writer.CreateArchiveFolder()
err = writer.CreateArchiveFolder()
require.NoError(t, err)
require.DirExists(t, writer.GetArchivePath())
}
@ -53,7 +55,7 @@ func TestCertificatesWriter_SaveResource(t *testing.T) {
require.NoFileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.key"))
require.NoFileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.json"))
writer.SaveResource(&certificate.Resource{
err = writer.SaveResource(&certificate.Resource{
Domain: "example.com",
CertURL: "https://acme.example.org/cert/123",
CertStableURL: "https://acme.example.org/cert/456",
@ -62,6 +64,7 @@ func TestCertificatesWriter_SaveResource(t *testing.T) {
IssuerCertificate: []byte("IssuerCertificate"),
CSR: []byte("CSR"),
})
require.NoError(t, err)
require.FileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.crt"))
require.FileExists(t, filepath.Join(basePath, baseCertificatesFolderName, "example.com.issuer.crt"))
@ -262,8 +265,11 @@ func setupCertificatesWriter(t *testing.T) *CertificatesWriter {
)
require.NoError(t, err)
writer.CreateRootFolder()
writer.CreateArchiveFolder()
err = writer.CreateRootFolder()
require.NoError(t, err)
err = writer.CreateArchiveFolder()
require.NoError(t, err)
return writer
}

View file

@ -5,9 +5,9 @@ import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"os"
"strings"
@ -24,35 +24,41 @@ import (
)
// setupClient creates a new client with challenge settings.
func setupClient(cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType) *lego.Client {
client := newClient(cmd, account, keyType)
func setupClient(cmd *cli.Command, account *storage.Account, keyType certcrypto.KeyType) (*lego.Client, error) {
client, err := newClient(cmd, account, keyType)
if err != nil {
return nil, fmt.Errorf("new client: %w", err)
}
setupChallenges(cmd, client)
return client
return client, nil
}
func setupAccount(ctx context.Context, keyType certcrypto.KeyType, accountsStorage *storage.AccountsStorage) *storage.Account {
privateKey := accountsStorage.GetPrivateKey(keyType)
func setupAccount(ctx context.Context, keyType certcrypto.KeyType, accountsStorage *storage.AccountsStorage) (*storage.Account, error) {
privateKey, err := accountsStorage.GetPrivateKey(keyType)
if err != nil {
return nil, fmt.Errorf("get private key: %w", err)
}
if accountsStorage.ExistsAccountFilePath() {
return accountsStorage.LoadAccount(ctx, privateKey)
}
return storage.NewAccount(accountsStorage.GetEmail(), privateKey)
return storage.NewAccount(accountsStorage.GetEmail(), privateKey), nil
}
func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
func newClient(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) (*lego.Client, error) {
client, err := lego.NewClient(newClientConfig(cmd, acc, keyType))
if err != nil {
log.Fatal("Could not create client.", log.ErrorAttr(err))
return nil, fmt.Errorf("new client: %w", err)
}
if client.GetExternalAccountRequired() && !cmd.IsSet(flgEAB) { // TODO(ldez): handle this flag.
log.Fatal(fmt.Sprintf("Server requires External Account Binding. Use --%s with --%s and --%s.", flgEAB, flgKID, flgHMAC))
return nil, errors.New("server requires External Account Binding (EAB)")
}
return client
return client, nil
}
func newClientConfig(cmd *cli.Command, acc registration.User, keyType certcrypto.KeyType) *lego.Config {
@ -96,26 +102,25 @@ func newClientConfig(cmd *cli.Command, acc registration.User, keyType certcrypto
}
// getKeyType the type from which private keys should be generated.
func getKeyType(cmd *cli.Command) certcrypto.KeyType {
keyType := cmd.String(flgKeyType)
func getKeyType(keyType string) (certcrypto.KeyType, error) {
switch strings.ToUpper(keyType) {
case "RSA2048":
return certcrypto.RSA2048
return certcrypto.RSA2048, nil
case "RSA3072":
return certcrypto.RSA3072
return certcrypto.RSA3072, nil
case "RSA4096":
return certcrypto.RSA4096
return certcrypto.RSA4096, nil
case "RSA8192":
return certcrypto.RSA8192
return certcrypto.RSA8192, nil
case "EC256":
return certcrypto.EC256
return certcrypto.EC256, nil
case "EC384":
return certcrypto.EC384
return certcrypto.EC384, nil
}
log.Fatal("Unsupported KeyType.", slog.String("keyType", keyType))
// TODO(ldez): log + fallback?
return ""
return "", fmt.Errorf("unsupported key type: %s", keyType)
}
func getUserAgent(cmd *cli.Command) string {