Fix ACME write when traefik is shutting down

This commit is contained in:
Julien Salleyron 2025-01-31 11:06:04 +01:00 committed by GitHub
parent c20af070e3
commit 86315e0f18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 17 deletions

View file

@ -188,7 +188,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
return nil, err return nil, err
} }
acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider) acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider, routinesPool)
// Entrypoints // Entrypoints
@ -366,7 +366,7 @@ func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP serv
} }
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration. // initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider { func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider, routinesPool *safe.Pool) []*acme.Provider {
localStores := map[string]*acme.LocalStore{} localStores := map[string]*acme.LocalStore{}
var resolvers []*acme.Provider var resolvers []*acme.Provider
@ -376,7 +376,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
} }
if localStores[resolver.ACME.Storage] == nil { if localStores[resolver.ACME.Storage] == nil {
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage) localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage, routinesPool)
} }
p := &acme.Provider{ p := &acme.Provider{

View file

@ -1,6 +1,7 @@
package acme package acme
import ( import (
"context"
"encoding/json" "encoding/json"
"io" "io"
"os" "os"
@ -22,9 +23,9 @@ type LocalStore struct {
} }
// NewLocalStore initializes a new LocalStore with a file name. // NewLocalStore initializes a new LocalStore with a file name.
func NewLocalStore(filename string) *LocalStore { func NewLocalStore(filename string, routinesPool *safe.Pool) *LocalStore {
store := &LocalStore{filename: filename, saveDataChan: make(chan map[string]*StoredData)} store := &LocalStore{filename: filename, saveDataChan: make(chan map[string]*StoredData)}
store.listenSaveAction() store.listenSaveAction(routinesPool)
return store return store
} }
@ -99,18 +100,31 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
} }
// listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`. // listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`.
func (s *LocalStore) listenSaveAction() { func (s *LocalStore) listenSaveAction(routinesPool *safe.Pool) {
safe.Go(func() { routinesPool.GoCtx(func(ctx context.Context) {
logger := log.WithoutContext().WithField(log.ProviderName, "acme") logger := log.WithoutContext().WithField(log.ProviderName, "acme")
for object := range s.saveDataChan { for {
data, err := json.MarshalIndent(object, "", " ") select {
if err != nil { case <-ctx.Done():
logger.Error(err) return
}
err = os.WriteFile(s.filename, data, 0o600) case object := <-s.saveDataChan:
if err != nil { select {
logger.Error(err) case <-ctx.Done():
// Stop handling events because Traefik is shutting down.
return
default:
}
data, err := json.MarshalIndent(object, "", " ")
if err != nil {
logger.Error(err)
}
err = os.WriteFile(s.filename, data, 0o600)
if err != nil {
logger.Error(err)
}
} }
} }
}) })

View file

@ -1,6 +1,7 @@
package acme package acme
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/traefik/traefik/v2/pkg/safe"
) )
func TestLocalStore_GetAccount(t *testing.T) { func TestLocalStore_GetAccount(t *testing.T) {
@ -45,7 +47,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
s := NewLocalStore(test.filename) s := NewLocalStore(test.filename, safe.NewPool(context.Background()))
account, err := s.GetAccount("test") account, err := s.GetAccount("test")
require.NoError(t, err) require.NoError(t, err)
@ -58,7 +60,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
func TestLocalStore_SaveAccount(t *testing.T) { func TestLocalStore_SaveAccount(t *testing.T) {
acmeFile := filepath.Join(t.TempDir(), "acme.json") acmeFile := filepath.Join(t.TempDir(), "acme.json")
s := NewLocalStore(acmeFile) s := NewLocalStore(acmeFile, safe.NewPool(context.Background()))
email := "some@email.com" email := "some@email.com"