Skip to content

Commit bd7e88a

Browse files
committed
storageconfig: move StoreEncryptionSpec to cli
The separate encryption specs are a byproduct of having to use a separate command-line flag. This change moves the related code to the cli package. Epic: none Release note: None
1 parent 287999c commit bd7e88a

File tree

10 files changed

+407
-461
lines changed

10 files changed

+407
-461
lines changed

pkg/base/store_spec.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -333,64 +333,3 @@ func (ssl *StoreSpecList) Set(value string) error {
333333
}
334334
return nil
335335
}
336-
337-
// PopulateWithEncryptionOpts iterates through the EncryptionSpecList and looks
338-
// for matching paths in the StoreSpecList and WAL failover config. Any
339-
// unmatched EncryptionSpec causes an error.
340-
func PopulateWithEncryptionOpts(
341-
storeSpecs StoreSpecList,
342-
walFailoverConfig *storageconfig.WALFailover,
343-
encryptionSpecs storageconfig.EncryptionSpecList,
344-
) error {
345-
for _, es := range encryptionSpecs.Specs {
346-
var storeMatched bool
347-
for i := range storeSpecs.Specs {
348-
if !es.PathMatches(storeSpecs.Specs[i].Path) {
349-
continue
350-
}
351-
352-
// Found a matching path.
353-
if storeSpecs.Specs[i].EncryptionOptions != nil {
354-
return fmt.Errorf("store with path %s already has an encryption setting",
355-
storeSpecs.Specs[i].Path)
356-
}
357-
358-
storeSpecs.Specs[i].EncryptionOptions = &es.Options
359-
storeMatched = true
360-
break
361-
}
362-
pathMatched, err := maybeSetExternalPathEncryption(&walFailoverConfig.Path, es)
363-
if err != nil {
364-
return err
365-
}
366-
prevPathMatched, err := maybeSetExternalPathEncryption(&walFailoverConfig.PrevPath, es)
367-
if err != nil {
368-
return err
369-
}
370-
if !storeMatched && !pathMatched && !prevPathMatched {
371-
return fmt.Errorf("no usage of path %s found for encryption setting: %v", es.Path, es)
372-
}
373-
}
374-
return nil
375-
}
376-
377-
// maybeSetExternalPathEncryption updates an ExternalPath to contain the provided
378-
// encryption options if the path matches. The ExternalPath must not already have
379-
// an encryption setting.
380-
func maybeSetExternalPathEncryption(
381-
externalPath *storageconfig.ExternalPath, es storageconfig.StoreEncryptionSpec,
382-
) (found bool, err error) {
383-
if !externalPath.IsSet() || !es.PathMatches(externalPath.Path) {
384-
return false, nil
385-
}
386-
// NB: The external paths WALFailoverConfig.Path and
387-
// WALFailoverConfig.PrevPath are only ever set in single-store
388-
// configurations. In multi-store with among-stores failover mode, these
389-
// will be empty (so we won't encounter the same path twice).
390-
if externalPath.Encryption != nil {
391-
return false, fmt.Errorf("WAL failover path %s already has an encryption setting",
392-
externalPath.Path)
393-
}
394-
externalPath.Encryption = &es.Options
395-
return true, nil
396-
}

