Skip to content

Commit

Permalink
Merge pull request #113 from cybozu-go/suppress-mysql-deprecate
Browse files Browse the repository at this point in the history
Use --defaults-extra-file to exec mysql command
  • Loading branch information
kfyharukz authored Nov 11, 2020
2 parents 7de1172 + 9aedbd3 commit 6ad8f87
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 5 deletions.
8 changes: 3 additions & 5 deletions cmd/kubectl-moco/cmd/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"context"
"fmt"
"os"
"time"

Expand Down Expand Up @@ -54,12 +55,9 @@ func runMySQLCommand(ctx context.Context, clusterName string, cmd *cobra.Command
if err != nil {
return err
}
password, err := getPassword(ctx, moco.UniqueName(cluster), mysqlConfig.user)
if err != nil {
return err
}

commands := append([]string{podName, "--", "mysql", "-u", mysqlConfig.user, "-p" + password}, args...)
myCnfPath := fmt.Sprintf("%s/%s-my.cnf", moco.MyCnfSecretPath, mysqlConfig.user)
commands := append([]string{podName, "--", "mysql", "--defaults-extra-file=" + myCnfPath}, args...)
argsLenAtDash := 2
options := &cmdexec.ExecOptions{
StreamOptions: cmdexec.StreamOptions{
Expand Down
15 changes: 15 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
)

const (
// RootUser is the name of root user in the MySQL context.
RootUser = "root"

// OperatorUser is a name of MOCO operator user in the MySQL context.
OperatorUser = "moco"

Expand Down Expand Up @@ -56,6 +59,9 @@ const (
// TmpPath is a path for /tmp.
TmpPath = "/tmp"

// MyCnfSecretPath is the path for my.cnf formated credentials for CLI
MyCnfSecretPath = "/mysql-credentials"

// MySQLConfTemplatePath is
MySQLConfTemplatePath = "/etc/mysql_template"

Expand Down Expand Up @@ -165,6 +171,15 @@ const (
// ReadOnlyPasswordKey is a Secret key for moco-writable user password.
WritablePasswordKey = "WRITABLE_PASSWORD"

// RootMyCnfKey is the username and password of root formated as my.cnf
RootMyCnfKey = RootUser + "-my.cnf"

// ReadOnlyMyCnfKey is the username and password of moco-readonly formated as my.cnf
ReadOnlyMyCnfKey = ReadOnlyUser + "-my.cnf"

// WritableMyCnfKey is the username and password or moco-writable formated as my.cnf
WritableMyCnfKey = WritableUser + "-my.cnf"

// ReplicationSourcePrimaryHostKey etc. are Secret key for replication source secret
ReplicationSourcePrimaryHostKey = "PRIMARY_HOST"
ReplicationSourcePrimaryUserKey = "PRIMARY_USER"
Expand Down
57 changes: 57 additions & 0 deletions controllers/mysqlcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
tmpVolumeName = "tmp"
mysqlConfTemplateVolumeName = "mysql-conf-template"
replicationSourceSecretVolumeName = "replication-source-secret"
myCnfSecretVolumeName = "my-cnf-secret"

passwordBytes = 16

Expand All @@ -56,6 +57,7 @@ const (
mysqlClusterFinalizer = "moco.cybozu.com/mysqlcluster"

rootPasswordSecretPrefix = "root-password-"
myCnfSecretPrefix = "my-cnf-"
serviceAccountPrefix = "mysqld-sa-"
)

Expand Down Expand Up @@ -347,9 +349,45 @@ func (r *MySQLClusterReconciler) createSecretIfNotExist(ctx context.Context, log
return false, err
}

err = r.createMyCnfSecretForLocalCLI(ctx, cluster)
if err != nil {
log.Error(err, "unable to create Secret for local CLI")
return false, err
}

return true, nil
}

func (r *MySQLClusterReconciler) createMyCnfSecretForLocalCLI(ctx context.Context, cluster *mocov1alpha1.MySQLCluster) error {
rootPasswordSecretName := rootPasswordSecretPrefix + moco.UniqueName(cluster)
rootPasswordSecret := &corev1.Secret{}
err := r.Get(ctx, client.ObjectKey{Namespace: cluster.Namespace, Name: rootPasswordSecretName}, rootPasswordSecret)
if err != nil {
return err
}
rootPass := rootPasswordSecret.Data[moco.RootPasswordKey]
readOnlyPass := rootPasswordSecret.Data[moco.ReadOnlyPasswordKey]
writablePass := rootPasswordSecret.Data[moco.WritablePasswordKey]

secretName := myCnfSecretPrefix + moco.UniqueName(cluster)
secret := &corev1.Secret{}
secret.SetNamespace(cluster.Namespace)
secret.SetName(secretName)
setStandardLabels(&secret.ObjectMeta, cluster)
secret.Data = map[string][]byte{
moco.RootMyCnfKey: formatCredentialAsMyCnf(moco.RootUser, string(rootPass)),
moco.ReadOnlyMyCnfKey: formatCredentialAsMyCnf(moco.ReadOnlyUser, string(readOnlyPass)),
moco.WritableMyCnfKey: formatCredentialAsMyCnf(moco.WritableUser, string(writablePass)),
}

err = ctrl.SetControllerReference(cluster, secret, r.Scheme)
if err != nil {
return err
}

return r.Client.Create(ctx, secret)
}

func (r *MySQLClusterReconciler) createPasswordSecretForInit(ctx context.Context, cluster *mocov1alpha1.MySQLCluster, operatorPass, replicatorPass, donorPass []byte) error {
var rootPass []byte
if cluster.Spec.RootPasswordSecretName != nil {
Expand Down Expand Up @@ -700,6 +738,14 @@ func (r *MySQLClusterReconciler) makePodTemplate(log logr.Logger, cluster *mocov
},
},
},
corev1.Volume{
Name: myCnfSecretVolumeName,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: myCnfSecretPrefix + moco.UniqueName(cluster),
},
},
},
)

// find "mysqld" container and update it
Expand Down Expand Up @@ -744,6 +790,10 @@ func (r *MySQLClusterReconciler) makePodTemplate(log logr.Logger, cluster *mocov
MountPath: moco.TmpPath,
Name: tmpVolumeName,
},
corev1.VolumeMount{
MountPath: moco.MyCnfSecretPath,
Name: myCnfSecretVolumeName,
},
)
newTemplate.Spec.Containers[i] = *c
mysqldContainer = &newTemplate.Spec.Containers[i]
Expand Down Expand Up @@ -1242,3 +1292,10 @@ func findCondition(conditions []mocov1alpha1.MySQLClusterCondition, conditionTyp
}
return nil
}

