Commit b7028b13 authored by Matthew Holt's avatar Matthew Holt

vendor: Update certmagic to include List fix

parent 620f9687
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sync" "sync"
...@@ -67,16 +68,34 @@ func (fs FileStorage) Delete(key string) error { ...@@ -67,16 +68,34 @@ func (fs FileStorage) Delete(key string) error {
} }
// List returns all keys that match prefix. // List returns all keys that match prefix.
func (fs FileStorage) List(prefix string) ([]string, error) { func (fs FileStorage) List(prefix string, recursive bool) ([]string, error) {
d, err := os.Open(fs.Filename(prefix)) var keys []string
if os.IsNotExist(err) { walkPrefix := fs.Filename(prefix)
return nil, ErrNotExist(err)
} err := filepath.Walk(walkPrefix, func(fpath string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return nil, err return err
} }
defer d.Close() if info == nil {
return d.Readdirnames(-1) return fmt.Errorf("%s: file info is nil", fpath)
}
if fpath == walkPrefix {
return nil
}
suffix, err := filepath.Rel(walkPrefix, fpath)
if err != nil {
return fmt.Errorf("%s: could not make path relative: %v", fpath, err)
}
keys = append(keys, path.Join(prefix, suffix))
if !recursive && info.IsDir() {
return filepath.SkipDir
}
return nil
})
return keys, err
} }
// Stat returns information about key. // Stat returns information about key.
...@@ -89,9 +108,10 @@ func (fs FileStorage) Stat(key string) (KeyInfo, error) { ...@@ -89,9 +108,10 @@ func (fs FileStorage) Stat(key string) (KeyInfo, error) {
return KeyInfo{}, err return KeyInfo{}, err
} }
return KeyInfo{ return KeyInfo{
Key: key, Key: key,
Modified: fi.ModTime(), Modified: fi.ModTime(),
Size: fi.Size(), Size: fi.Size(),
IsTerminal: !fi.IsDir(),
}, nil }, nil
} }
......
...@@ -261,7 +261,7 @@ func (certCache *Cache) updateOCSPStaples() { ...@@ -261,7 +261,7 @@ func (certCache *Cache) updateOCSPStaples() {
// deleteOldStapleFiles deletes cached OCSP staples that have expired. // deleteOldStapleFiles deletes cached OCSP staples that have expired.
// TODO: We should do this for long-expired certificates, too. // TODO: We should do this for long-expired certificates, too.
func (certCache *Cache) deleteOldStapleFiles() { func (certCache *Cache) deleteOldStapleFiles() {
ocspKeys, err := certCache.storage.List(prefixOCSP) ocspKeys, err := certCache.storage.List(prefixOCSP, false)
if err != nil { if err != nil {
// maybe just hasn't been created yet; no big deal // maybe just hasn't been created yet; no big deal
return return
......
// Copyright 2015 Matthew Holt
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package certmagic
import (
"fmt"
"sync"
)
// MemoryLocker implements the Locker interface
// using memory. An empty value is NOT VALID,
// so you must use NewMemoryLocker() to get one.
type MemoryLocker struct {
nameLocks map[string]*MemoryWaiter
nameLocksMu *sync.Mutex
}
// NewMemoryLocker returns a valid Locker backed by fs.
func NewMemoryLocker() *MemoryLocker {
return &MemoryLocker{
nameLocks: make(map[string]*MemoryWaiter),
nameLocksMu: new(sync.Mutex),
}
}
// TryLock attempts to get a lock for name, otherwise it returns
// a Waiter value to wait until the other process is finished.
func (l *MemoryLocker) TryLock(name string) (Waiter, error) {
l.nameLocksMu.Lock()
defer l.nameLocksMu.Unlock()
// see if lock already exists within this process
w, ok := l.nameLocks[name]
if ok {
return w, nil
}
// we got the lock, so create it
w = &MemoryWaiter{wg: new(sync.WaitGroup)}
w.wg.Add(1)
l.nameLocks[name] = w
return nil, nil
}
// Unlock releases the lock for name.
func (l *MemoryLocker) Unlock(name string) error {
l.nameLocksMu.Lock()
defer l.nameLocksMu.Unlock()
w, ok := l.nameLocks[name]
if !ok {
return fmt.Errorf("MemoryLocker: no lock to release for %s", name)
}
w.wg.Done()
delete(l.nameLocks, name)
return nil
}
// MemoryWaiter implements Waiter in memory.
type MemoryWaiter struct {
wg *sync.WaitGroup
}
// Wait waits until w.wg is done.
func (w *MemoryWaiter) Wait() {
w.Wait()
}
var _ Locker = &MemoryLocker{}
var _ Waiter = &MemoryWaiter{}
...@@ -49,7 +49,11 @@ type Storage interface { ...@@ -49,7 +49,11 @@ type Storage interface {
Exists(key string) bool Exists(key string) bool
// List returns all keys that match prefix. // List returns all keys that match prefix.
List(prefix string) ([]string, error) // If recursive is true, non-terminal keys
// will be enumerated (i.e. "directories"
// should be walked); otherwise, only keys
// prefixed exactly by prefix will be listed.
List(prefix string, recursive bool) ([]string, error)
// Stat returns information about key. // Stat returns information about key.
Stat(key string) (KeyInfo, error) Stat(key string) (KeyInfo, error)
...@@ -92,9 +96,10 @@ type Waiter interface { ...@@ -92,9 +96,10 @@ type Waiter interface {
// KeyInfo holds information about a key in storage. // KeyInfo holds information about a key in storage.
type KeyInfo struct { type KeyInfo struct {
Key string Key string
Modified time.Time Modified time.Time
Size int64 Size int64
IsTerminal bool // false for keys that only contain other keys (like directories)
} }
// storeTx stores all the values or none at all. // storeTx stores all the values or none at all.
......
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path"
"sort" "sort"
"strings" "strings"
...@@ -240,16 +241,16 @@ func (cfg *Config) askUserAgreement(agreementURL string) bool { ...@@ -240,16 +241,16 @@ func (cfg *Config) askUserAgreement(agreementURL string) bool {
// account, errors here are discarded to simplify code flow in // account, errors here are discarded to simplify code flow in
// the caller, and errors are not important here anyway. // the caller, and errors are not important here anyway.
func (cfg *Config) mostRecentUserEmail() string { func (cfg *Config) mostRecentUserEmail() string {
userList, err := cfg.certCache.storage.List(StorageKeys.UsersPrefix(cfg.CA)) userList, err := cfg.certCache.storage.List(StorageKeys.UsersPrefix(cfg.CA), false)
if err != nil || len(userList) == 0 { if err != nil || len(userList) == 0 {
return "" return ""
} }
sort.Slice(userList, func(i, j int) bool { sort.Slice(userList, func(i, j int) bool {
iInfo, _ := cfg.certCache.storage.Stat(StorageKeys.UserPrefix(cfg.CA, userList[i])) iInfo, _ := cfg.certCache.storage.Stat(userList[i])
jInfo, _ := cfg.certCache.storage.Stat(StorageKeys.UserPrefix(cfg.CA, userList[j])) jInfo, _ := cfg.certCache.storage.Stat(userList[j])
return jInfo.Modified.Before(iInfo.Modified) return jInfo.Modified.Before(iInfo.Modified)
}) })
user, err := cfg.getUser(userList[0]) user, err := cfg.getUser(path.Base(userList[0]))
if err != nil { if err != nil {
return "" return ""
} }
......
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
"importpath": "github.com/mholt/certmagic", "importpath": "github.com/mholt/certmagic",
"repository": "https://github.com/mholt/certmagic", "repository": "https://github.com/mholt/certmagic",
"vcs": "git", "vcs": "git",
"revision": "5b3085c491553887f36460365533eb5955fdeef0", "revision": "dc98c40439d15f67021f10f0d9219a39d7cf2990",
"branch": "master", "branch": "master",
"notests": true "notests": true
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment