Browse code

libnet/ds: simplify datastore.New()

That function was needlessly complex. Instead of relying on a struct and
a sub-struct, it now just takes two string params: a path and a bucket
name.

Libnetwork config is now initialized with default values. A new struct
is introduced in libnetwork/config to let tests customize the path and
bucket name.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>

Albin Kerouanton authored on 2024/06/14 18:09:52
Showing 9 changed files
... ...
@@ -39,7 +39,7 @@ type Config struct {
39 39
 	ClusterProvider        cluster.Provider
40 40
 	NetworkControlPlaneMTU int
41 41
 	DefaultAddressPool     []*ipamutils.NetworkToSplit
42
-	Scope                  datastore.ScopeCfg
42
+	DatastoreBucket        string
43 43
 	ActiveSandboxes        map[string]interface{}
44 44
 	PluginGetter           plugingetter.PluginGetter
45 45
 }
... ...
@@ -47,7 +47,8 @@ type Config struct {
47 47
 // New creates a new Config and initializes it with the given Options.
48 48
 func New(opts ...Option) *Config {
49 49
 	cfg := &Config{
50
-		driverCfg: make(map[string]map[string]any),
50
+		driverCfg:       make(map[string]map[string]any),
51
+		DatastoreBucket: datastore.DefaultBucket,
51 52
 	}
52 53
 
53 54
 	for _, opt := range opts {
... ...
@@ -56,10 +57,6 @@ func New(opts ...Option) *Config {
56 56
 		}
57 57
 	}
58 58
 
59
-	// load default scope configs which don't have explicit user specified configs.
60
-	if cfg.Scope == (datastore.ScopeCfg{}) {
61
-		cfg.Scope = datastore.DefaultScope(cfg.DataDir)
62
-	}
63 59
 	return cfg
64 60
 }
65 61
 
... ...
@@ -108,7 +108,7 @@ type Controller struct {
108 108
 // New creates a new instance of network controller.
109 109
 func New(cfgOptions ...config.Option) (*Controller, error) {
110 110
 	cfg := config.New(cfgOptions...)
111
-	store, err := datastore.New(cfg.Scope)
111
+	store, err := datastore.New(cfg.DataDir, cfg.DatastoreBucket)
112 112
 	if err != nil {
113 113
 		return nil, fmt.Errorf("libnet controller initialization: %w", err)
114 114
 	}
... ...
@@ -333,7 +333,9 @@ func (c *Controller) makeDriverConfig(ntype string) map[string]interface{} {
333 333
 		return nil
334 334
 	}
335 335
 
336
-	cfg := map[string]interface{}{}
336
+	cfg := map[string]interface{}{
337
+		netlabel.LocalKVClient: c.store,
338
+	}
337 339
 	for _, label := range c.cfg.Labels {
338 340
 		key, val, _ := strings.Cut(label, "=")
339 341
 		if !strings.HasPrefix(key, netlabel.DriverPrefix+"."+ntype) {
... ...
@@ -348,10 +350,6 @@ func (c *Controller) makeDriverConfig(ntype string) map[string]interface{} {
348 348
 		cfg[k] = v
349 349
 	}
350 350
 
351
-	if c.cfg.Scope.IsValid() {
352
-		cfg[netlabel.LocalKVClient] = c.store
353
-	}
354
-
355 351
 	return cfg
356 352
 }
357 353
 
... ...
@@ -1,10 +1,10 @@
1 1
 package datastore
2 2
 
3 3
 import (
4
-	"fmt"
4
+	"errors"
5
+	"path"
5 6
 	"strings"
6 7
 	"sync"
7
-	"time"
8 8
 
9 9
 	store "github.com/docker/docker/libnetwork/internal/kvstore"
10 10
 	"github.com/docker/docker/libnetwork/internal/kvstore/boltdb"
... ...
@@ -50,18 +50,6 @@ type KVObject interface {
50 50
 	CopyTo(KVObject) error
51 51
 }
52 52
 
53
-// ScopeCfg represents Datastore configuration.
54
-type ScopeCfg struct {
55
-	Client ScopeClientCfg
56
-}
57
-
58
-// ScopeClientCfg represents Datastore Client-only mode configuration
59
-type ScopeClientCfg struct {
60
-	Provider string
61
-	Address  string
62
-	Config   *store.Config
63
-}
64
-
65 53
 const (
66 54
 	// NetworkKeyPrefix is the prefix for network key in the kv store
67 55
 	NetworkKeyPrefix = "network"
... ...
@@ -74,37 +62,7 @@ var (
74 74
 	rootChain        = defaultRootChain
75 75
 )
76 76
 
77
-const defaultPrefix = "/var/lib/docker/network/files"
78
-
79
-// DefaultScope returns a default scope config for clients to use.
80
-func DefaultScope(dataDir string) ScopeCfg {
81
-	var dbpath string
82
-	if dataDir == "" {
83
-		dbpath = defaultPrefix + "/local-kv.db"
84
-	} else {
85
-		dbpath = dataDir + "/network/files/local-kv.db"
86
-	}
87
-
88
-	return ScopeCfg{
89
-		Client: ScopeClientCfg{
90
-			Provider: string(store.BOLTDB),
91
-			Address:  dbpath,
92
-			Config: &store.Config{
93
-				Bucket:            "libnetwork",
94
-				ConnectionTimeout: time.Minute,
95
-			},
96
-		},
97
-	}
98
-}
99
-
100
-// IsValid checks if the scope config has valid configuration.
101
-func (cfg *ScopeCfg) IsValid() bool {
102
-	if cfg == nil || strings.TrimSpace(cfg.Client.Provider) == "" || strings.TrimSpace(cfg.Client.Address) == "" {
103
-		return false
104
-	}
105
-
106
-	return true
107
-}
77
+const DefaultBucket = "libnetwork"
108 78
 
109 79
 // Key provides convenient method to create a Key
110 80
 func Key(key ...string) string {
... ...
@@ -118,17 +76,16 @@ func Key(key ...string) string {
118 118
 	return b.String()
119 119
 }
120 120
 
121
-// newClient used to connect to KV Store
122
-func newClient(kv string, addr string, config *store.Config) (*Store, error) {
123
-	if kv != string(store.BOLTDB) {
124
-		return nil, fmt.Errorf("unsupported KV store")
121
+// New creates a new Store instance.
122
+func New(dir, bucket string) (*Store, error) {
123
+	if dir == "" {
124
+		return nil, errors.New("empty dir")
125 125
 	}
126
-
127
-	if config == nil {
128
-		config = &store.Config{}
126
+	if bucket == "" {
127
+		return nil, errors.New("empty bucket")
129 128
 	}
130 129
 
131
-	s, err := boltdb.New(addr, config)
130
+	s, err := boltdb.New(path.Join(dir, "local-kv.db"), bucket)
132 131
 	if err != nil {
133 132
 		return nil, err
134 133
 	}
... ...
@@ -136,15 +93,6 @@ func newClient(kv string, addr string, config *store.Config) (*Store, error) {
136 136
 	return &Store{store: s, cache: newCache(s)}, nil
137 137
 }
138 138
 
139
-// New creates a new Store instance.
140
-func New(cfg ScopeCfg) (*Store, error) {
141
-	if cfg.Client.Provider == "" || cfg.Client.Address == "" {
142
-		cfg = DefaultScope("")
143
-	}
144
-
145
-	return newClient(cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config)
146
-}
147
-
148 139
 // Close closes the data store.
149 140
 func (ds *Store) Close() {
150 141
 	ds.store.Close()
... ...
@@ -23,16 +23,6 @@ func TestKey(t *testing.T) {
23 23
 	assert.Check(t, is.Equal(sKey, expected))
24 24
 }
25 25
 
26
-func TestInvalidDataStore(t *testing.T) {
27
-	_, err := New(ScopeCfg{
28
-		Client: ScopeClientCfg{
29
-			Provider: "invalid",
30
-			Address:  "localhost:8500",
31
-		},
32
-	})
33
-	assert.Check(t, is.Error(err, "unsupported KV store"))
34
-}
35
-
36 26
 func TestKVObjectFlatKey(t *testing.T) {
37 27
 	store := NewTestDataStore()
38 28
 	expected := dummyKVObject("1000", true)
... ...
@@ -3,19 +3,16 @@ package boltdb
3 3
 import (
4 4
 	"bytes"
5 5
 	"encoding/binary"
6
-	"errors"
7 6
 	"os"
8 7
 	"path/filepath"
9 8
 	"sync"
10 9
 	"sync/atomic"
10
+	"time"
11 11
 
12 12
 	store "github.com/docker/docker/libnetwork/internal/kvstore"
13 13
 	bolt "go.etcd.io/bbolt"
14 14
 )
15 15
 
16
-// ErrBoltBucketOptionMissing is thrown when boltBucket config option is missing
17
-var ErrBoltBucketOptionMissing = errors.New("boltBucket config option missing")
18
-
19 16
 const filePerm = 0o644
20 17
 
21 18
 // BoltDB type implements the Store interface
... ...
@@ -30,18 +27,14 @@ type BoltDB struct {
30 30
 const libkvmetadatalen = 8
31 31
 
32 32
 // New opens a new BoltDB connection to the specified path and bucket
33
-func New(endpoint string, options *store.Config) (store.Store, error) {
34
-	if (options == nil) || (len(options.Bucket) == 0) {
35
-		return nil, ErrBoltBucketOptionMissing
36
-	}
37
-
38
-	dir, _ := filepath.Split(endpoint)
33
+func New(path, bucket string) (store.Store, error) {
34
+	dir, _ := filepath.Split(path)
39 35
 	if err := os.MkdirAll(dir, 0o750); err != nil {
40 36
 		return nil, err
41 37
 	}
42 38
 
43
-	db, err := bolt.Open(endpoint, filePerm, &bolt.Options{
44
-		Timeout: options.ConnectionTimeout,
39
+	db, err := bolt.Open(path, filePerm, &bolt.Options{
40
+		Timeout: time.Minute,
45 41
 	})
46 42
 	if err != nil {
47 43
 		return nil, err
... ...
@@ -49,8 +42,8 @@ func New(endpoint string, options *store.Config) (store.Store, error) {
49 49
 
50 50
 	b := &BoltDB{
51 51
 		client:     db,
52
-		path:       endpoint,
53
-		boltBucket: []byte(options.Bucket),
52
+		path:       path,
53
+		boltBucket: []byte(bucket),
54 54
 	}
55 55
 
56 56
 	return b, nil
... ...
@@ -2,7 +2,6 @@ package kvstore
2 2
 
3 3
 import (
4 4
 	"errors"
5
-	"time"
6 5
 )
7 6
 
8 7
 // Backend represents a KV Store Backend
... ...
@@ -24,12 +23,6 @@ var (
24 24
 	ErrKeyExists = errors.New("Previous K/V pair exists, cannot complete Atomic operation")
25 25
 )
26 26
 
27
-// Config contains the options for a storage client
28
-type Config struct {
29
-	ConnectionTimeout time.Duration
30
-	Bucket            string
31
-}
32
-
33 27
 // Store represents the backend K/V storage
34 28
 // Each store should support every call listed
35 29
 // here. Or it couldn't be implemented as a K/V
... ...
@@ -21,7 +21,6 @@ import (
21 21
 	"github.com/docker/docker/internal/testutils/netnsutils"
22 22
 	"github.com/docker/docker/libnetwork"
23 23
 	"github.com/docker/docker/libnetwork/config"
24
-	"github.com/docker/docker/libnetwork/datastore"
25 24
 	"github.com/docker/docker/libnetwork/driverapi"
26 25
 	"github.com/docker/docker/libnetwork/ipams/defaultipam"
27 26
 	"github.com/docker/docker/libnetwork/ipams/null"
... ...
@@ -45,7 +44,7 @@ const (
45 45
 
46 46
 func TestMain(m *testing.M) {
47 47
 	// Cleanup local datastore file
48
-	_ = os.Remove(datastore.DefaultScope("").Client.Address)
48
+	_ = os.Remove("/var/lib/docker/network/files/local-kv.db")
49 49
 
50 50
 	os.Exit(m.Run())
51 51
 }
... ...
@@ -12,9 +12,7 @@ import (
12 12
 
13 13
 func TestBoltdbBackend(t *testing.T) {
14 14
 	tmpPath := filepath.Join(t.TempDir(), "boltdb.db")
15
-	testLocalBackend(t, "boltdb", tmpPath, &store.Config{
16
-		Bucket: "testBackend",
17
-	})
15
+	testLocalBackend(t, tmpPath, "testBackend")
18 16
 }
19 17
 
20 18
 func TestNoPersist(t *testing.T) {
... ...
@@ -5,21 +5,18 @@ import (
5 5
 	"testing"
6 6
 
7 7
 	"github.com/docker/docker/libnetwork/config"
8
-	store "github.com/docker/docker/libnetwork/internal/kvstore"
9 8
 	"github.com/docker/docker/libnetwork/netlabel"
10 9
 	"github.com/docker/docker/libnetwork/options"
11 10
 )
12 11
 
13
-func testLocalBackend(t *testing.T, provider, url string, storeConfig *store.Config) {
14
-	cfgOptions := []config.Option{func(c *config.Config) {
15
-		c.Scope.Client.Provider = provider
16
-		c.Scope.Client.Address = url
17
-		c.Scope.Client.Config = storeConfig
18
-	}}
19
-
20
-	cfgOptions = append(cfgOptions, config.OptionDriverConfig("host", map[string]interface{}{
21
-		netlabel.GenericData: options.Generic{},
22
-	}))
12
+func testLocalBackend(t *testing.T, path, bucket string) {
13
+	cfgOptions := []config.Option{
14
+		config.OptionDataDir(path),
15
+		func(c *config.Config) { c.DatastoreBucket = bucket },
16
+		config.OptionDriverConfig("host", map[string]interface{}{
17
+			netlabel.GenericData: options.Generic{},
18
+		}),
19
+	}
23 20
 
24 21
 	testController, err := New(cfgOptions...)
25 22
 	if err != nil {