engine: local: Add a vardir API to our local API collection
This commit is contained in:
@@ -54,7 +54,11 @@ type API struct {
|
||||
Logf func(format string, v ...interface{})
|
||||
|
||||
// Each piece of the API can take a handle here.
|
||||
*Value
|
||||
*Value // TODO: Rename to ValueImpl?
|
||||
|
||||
// VarDirImpl is the implementation for the VarDir API's. The API's are
|
||||
// the collection of public methods that exist on this struct.
|
||||
*VarDirImpl
|
||||
}
|
||||
|
||||
// Init initializes the API before first use. It returns itself so it can be
|
||||
@@ -67,6 +71,13 @@ func (obj *API) Init() *API {
|
||||
Logf: obj.Logf,
|
||||
})
|
||||
|
||||
obj.VarDirImpl = &VarDirImpl{}
|
||||
obj.VarDirImpl.Init(&VarDirInit{
|
||||
Prefix: obj.Prefix,
|
||||
Debug: obj.Debug,
|
||||
Logf: obj.Logf,
|
||||
})
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -332,3 +343,81 @@ func valueRemove(ctx context.Context, prefix, key string) error {
|
||||
}
|
||||
return nil // ignore not found errors
|
||||
}
|
||||
|
||||
// VarDirInit are the init values that the VarDir API needs to work correctly.
|
||||
type VarDirInit struct {
|
||||
Prefix string
|
||||
Debug bool
|
||||
Logf func(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// VarDirImpl is the implementation for the VarDir API's. The API's are the
|
||||
// collection of public methods that exist on this struct.
|
||||
type VarDirImpl struct {
|
||||
init *VarDirInit
|
||||
mutex *sync.Mutex
|
||||
prefix string
|
||||
prefixExists bool // is it okay to use the prefix?
|
||||
}
|
||||
|
||||
// Init runs some initialization code for the VarDir API.
|
||||
func (obj *VarDirImpl) Init(init *VarDirInit) {
|
||||
obj.init = init
|
||||
obj.mutex = &sync.Mutex{}
|
||||
obj.prefix = fmt.Sprintf("%s/", path.Join(obj.init.Prefix, "vardir"))
|
||||
}
|
||||
|
||||
// VarDir returns a directory rooted at the internal prefix.
|
||||
func (obj *VarDirImpl) VarDir(ctx context.Context, reldir string) (string, error) {
|
||||
if strings.HasPrefix(reldir, "/") {
|
||||
return "", fmt.Errorf("path must be relative")
|
||||
}
|
||||
if !strings.HasSuffix(reldir, "/") {
|
||||
return "", fmt.Errorf("path must be a dir")
|
||||
}
|
||||
// NOTE: The above checks ensure we don't get either "" or "/" as input!
|
||||
|
||||
prefix, err := obj.getPrefix()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("%s/", path.Join(prefix, reldir))
|
||||
|
||||
// TODO: Should we mkdir this?
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
if err := os.MkdirAll(result, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getPrefix gets the prefix dir to use, or errors if it can't make one. It
|
||||
// makes it on first use, and returns quickly from any future calls to it.
|
||||
func (obj *VarDirImpl) getPrefix() (string, error) {
|
||||
// NOTE: Moving this mutex to just below the first early return, would
|
||||
// be a benign race, but as it turns out, it's possible that a compiler
|
||||
// would see this behaviour as "undefined" and things might not work as
|
||||
// intended. It could perhaps be replaced with a sync/atomic primitive
|
||||
// if we wanted better performance here.
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
if obj.prefixExists { // former race read
|
||||
return obj.prefix, nil
|
||||
}
|
||||
|
||||
// MkdirAll instead of Mkdir because we have no idea if the parent
|
||||
// local/ directory was already made yet or not. (If at all.) If path is
|
||||
// already a directory, MkdirAll does nothing and returns nil. (Good!)
|
||||
// TODO: I hope MkdirAll is thread-safe on path creation in case another
|
||||
// future local API tries to make the base (parent) directory too!
|
||||
if err := os.MkdirAll(obj.prefix, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
obj.prefixExists = true // former race write
|
||||
|
||||
return obj.prefix, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user