From 60e25e3cc2869803071d15d9d736ab8483a65afd Mon Sep 17 00:00:00 2001 From: acekingke Date: Tue, 29 Nov 2022 09:48:19 +0800 Subject: [PATCH] *: support juicefs #745 --- Dockerfile.sidecar | 13 +- api/v1alpha1/backup_types.go | 11 + api/v1alpha1/zz_generated.deepcopy.go | 21 +- backup/syncer/job.go | 90 +++++ .../crds/mysql.radondb.com_backups.yaml | 16 + .../crd/bases/mysql.radondb.com_backups.yaml | 16 + controllers/mysqlcluster_controller.go | 9 +- mysqlcluster/container/backup.go | 10 +- mysqlcluster/container/container.go | 10 + mysqlcluster/mysqlcluster.go | 23 +- mysqlcluster/mysqlcluster_test.go | 20 ++ mysqlcluster/syncer/headless_service.go | 8 +- mysqlcluster/syncer/pdb.go | 2 +- mysqlcluster/syncer/sfsRestoreJob.go | 319 ++++++++++++++++++ mysqlcluster/syncer/sshSecret.go | 59 ++++ mysqlcluster/syncer/sshkey.go | 101 ++++++ mysqlcluster/syncer/statefulset.go | 5 +- mysqlcluster/syncer/statefulset_test.go | 7 + script/backup.sh | 158 +++++++++ script/sshd.sh | 4 + sidecar/init.go | 12 + sidecar/util.go | 3 + utils/common.go | 4 + utils/constants.go | 16 + 24 files changed, 926 insertions(+), 11 deletions(-) create mode 100644 mysqlcluster/syncer/sfsRestoreJob.go create mode 100644 mysqlcluster/syncer/sshSecret.go create mode 100644 mysqlcluster/syncer/sshkey.go create mode 100755 script/backup.sh create mode 100755 script/sshd.sh diff --git a/Dockerfile.sidecar b/Dockerfile.sidecar index 4eac67be..39339044 100644 --- a/Dockerfile.sidecar +++ b/Dockerfile.sidecar @@ -43,13 +43,20 @@ RUN set -ex; \ ARG XTRABACKUP_PKG=percona-xtrabackup-24 RUN set -ex; \ apt-get update; \ - apt-get install -y --no-install-recommends gnupg2 wget lsb-release curl bc; \ + apt-get install -y --no-install-recommends gnupg2 wget lsb-release curl bc fuse jq openssh-server; \ wget -P /tmp --no-check-certificate https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb; \ dpkg -i /tmp/percona-release_latest.$(lsb_release -sc)_all.deb; \ apt-get update; \ apt-get install -y --no-install-recommends ${XTRABACKUP_PKG}; \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - +#ADD http://mirrors.woqutech.com/download/qfusion/files/bin/juicefs-1.0.0-rc1-linux-amd64 /usr/local/bin/juicefs +# COPY juicefs/juicefs /usr/local/bin/juicefs +RUN wget --no-check-certificate "https://d.juicefs.com/juicefs/releases/download/v1.0.2/juicefs-1.0.2-linux-amd64.tar.gz" && tar -zxf "juicefs-1.0.2-linux-amd64.tar.gz" ;\ + mv juicefs /usr/local/bin/juicefs; \ + chmod +x /usr/local/bin/juicefs ; mkdir -p /run/sshd; \ + mkdir -p /root/.ssh; \ + chmod 700 /root/.ssh WORKDIR / COPY --from=builder /workspace/bin/sidecar /usr/local/bin/sidecar -ENTRYPOINT ["sidecar"] +COPY script/*.sh / +CMD [ "sidecar" ] diff --git a/api/v1alpha1/backup_types.go b/api/v1alpha1/backup_types.go index 3fc018a2..d177fbde 100644 --- a/api/v1alpha1/backup_types.go +++ b/api/v1alpha1/backup_types.go @@ -21,6 +21,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +type JuiceOpt struct { + // sqlite or redis + JuiceMeta string `json:"juiceMeta"` + // backupSecrete name for S3 + BackupSecretName string `json:"backupSecretName"` + JuiceName string `json:"juiceName"` +} + // This is the backup Job CRD. // BackupSpec defines the desired state of Backup type BackupSpec struct { @@ -40,6 +48,9 @@ type BackupSpec struct { // +optional NFSServerAddress string `json:"nfsServerAddress,omitempty"` + // Represents the juicefs parameters which need. + // +optional + JuiceOpt *JuiceOpt `json:"juiceOpt,omitempty"` // ClusterName represents the cluster name to backup ClusterName string `json:"clusterName"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index db850e7c..6a74f547 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1,4 +1,3 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated /* @@ -104,6 +103,11 @@ func (in *BackupList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = *in + if in.JuiceOpt != nil { + in, out := &in.JuiceOpt, &out.JuiceOpt + *out = new(JuiceOpt) + **out = **in + } if in.HistoryLimit != nil { in, out := &in.HistoryLimit, &out.HistoryLimit *out = new(int32) @@ -174,6 +178,21 @@ func (in *ClusterCondition) DeepCopy() *ClusterCondition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JuiceOpt) DeepCopyInto(out *JuiceOpt) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JuiceOpt. +func (in *JuiceOpt) DeepCopy() *JuiceOpt { + if in == nil { + return nil + } + out := new(JuiceOpt) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MetricsOpts) DeepCopyInto(out *MetricsOpts) { *out = *in diff --git a/backup/syncer/job.go b/backup/syncer/job.go index b1e7e8a2..54d7fda4 100644 --- a/backup/syncer/job.go +++ b/backup/syncer/job.go @@ -17,13 +17,16 @@ limitations under the License. package syncer import ( + "context" "fmt" + "strings" "github.com/presslabs/controller-util/pkg/syncer" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" v1alpha1 "github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1" @@ -33,6 +36,7 @@ import ( ) type jobSyncer struct { + client client.Client job *batchv1.Job backup *backup.Backup } @@ -50,6 +54,7 @@ func NewJobSyncer(c client.Client, backup *backup.Backup) syncer.Interface { } sync := &jobSyncer{ + client: c, job: obj, backup: backup, } @@ -174,6 +179,10 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec { MountPath: utils.XtrabckupLocal, }, } + } else if s.backup.Spec.JuiceOpt != nil { + // Deal it for juiceOpt + s.buildJuicefsBackPod(&in) + } else { // in.Containers[0].ImagePullPolicy = s.opt.ImagePullPolicy in.Containers[0].Args = []string{ @@ -238,3 +247,84 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec { } return in } + +func (s *jobSyncer) buildJuicefsBackPod(in *corev1.PodSpec) error { + // add volumn about pvc + var defMode int32 = 0600 + var err error + var cmdstr string + in.Volumes = []corev1.Volume{ + { + Name: utils.SShVolumnName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: fmt.Sprintf("%s-ssh-key", s.backup.Spec.ClusterName), + DefaultMode: &defMode, + }, + }, + }, + } + + in.Containers[0].VolumeMounts = []corev1.VolumeMount{ + { + Name: utils.SShVolumnName, + MountPath: utils.SshVolumnPath, + }, + } + + // PodName.clusterName-mysql.Namespace + // sample-mysql-0.sample-mysql.default + hostname := fmt.Sprintf("%s.%s-mysql.%s", s.backup.Spec.HostName, s.backup.Spec.ClusterName, s.backup.Namespace) + if cmdstr, err = s.buildJuicefsCmd(s.backup.Spec.JuiceOpt.BackupSecretName); err != nil { + return err + } + + in.Containers[0].Command = []string{"bash", "-c", "--", `cp /etc/secret-ssh/* /root/.ssh +chmod 600 /root/.ssh/authorized_keys ;` + + strings.Join([]string{ + "ssh", "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no", hostname, cmdstr, + }, " ")} + + return nil +} + +func (s *jobSyncer) buildJuicefsCmd(secName string) (string, error) { + juiceopt := s.backup.Spec.JuiceOpt + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: secName, + Namespace: s.backup.Namespace, + }, + } + err := s.client.Get(context.TODO(), + types.NamespacedName{Namespace: s.backup.Namespace, + Name: secName}, secret) + + if err != nil { + return "", err + } + url, bucket := secret.Data["s3-endpoint"], secret.Data["s3-bucket"] + accesskey, secretkey := secret.Data["s3-access-key"], secret.Data["s3-secret-key"] + juicebucket := utils.InstallBucket(string(url), string(bucket)) + cmdstr := fmt.Sprintf(`<= 1: + return fmt.Errorf("restore job %s fail", job.Name) + } + } + return nil +} + +// 5. set pvc owner. +func (s *StatefulSetSyncer) setPvcOwner(ctx context.Context, pvc *corev1.PersistentVolumeClaim) error { + ownerRefs := s.sfs.GetOwnerReferences() + pvc.SetOwnerReferences(ownerRefs) + return nil +} + +func (s *StatefulSetSyncer) CreateOnePVC(name string) *corev1.PersistentVolumeClaim { + return &corev1.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "PersistentVolumeClaim", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: s.Namespace, + }, + Spec: s.sfs.Spec.VolumeClaimTemplates[0].Spec, + } +} diff --git a/mysqlcluster/syncer/sshSecret.go b/mysqlcluster/syncer/sshSecret.go new file mode 100644 index 00000000..0744ba64 --- /dev/null +++ b/mysqlcluster/syncer/sshSecret.go @@ -0,0 +1,59 @@ +/* +Copyright 2021 RadonDB. + +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 syncer + +import ( + "github.com/presslabs/controller-util/pkg/syncer" + "github.com/radondb/radondb-mysql-kubernetes/mysqlcluster" + "github.com/radondb/radondb-mysql-kubernetes/utils" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// NewSecretSyncer returns secret syncer. +func NewSShKeySyncer(cli client.Client, c *mysqlcluster.MysqlCluster) syncer.Interface { + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: c.GetNameForResource(utils.SShKey), + Namespace: c.Namespace, + }, + } + + return syncer.NewObjectSyncer("Secret", c.Unwrap(), secret, cli, func() error { + + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + + if len(secret.Data["id_ecdsa"]) == 0 { + pub, priv, err := GenSSHKey() + if err != nil { + return err + } + secret.Data["id_ecdsa"] = priv + secret.Data["authorized_keys"] = pub + + } + + return nil + }) +} diff --git a/mysqlcluster/syncer/sshkey.go b/mysqlcluster/syncer/sshkey.go new file mode 100644 index 00000000..80d1afdc --- /dev/null +++ b/mysqlcluster/syncer/sshkey.go @@ -0,0 +1,101 @@ +/* +Copyright 2021 RadonDB. + +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 syncer + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "log" + + "golang.org/x/crypto/ssh" +) + +// ssh -o UserKnownHostsFile=/dev/null +func GenSSHKey() (pubkey, privekey []byte, err error) { + + bitSize := 4096 + + privateKey, err := generatePrivateKey(bitSize) + if err != nil { + return nil, nil, err + } + + publicKeyBytes, err := generatePublicKey(&privateKey.PublicKey) + if err != nil { + return nil, nil, err + } + + privateKeyBytes := encodePrivateKeyToPEM(privateKey) + + return publicKeyBytes, privateKeyBytes, err + +} + +// generatePrivateKey creates a RSA Private Key of specified byte size +func generatePrivateKey(bitSize int) (*ecdsa.PrivateKey, error) { + // Private Key generation + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + + // Validate Private Key + // err = privateKey.Validate() + // if err != nil { + // return nil, err + // } + + log.Println("Private Key generated") + return privateKey, nil +} + +// encodePrivateKeyToPEM encodes Private Key from RSA to PEM format +func encodePrivateKeyToPEM(privateKey *ecdsa.PrivateKey) []byte { + // Get ASN.1 DER format + privDER, err := x509.MarshalECPrivateKey(privateKey) + if err != nil { + panic(err) + } + // pem.Block + privBlock := pem.Block{ + Type: "EC PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + + // Private key in PEM format + privatePEM := pem.EncodeToMemory(&privBlock) + + return privatePEM +} + +// generatePublicKey take a rsa.PublicKey and return bytes suitable for writing to .pub file +// returns in the format "ssh-rsa ..." +func generatePublicKey(privatekey *ecdsa.PublicKey) ([]byte, error) { + publicKey, err := ssh.NewPublicKey(privatekey) + if err != nil { + return nil, err + } + + pubKeyBytes := ssh.MarshalAuthorizedKey(publicKey) + + log.Println("Public key generated") + return pubKeyBytes, nil +} diff --git a/mysqlcluster/syncer/statefulset.go b/mysqlcluster/syncer/statefulset.go index c885fdcd..b8a7b066 100644 --- a/mysqlcluster/syncer/statefulset.go +++ b/mysqlcluster/syncer/statefulset.go @@ -262,7 +262,10 @@ func (s *StatefulSetSyncer) createOrUpdate(ctx context.Context) (controllerutil. if err = s.mutate(); err != nil { return controllerutil.OperationResultNone, err } - + //TODO: Do the Restore job + if err = s.createRestoreJob(ctx); err != nil { + s.log.Info("do not need restore the pvcs") + } if err = s.cli.Create(ctx, s.sfs); err != nil { return controllerutil.OperationResultNone, err } else { diff --git a/mysqlcluster/syncer/statefulset_test.go b/mysqlcluster/syncer/statefulset_test.go index abb8f4db..23c6e571 100644 --- a/mysqlcluster/syncer/statefulset_test.go +++ b/mysqlcluster/syncer/statefulset_test.go @@ -17,6 +17,7 @@ limitations under the License. package syncer import ( + "fmt" "testing" appsv1 "k8s.io/api/apps/v1" @@ -145,3 +146,9 @@ func TestStatefulSetSyncer_sfsUpdated(t *testing.T) { }) } } + +func TestSecretKey(t *testing.T) { + pub, priv, _ := GenSSHKey() + fmt.Println(string(pub)) + fmt.Println(string(priv)) +} diff --git a/script/backup.sh b/script/backup.sh new file mode 100755 index 00000000..43688779 --- /dev/null +++ b/script/backup.sh @@ -0,0 +1,158 @@ +# CLUSTER_NAME=sample +BASE=/juicefs +JSONFILE=$BASE/$CLUSTER_NAME-backup.json + +function checkfile() { + if ! [ -r $JSONFILE ] ; then + jq -n --arg cluster $CLUSTER_NAME --arg namespace $NAMESPACE '{"cluster_name": $cluster, "namespace": $namespace,"backup_chains": []}' >$JSONFILE + else + echo exist the json file + fi +} +function read() { + max=0 + IFS_OLD=$IFS + IFS=$(echo -en "\n\b") + for i in $(jq -c '.backup_chains[]' $JSONFILE); + do + #echo $i | jq '.type' + val=$(echo $i | jq '."target-dir"|match("\\d+")|.string|tonumber') + #echo $val + if [[ $max < $val ]] ; then + max=$val + fi + done + IFS=$IFS_OLD + echo $max +} + +function getDate() { + date '+%Y-%m-%d %H:%M:%S' +} + +function parseDateToUnix() { + local t=$1 + + echo date -d $t '+%s'|sh +} +function checkTime() { + local time=$1 # get the parameter + val=0 + IFS_OLD=$IFS + IFS=$(echo -en "\n\b") + for i in $(jq -c '.backup_chains[]' $JSONFILE); + do + traw=$(echo $i|jq '."time"') + val=$(echo $i | jq '."target-dir"|match("\\d+")|.string|tonumber') + t=$(echo date -d $traw '+%s'|sh) + cmptime=$(echo date -d "\"$time\"" '+%s'|sh) + if [ $t -ge $cmptime ]; then + break + fi + done + IFS=$IFS_OLD + echo $val + +} + +function appendinc() { + num=$1 + incbase="$BASE/backups/base" + #echo $BASE/backups/inc$(echo $num + 1|bc) + if ! [ $num -eq 0 ]; then + incbase=$BASE/backups/inc$num + fi + jq ".backup_chains += [{\"type\": \"incr-backup\", \"time\": \"$(getDate)\", \"target-dir\": \"$BASE/backups/inc$(echo $num + 1|bc)\", + \"incremental-basedir\": \"$incbase\" }]" $JSONFILE >"tmp.json" && mv ./tmp.json $JSONFILE +} + +function appendbase() { + jq ".backup_chains += [{\"type\": \"full-backup\", \"time\": \"$(getDate)\", \"target-dir\": \"$BASE/backups/base\"}]" $JSONFILE >"tmp.json" && mv ./tmp.json $JSONFILE + sleep 2 +} + +function fullbackup() { + mkdir -p /$BASE/backups/base + xtrabackup --backup --host=127.0.0.1 --user=root --password='' --datadir=/var/lib/mysql/ --target-dir=/$BASE/backups/base + success=$? + if [ $success ]; then + appendbase + fi +} + +function incrbackup() { + num=$1 + incbase="$BASE/backups/base" + #echo $BASE/backups/inc$(echo $num + 1|bc) + if ! [ $num -eq 0 ]; then + incbase=$BASE/backups/inc$num + fi + xtrabackup --backup --host=127.0.0.1 --user=root --password='' --datadir=/var/lib/mysql/ --target-dir=$BASE/backups/inc$(echo $num + 1|bc) \ + --incremental-basedir=$incbase + success=$? + if [ $success ]; then + appendinc $num + fi +} + +function backup() { + if ! [ -r $JSONFILE ] ; then + jq -n --arg cluster $CLUSTER_NAME --arg namespace $NAMESPACE '{"cluster_name": $cluster, "namespace": $namespace,"backup_chains": []}' >$JSONFILE + sleep 3 + echo now do the fullbackup + fullbackup + else + num=$(read) + incrbackup $num + fi + +} + + +function restore() { + local restorTime=$1 + local from=$2 + jsonfile=$BASE/$from-backup.json + if [ $# -ne 2 ] ; then + echo you can use it as restore date cluster-from + fi + local total=$(checkTime $restorTime) + for index in $(seq 0 $total); do + # at restore, base always use /backups/base + base=$(jq -c ".backup_chains[0][\"target-dir\"]" $jsonfile) + type=$(jq -c ".backup_chains[$index][\"type\"]" $jsonfile) + inc=$(jq -c ".backup_chains[$index][\"target-dir\"]" $jsonfile) + cmd="" + # echo $i, $base, $type,$inc + case $type in + "\"full-backup\"") + cmd=$(echo xtrabackup --prepare --apply-log-only --target-dir=$base) + echo $cmd + echo $cmd|sh + ;; + "\"incr-backup\"") + if [ $index -eq $total ]; then + cmd=$(echo xtrabackup --prepare --target-dir=$base --incremental-dir=$inc) + else + cmd=$(echo xtrabackup --prepare --apply-log-only --target-dir=$base --incremental-dir=$inc) + fi + echo $cmd + echo $cmd|sh + ;; + *) + echo nothing + ;; + esac + done + # check /var/lib/mysql is emtpty + if ! [ -d "/var/lib/mysql/mysql" ]; then + base=$(jq -c ".backup_chains[0][\"target-dir\"]" $JSONFILE) + cmd=$(echo xtrabackup --copy-back --target-dir=$base --datadir=/var/lib/mysql) + echo $cmd + echo $cmd|sh + chown -R mysql.mysql /var/lib/mysql + else + echo the dir is not empty, cannot copy back + fi +} + diff --git a/script/sshd.sh b/script/sshd.sh new file mode 100755 index 00000000..b0a49bbc --- /dev/null +++ b/script/sshd.sh @@ -0,0 +1,4 @@ +cp /etc/secret-ssh/* /root/.ssh +chmod 600 /root/.ssh/authorized_keys +/usr/sbin/sshd -D -e -f /etc/ssh/sshd_config & +echo "start..." diff --git a/sidecar/init.go b/sidecar/init.go index 4ae83767..1e6278a0 100644 --- a/sidecar/init.go +++ b/sidecar/init.go @@ -127,6 +127,8 @@ func runCloneAndInit(cfg *Config) (bool, error) { return hasInitialized, nil } log.Info("no leader or follower found") + // for restore job create, it must set hasInitialized to true + hasInitialized, _ = checkIfPathExists(path.Join(dataPath, xrestorefile)) return hasInitialized, nil } @@ -260,6 +262,16 @@ func runInitCommand(cfg *Config, hasInitialized bool) error { if err = ioutil.WriteFile(initSqlPath, cfg.buildInitSql(hasInitialized), 0644); err != nil { return fmt.Errorf("failed to write init.sql: %s", err) } + // check restore-file exist, remove it + if exist, _ := checkIfPathExists(path.Join(dataPath, xrestorefile)); exist { + //remove the xrestorefile + cmd := exec.Command("rm", "-rf", path.Join(dataPath, xrestorefile)) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to remove restore-file : %s", err) + } + } + // build xenon.json. xenonFilePath := path.Join(xenonPath, "xenon.json") if err = ioutil.WriteFile(xenonFilePath, cfg.buildXenonConf(), 0644); err != nil { diff --git a/sidecar/util.go b/sidecar/util.go index f48c710b..f75f2bde 100644 --- a/sidecar/util.go +++ b/sidecar/util.go @@ -69,6 +69,9 @@ var ( // xcloudCommand is the upload tool file name. xcloudCommand = "xbcloud" + + // restore file check + xrestorefile = "restore-file" ) // copyFile the src file to dst. diff --git a/utils/common.go b/utils/common.go index 0efd9c45..85386514 100644 --- a/utils/common.go +++ b/utils/common.go @@ -159,3 +159,7 @@ func ParseIPAndPath(nfsaddr string) (string, string) { return res[0], "/" } } + +func InstallBucket(url, bucket string) string { + return strings.Join(strings.Split(url, "//"), "//"+bucket+".") +} diff --git a/utils/constants.go b/utils/constants.go index 8cab16bf..e08422cb 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -88,6 +88,12 @@ const ( LogsVolumeName = "logs" DataVolumeName = "data" SysVolumeName = "host-sys" + + // just for juicefs + SysFuseVolume = "host-fuse" + SshPortName = "ssh" + SshPort = 22 + ScriptsVolumeName = "scripts" XenonConfVolumeName = "xenon-conf" InitFileVolumeName = "init-mysql" @@ -100,6 +106,8 @@ const ( LogsVolumeMountPath = "/var/log/mysql" DataVolumeMountPath = "/var/lib/mysql" SysVolumeMountPath = "/host-sys" + + SysFuseVolumnMountPath = "/dev/fuse" ScriptsVolumeMountPath = "/scripts" XenonConfVolumeMountPath = "/etc/xenon" InitFileVolumeMountPath = "/docker-entrypoint-initdb.d" @@ -129,6 +137,10 @@ const ( TlsVolumeName = "tls" // TlsMountPath is the volume mount path for tls TlsMountPath = "/etc/mysql-ssl" + + // ssh path + SShVolumnName = "ssh-key" + SshVolumnPath = "/etc/secret-ssh" ) // ResourceName is the type for aliasing resources that will be created. @@ -165,6 +177,10 @@ const ( JobAnonationDate = "backupDate" // Job Annonations type JobAnonationType = "backupType" + // SSh key + SShKey = "ssh" + // restore config + RestoreCMN = "restore" ) // JobType