Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAULT-8631 Make upgrade synchronous when no keys to upgrade #66

Merged
merged 1 commit into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend.go
Expand Up @@ -90,7 +90,7 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
return b, nil
}

// Factory returns a new backend as logical.Backend.
// VersionedKVFactory returns a new KVV2 backend as logical.Backend.
func VersionedKVFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
upgradeCtx, upgradeCancelFunc := context.WithCancel(ctx)

Expand Down
1 change: 0 additions & 1 deletion path_config_test.go
Expand Up @@ -157,5 +157,4 @@ func TestVersionedKV_Config_DeleteVersionAfter(t *testing.T) {
}
})
}

}
45 changes: 16 additions & 29 deletions path_data_test.go
Expand Up @@ -28,36 +28,23 @@ func getBackend(t *testing.T) (logical.Backend, logical.Storage) {
t.Fatalf("unable to create backend: %v", err)
}

// Wait for the upgrade to finish
timeout := time.After(20 * time.Second)
ticker := time.Tick(time.Second)

for {
select {
case <-timeout:
t.Fatal("timeout expired waiting for upgrade")
case <-ticker:
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "config",
Storage: config.StorageView,
}

resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("unable to read config: %s", err.Error())
return nil, nil
}

if resp != nil && !resp.IsError() {
return b, config.StorageView
}

if resp == nil || (resp.IsError() && strings.Contains(resp.Error().Error(), "Upgrading from non-versioned to versioned")) {
t.Log("waiting for upgrade to complete")
}
}
req := &logical.Request{
Operation: logical.ReadOperation,
Path: "config",
Storage: config.StorageView,
}

resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("unable to read config: %s", err.Error())
return nil, nil
}

if resp == nil || resp.IsError() {
t.Fatalf("Error during mount creation: %x", resp.Error().Error())
}

return b, config.StorageView
}

// getKeySet will produce a set of the keys that exist in m
Expand Down
29 changes: 23 additions & 6 deletions upgrade.go
Expand Up @@ -118,6 +118,18 @@ func (b *versionedKVBackend) Upgrade(ctx context.Context, s logical.Storage) err
return nil
}

// If we have 0 keys, it's either a new mount or one that's trivial to upgrade,
// so we should do the upgrade synchronously
upgradeSynchronously := false
keys, err := logical.CollectKeys(ctx, s)
ncabatoff marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
b.Logger().Error("upgrading resulted in error", "error", err)
return err
}
if len(keys) == 0 {
upgradeSynchronously = true
}

upgradeInfo := &UpgradeInfo{
StartedTime: ptypes.TimestampNow(),
}
Expand All @@ -128,7 +140,7 @@ func (b *versionedKVBackend) Upgrade(ctx context.Context, s logical.Storage) err
return err
}

// Because this is a long running process we need a new context.
// Because this is a long-running process we need a new context.
ctx = context.Background()

upgradeKey := func(key string) error {
Expand Down Expand Up @@ -189,10 +201,7 @@ func (b *versionedKVBackend) Upgrade(ctx context.Context, s logical.Storage) err
return nil
}

// Run the actual upgrade in a go routine so we don't block the client on a
// potentially long process.
go func() {

upgradeFunc := func() {
// Write the canary value and if we are read only wait until the setup
// process has finished.
READONLY_LOOP:
Expand Down Expand Up @@ -257,7 +266,15 @@ func (b *versionedKVBackend) Upgrade(ctx context.Context, s logical.Storage) err
}

atomic.StoreUint32(b.upgrading, 0)
}()
}

if upgradeSynchronously {
upgradeFunc()
} else {
// We run the actual upgrade in a go routine, so we don't block the client on a
// potentially long process.
go upgradeFunc()
}

return nil
}