func formatCredentialAsMyCnf(user, password string) []byte {
return []byte(fmt.Sprintf(`[client]
user="%s"
password="%s"
`, user, password))
}
47 changes: 47 additions & 0 deletions controllers/mysqlcluster_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ var _ = Describe("MySQLCluster controller", func() {
ctrlSecretNS, ctrlSecretName := moco.GetSecretNameForController(cluster)
initSecretNS := cluster.Namespace
initSecretName := rootPasswordSecretPrefix + moco.UniqueName(cluster)
myCnfSecretName := myCnfSecretPrefix + moco.UniqueName(cluster)
myCnfSecretNS := cluster.Namespace

initSecret := &corev1.Secret{}
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: initSecretNS, Name: initSecretName}, initSecret)
Expand All @@ -151,6 +153,14 @@ var _ = Describe("MySQLCluster controller", func() {
Expect(ctrlSecret.Data).Should(HaveKey(moco.ReplicationPasswordKey))
Expect(ctrlSecret.Data).Should(HaveKey(moco.CloneDonorPasswordKey))

myCnfSecret := &corev1.Secret{}
err = k8sClient.Get(ctx, client.ObjectKey{Namespace: myCnfSecretNS, Name: myCnfSecretName}, myCnfSecret)
Expect(err).ShouldNot(HaveOccurred())

Expect(myCnfSecret.Data).Should(HaveKey(moco.RootMyCnfKey))
Expect(myCnfSecret.Data).Should(HaveKey(moco.ReadOnlyMyCnfKey))
Expect(myCnfSecret.Data).Should(HaveKey(moco.WritableMyCnfKey))

isUpdated, err = reconciler.createSecretIfNotExist(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeFalse())
Expand Down Expand Up @@ -366,6 +376,43 @@ var _ = Describe("MySQLCluster controller", func() {
Expect(isUpdated).Should(BeFalse())
})

It("should mount volumes of MyCnfSecret", func() {
serverIDBase := mathrand.Uint32()
cluster.Status.ServerIDBase = &serverIDBase
cluster.Spec.ReplicationSourceSecretName = &replicationSourceSecretName

isUpdated, err := reconciler.createOrUpdateStatefulSet(ctx, reconciler.Log, cluster)
Expect(err).ShouldNot(HaveOccurred())
Expect(isUpdated).Should(BeTrue())

sts := &appsv1.StatefulSet{}
err = k8sClient.Get(ctx, client.ObjectKey{Name: moco.UniqueName(cluster), Namespace: cluster.Namespace}, sts)
Expect(err).ShouldNot(HaveOccurred())

var mysqldContainer *corev1.Container
for i, c := range sts.Spec.Template.Spec.Containers {
if c.Name == "mysqld" {
mysqldContainer = &sts.Spec.Template.Spec.Containers[i]
}
}
Expect(mysqldContainer).ShouldNot(BeNil())
Expect(len(mysqldContainer.VolumeMounts)).Should(Equal(6))
Expect(mysqldContainer.VolumeMounts).Should(ContainElement(corev1.VolumeMount{
MountPath: moco.MyCnfSecretPath,
Name: myCnfSecretVolumeName,
}))
defaultMode := corev1.SecretVolumeSourceDefaultMode
Expect(sts.Spec.Template.Spec.Volumes).Should(ContainElement(corev1.Volume{
Name: myCnfSecretVolumeName,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: myCnfSecretPrefix + moco.UniqueName(cluster),
DefaultMode: &defaultMode,
},
},
}))
})

It("should mount volumes of ReplicationSourceSecret", func() {
serverIDBase := mathrand.Uint32()
cluster.Status.ServerIDBase = &serverIDBase
Expand Down

0 comments on commit 6ad8f87

Please sign in to comment.