Commit 1dfe1e5a authored by Luna Duclos's avatar Luna Duclos

Add plugin capabilities for tls storage.

To use a plugged in storage, specify "storage storage_name" in the tls block of the Caddyfile, by default, file storage will be used
parent 628920e2
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
// ensure that the standard plugins are in fact plugged in // ensure that the standard plugins are in fact plugged in
// and registered properly; this is a quick/naive way to do it. // and registered properly; this is a quick/naive way to do it.
func TestStandardPlugins(t *testing.T) { func TestStandardPlugins(t *testing.T) {
numStandardPlugins := 25 // importing caddyhttp plugs in this many plugins numStandardPlugins := 26 // importing caddyhttp plugs in this many plugins
s := caddy.DescribePlugins() s := caddy.DescribePlugins()
if got, want := strings.Count(s, "\n"), numStandardPlugins+5; got != want { if got, want := strings.Count(s, "\n"), numStandardPlugins+5; got != want {
t.Errorf("Expected all standard plugins to be plugged in, got:\n%s", s) t.Errorf("Expected all standard plugins to be plugged in, got:\n%s", s)
......
...@@ -99,12 +99,10 @@ type Config struct { ...@@ -99,12 +99,10 @@ type Config struct {
// certificates // certificates
KeyType acme.KeyType KeyType acme.KeyType
// The explicitly set storage creator or nil; use // The storage creator; use StorageFor() to get a guaranteed
// StorageFor() to get a guaranteed non-nil Storage // non-nil Storage instance. Note, Caddy may call this frequently
// instance. Note, Caddy may call this frequently so // so implementors are encouraged to cache any heavy instantiations.
// implementors are encouraged to cache any heavy StorageProvider string
// instantiations.
StorageCreator StorageCreator
// The state needed to operate on-demand TLS // The state needed to operate on-demand TLS
OnDemandState OnDemandState OnDemandState OnDemandState
...@@ -285,19 +283,20 @@ func (c *Config) StorageFor(caURL string) (Storage, error) { ...@@ -285,19 +283,20 @@ func (c *Config) StorageFor(caURL string) (Storage, error) {
// Create the storage based on the URL // Create the storage based on the URL
var s Storage var s Storage
if c.StorageCreator != nil { if c.StorageProvider == "" {
s, err = c.StorageCreator(u) c.StorageProvider = "file"
if err != nil {
return nil, fmt.Errorf("%s: unable to create custom storage: %v", caURL, err)
}
} }
if s == nil {
// We trust that this does not return a nil s when there's a nil err creator, ok := storageProviders[c.StorageProvider]
s, err = FileStorageCreator(u) if !ok {
if err != nil { return nil, fmt.Errorf("%s: Unknown storage: %v", caURL, c.StorageProvider)
return nil, fmt.Errorf("%s: unable to create file storage: %v", caURL, err)
}
} }
s, err = creator(u)
if err != nil {
return nil, fmt.Errorf("%s: unable to create custom storage '%v': %v", caURL, c.StorageProvider, err)
}
return s, nil return s, nil
} }
......
...@@ -38,11 +38,12 @@ func TestStorageForNoURL(t *testing.T) { ...@@ -38,11 +38,12 @@ func TestStorageForNoURL(t *testing.T) {
func TestStorageForLowercasesAndPrefixesScheme(t *testing.T) { func TestStorageForLowercasesAndPrefixesScheme(t *testing.T) {
resultStr := "" resultStr := ""
RegisterStorageProvider("fake-TestStorageForLowercasesAndPrefixesScheme", func(caURL *url.URL) (Storage, error) {
resultStr = caURL.String()
return nil, nil
})
c := &Config{ c := &Config{
StorageCreator: func(caURL *url.URL) (Storage, error) { StorageProvider: "fake-TestStorageForLowercasesAndPrefixesScheme",
resultStr = caURL.String()
return nil, nil
},
} }
if _, err := c.StorageFor("EXAMPLE.COM/BLAH"); err != nil { if _, err := c.StorageFor("EXAMPLE.COM/BLAH"); err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -71,11 +72,10 @@ func TestStorageForDefault(t *testing.T) { ...@@ -71,11 +72,10 @@ func TestStorageForDefault(t *testing.T) {
} }
func TestStorageForCustom(t *testing.T) { func TestStorageForCustom(t *testing.T) {
storage := fakeStorage("fake") storage := fakeStorage("fake-TestStorageForCustom")
RegisterStorageProvider("fake-TestStorageForCustom", func(caURL *url.URL) (Storage, error) { return storage, nil })
c := &Config{ c := &Config{
StorageCreator: func(caURL *url.URL) (Storage, error) { StorageProvider: "fake-TestStorageForCustom",
return storage, nil
},
} }
s, err := c.StorageFor("example.com") s, err := c.StorageFor("example.com")
if err != nil { if err != nil {
...@@ -87,10 +87,9 @@ func TestStorageForCustom(t *testing.T) { ...@@ -87,10 +87,9 @@ func TestStorageForCustom(t *testing.T) {
} }
func TestStorageForCustomError(t *testing.T) { func TestStorageForCustomError(t *testing.T) {
RegisterStorageProvider("fake-TestStorageForCustomError", func(caURL *url.URL) (Storage, error) { return nil, errors.New("some error") })
c := &Config{ c := &Config{
StorageCreator: func(caURL *url.URL) (Storage, error) { StorageProvider: "fake-TestStorageForCustomError",
return nil, errors.New("some error")
},
} }
if _, err := c.StorageFor("example.com"); err == nil { if _, err := c.StorageFor("example.com"); err == nil {
t.Fatal("Expecting error") t.Fatal("Expecting error")
...@@ -99,11 +98,7 @@ func TestStorageForCustomError(t *testing.T) { ...@@ -99,11 +98,7 @@ func TestStorageForCustomError(t *testing.T) {
func TestStorageForCustomNil(t *testing.T) { func TestStorageForCustomNil(t *testing.T) {
// Should fall through to the default // Should fall through to the default
c := &Config{ c := &Config{StorageProvider: ""}
StorageCreator: func(caURL *url.URL) (Storage, error) {
return nil, nil
},
}
s, err := c.StorageFor("example.com") s, err := c.StorageFor("example.com")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
......
...@@ -9,6 +9,10 @@ import ( ...@@ -9,6 +9,10 @@ import (
"strings" "strings"
) )
func init() {
RegisterStorageProvider("file", FileStorageCreator)
}
// storageBasePath is the root path in which all TLS/ACME assets are // storageBasePath is the root path in which all TLS/ACME assets are
// stored. Do not change this value during the lifetime of the program. // stored. Do not change this value during the lifetime of the program.
var storageBasePath = filepath.Join(caddy.AssetsPath(), "acme") var storageBasePath = filepath.Join(caddy.AssetsPath(), "acme")
......
...@@ -146,6 +146,16 @@ func setupTLS(c *caddy.Controller) error { ...@@ -146,6 +146,16 @@ func setupTLS(c *caddy.Controller) error {
return c.Errf("Unsupported DNS provider '%s'", args[0]) return c.Errf("Unsupported DNS provider '%s'", args[0])
} }
config.DNSProvider = args[0] config.DNSProvider = args[0]
case "storage":
args := c.RemainingArgs()
if len(args) != 1 {
return c.ArgErr()
}
storageProvName := args[0]
if _, ok := storageProviders[storageProvName]; !ok {
return c.Errf("Unsupported Storage provider '%s'", args[0])
}
config.StorageProvider = args[0]
default: default:
return c.Errf("Unknown keyword '%s'", c.Val()) return c.Errf("Unknown keyword '%s'", c.Val())
} }
......
...@@ -168,3 +168,11 @@ var ( ...@@ -168,3 +168,11 @@ var (
// when no other key type is specified. // when no other key type is specified.
DefaultKeyType = acme.RSA2048 DefaultKeyType = acme.RSA2048
) )
var storageProviders = make(map[string]StorageCreator)
// RegisterStorageProvider registers provider by name for storing tls data
func RegisterStorageProvider(name string, provider StorageCreator) {
storageProviders[name] = provider
caddy.RegisterPlugin("tls.storage."+name, caddy.Plugin{})
}
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