374 lines
9.8 KiB
Go
374 lines
9.8 KiB
Go
// Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
|
|
|
|
package task
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"testing"
|
|
|
|
backup "github.com/pingcap/kvproto/pkg/brpb"
|
|
"github.com/pingcap/kvproto/pkg/encryptionpb"
|
|
kvconfig "github.com/pingcap/tidb/br/pkg/config"
|
|
"github.com/pingcap/tidb/br/pkg/storage"
|
|
"github.com/pingcap/tidb/br/pkg/utils"
|
|
"github.com/pingcap/tidb/pkg/config"
|
|
filter "github.com/pingcap/tidb/pkg/util/table-filter"
|
|
"github.com/spf13/pflag"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeValue string
|
|
|
|
func (f fakeValue) String() string {
|
|
return string(f)
|
|
}
|
|
|
|
func (f fakeValue) Set(string) error {
|
|
panic("implement me")
|
|
}
|
|
|
|
func (f fakeValue) Type() string {
|
|
panic("implement me")
|
|
}
|
|
|
|
func TestUrlNoQuery(t *testing.T) {
|
|
flag := &pflag.Flag{
|
|
Name: flagStorage,
|
|
Value: fakeValue("s3://some/what?secret=a123456789&key=987654321"),
|
|
}
|
|
field := flagToZapField(flag)
|
|
require.Equal(t, flagStorage, field.Key)
|
|
require.Equal(t, "s3://some/what", field.Interface.(fmt.Stringer).String())
|
|
}
|
|
|
|
func TestTiDBConfigUnchanged(t *testing.T) {
|
|
cfg := config.GetGlobalConfig()
|
|
restoreConfig := enableTiDBConfig()
|
|
require.NotEqual(t, config.GetGlobalConfig(), cfg)
|
|
restoreConfig()
|
|
require.Equal(t, config.GetGlobalConfig(), cfg)
|
|
}
|
|
|
|
func TestStripingPDURL(t *testing.T) {
|
|
nor1, err := normalizePDURL("https://pd:5432", true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "pd:5432", nor1)
|
|
_, err = normalizePDURL("https://pd.pingcap.com", false)
|
|
require.Error(t, err)
|
|
require.Regexp(t, ".*pd url starts with https while TLS disabled.*", err.Error())
|
|
_, err = normalizePDURL("http://127.0.0.1:2379", true)
|
|
require.Error(t, err)
|
|
require.Regexp(t, ".*pd url starts with http while TLS enabled.*", err.Error())
|
|
nor, err := normalizePDURL("http://127.0.0.1", false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "127.0.0.1", nor)
|
|
noChange, err := normalizePDURL("127.0.0.1:2379", false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "127.0.0.1:2379", noChange)
|
|
}
|
|
|
|
func TestCheckCipherKeyMatch(t *testing.T) {
|
|
cases := []struct {
|
|
CipherType encryptionpb.EncryptionMethod
|
|
CipherKey string
|
|
ok bool
|
|
}{
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_PLAINTEXT,
|
|
ok: true,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_UNKNOWN,
|
|
ok: false,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES128_CTR,
|
|
CipherKey: "0123456789abcdef0123456789abcdef",
|
|
ok: true,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES128_CTR,
|
|
CipherKey: "0123456789abcdef0123456789abcd",
|
|
ok: false,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES192_CTR,
|
|
CipherKey: "0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
ok: true,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES192_CTR,
|
|
CipherKey: "0123456789abcdef0123456789abcdef0123456789abcdefff",
|
|
ok: false,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES256_CTR,
|
|
CipherKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
ok: true,
|
|
},
|
|
{
|
|
CipherType: encryptionpb.EncryptionMethod_AES256_CTR,
|
|
CipherKey: "",
|
|
ok: false,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
cipherKey, err := hex.DecodeString(c.CipherKey)
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.ok, checkCipherKeyMatch(&backup.CipherInfo{
|
|
CipherType: c.CipherType,
|
|
CipherKey: cipherKey,
|
|
}))
|
|
}
|
|
}
|
|
|
|
func TestCheckCipherKey(t *testing.T) {
|
|
cases := []struct {
|
|
cipherKey string
|
|
keyFile string
|
|
ok bool
|
|
}{
|
|
{
|
|
cipherKey: "0123456789abcdef0123456789abcdef",
|
|
keyFile: "",
|
|
ok: true,
|
|
},
|
|
{
|
|
cipherKey: "0123456789abcdef0123456789abcdef",
|
|
keyFile: "/tmp/abc",
|
|
ok: false,
|
|
},
|
|
{
|
|
cipherKey: "",
|
|
keyFile: "/tmp/abc",
|
|
ok: true,
|
|
},
|
|
{
|
|
cipherKey: "",
|
|
keyFile: "",
|
|
ok: false,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
err := checkCipherKey(c.cipherKey, c.keyFile)
|
|
if c.ok {
|
|
require.NoError(t, err)
|
|
} else {
|
|
require.Error(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func must[T any](t T, err error) T {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func expectedDefaultConfig() Config {
|
|
return Config{
|
|
BackendOptions: storage.BackendOptions{S3: storage.S3BackendOptions{ForcePathStyle: true}},
|
|
PD: []string{"127.0.0.1:2379"},
|
|
ChecksumConcurrency: 4,
|
|
Checksum: true,
|
|
SendCreds: true,
|
|
CheckRequirements: true,
|
|
FilterStr: []string(nil),
|
|
TableFilter: filter.CaseInsensitive(must(filter.Parse([]string{"*.*"}))),
|
|
Schemas: map[string]struct{}{},
|
|
Tables: map[string]struct{}{},
|
|
SwitchModeInterval: 300000000000,
|
|
GRPCKeepaliveTime: 10000000000,
|
|
GRPCKeepaliveTimeout: 3000000000,
|
|
CipherInfo: backup.CipherInfo{CipherType: 1},
|
|
LogBackupCipherInfo: backup.CipherInfo{CipherType: 1},
|
|
MetadataDownloadBatchSize: 0x80,
|
|
}
|
|
}
|
|
|
|
func expectedDefaultBackupConfig() BackupConfig {
|
|
return BackupConfig{
|
|
Config: expectedDefaultConfig(),
|
|
GCTTL: utils.DefaultBRGCSafePointTTL,
|
|
CompressionConfig: CompressionConfig{
|
|
CompressionType: backup.CompressionType_ZSTD,
|
|
},
|
|
IgnoreStats: true,
|
|
UseBackupMetaV2: true,
|
|
UseCheckpoint: true,
|
|
}
|
|
}
|
|
|
|
func expectedDefaultRestoreConfig() RestoreConfig {
|
|
defaultConfig := expectedDefaultConfig()
|
|
defaultConfig.Concurrency = defaultRestoreConcurrency
|
|
return RestoreConfig{
|
|
Config: defaultConfig,
|
|
RestoreCommonConfig: RestoreCommonConfig{Online: false,
|
|
Granularity: "fine-grained",
|
|
MergeSmallRegionSizeBytes: kvconfig.ConfigTerm[uint64]{Value: 0x6000000},
|
|
MergeSmallRegionKeyCount: kvconfig.ConfigTerm[uint64]{Value: 0xea600},
|
|
WithSysTable: true,
|
|
ResetSysUsers: []string{"cloud_admin", "root"}},
|
|
NoSchema: false,
|
|
LoadStats: true,
|
|
PDConcurrency: 0x1,
|
|
StatsConcurrency: 0xc,
|
|
BatchFlushInterval: 16000000000,
|
|
DdlBatchSize: 0x80,
|
|
WithPlacementPolicy: "STRICT",
|
|
UseCheckpoint: true,
|
|
}
|
|
}
|
|
|
|
func TestDefault(t *testing.T) {
|
|
def := DefaultConfig()
|
|
defaultConfig := expectedDefaultConfig()
|
|
require.Equal(t, defaultConfig, def)
|
|
}
|
|
|
|
func TestDefaultBackup(t *testing.T) {
|
|
def := DefaultBackupConfig()
|
|
defaultConfig := expectedDefaultBackupConfig()
|
|
require.Equal(t, defaultConfig, def)
|
|
}
|
|
|
|
func TestDefaultRestore(t *testing.T) {
|
|
def := DefaultRestoreConfig()
|
|
defaultConfig := expectedDefaultRestoreConfig()
|
|
require.Equal(t, defaultConfig, def)
|
|
}
|
|
|
|
func TestParseAndValidateMasterKeyInfo(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expectedKeys []*encryptionpb.MasterKey
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Empty input",
|
|
input: "",
|
|
expectedKeys: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Single local config",
|
|
input: "local:///path/to/key",
|
|
expectedKeys: []*encryptionpb.MasterKey{
|
|
{
|
|
Backend: &encryptionpb.MasterKey_File{
|
|
File: &encryptionpb.MasterKeyFile{Path: "/path/to/key"},
|
|
},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Single AWS config",
|
|
input: "aws-kms:///key-id?AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE&AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY®ION=us-west-2",
|
|
expectedKeys: []*encryptionpb.MasterKey{
|
|
{
|
|
Backend: &encryptionpb.MasterKey_Kms{
|
|
Kms: &encryptionpb.MasterKeyKms{
|
|
Vendor: "aws",
|
|
KeyId: "key-id",
|
|
Region: "us-west-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Single Azure config",
|
|
input: "azure-kms:///key-name/key-version?AZURE_TENANT_ID=tenant-id&AZURE_CLIENT_ID=client-id&AZURE_CLIENT_SECRET=client-secret&AZURE_VAULT_NAME=vault-name",
|
|
expectedKeys: []*encryptionpb.MasterKey{
|
|
{
|
|
Backend: &encryptionpb.MasterKey_Kms{
|
|
Kms: &encryptionpb.MasterKeyKms{
|
|
Vendor: "azure",
|
|
KeyId: "key-name/key-version",
|
|
AzureKms: &encryptionpb.AzureKms{
|
|
TenantId: "tenant-id",
|
|
ClientId: "client-id",
|
|
ClientSecret: "client-secret",
|
|
KeyVaultUrl: "vault-name",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Single GCP config",
|
|
input: "gcp-kms:///projects/project-id/locations/global/keyRings/ring-name/cryptoKeys/key-name?CREDENTIALS=credentials",
|
|
expectedKeys: []*encryptionpb.MasterKey{
|
|
{
|
|
Backend: &encryptionpb.MasterKey_Kms{
|
|
Kms: &encryptionpb.MasterKeyKms{
|
|
Vendor: "gcp",
|
|
KeyId: "projects/project-id/locations/global/keyRings/ring-name/cryptoKeys/key-name",
|
|
GcpKms: &encryptionpb.GcpKms{
|
|
Credential: "credentials",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Multiple configs",
|
|
input: "local:///path/to/key," +
|
|
"aws-kms:///key-id?AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE&AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY®ION=us-west-2",
|
|
expectedKeys: []*encryptionpb.MasterKey{
|
|
{
|
|
Backend: &encryptionpb.MasterKey_File{
|
|
File: &encryptionpb.MasterKeyFile{Path: "/path/to/key"},
|
|
},
|
|
},
|
|
{
|
|
Backend: &encryptionpb.MasterKey_Kms{
|
|
Kms: &encryptionpb.MasterKeyKms{
|
|
Vendor: "aws",
|
|
KeyId: "key-id",
|
|
Region: "us-west-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Invalid config",
|
|
input: "invalid:///config",
|
|
expectedKeys: nil,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
cfg := &Config{}
|
|
flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
|
flags.String(flagMasterKeyConfig, tt.input, "")
|
|
flags.String(flagMasterKeyCipherType, "aes256-ctr", "")
|
|
|
|
err := cfg.parseAndValidateMasterKeyInfo(false, flags)
|
|
|
|
if tt.expectError {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expectedKeys, cfg.MasterKeyConfig.MasterKeys)
|
|
}
|
|
})
|
|
}
|
|
}
|