diff --git a/cmd/kv.go b/cmd/kv.go index 5ede25d..0800a6e 100644 --- a/cmd/kv.go +++ b/cmd/kv.go @@ -92,7 +92,7 @@ func createTLSConfig(cmd *cobra.Command) (*tls.Config, error) { cert, err := getCertificate(privateKey, certContent) if err != nil { - return nil, fmt.Errorf("failed to load TLS keypair: %s", err) + return nil, fmt.Errorf("failed to load TLS keypair: %w", err) } return &tls.Config{ @@ -109,7 +109,7 @@ func getCertPool(ca string) (*x509.CertPool, error) { if ca != "" { caContent, err := getCAContent(ca) if err != nil { - return nil, fmt.Errorf("failed to read CA. %s", err) + return nil, fmt.Errorf("failed to read CA. %w", err) } if !caPool.AppendCertsFromPEM(caContent) { diff --git a/cmd/root.go b/cmd/root.go index f281016..1d24236 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -109,7 +109,7 @@ func runE(apply func(*dumper.BaseConfig, *cobra.Command) error) func(*cobra.Comm func tree(root, indent string) error { fi, err := os.Stat(root) if err != nil { - return fmt.Errorf("could not stat %s: %v", root, err) + return fmt.Errorf("could not stat %s: %w", root, err) } fmt.Println(fi.Name()) @@ -119,7 +119,7 @@ func tree(root, indent string) error { fis, err := ioutil.ReadDir(root) if err != nil { - return fmt.Errorf("could not read dir %s: %v", root, err) + return fmt.Errorf("could not read dir %s: %w", root, err) } var names []string diff --git a/dumper/file/file.go b/dumper/file/file.go index 174bbce..5b4b58e 100644 --- a/dumper/file/file.go +++ b/dumper/file/file.go @@ -4,6 +4,8 @@ import ( "bytes" "crypto/md5" "encoding/json" + "errors" + "fmt" "io" "log" "os" @@ -34,20 +36,24 @@ func Dump(acmeFile string, baseConfig *dumper.BaseConfig) error { func dump(acmeFile string, baseConfig *dumper.BaseConfig) error { if baseConfig.Version == "v2" { - return dumpV2(acmeFile, baseConfig) + err := dumpV2(acmeFile, baseConfig) + if err != nil { + return fmt.Errorf("v2: dump failed: %w", err) + } + return nil } - return dumpV1(acmeFile, baseConfig) + err := dumpV1(acmeFile, baseConfig) + if err != nil { + return fmt.Errorf("v1: dump failed: %w", err) + } + return nil } func dumpV1(acmeFile string, baseConfig *dumper.BaseConfig) error { - source, err := os.Open(acmeFile) - if err != nil { - return err - } - data := &v1.StoredData{} - if err = json.NewDecoder(source).Decode(data); err != nil { + err := readJSONFile(acmeFile, data) + if err != nil { return err } @@ -55,23 +61,38 @@ func dumpV1(acmeFile string, baseConfig *dumper.BaseConfig) error { } func dumpV2(acmeFile string, baseConfig *dumper.BaseConfig) error { - source, err := os.Open(acmeFile) - if err != nil { - return err - } - data := map[string]*acme.StoredData{} - if err = json.NewDecoder(source).Decode(&data); err != nil { + err := readJSONFile(acmeFile, &data) + if err != nil { return err } return v2.Dump(data, baseConfig) } +func readJSONFile(acmeFile string, data interface{}) error { + source, err := os.Open(acmeFile) + if err != nil { + return fmt.Errorf("failed to open file %q: %w", acmeFile, err) + } + defer func() { _ = source.Close() }() + + err = json.NewDecoder(source).Decode(data) + if errors.Is(err, io.EOF) { + log.Printf("warn: file %q may not be ready: %v", acmeFile, err) + return nil + } + if err != nil { + return fmt.Errorf("failed to unmarshal file %q: %w", acmeFile, err) + } + + return nil +} + func watch(acmeFile string, baseConfig *dumper.BaseConfig) error { watcher, err := fsnotify.NewWatcher() if err != nil { - return err + return fmt.Errorf("failed to create new watcher: %w", err) } defer func() { _ = watcher.Close() }() @@ -114,7 +135,7 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error { err = watcher.Add(acmeFile) if err != nil { - return err + return fmt.Errorf("failed to add a new watcher: %w", err) } <-done @@ -125,12 +146,12 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error { func manageEvent(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string, previousHash []byte, baseConfig *dumper.BaseConfig) ([]byte, error) { err := manageRename(watcher, event, acmeFile) if err != nil { - return nil, err + return nil, fmt.Errorf("watcher renewal failed: %w", err) } hash, err := calculateHash(acmeFile) if err != nil { - return nil, err + return nil, fmt.Errorf("file hash calculation failed: %w", err) } if !bytes.Equal(previousHash, hash) { diff --git a/dumper/kv/convert.go b/dumper/kv/convert.go index a76efb2..f802784 100644 --- a/dumper/kv/convert.go +++ b/dumper/kv/convert.go @@ -45,13 +45,15 @@ type DomainsCertificate struct { // convertOldAccount converts account information from old account format. func convertOldAccount(account *AccountOld) *v1.StoredData { - storedData := &v1.StoredData{} - storedData.Account = &v1.Account{ - PrivateKey: account.PrivateKey, - Registration: account.Registration, - Email: account.Email, - KeyType: account.KeyType, + storedData := &v1.StoredData{ + Account: &v1.Account{ + PrivateKey: account.PrivateKey, + Registration: account.Registration, + Email: account.Email, + KeyType: account.KeyType, + }, } + var certs []*v1.Certificate for _, oldCert := range account.DomainsCertificate.Certs { certs = append(certs, &v1.Certificate{ @@ -61,5 +63,6 @@ func convertOldAccount(account *AccountOld) *v1.StoredData { }) } storedData.Certificates = certs + return storedData } diff --git a/dumper/kv/kv.go b/dumper/kv/kv.go index 30f6a3f..aea8ba6 100644 --- a/dumper/kv/kv.go +++ b/dumper/kv/kv.go @@ -24,7 +24,7 @@ const DefaultStoreKeySuffix = "/acme/account/object" func Dump(config *Config, baseConfig *dumper.BaseConfig) error { kvStore, err := valkeyrie.NewStore(config.Backend, config.Endpoints, config.Options) if err != nil { - return fmt.Errorf("unable to create client of the store: %v", err) + return fmt.Errorf("unable to create client of the store: %w", err) } storeKey := config.Prefix + config.Suffix @@ -35,7 +35,7 @@ func Dump(config *Config, baseConfig *dumper.BaseConfig) error { pair, err := kvStore.Get(storeKey, nil) if err != nil { - return fmt.Errorf("unable to retrieve %s value: %v", storeKey, err) + return fmt.Errorf("unable to retrieve %s value: %w", storeKey, err) } return dumpPair(pair, baseConfig) @@ -80,17 +80,17 @@ func dumpPair(pair *store.KVPair, baseConfig *dumper.BaseConfig) error { func getStoredDataFromGzip(pair *store.KVPair) (*v1.StoredData, error) { reader, err := gzip.NewReader(bytes.NewBuffer(pair.Value)) if err != nil { - return nil, fmt.Errorf("fail to create GZip reader: %v", err) + return nil, fmt.Errorf("fail to create GZip reader: %w", err) } acmeData, err := ioutil.ReadAll(reader) if err != nil { - return nil, fmt.Errorf("unable to read the pair content: %v", err) + return nil, fmt.Errorf("unable to read the pair content: %w", err) } account := &AccountOld{} if err := json.Unmarshal(acmeData, &account); err != nil { - return nil, fmt.Errorf("unable marshal AccountOld: %v", err) + return nil, fmt.Errorf("unable marshal AccountOld: %w", err) } return convertOldAccount(account), nil diff --git a/dumper/v1/dumper.go b/dumper/v1/dumper.go index b8a1a6f..a8d849e 100644 --- a/dumper/v1/dumper.go +++ b/dumper/v1/dumper.go @@ -2,6 +2,7 @@ package v1 import ( "encoding/pem" + "fmt" "io/ioutil" "os" "path/filepath" @@ -20,29 +21,29 @@ func Dump(data *StoredData, baseConfig *dumper.BaseConfig) error { if baseConfig.Clean { err := cleanDir(baseConfig.DumpPath) if err != nil { - return err + return fmt.Errorf("folder cleaning failed: %w", err) } } if !baseConfig.DomainSubDir { if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0755); err != nil { - return err + return fmt.Errorf("certs folder creation failure: %w", err) } } if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0755); err != nil { - return err + return fmt.Errorf("keys folder creation failure: %w", err) } for _, cert := range data.Certificates { err := writeCert(baseConfig.DumpPath, cert, baseConfig.CrtInfo, baseConfig.DomainSubDir) if err != nil { - return err + return fmt.Errorf("failed to write certificates: %w", err) } err = writeKey(baseConfig.DumpPath, cert, baseConfig.KeyInfo, baseConfig.DomainSubDir) if err != nil { - return err + return fmt.Errorf("failed to write certificate keys: %w", err) } } diff --git a/dumper/v2/dumper.go b/dumper/v2/dumper.go index 34e8077..989cc2a 100644 --- a/dumper/v2/dumper.go +++ b/dumper/v2/dumper.go @@ -2,6 +2,7 @@ package v2 import ( "encoding/pem" + "fmt" "io/ioutil" "os" "path/filepath" @@ -21,30 +22,30 @@ func Dump(data map[string]*acme.StoredData, baseConfig *dumper.BaseConfig) error if baseConfig.Clean { err := cleanDir(baseConfig.DumpPath) if err != nil { - return err + return fmt.Errorf("folder cleaning failed: %w", err) } } if !baseConfig.DomainSubDir { if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0755); err != nil { - return err + return fmt.Errorf("certs folder creation failure: %w", err) } } if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0755); err != nil { - return err + return fmt.Errorf("keys folder creation failure: %w", err) } for _, store := range data { for _, cert := range store.Certificates { err := writeCert(baseConfig.DumpPath, cert.Certificate, baseConfig.CrtInfo, baseConfig.DomainSubDir) if err != nil { - return err + return fmt.Errorf("failed to write certificates: %w", err) } err = writeKey(baseConfig.DumpPath, cert.Certificate, baseConfig.KeyInfo, baseConfig.DomainSubDir) if err != nil { - return err + return fmt.Errorf("failed to write certificate keys: %w", err) } } @@ -56,7 +57,7 @@ func Dump(data map[string]*acme.StoredData, baseConfig *dumper.BaseConfig) error err := ioutil.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0600) if err != nil { - return err + return fmt.Errorf("failed to write private key: %w", err) } }