Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't exit tablet server on reloading invalid ACL #17485

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
5 changes: 4 additions & 1 deletion go/cmd/vttablet/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ func createTabletServer(ctx context.Context, env *vtenv.Environment, config *tab
addStatusParts(qsc)
})
servenv.OnClose(qsc.StopService)
qsc.InitACL(tableACLConfig, enforceTableACLConfig, tableACLConfigReloadInterval)
err := qsc.InitACL(tableACLConfig, tableACLConfigReloadInterval)
if err != nil && enforceTableACLConfig {
return nil, fmt.Errorf("failed to initialize table acl: %w", err)
}
mattlord marked this conversation as resolved.
Show resolved Hide resolved
return qsc, nil
}

Expand Down
4 changes: 2 additions & 2 deletions go/vt/tableacl/tableacl.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ func (tacl *tableACL) init(configFile string, aclCB func()) error {
}
data, err := os.ReadFile(configFile)
if err != nil {
log.Infof("unable to read tableACL config file: %v Error: %v", configFile, err)
log.Errorf("unable to read tableACL config file: %v Error: %v", configFile, err)
return err
}
config := &tableaclpb.Config{}
if err := config.UnmarshalVT(data); err != nil {
// try to parse tableacl as json file
if jsonErr := json2.UnmarshalPB(data, config); jsonErr != nil {
log.Infof("unable to parse tableACL config file as a protobuf or json file. protobuf err: %v json err: %v", err, jsonErr)
log.Errorf("unable to parse tableACL config file as a protobuf or json file. protobuf err: %v json err: %v", err, jsonErr)
return fmt.Errorf("unable to unmarshal Table ACL data: %s", data)
}
}
Expand Down
24 changes: 13 additions & 11 deletions go/vt/vttablet/tabletserver/tabletserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,31 +359,32 @@ func (tsv *TabletServer) SetQueryRules(ruleSource string, qrs *rules.Rules) erro
return nil
}

func (tsv *TabletServer) initACL(tableACLConfigFile string, enforceTableACLConfig bool) {
func (tsv *TabletServer) initACL(tableACLConfigFile string) error {
// tabletacl.Init loads ACL from file if *tableACLConfig is not empty
err := tableacl.Init(
return tableacl.Init(
tableACLConfigFile,
func() {
tsv.ClearQueryPlanCache()
},
)
if err != nil {
log.Errorf("Fail to initialize Table ACL: %v", err)
if enforceTableACLConfig {
log.Exit("Need a valid initial Table ACL when enforce-tableacl-config is set, exiting.")
}
}
}

// InitACL loads the table ACL and sets up a SIGHUP handler for reloading it.
func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfig bool, reloadACLConfigFileInterval time.Duration) {
tsv.initACL(tableACLConfigFile, enforceTableACLConfig)
func (tsv *TabletServer) InitACL(tableACLConfigFile string, reloadACLConfigFileInterval time.Duration) error {
if err := tsv.initACL(tableACLConfigFile); err != nil {
return err
}

sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGHUP)
go func() {
for range sigChan {
tsv.initACL(tableACLConfigFile, enforceTableACLConfig)
err := tsv.initACL(tableACLConfigFile)
if err != nil {
log.Errorf("Error reloading ACL config file %s in SIGHUP handler: %v", tableACLConfigFile, err)
} else {
log.Info("Successfully reloaded ACL file %s in SIGHUP handler", tableACLConfigFile)
}
}
}()

Expand All @@ -395,6 +396,7 @@ func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfi
}
}()
}
return nil
}

// SetServingType changes the serving type of the tabletserver. It starts or
Expand Down
66 changes: 52 additions & 14 deletions go/vt/vttablet/tabletserver/tabletserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2154,6 +2154,16 @@ var aclJSON2 = `{
}
]
}`
var aclJSONOverlapError = `{
"table_groups": [
{
"name": "group02",
"table_names_or_prefixes": ["test_table2", "test_table2"],
"readers": ["vt2"],
"admins": ["vt2"]
}
]
}`

func TestACLHUP(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -2167,12 +2177,23 @@ func TestACLHUP(t *testing.T) {
require.NoError(t, err)
defer os.Remove(f.Name())

// We need to confirm this ACL JSON is broken first, for later use.
_, err = io.WriteString(f, aclJSONOverlapError)
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)
err = tsv.InitACL(f.Name(), 0)
require.Error(t, err)

f, err = os.Create(f.Name())
require.NoError(t, err)
_, err = io.WriteString(f, aclJSON1)
require.NoError(t, err)
err = f.Close()
require.NoError(t, err)

tsv.InitACL(f.Name(), true, 0)
err = tsv.InitACL(f.Name(), 0)
require.NoError(t, err)

groups1 := tableacl.GetCurrentConfig().TableGroups
if name1 := groups1[0].GetName(); name1 != "group01" {
Expand All @@ -2187,20 +2208,37 @@ func TestACLHUP(t *testing.T) {
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
time.Sleep(100 * time.Millisecond) // wait for signal handler

groups2 := tableacl.GetCurrentConfig().TableGroups
if len(groups2) != 1 {
t.Fatalf("Expected only one table group")
}
group2 := groups2[0]
if name2 := group2.GetName(); name2 != "group02" {
t.Fatalf("Expected name 'group02', got '%s'", name2)
}
if group2.GetAdmins() == nil {
t.Fatalf("Expected 'admins' to exist, but it didn't")
}
if group2.GetWriters() != nil {
t.Fatalf("Expected 'writers' to not exist, got '%s'", group2.GetWriters())
test_loaded_acl := func() {
groups2 := tableacl.GetCurrentConfig().TableGroups
if len(groups2) != 1 {
t.Fatalf("Expected only one table group")
}
group2 := groups2[0]
if name2 := group2.GetName(); name2 != "group02" {
t.Fatalf("Expected name 'group02', got '%s'", name2)
}
if group2.GetAdmins() == nil {
t.Fatalf("Expected 'admins' to exist, but it didn't")
}
if group2.GetWriters() != nil {
t.Fatalf("Expected 'writers' to not exist, got '%s'", group2.GetWriters())
}
}

test_loaded_acl()

// Now try to reload an invalid ACL and see if we are still operating with the same ACL config as before.

f, err = os.Create(f.Name())
require.NoError(t, err)
_, err = io.WriteString(f, aclJSONOverlapError)
require.NoError(t, err)

syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
time.Sleep(100 * time.Millisecond) // wait for signal handler

test_loaded_acl()

}

func TestConfigChanges(t *testing.T) {
Expand Down
Loading