From c1a69db2c1bfc6d9064ab948f987e65be3529c68 Mon Sep 17 00:00:00 2001 From: amecea Date: Mon, 11 Mar 2019 18:18:30 +0200 Subject: [PATCH 1/2] Fix how node role is determined. --- pkg/sidecar/configs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/sidecar/configs.go b/pkg/sidecar/configs.go index c3db1b938..c074062b7 100644 --- a/pkg/sidecar/configs.go +++ b/pkg/sidecar/configs.go @@ -117,7 +117,7 @@ func (cfg *Config) MasterFQDN() string { // NodeRole returns the role of the current node func (cfg *Config) NodeRole() NodeRole { - if cfg.Hostname == cfg.MasterFQDN() { + if cfg.FQDNForServer(cfg.ServerID) == cfg.MasterFQDN() { return MasterNode } @@ -154,7 +154,7 @@ func NewConfig() *Config { var err error if cfg.MysqlDSN, err = getMySQLConnectionString(); err != nil { - log.Error(err, "get mysql dsn") + log.Info("failed to get mysql DSN string", "error", err) } return cfg From 6f2354dc3807b00554db8e39bea2ed2eb23a86d1 Mon Sep 17 00:00:00 2001 From: amecea Date: Mon, 11 Mar 2019 19:20:49 +0200 Subject: [PATCH 2/2] Add sidecar tests --- .gitignore | 3 ++ pkg/sidecar/appclone.go | 4 +-- pkg/sidecar/appconf.go | 4 +-- pkg/sidecar/apphelper.go | 32 +++++++++++++---- pkg/sidecar/configs.go | 43 +++++++++++------------ pkg/sidecar/configs_test.go | 57 +++++++++++++++++++++++++++++++ pkg/sidecar/constants.go | 3 ++ pkg/sidecar/sidecar_suite_test.go | 33 ++++++++++++++++++ pkg/sidecar/util.go | 11 ++++-- 9 files changed, 155 insertions(+), 35 deletions(-) create mode 100644 pkg/sidecar/configs_test.go create mode 100644 pkg/sidecar/sidecar_suite_test.go diff --git a/.gitignore b/.gitignore index ad9c2a373..c283f2d07 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,6 @@ hack/docker/mysql-operator/mysql-operator # ignore vscode .vscode + +# goland +.idea diff --git a/pkg/sidecar/appclone.go b/pkg/sidecar/appclone.go index 1831e9191..ef2c85e1a 100644 --- a/pkg/sidecar/appclone.go +++ b/pkg/sidecar/appclone.go @@ -55,8 +55,8 @@ func RunCloneCommand(cfg *Config) error { } } else { // clonging from prior node - if cfg.ServerID > 100 { - sourceHost := cfg.FQDNForServer(cfg.ServerID - 1) + if cfg.ServerID() > 100 { + sourceHost := cfg.FQDNForServer(cfg.ServerID() - 1) err := cloneFromSource(cfg, sourceHost) if err != nil { return fmt.Errorf("failed to clone from %s, err: %s", sourceHost, err) diff --git a/pkg/sidecar/appconf.go b/pkg/sidecar/appconf.go index 9216b3d88..ccadcac13 100644 --- a/pkg/sidecar/appconf.go +++ b/pkg/sidecar/appconf.go @@ -47,12 +47,12 @@ func RunConfigCommand(cfg *Config) error { } uPass := pkgutil.RandomString(rStrLen) - reportHost := cfg.FQDNForServer(cfg.ServerID) + reportHost := cfg.FQDNForServer(cfg.ServerID()) var identityCFG, utilityCFG, clientCFG *ini.File // mysql server identity configs - if identityCFG, err = getIdentityConfigs(cfg.ServerID, reportHost); err != nil { + if identityCFG, err = getIdentityConfigs(cfg.ServerID(), reportHost); err != nil { return fmt.Errorf("failed to get dynamic configs: %s", err) } if err = identityCFG.SaveTo(path.Join(confDPath, "10-identity.cnf")); err != nil { diff --git a/pkg/sidecar/apphelper.go b/pkg/sidecar/apphelper.go index 028fd0724..2e6c1f8fc 100644 --- a/pkg/sidecar/apphelper.go +++ b/pkg/sidecar/apphelper.go @@ -91,7 +91,11 @@ func RunSidecarCommand(cfg *Config, stop <-chan struct{}) error { func configureOrchestratorUser(cfg *Config) error { query := ` SET @@SESSION.SQL_LOG_BIN = 0; - GRANT SUPER, PROCESS, REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO ?@'%%' IDENTIFIED BY ?; + + CREATE USER IF NOT EXISTS ?@'%%'; + ALTER USER ?@'%%' IDENTIFIED BY ?; + + GRANT SUPER, PROCESS, REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO ?@'%%'; GRANT SELECT ON %s.* TO ?@'%%'; GRANT SELECT ON mysql.slave_master_info TO ?@'%%'; ` @@ -101,8 +105,9 @@ func configureOrchestratorUser(cfg *Config) error { // https://github.com/golang/go/issues/18478 query = fmt.Sprintf(query, toolsDbName) - if err := runQuery(cfg, query, cfg.OrchestratorUser, cfg.OrchestratorPassword, - cfg.OrchestratorUser, cfg.OrchestratorPassword); err != nil { + user := cfg.OrchestratorUser + pass := cfg.OrchestratorPassword + if err := runQuery(cfg, query, user, user, pass, user, user, user); err != nil { return fmt.Errorf("failed to configure orchestrator (user/pass/access), err: %s", err) } @@ -112,9 +117,15 @@ func configureOrchestratorUser(cfg *Config) error { func configureReplicationUser(cfg *Config) error { query := ` SET @@SESSION.SQL_LOG_BIN = 0; - GRANT SELECT, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO ?@'%' IDENTIFIED BY ?; + + CREATE USER IF NOT EXISTS ?@'%'; + ALTER USER ?@'%' IDENTIFIED BY ?; + + GRANT SELECT, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO ?@'%'; ` - if err := runQuery(cfg, query, cfg.ReplicationUser, cfg.ReplicationPassword); err != nil { + user := cfg.ReplicationUser + pass := cfg.ReplicationPassword + if err := runQuery(cfg, query, user, user, pass, user); err != nil { return fmt.Errorf("failed to configure replication user: %s", err) } @@ -124,9 +135,16 @@ func configureReplicationUser(cfg *Config) error { func configureExporterUser(cfg *Config) error { query := ` SET @@SESSION.SQL_LOG_BIN = 0; - GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO ?@'127.0.0.1' IDENTIFIED BY ? WITH MAX_USER_CONNECTIONS 3; + + CREATE USER IF NOT EXISTS ?@'localhost'; + ALTER USER ?@'localhost' IDENTIFIED BY ? WITH MAX_USER_CONNECTIONS 3; + + GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO ?@'localhost'; ` - if err := runQuery(cfg, query, cfg.MetricsUser, cfg.MetricsPassword); err != nil { + + user := cfg.MetricsUser + pass := cfg.MetricsPassword + if err := runQuery(cfg, query, user, user, pass, user); err != nil { return fmt.Errorf("failed to metrics exporter user: %s", err) } diff --git a/pkg/sidecar/configs.go b/pkg/sidecar/configs.go index c074062b7..33a33a164 100644 --- a/pkg/sidecar/configs.go +++ b/pkg/sidecar/configs.go @@ -52,9 +52,6 @@ type Config struct { // ServiceName is the name of the headless service ServiceName string - // ServerID represents the MySQL server id - ServerID int - // InitBucketURL represents the init bucket to initialize mysql InitBucketURL string @@ -65,14 +62,11 @@ type Config struct { BackupUser string BackupPassword string - // MysqlDSN represents the connection string to connect to MySQL - MysqlDSN string - // replication user and password ReplicationUser string ReplicationPassword string - // metrcis exporter user and password + // metrics exporter user and password MetricsUser string MetricsPassword string @@ -84,7 +78,7 @@ type Config struct { // FQDNForServer returns the pod hostname for given MySQL server id func (cfg *Config) FQDNForServer(id int) string { base := mysqlcluster.GetNameForResource(mysqlcluster.StatefulSet, cfg.ClusterName) - return fmt.Sprintf("%s-%d.%s.%s", base, id-100, cfg.ServiceName, cfg.Namespace) + return fmt.Sprintf("%s-%d.%s.%s", base, id-MysqlServerIDOffset, cfg.ServiceName, cfg.Namespace) } func (cfg *Config) newOrcClient() orc.Interface { @@ -110,20 +104,36 @@ func (cfg *Config) MasterFQDN() string { } log.V(-1).Info("failed to obtain master from orchestrator, go for default master", - "master", cfg.FQDNForServer(100)) - return cfg.FQDNForServer(100) + "master", cfg.FQDNForServer(MysqlServerIDOffset)) + return cfg.FQDNForServer(MysqlServerIDOffset) } // NodeRole returns the role of the current node func (cfg *Config) NodeRole() NodeRole { - if cfg.FQDNForServer(cfg.ServerID) == cfg.MasterFQDN() { + if cfg.FQDNForServer(cfg.ServerID()) == cfg.MasterFQDN() { return MasterNode } return SlaveNode } +// ServerID returns the MySQL server id +func (cfg *Config) ServerID() int { + ordinal := getOrdinalFromHostname(cfg.Hostname) + return ordinal + MysqlServerIDOffset +} + +// MysqlDSN returns the connection string to MySQL server +func (cfg *Config) MysqlDSN() string { + var dsn string + var err error + if dsn, err = getMySQLConnectionString(); err != nil { + log.Info("failed to get mysql DSN string", "error", err) + } + return dsn +} + // NewConfig returns a pointer to Config configured from environment variables func NewConfig() *Config { cfg := &Config{ @@ -148,22 +158,13 @@ func NewConfig() *Config { OrchestratorPassword: getEnvValue("MYSQL_ORC_TOPOLOGY_PASSWORD"), } - // get server id - ordinal := getOrdinalFromHostname(cfg.Hostname) - cfg.ServerID = ordinal + 100 - - var err error - if cfg.MysqlDSN, err = getMySQLConnectionString(); err != nil { - log.Info("failed to get mysql DSN string", "error", err) - } - return cfg } func getEnvValue(key string) string { value := os.Getenv(key) if len(value) == 0 { - log.Info("envirorment is not set", "key", key) + log.Info("environment is not set", "key", key) } return value diff --git a/pkg/sidecar/configs_test.go b/pkg/sidecar/configs_test.go new file mode 100644 index 000000000..a3cfe6fbe --- /dev/null +++ b/pkg/sidecar/configs_test.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 Pressinfra SRL + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecar + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Test sidecar configs", func() { + var ( + cfg *Config + ) + + BeforeEach(func() { + cfg = &Config{ + Hostname: "cluster-mysql-0", + ClusterName: "cluster", + Namespace: "default", + ServiceName: "cluster-mysql-nodes", + BackupUser: "backup-user", + BackupPassword: "backup-password", + } + }) + + It("should fill the server id", func() { + Expect(cfg.ServerID()).To(Equal(MysqlServerIDOffset)) + + cfg.Hostname = "cluster-mysql-3" + Expect(cfg.ServerID()).To(Equal(MysqlServerIDOffset + 3)) + }) + + It("should get the default master", func() { + Expect(cfg.MasterFQDN()).To(Equal("cluster-mysql-0.cluster-mysql-nodes.default")) + }) + + It("should determine the node role", func() { + Expect(cfg.NodeRole()).To(Equal(MasterNode)) + + cfg.Hostname = "cluster-mysql-2" + Expect(cfg.NodeRole()).To(Equal(SlaveNode)) + }) +}) diff --git a/pkg/sidecar/constants.go b/pkg/sidecar/constants.go index 442d9d87c..059a7312c 100644 --- a/pkg/sidecar/constants.go +++ b/pkg/sidecar/constants.go @@ -26,6 +26,9 @@ import ( ) var ( + // MysqlServerIDOffset represents the offset with which all server ids are shifted from 0 + MysqlServerIDOffset = 100 + // MysqlPort represents port on which mysql works mysqlPort = strconv.Itoa(constants.MysqlPort) diff --git a/pkg/sidecar/sidecar_suite_test.go b/pkg/sidecar/sidecar_suite_test.go new file mode 100644 index 000000000..b16be3281 --- /dev/null +++ b/pkg/sidecar/sidecar_suite_test.go @@ -0,0 +1,33 @@ +/* +Copyright 2019 Pressinfra SRL + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecar + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + logf "github.com/presslabs/controller-util/log" +) + +func TestSidecarApp(t *testing.T) { + logf.SetLogger(logf.ZapLoggerTo(GinkgoWriter, true)) + + RegisterFailHandler(Fail) + RunSpecs(t, "Sidecar App Suite") +} diff --git a/pkg/sidecar/util.go b/pkg/sidecar/util.go index 7c32f1bcf..49393d39d 100644 --- a/pkg/sidecar/util.go +++ b/pkg/sidecar/util.go @@ -32,17 +32,22 @@ var log = logf.Log.WithName("sidecar") // runQuery executes a query func runQuery(cfg *Config, q string, args ...interface{}) error { - if len(cfg.MysqlDSN) == 0 { + if len(cfg.MysqlDSN()) == -1 { log.Info("could not get mysql connection DSN") return fmt.Errorf("no DSN specified") } - db, err := sql.Open("mysql", cfg.MysqlDSN) + db, err := sql.Open("mysql", cfg.MysqlDSN()) if err != nil { return err } + defer func() { + if cErr := db.Close(); cErr != nil { + log.Error(cErr, "failed closing the database connection") + } + }() - log.V(4).Info("running query", "query", q, "args", args) + log.V(1).Info("running query", "query", q, "args", args) if _, err := db.Exec(q, args...); err != nil { return err }