Skip to content

Commit

Permalink
Set credentials with the init-file
Browse files Browse the repository at this point in the history
  • Loading branch information
AMecea committed May 10, 2019
1 parent 78921fd commit beb41ce
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 68 deletions.
86 changes: 60 additions & 26 deletions pkg/sidecar/appconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,17 @@ package sidecar

import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"

"github.com/go-ini/ini"

pkgutil "github.com/presslabs/mysql-operator/pkg/util"
)

const (
rStrLen = 18
)

// RunConfigCommand generates my.cnf, client.cnf and 10-dynamic.cnf files.
// nolint: gocyclo
func RunConfigCommand(cfg *Config) error {
log.Info("configuring server", "host", cfg.Hostname, "role", cfg.NodeRole())
var err error
Expand All @@ -46,10 +43,9 @@ func RunConfigCommand(cfg *Config) error {
}
}

uPass := pkgutil.RandomString(rStrLen)
reportHost := cfg.FQDNForServer(cfg.ServerID())

var identityCFG, utilityCFG, clientCFG *ini.File
var identityCFG, initCFG, clientCFG *ini.File

// mysql server identity configs
if identityCFG, err = getIdentityConfigs(cfg.ServerID(), reportHost); err != nil {
Expand All @@ -59,16 +55,21 @@ func RunConfigCommand(cfg *Config) error {
return fmt.Errorf("failed to save configs: %s", err)
}

// make init-file
initFilePath := path.Join(confDPath, "operator-init.sql")
if err = ioutil.WriteFile(initFilePath, initFileQuery(cfg), 0644); err != nil {
return fmt.Errorf("failed to write init-file: %s", err)
}
// mysql server utility user configs
if utilityCFG, err = getUtilityUserConfigs(utilityUser, uPass); err != nil {
return fmt.Errorf("failed to configure utility user: %s", err)
if initCFG, err = getInitFileConfigs(initFilePath); err != nil {
return fmt.Errorf("failed to configure init file: %s", err)
}
if err = utilityCFG.SaveTo(path.Join(confDPath, "10-utility-user.cnf")); err != nil {
return fmt.Errorf("failed to configure utility user: %s", err)
if err = initCFG.SaveTo(path.Join(confDPath, "10-init-file.cnf")); err != nil {
return fmt.Errorf("failed to configure init file: %s", err)
}

// mysql client connect credentials
if clientCFG, err = getClientConfigs(utilityUser, uPass); err != nil {
if clientCFG, err = getClientConfigs(cfg.OperatorUser, cfg.OperatorPassword); err != nil {
return fmt.Errorf("failed to get client configs: %s", err)
}

Expand Down Expand Up @@ -114,25 +115,58 @@ func getIdentityConfigs(id int, reportHost string) (*ini.File, error) {
return cfg, nil
}

func getUtilityUserConfigs(user, pass string) (*ini.File, error) {
func getInitFileConfigs(filePath string) (*ini.File, error) {
cfg := ini.Empty()
mysqld := cfg.Section("mysqld")

if _, err := mysqld.NewKey("utility-user", fmt.Sprintf("%s@%%", user)); err != nil {
return nil, err
}
if _, err := mysqld.NewKey("utility-user-password", pass); err != nil {
if _, err := mysqld.NewKey("init-file", filePath); err != nil {
return nil, err
}
if _, err := mysqld.NewKey("utility-user-schema-access", "mysql"); err != nil {
return nil, err

return cfg, nil
}

func initFileQuery(cfg *Config) []byte {
queries := []string{
"SET @@SESSION.SQL_LOG_BIN = 0;",
}
if _, err := mysqld.NewKey("utility-user-privileges",
"SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,GRANT,ALTER,SHOW DATABASES,SUPER,CREATE USER,"+
"PROCESS,RELOAD,LOCK TABLES,REPLICATION CLIENT,REPLICATION SLAVE",
); err != nil {
return nil, err

queries = append(queries, createUserQuery(cfg.OperatorUser, cfg.OperatorPassword, "%", []string{"SUPER"}, "*.*"))
queries = append(queries, createUserQuery(cfg.OrchestratorUser, cfg.OrchestratorPassword, "%",
[]string{"SUPER", "PROCESS", "REPLICATION SLAVE", "REPLICATION CLIENT", "RELOAD"}, "*.*",
[]string{"SELECT"}, "mysql.slave_master_info"))
queries = append(queries, createUserQuery(cfg.ReplicationUser, cfg.ReplicationPassword, "%",
[]string{"SELECT", "PROCESS", "RELOAD", "LOCK TABLES", "REPLICATION CLIENT", "REPLICATION SLAVE"}, "*.*"))
queries = append(queries, createUserQuery(cfg.MetricsUser, cfg.MetricsPassword, "127.0.0.1",
[]string{"SELECT", "PROCESS", "REPLICATION CLIENT"}, "*.*"))

return []byte(strings.Join(queries, "\n"))
}

func createUserQuery(name, pass, host string, rights ...interface{}) string {
user := fmt.Sprintf("%s@'%s'", name, host)

if len(rights)%2 != 0 {
panic("not a good number of parameters")
}
grants := []string{}
for i := 0; i < len(rights); i += 2 {
var (
right []string
on string
ok bool
)
if right, ok = rights[i].([]string); !ok {
panic("[right] not a good parameter")
}
if on, ok = rights[i+1].(string); !ok {
panic("[on] not a good parameter")
}
grant := fmt.Sprintf("GRANT %s ON %s TO %s;", strings.Join(right, ", "), on, user)
grants = append(grants, grant)
}

return cfg, nil
return fmt.Sprintf("\nCREATE USER IF NOT EXISTS %s;\n"+
"ALTER USER %s IDENTIFIED BY '%s';\n%s",
user, user, pass, strings.Join(grants, "\n"))
}
52 changes: 52 additions & 0 deletions pkg/sidecar/appconf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
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 appconf", func() {
It("should create the right query for users", func() {
Expect(createUserQuery("uName", "uPass", "%")).To(Equal(`
CREATE USER IF NOT EXISTS uName@'%';
ALTER USER uName@'%' IDENTIFIED BY 'uPass';
`))
})

It("should create the right query for users with grants", func() {
Expect(createUserQuery("uName", "uPass", "%", []string{"SELECT", "SUPER"}, "*.*")).To(Equal(`
CREATE USER IF NOT EXISTS uName@'%';
ALTER USER uName@'%' IDENTIFIED BY 'uPass';
GRANT SELECT, SUPER ON *.* TO uName@'%';`))
})

It("should create the right query for users with grants", func() {
Expect(createUserQuery("uName", "uPass", "%", []string{"SELECT"}, "*.*", []string{"SUPER"}, "a.b")).To(Equal(`
CREATE USER IF NOT EXISTS uName@'%';
ALTER USER uName@'%' IDENTIFIED BY 'uPass';
GRANT SELECT ON *.* TO uName@'%';
GRANT SUPER ON a.b TO uName@'%';`))
})

It("should fail if bad parameters passed", func() {
Expect(func() { createUserQuery("a", "b", "c", "d") }).To(Panic())
Expect(func() { createUserQuery("a", "b", "c", "d", "c") }).To(Panic())
Expect(func() { createUserQuery("a", "b", "c", []string{"d"}, 1) }).To(Panic())
})
})
57 changes: 18 additions & 39 deletions pkg/sidecar/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ package sidecar
import (
"fmt"
"os"
"path"
"strconv"
"strings"

"github.com/go-ini/ini"
// add mysql driver
_ "github.com/go-sql-driver/mysql"

Expand Down Expand Up @@ -58,6 +56,10 @@ type Config struct {
// OrchestratorURL is the URL to connect to orchestrator
OrchestratorURL string

// OperatorUser represents the credentials that the operator will use to connect to the mysql
OperatorUser string
OperatorPassword string

// backup user and password for http endpoint
BackupUser string
BackupPassword string
Expand Down Expand Up @@ -126,12 +128,9 @@ func (cfg *Config) ServerID() int {

// 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
return fmt.Sprintf("%s:%s@tcp(127.0.0.1:%s)/?timeout=5s&multiStatements=true&interpolateParams=true",
cfg.OperatorUser, cfg.OperatorPassword, mysqlPort,
)
}

// NewConfig returns a pointer to Config configured from environment variables
Expand All @@ -145,17 +144,20 @@ func NewConfig() *Config {
InitBucketURL: getEnvValue("INIT_BUCKET_URI"),
OrchestratorURL: getEnvValue("ORCHESTRATOR_URI"),

BackupUser: getEnvValue("MYSQL_BACKUP_USER"),
BackupPassword: getEnvValue("MYSQL_BACKUP_PASSWORD"),
OperatorUser: getEnvValue("OPERATOR_USER"),
OperatorPassword: getEnvValue("OPERATOR_PASSWORD"),

ReplicationUser: getEnvValue("MYSQL_REPLICATION_USER"),
ReplicationPassword: getEnvValue("MYSQL_REPLICATION_PASSWORD"),
BackupUser: getEnvValue("BACKUP_USER"),
BackupPassword: getEnvValue("BACKUP_PASSWORD"),

MetricsUser: getEnvValue("MYSQL_METRICS_EXPORTER_USER"),
MetricsPassword: getEnvValue("MYSQL_METRICS_EXPORTER_PASSWORD"),
ReplicationUser: getEnvValue("REPLICATION_USER"),
ReplicationPassword: getEnvValue("REPLICATION_PASSWORD"),

OrchestratorUser: getEnvValue("MYSQL_ORC_TOPOLOGY_USER"),
OrchestratorPassword: getEnvValue("MYSQL_ORC_TOPOLOGY_PASSWORD"),
MetricsUser: getEnvValue("METRICS_EXPORTER_USER"),
MetricsPassword: getEnvValue("METRICS_EXPORTER_PASSWORD"),

OrchestratorUser: getEnvValue("ORC_TOPOLOGY_USER"),
OrchestratorPassword: getEnvValue("ORC_TOPOLOGY_PASSWORD"),
}

return cfg
Expand Down Expand Up @@ -183,26 +185,3 @@ func getOrdinalFromHostname(hn string) int {

return 0
}

// getMySQLConnectionString returns the mysql DSN
func getMySQLConnectionString() (string, error) {
cnfPath := path.Join(configDir, "client.cnf")
cfg, err := ini.Load(cnfPath)
if err != nil {
return "", fmt.Errorf("Could not open %s: %s", cnfPath, err)
}

client := cfg.Section("client")
host := client.Key("host").String()
user := client.Key("user").String()
password := client.Key("password").String()
port, err := client.Key("port").Int()
if err != nil {
return "", fmt.Errorf("Invalid port in %s: %s", cnfPath, err)
}

dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=5s&multiStatements=true&interpolateParams=true",
user, password, host, port,
)
return dsn, nil
}
3 changes: 0 additions & 3 deletions pkg/sidecar/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ var (
// ToolsInitTableName is the name of the init table
toolsInitTableName = "init"

// UtilityUser is the name of the percona utility user.
utilityUser = "sys_utility_sidecar"

// ServerPort http server port
serverPort = constants.SidecarServerPort
// ServerProbeEndpoint is the http server endpoint for probe
Expand Down

0 comments on commit beb41ce

Please sign in to comment.