2020-08-06 20:58:47 +03:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2023-04-22 16:37:07 +03:00
|
|
|
"context"
|
2024-01-15 04:45:34 +03:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/libsq/core/errz"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/ioz/lockfile"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/options"
|
2020-08-06 20:58:47 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Store saves and loads config.
|
|
|
|
type Store interface {
|
2024-01-30 08:03:03 +03:00
|
|
|
// Exists returns true if the config exists in the store.
|
|
|
|
Exists() bool
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
// Save writes config to the store.
|
2023-04-22 16:37:07 +03:00
|
|
|
Save(ctx context.Context, cfg *Config) error
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
// Load reads config from the store.
|
2023-05-01 06:59:34 +03:00
|
|
|
Load(ctx context.Context) (*Config, error)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
// Location returns the location of the store, typically
|
|
|
|
// a file path.
|
|
|
|
Location() string
|
2024-01-15 04:45:34 +03:00
|
|
|
|
|
|
|
// Lockfile returns the lockfile used by the store, but does not acquire
|
|
|
|
// the lock, which is the caller's responsibility. The lock should always
|
|
|
|
// be acquired before mutating config. It is also the caller's responsibility
|
|
|
|
// to release the acquired lock when done.
|
|
|
|
Lockfile() (lockfile.Lockfile, error)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// DiscardStore implements Store but its Save method is no-op
|
|
|
|
// and Load always returns a new empty Config. Useful for testing.
|
2022-12-18 11:35:59 +03:00
|
|
|
type DiscardStore struct{}
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2024-01-30 08:03:03 +03:00
|
|
|
// Exists implements Store.Exists. It returns true.
|
|
|
|
func (s DiscardStore) Exists() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
// Lockfile implements Store.Lockfile.
|
|
|
|
func (DiscardStore) Lockfile() (lockfile.Lockfile, error) {
|
|
|
|
f, err := os.CreateTemp("", fmt.Sprintf("sq-%d.lock", os.Getpid()))
|
|
|
|
if err != nil {
|
|
|
|
return "", errz.Err(err)
|
|
|
|
}
|
|
|
|
fname := f.Name()
|
|
|
|
if err = f.Close(); err != nil {
|
|
|
|
return "", errz.Err(err)
|
|
|
|
}
|
|
|
|
return lockfile.Lockfile(fname), nil
|
|
|
|
}
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
var _ Store = (*DiscardStore)(nil)
|
|
|
|
|
|
|
|
// Load returns a new empty Config.
|
2023-05-01 06:59:34 +03:00
|
|
|
func (DiscardStore) Load(context.Context) (*Config, error) {
|
2020-08-06 20:58:47 +03:00
|
|
|
return New(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save is no-op.
|
2023-04-22 16:37:07 +03:00
|
|
|
func (DiscardStore) Save(context.Context, *Config) error {
|
2020-08-06 20:58:47 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-18 09:07:38 +03:00
|
|
|
// Location returns /dev/null.
|
2020-08-06 20:58:47 +03:00
|
|
|
func (DiscardStore) Location() string {
|
|
|
|
return "/dev/null"
|
|
|
|
}
|
2024-01-15 04:45:34 +03:00
|
|
|
|
|
|
|
// OptConfigLockTimeout is the time allowed to acquire the config lock.
|
|
|
|
var OptConfigLockTimeout = options.NewDuration(
|
|
|
|
"config.lock.timeout",
|
|
|
|
"",
|
|
|
|
0,
|
|
|
|
time.Second*5,
|
|
|
|
"Wait timeout to acquire config lock",
|
2024-01-29 00:55:51 +03:00
|
|
|
`Wait timeout to acquire the config lock (which prevents multiple sq instances
|
|
|
|
stepping on each other's config changes). During this period, retry will occur
|
2024-01-15 04:45:34 +03:00
|
|
|
if the lock is already held by another process. If zero, no retry occurs.`,
|
|
|
|
)
|