mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-18 05:31:38 +03:00
sq fails on first config write (#387)
* Fixed bug with config write on fresh install * Added test for config write on fresh install
This commit is contained in:
parent
fd070eefd3
commit
135318f542
13
CHANGELOG.md
13
CHANGELOG.md
@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
Breaking changes are annotated with ☢️, and alpha/beta features with 🐥.
|
Breaking changes are annotated with ☢️, and alpha/beta features with 🐥.
|
||||||
|
|
||||||
|
## [v0.47.2] - 2024-01-29
|
||||||
|
|
||||||
|
Yet another morning-after-the-big-release issue, a nasty little one this time.
|
||||||
|
See the earlier [`v0.47.0`](https://github.com/neilotoole/sq/releases/tag/v0.47.0) release
|
||||||
|
for recent headline features.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `sq` was failing to write config when there was no pre-existing config file. This was due to
|
||||||
|
a bug in the newly-introduced (as of `v0.47.0`) config locking mechanism. Fixed.
|
||||||
|
|
||||||
|
|
||||||
## [v0.47.1] - 2024-01-29
|
## [v0.47.1] - 2024-01-29
|
||||||
|
|
||||||
This is a tiny bugfix release for a runtime issue on some Linux distros. See
|
This is a tiny bugfix release for a runtime issue on some Linux distros. See
|
||||||
@ -1103,3 +1115,4 @@ make working with lots of sources much easier.
|
|||||||
[v0.46.1]: https://github.com/neilotoole/sq/compare/v0.46.0...v0.46.1
|
[v0.46.1]: https://github.com/neilotoole/sq/compare/v0.46.0...v0.46.1
|
||||||
[v0.47.0]: https://github.com/neilotoole/sq/compare/v0.46.1...v0.47.0
|
[v0.47.0]: https://github.com/neilotoole/sq/compare/v0.46.1...v0.47.0
|
||||||
[v0.47.1]: https://github.com/neilotoole/sq/compare/v0.47.0...v0.47.1
|
[v0.47.1]: https://github.com/neilotoole/sq/compare/v0.47.0...v0.47.1
|
||||||
|
[v0.47.2]: https://github.com/neilotoole/sq/compare/v0.47.1...v0.47.2
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/neilotoole/sq/cli/config"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -26,6 +28,27 @@ import (
|
|||||||
"github.com/neilotoole/sq/testh/tu"
|
"github.com/neilotoole/sq/testh/tu"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestConfigWriteOnFreshInstall verifies that sq can write
|
||||||
|
// to the config store when there is no existing config.
|
||||||
|
func TestConfigWriteOnFreshInstall(t *testing.T) {
|
||||||
|
cfgDir := tu.TempDir(t)
|
||||||
|
t.Setenv(config.EnvarConfigDir, cfgDir)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// We want to write to the config, it doesn't matter what
|
||||||
|
// we write, just that we write something.
|
||||||
|
args := []string{"config", "set", cli.OptPingCmdTimeout.Key(), "3m33s"}
|
||||||
|
|
||||||
|
err := cli.Execute(ctx, os.Stdin, os.Stdout, os.Stderr, args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
args = []string{"config", "get", cli.OptPingCmdTimeout.Key()}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
err = cli.Execute(ctx, os.Stdin, buf, os.Stderr, args)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "3m33s\n", buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestSmoke(t *testing.T) {
|
func TestSmoke(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Execute a bunch of smoke test cases.
|
// Execute a bunch of smoke test cases.
|
||||||
|
@ -13,6 +13,9 @@ import (
|
|||||||
|
|
||||||
// Store saves and loads config.
|
// Store saves and loads config.
|
||||||
type Store interface {
|
type Store interface {
|
||||||
|
// Exists returns true if the config exists in the store.
|
||||||
|
Exists() bool
|
||||||
|
|
||||||
// Save writes config to the store.
|
// Save writes config to the store.
|
||||||
Save(ctx context.Context, cfg *Config) error
|
Save(ctx context.Context, cfg *Config) error
|
||||||
|
|
||||||
@ -34,6 +37,11 @@ type Store interface {
|
|||||||
// and Load always returns a new empty Config. Useful for testing.
|
// and Load always returns a new empty Config. Useful for testing.
|
||||||
type DiscardStore struct{}
|
type DiscardStore struct{}
|
||||||
|
|
||||||
|
// Exists implements Store.Exists. It returns true.
|
||||||
|
func (s DiscardStore) Exists() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Lockfile implements Store.Lockfile.
|
// Lockfile implements Store.Lockfile.
|
||||||
func (DiscardStore) Lockfile() (lockfile.Lockfile, error) {
|
func (DiscardStore) Lockfile() (lockfile.Lockfile, error) {
|
||||||
f, err := os.CreateTemp("", fmt.Sprintf("sq-%d.lock", os.Getpid()))
|
f, err := os.CreateTemp("", fmt.Sprintf("sq-%d.lock", os.Getpid()))
|
||||||
|
@ -48,7 +48,7 @@ func Load(ctx context.Context, osArgs []string, optsReg *options.Registry,
|
|||||||
OptionsRegistry: optsReg,
|
OptionsRegistry: optsReg,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfgStore.fileExists() {
|
if !cfgStore.Exists() {
|
||||||
cfg := config.New()
|
cfg := config.New()
|
||||||
return cfg, cfgStore, nil
|
return cfg, cfgStore, nil
|
||||||
}
|
}
|
||||||
|
@ -209,9 +209,9 @@ func (fs *Store) write(ctx context.Context, data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileExists returns true if the backing file can be accessed, false if it doesn't
|
// Exists returns true if the backing file can be accessed, false if it doesn't
|
||||||
// exist or on any error.
|
// exist or on any error.
|
||||||
func (fs *Store) fileExists() bool {
|
func (fs *Store) Exists() bool {
|
||||||
_, err := os.Stat(fs.Path)
|
_, err := os.Stat(fs.Path)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
26
cli/run.go
26
cli/run.go
@ -348,7 +348,9 @@ func cmdRequiresConfigLock(cmd *cobra.Command) bool {
|
|||||||
|
|
||||||
// lockReloadConfig acquires the lock for the config store, and updates the
|
// lockReloadConfig acquires the lock for the config store, and updates the
|
||||||
// run (as found on cmd's context) with a fresh copy of the config, loaded
|
// run (as found on cmd's context) with a fresh copy of the config, loaded
|
||||||
// after lock acquisition.
|
// after lock acquisition. If there's no config persisted in the store,
|
||||||
|
// the run's config is left untouched. (That run config will later be
|
||||||
|
// saved to the store, if appropriate.)
|
||||||
//
|
//
|
||||||
// The config lock should be acquired before making any changes to config.
|
// The config lock should be acquired before making any changes to config.
|
||||||
// Timeout and progress options from ctx are honored.
|
// Timeout and progress options from ctx are honored.
|
||||||
@ -387,18 +389,20 @@ func lockReloadConfig(cmd *cobra.Command) (unlock func(), err error) {
|
|||||||
return nil, errz.Wrap(err, "acquire config lock")
|
return nil, errz.Wrap(err, "acquire config lock")
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg *config.Config
|
if ru.ConfigStore.Exists() {
|
||||||
if cfg, err = ru.ConfigStore.Load(ctx); err != nil {
|
var cfg *config.Config
|
||||||
// An error occurred reloading config; release the lock before returning.
|
if cfg, err = ru.ConfigStore.Load(ctx); err != nil {
|
||||||
if unlockErr := lock.Unlock(); unlockErr != nil {
|
// An error occurred reloading config; release the lock before returning.
|
||||||
lg.FromContext(ctx).Warn("Failed to release config lock",
|
if unlockErr := lock.Unlock(); unlockErr != nil {
|
||||||
lga.Lock, lock, lga.Err, unlockErr)
|
lg.FromContext(ctx).Warn("Failed to release config lock",
|
||||||
|
lga.Lock, lock, lga.Err, unlockErr)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the run with the fresh config.
|
// Assign the newly-reloaded config to the run.
|
||||||
ru.Config = cfg
|
ru.Config = cfg
|
||||||
|
} // Else, the config doesn't currently exist on disk; no reload required.
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
if unlockErr := lock.Unlock(); unlockErr != nil {
|
if unlockErr := lock.Unlock(); unlockErr != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user