pkg/cli/context.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -720,23 +720,3 @@ var userfileCtx struct {
720720
func setUserfileContextDefaults() {
721721
userfileCtx.recursive = false
722722
}
723-
724-
// GetServerCfgStores provides direct public access to the StoreSpecList inside
725-
// serverCfg. This is used by CCL code to populate some fields.
726-
//
727-
// WARNING: consider very carefully whether you should be using this.
728-
// If you are not writing CCL code that performs command-line flag
729-
// parsing, you probably should not be using this.
730-
func GetServerCfgStores() base.StoreSpecList {
731-
return serverCfg.Stores
732-
}
733-
734-
// GetWALFailoverConfig provides direct public access to the WALFailoverConfig
735-
// inside serverCfg. This is used by CCL code to populate some fields.
736-
//
737-
// WARNING: consider very carefully whether you should be using this.
738-
// If you are not writing CCL code that performs command-line flag
739-
// parsing, you probably should not be using this.
740-
func GetWALFailoverConfig() *storageconfig.WALFailover {
741-
return &serverCfg.StorageConfig.WALFailover
742-
}

pkg/cli/debug_ear.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"github.com/cockroachdb/cockroach/pkg/storage"
2121
"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
2222
"github.com/cockroachdb/cockroach/pkg/storage/fs"
23-
"github.com/cockroachdb/cockroach/pkg/storage/storageconfig"
2423
"github.com/cockroachdb/cockroach/pkg/util/protoutil"
2524
"github.com/cockroachdb/cockroach/pkg/util/stop"
2625
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
@@ -169,7 +168,7 @@ with their env type and encryption settings (if applicable).
169168
// fillEncryptionOptionsForStore fills the EnvConfig fields
170169
// based on the --enterprise-encryption flag value.
171170
func fillEncryptionOptionsForStore(dir string, cfg *fs.EnvConfig) error {
172-
opts, err := storageconfig.EncryptionOptionsForStore(dir, encryptionSpecs)
171+
opts, err := encryptionOptionsForStore(dir, encryptionSpecs)
173172
if err != nil {
174173
return err
175174
}

pkg/cli/ear_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
1919
"github.com/cockroachdb/cockroach/pkg/storage"
2020
"github.com/cockroachdb/cockroach/pkg/storage/fs"
21-
"github.com/cockroachdb/cockroach/pkg/storage/storageconfig"
2221
"github.com/cockroachdb/cockroach/pkg/testutils/datapathutils"
2322
"github.com/cockroachdb/cockroach/pkg/util/envutil"
2423
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
@@ -44,7 +43,7 @@ func TestDecrypt(t *testing.T) {
4443

4544
// Spin up a new encrypted store.
4645
encSpecStr := fmt.Sprintf("path=%s,key=%s,old-key=plain", dir, keyPath)
47-
encSpec, err := storageconfig.NewStoreEncryptionSpec(encSpecStr)
46+
encSpec, err := parseStoreEncryptionSpec(encSpecStr)
4847
require.NoError(t, err)
4948
encOpts := &encSpec.Options
5049

@@ -126,7 +125,7 @@ func TestList(t *testing.T) {
126125

127126
// Spin up a new encrypted store.
128127
encSpecStr := fmt.Sprintf("path=%s,key=%s,old-key=plain", dir, keyPath)
129-
encSpec, err := storageconfig.NewStoreEncryptionSpec(encSpecStr)
128+
encSpec, err := parseStoreEncryptionSpec(encSpecStr)
130129
require.NoError(t, err)
131130
encOpts := &encSpec.Options
132131
env, err := fs.InitEnv(ctx, vfs.Default, dir, fs.EnvConfig{EncryptionOptions: encOpts}, nil /* statsCollector */)

pkg/cli/flags.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ var storeSpecs base.StoreSpecList
6262
var goMemLimit int64
6363
var tenantIDFile string
6464
var localityFile string
65-
var encryptionSpecs storageconfig.EncryptionSpecList
65+
var encryptionSpecs encryptionSpecList
6666

6767
// initPreFlagsDefaults initializes the values of the global variables
6868
// defined above.
@@ -431,7 +431,11 @@ func init() {
431431

432432
// Add a new pre-run command to match encryption specs to store specs.
433433
AddPersistentPreRunE(cmd, func(cmd *cobra.Command, _ []string) error {
434-
return populateStoreSpecsEncryption()
434+
return populateWithEncryptionOpts(
435+
serverCfg.Stores,
436+
&serverCfg.StorageConfig.WALFailover,
437+
encryptionSpecs,
438+
)
435439
})
436440
}
437441

@@ -1485,17 +1489,6 @@ func mtStartSQLFlagsInit(cmd *cobra.Command) error {
14851489
return nil
14861490
}
14871491

1488-
// populateStoreSpecsEncryption is a PreRun hook that matches store encryption
1489-
// specs with the parsed stores and populates some fields in the StoreSpec and
1490-
// WAL failover config.
1491-
func populateStoreSpecsEncryption() error {
1492-
return base.PopulateWithEncryptionOpts(
1493-
GetServerCfgStores(),
1494-
GetWALFailoverConfig(),
1495-
encryptionSpecs,
1496-
)
1497-
}
1498-
14991492
// sizeFlagVal is a pflag.Value wrapper for storageconfig.Size. It can be
15001493
// set to an absolute bytes amount or a percentage.
15011494
type sizeFlagVal struct {

pkg/cli/flags_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/cockroachdb/cockroach/pkg/roachpb"
2828
"github.com/cockroachdb/cockroach/pkg/security/securitytest"
2929
"github.com/cockroachdb/cockroach/pkg/server/status"
30+
"github.com/cockroachdb/cockroach/pkg/storage/storageconfig"
3031
"github.com/cockroachdb/cockroach/pkg/testutils"
3132
"github.com/cockroachdb/cockroach/pkg/testutils/skip"
3233
"github.com/cockroachdb/cockroach/pkg/util"
@@ -1731,3 +1732,134 @@ func TestTenantIDFromFile(t *testing.T) {
17311732
require.Eventually(t, func() bool { return runSuccessfuly.Load() }, 10*time.Second, 10*time.Millisecond)
17321733
})
17331734
}
1735+
1736+
// TestParseEncryptionSpec verifies that the --enterprise-encryption arguments
1737+
// are correctly parsed into storeEncryptionSpecs.
1738+
func TestParseEncryptionSpec(t *testing.T) {
1739+
defer leaktest.AfterTest(t)()
1740+
1741+
absDataPath, err := filepath.Abs("data")
1742+
if err != nil {
1743+
t.Fatal(err)
1744+
}
1745+
1746+
testCases := []struct {
1747+
value string
1748+
expectedErr string
1749+
expected storeEncryptionSpec
1750+
}{
1751+
// path
1752+
{",", "no path specified", storeEncryptionSpec{}},
1753+
{"", "no path specified", storeEncryptionSpec{}},
1754+
{"/mnt/hda1", "field not in the form <key>=<value>: /mnt/hda1", storeEncryptionSpec{}},
1755+
{"path=", "no value specified for path", storeEncryptionSpec{}},
1756+
{"path=~/data", "path cannot start with '~': ~/data", storeEncryptionSpec{}},
1757+
{"path=data,path=data2", "path field was used twice in encryption definition", storeEncryptionSpec{}},
1758+
1759+
// The same logic applies to key and old-key, don't repeat everything.
1760+
{"path=data", "no key specified", storeEncryptionSpec{}},
1761+
{"path=data,key=new.key", "no old-key specified", storeEncryptionSpec{}},
1762+
1763+
// Rotation period.
1764+
{"path=data,key=new.key,old-key=old.key,rotation-period", "field not in the form <key>=<value>: rotation-period", storeEncryptionSpec{}},
1765+
{"path=data,key=new.key,old-key=old.key,rotation-period=", "no value specified for rotation-period", storeEncryptionSpec{}},
1766+
{"path=data,key=new.key,old-key=old.key,rotation-period=1", `could not parse rotation-duration value: 1: time: missing unit in duration "1"`, storeEncryptionSpec{}},
1767+
{"path=data,key=new.key,old-key=old.key,rotation-period=1d", `could not parse rotation-duration value: 1d: time: unknown unit "d" in duration "1d"`, storeEncryptionSpec{}},
1768+
1769+
// Good values. Note that paths get absolutized so we start most of them
1770+
// with / so we can used fixed expected values.
1771+
{
1772+
"path=/data,key=/new.key,old-key=/old.key", "",
1773+
storeEncryptionSpec{Path: "/data",
1774+
Options: storageconfig.EncryptionOptions{
1775+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "/new.key", OldKey: "/old.key"},
1776+
DataKeyRotationPeriod: int64(storageconfig.DefaultRotationPeriod / time.Second),
1777+
},
1778+
},
1779+
},
1780+
{
1781+
"path=/data,key=/new.key,old-key=/old.key,rotation-period=1h", "",
1782+
storeEncryptionSpec{Path: "/data",
1783+
Options: storageconfig.EncryptionOptions{
1784+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "/new.key", OldKey: "/old.key"},
1785+
DataKeyRotationPeriod: int64(time.Hour / time.Second),
1786+
},
1787+
},
1788+
},
1789+
{
1790+
"path=/data,key=plain,old-key=/old.key,rotation-period=1h", "",
1791+
storeEncryptionSpec{Path: "/data",
1792+
Options: storageconfig.EncryptionOptions{
1793+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "plain", OldKey: "/old.key"},
1794+
DataKeyRotationPeriod: int64(time.Hour / time.Second),
1795+
},
1796+
},
1797+
},
1798+
{
1799+
"path=/data,key=/new.key,old-key=plain,rotation-period=1h", "",
1800+
storeEncryptionSpec{Path: "/data",
1801+
Options: storageconfig.EncryptionOptions{
1802+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "/new.key", OldKey: "plain"},
1803+
DataKeyRotationPeriod: int64(time.Hour / time.Second),
1804+
},
1805+
},
1806+
},
1807+
1808+
// One relative path to test absolutization.
1809+
{
1810+
"path=data,key=/new.key,old-key=/old.key", "",
1811+
storeEncryptionSpec{Path: absDataPath,
1812+
Options: storageconfig.EncryptionOptions{
1813+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "/new.key", OldKey: "/old.key"},
1814+
DataKeyRotationPeriod: int64(storageconfig.DefaultRotationPeriod / time.Second),
1815+
},
1816+
},
1817+
},
1818+
1819+
// Special path * is not absolutized.
1820+
{
1821+
"path=*,key=/new.key,old-key=/old.key", "",
1822+
storeEncryptionSpec{Path: "*",
1823+
Options: storageconfig.EncryptionOptions{
1824+
KeyFiles: &storageconfig.EncryptionKeyFiles{CurrentKey: "/new.key", OldKey: "/old.key"},
1825+
DataKeyRotationPeriod: int64(storageconfig.DefaultRotationPeriod / time.Second),
1826+
},
1827+
},
1828+
},
1829+
}
1830+
1831+
for i, testCase := range testCases {
1832+
storeEncryptionSpec, err := parseStoreEncryptionSpec(testCase.value)
1833+
if err != nil {
1834+
if len(testCase.expectedErr) == 0 {
1835+
t.Errorf("%d(%s): no expected error, got %s", i, testCase.value, err)
1836+
}
1837+
if testCase.expectedErr != fmt.Sprint(err) {
1838+
t.Errorf("%d(%s): expected error \"%s\" does not match actual \"%s\"", i, testCase.value,
1839+
testCase.expectedErr, err)
1840+
}
1841+
continue
1842+
}
1843+
if len(testCase.expectedErr) > 0 {
1844+
t.Errorf("%d(%s): expected error %s but there was none", i, testCase.value, testCase.expectedErr)
1845+
continue
1846+
}
1847+
if !reflect.DeepEqual(testCase.expected, storeEncryptionSpec) {
1848+
t.Errorf("%d(%s): actual doesn't match expected\nactual: %+v\nexpected: %+v", i,
1849+
testCase.value, storeEncryptionSpec, testCase.expected)
1850+
}
1851+
1852+
// Now test String() to make sure the result can be parsed.
1853+
storeEncryptionSpecString := storeEncryptionSpec.String()
1854+
storeEncryptionSpec2, err := parseStoreEncryptionSpec(storeEncryptionSpecString)
1855+
if err != nil {
1856+
t.Errorf("%d(%s): error parsing String() result: %s", i, testCase.value, err)
1857+
continue
1858+
}
1859+
// Compare strings to deal with floats not matching exactly.
1860+
if !reflect.DeepEqual(storeEncryptionSpecString, storeEncryptionSpec2.String()) {
1861+
t.Errorf("%d(%s): actual doesn't match expected\nactual: %#+v\nexpected: %#+v", i, testCase.value,
1862+
storeEncryptionSpec, storeEncryptionSpec2)
1863+
}
1864+
}
1865+
}

0 commit comments

Comments
 (0)