Skip to content

Commit 1dbac72

Browse files
committed
refactor(core): redact credentials, fail Start after Stop, tidy docs and errors
Names the core logger data-server, validates the datastore name in one place, drops the error alias and documents swallowed errors. Signed-off-by: Markus Vahlenkamp <markus.vahlenkamp@nokia.com>
1 parent 3691568 commit 1dbac72

4 files changed

Lines changed: 54 additions & 30 deletions

File tree

‎pkg/core/core.go‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"sync"
1010

11+
"github.com/sdcio/data-server/pkg/api"
1112
"github.com/sdcio/data-server/pkg/cache"
1213
"github.com/sdcio/data-server/pkg/config"
1314
"github.com/sdcio/data-server/pkg/datastore"
@@ -23,8 +24,8 @@ type Core struct {
2324
cfg *config.Config
2425
dialOpts []grpc.DialOption
2526

26-
ctx context.Context
27-
cfn context.CancelFunc
27+
ctx context.Context
28+
cancel context.CancelFunc
2829

2930
startOnce sync.Once
3031
stopOnce sync.Once
@@ -56,7 +57,7 @@ func New(ctx context.Context, cfg *config.Config, opts ...Option) (*Core, error)
5657
c := &Core{
5758
cfg: cfg,
5859
ctx: ctx,
59-
cfn: cancel,
60+
cancel: cancel,
6061
ready: make(chan struct{}),
6162
datastores: map[string]*datastore.Datastore{},
6263
}
@@ -89,21 +90,27 @@ func (c *Core) WaitReady(ctx context.Context) error {
8990
}
9091
}
9192

92-
// Stop stops all datastores and cancels the Core's context. It is idempotent.
93+
// Stop stops all datastores and cancels the Core's context. It is idempotent
94+
// and always returns nil: datastore stop errors are discarded. A Start after
95+
// Stop fails with context.Canceled.
9396
func (c *Core) Stop(ctx context.Context) error {
9497
c.stopOnce.Do(func() {
9598
c.mu.Lock()
9699
defer c.mu.Unlock()
97100
for _, ds := range c.datastores {
98101
_ = ds.Stop(ctx)
99102
}
100-
c.cfn()
103+
c.cancel()
101104
})
102105
return nil
103106
}
104107

105108
func (c *Core) start(ctx context.Context) error {
106-
log := logf.FromContext(ctx)
109+
if err := c.ctx.Err(); err != nil {
110+
return err
111+
}
112+
log := logf.FromContext(ctx).WithName("data-server")
113+
ctx = logf.IntoContext(ctx, log)
107114

108115
var wg sync.WaitGroup
109116
var schemaErr, cacheErr error
@@ -158,12 +165,17 @@ func (c *Core) addDatastore(ds *datastore.Datastore) error {
158165
return nil
159166
}
160167

168+
// getDatastore fails with api.ErrInvalidArgument for an empty name and
169+
// api.ErrUnknownDatastore when the name is not registered.
161170
func (c *Core) getDatastore(name string) (*datastore.Datastore, error) {
171+
if name == "" {
172+
return nil, fmt.Errorf("%w: missing datastore name", api.ErrInvalidArgument)
173+
}
162174
c.mu.RLock()
163175
defer c.mu.RUnlock()
164176
ds, ok := c.datastores[name]
165177
if !ok {
166-
return nil, fmt.Errorf("%w %s", errUnknownDatastore, name)
178+
return nil, fmt.Errorf("%w %s", api.ErrUnknownDatastore, name)
167179
}
168180
return ds, nil
169181
}

‎pkg/core/core_test.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,14 @@ func TestWaitReadyHonoursContext(t *testing.T) {
130130
t.Fatalf("got %v", err)
131131
}
132132
}
133+
134+
func TestStartAfterStopFails(t *testing.T) {
135+
c, err := New(context.Background(), newTestConfig(t))
136+
if err != nil {
137+
t.Fatal(err)
138+
}
139+
_ = c.Stop(context.Background())
140+
if err := c.Start(context.Background()); !errors.Is(err, context.Canceled) {
141+
t.Fatalf("got %v", err)
142+
}
143+
}

‎pkg/core/datastore.go‎

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,20 @@ package core
22

33
import (
44
"context"
5-
"fmt"
65
"strings"
7-
"time"
86

97
"github.com/sdcio/data-server/pkg/api"
108
"github.com/sdcio/data-server/pkg/config"
119
"github.com/sdcio/data-server/pkg/datastore"
10+
"github.com/sdcio/data-server/pkg/utils"
1211
sdcpb "github.com/sdcio/sdc-protos/sdcpb"
1312
)
1413

