Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions client/setec/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,12 +299,20 @@ func (s *Store) Refresh(ctx context.Context) error {
s.countPolls.Add(1)
s.latestPoll.Set(float64(time.Now().UTC().UnixMilli()) / 1000)
updates := make(map[string]*cachedSecret)
if err := s.poll(ctx, updates); err != nil {

// Count errors from polling, but do not report them until we have
// applied any updates that might have succeeded.
perr := s.poll(ctx, updates)
if perr != nil {
s.countPollErrors.Add(1)
return nil, fmt.Errorf("[store] update poll failed: %w", err)
}
if err := s.applyUpdates(updates); err != nil {
return nil, fmt.Errorf("[store] applying updates failed: %w", err)
} else if perr != nil {
if len(updates) != 0 {
return nil, fmt.Errorf("[store] update poll partially failed: %w", perr)
}
return nil, fmt.Errorf("[store] update poll failed: %w", perr)
Comment thread
creachadair marked this conversation as resolved.
}
return nil, nil
})
Expand Down
43 changes: 43 additions & 0 deletions client/setec/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,49 @@ func TestNewFileCache(t *testing.T) {
})
}

func TestPartialRefresh(t *testing.T) {
d := setectest.NewDB(t, nil)
d.MustPut(d.Superuser, "small", "1")
d.MustPut(d.Superuser, "large", "500")

ts := setectest.NewServer(t, d, nil)
hs := httptest.NewServer(ts.Mux)
defer hs.Close()

st, err := setec.NewStore(t.Context(), setec.StoreConfig{
Client: setec.Client{Server: hs.URL, DoHTTP: hs.Client().Do},
Secrets: []string{"small", "large"},
})
if err != nil {
t.Fatalf("NewStore: unexpected error: %v", err)
}
defer st.Close()

small := st.Secret("small")
if small.GetString() != "1" {
t.Errorf("Value of small before: got %q, want 1", small.GetString())
}

// Update small to a new value, and delete large.
// This means a refresh will partially fail (since "large" is now gone).
// But the update to "small" should still be observed.
d.MustActivate(d.Superuser, "small", d.MustPut(d.Superuser, "small", "2"))
if err := d.Actual.Delete(d.Superuser, "large"); err != nil {
t.Fatalf("Delete large: unexpected error: %v", err)
}

// Refreshing should still report an error.
if err := st.Refresh(t.Context()); err == nil {
t.Error("Refresh should have reported an error")
} else {
t.Logf("Refresh reported (expected) error: %v", err)
}

if small.GetString() != "2" {
t.Errorf("Value of small after: got %q, want 2", small.GetString())
}
}

func TestNilSecret(t *testing.T) {
var s setec.Secret

Expand Down