15-
var errUnknownDatastore = api.ErrUnknownDatastore
16-
17-
// GetDataStore returns the observed state of the named datastore. It fails
18-
// with api.ErrInvalidArgument for an empty name and api.ErrUnknownDatastore
19-
// when no such datastore exists.
14+
// GetDataStore returns the observed state of the named datastore, with
15+
// credentials in the effective spec redacted. It fails with
16+
// api.ErrInvalidArgument for an empty name and api.ErrUnknownDatastore when no
17+
// such datastore exists.
2018
func (c *Core) GetDataStore(ctx context.Context, name string) (*api.DatastoreInfo, error) {
21-
if name == "" {
22-
return nil, fmt.Errorf("%w: missing datastore name", api.ErrInvalidArgument)
23-
}
2419
ds, err := c.getDatastore(name)
2520
if err != nil {
2621
return nil, err
@@ -29,12 +24,9 @@ func (c *Core) GetDataStore(ctx context.Context, name string) (*api.DatastoreInf
2924
}
3025

3126
// WatchDeviations returns a channel of deviation events for the named
32-
// datastore. The channel closes when ctx is done or the Core stops. Events are
33-
// not produced yet; the datastore deviation feed is wired in a later step.
27+
// datastore. The channel closes when ctx is done or the Core stops. It never
28+
// emits events: no deviation producer feeds it.
3429
func (c *Core) WatchDeviations(ctx context.Context, name string) (<-chan api.DeviationEvent, error) {
35-
if name == "" {
36-
return nil, fmt.Errorf("%w: missing datastore name", api.ErrInvalidArgument)
37-
}
3830
if _, err := c.getDatastore(name); err != nil {
3931
return nil, err
4032
}
@@ -98,7 +90,7 @@ func specFromConfig(dc *config.DatastoreConfig) api.DatastoreSpec {
9890
Target: &api.Target{Type: api.TargetType(strings.ToLower(sp.Protocol)), ProtocolOptions: api.ProtocolOptions{GNMI: gnmiSyncOptions(sp)}},
9991
Paths: sp.Paths,
10092
Mode: syncMode(sp.Mode),
101-
Interval: uint64(sp.Interval / time.Nanosecond),
93+
Interval: uint64(sp.Interval),
10294
})
10395
}
10496
}
@@ -132,7 +124,7 @@ func targetFromSBI(s *config.SBI) api.Target {
132124
Port: s.Port,
133125
}
134126
if s.Credentials != nil {
135-
t.Credentials = &api.Credentials{Username: s.Credentials.Username, Password: s.Credentials.Password, Token: s.Credentials.Token}
127+
t.Credentials = &api.Credentials{Username: s.Credentials.Username, Password: redact(s.Credentials.Password), Token: redact(s.Credentials.Token)}
136128
}
137129
if s.TLS != nil {
138130
t.TLS = &api.TLS{CA: s.TLS.CA, Cert: s.TLS.Cert, Key: s.TLS.Key, SkipVerify: s.TLS.SkipVerify}
@@ -141,16 +133,23 @@ func targetFromSBI(s *config.SBI) api.Target {
141133
t.ProtocolOptions.GNMI = &api.GNMIOptions{Encoding: s.GnmiOptions.Encoding, TargetName: s.GnmiOptions.TargetName}
142134
}
143135
if o := s.NetconfOptions; o != nil {
144-
cc := api.CommitCandidateCandidate
136+
commit := api.CommitCandidateCandidate
145137
if o.CommitDatastore == "running" {
146-
cc = api.CommitCandidateRunning
138+
commit = api.CommitCandidateRunning
147139
}
148140
t.ProtocolOptions.Netconf = &api.NetconfOptions{
149141
IncludeNs: o.IncludeNS,
150142
OperationWithNs: o.OperationWithNamespace,
151143
UseOperationRemove: o.UseOperationRemove,
152-
CommitCandidate: cc,
144+
CommitCandidate: commit,
153145
}
154146
}
155147
return t
156148
}
149+
150+
func redact(v string) string {
151+
if v == "" {
152+
return ""
153+
}
154+
return utils.Redacted
155+
}

‎pkg/core/init.go‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ func (c *Core) createSchemaClient(ctx context.Context) error {
4545
return c.createRemoteSchemaClient(ctx)
4646
}
4747

48+
// createLocalSchemaStore fails only when the store cannot be opened; a schema
49+
// that fails to parse or add is logged and skipped.
4850
func (c *Core) createLocalSchemaStore(ctx context.Context) error {
4951
log := logf.FromContext(ctx)
5052
var store schemaStore.Store
@@ -67,9 +69,9 @@ func (c *Core) createLocalSchemaStore(ctx context.Context) error {
6769
wg.Add(1)
6870
go func(sCfg *schemaConfig.SchemaConfig) {
6971
defer wg.Done()
70-
sck := schemaStore.SchemaKey{Name: sCfg.Name, Vendor: sCfg.Vendor, Version: sCfg.Version}
71-
log := log.WithValues("schema", sck)
72-
if store.HasSchema(sck) {
72+
key := schemaStore.SchemaKey{Name: sCfg.Name, Vendor: sCfg.Vendor, Version: sCfg.Version}
73+
log := log.WithValues("schema", key)
74+
if store.HasSchema(key) {
7375
log.Info("schema already exists in the store, not reloading it")
7476
return
7577
}

0 commit comments

Comments
 (0)