diff --git a/pkg/etcd/module.go b/pkg/etcd/module.go index bbed1c41..0bd1f9b9 100644 --- a/pkg/etcd/module.go +++ b/pkg/etcd/module.go @@ -17,10 +17,13 @@ package etcd import ( + "path/filepath" + kubekeyapiv1alpha2 "github.com/kubesphere/kubekey/apis/kubekey/v1alpha2" "github.com/kubesphere/kubekey/pkg/common" "github.com/kubesphere/kubekey/pkg/core/action" "github.com/kubesphere/kubekey/pkg/core/task" + "github.com/kubesphere/kubekey/pkg/core/util" "github.com/kubesphere/kubekey/pkg/etcd/templates" ) @@ -387,7 +390,46 @@ func (b *BackupModule) Init() { Parallel: true, } + generateBackupETCDService := &task.RemoteTask{ + Name: "GenerateBackupETCDService", + Desc: "Generate backup ETCD service", + Hosts: b.Runtime.GetHostsByRole(common.ETCD), + Action: &action.Template{ + Template: templates.BackupETCDService, + Dst: filepath.Join("/etc/systemd/system/", templates.BackupETCDService.Name()), + Data: util.Data{ + "ScriptPath": filepath.Join(b.KubeConf.Cluster.Etcd.BackupScriptDir, "etcd-backup.sh"), + }, + }, + Parallel: true, + } + + generateBackupETCDTimer := &task.RemoteTask{ + Name: "GenerateBackupETCDTimer", + Desc: "Generate backup ETCD timer", + Hosts: b.Runtime.GetHostsByRole(common.ETCD), + Action: &action.Template{ + Template: templates.BackupETCDTimer, + Dst: filepath.Join("/etc/systemd/system/", templates.BackupETCDTimer.Name()), + Data: util.Data{ + "OnCalendarStr": templates.BackupTimeOnCalendar(b.KubeConf.Cluster.Etcd.BackupPeriod), + }, + }, + Parallel: true, + } + + enable := &task.RemoteTask{ + Name: "EnableBackupETCDService", + Desc: "Enable backup etcd service", + Hosts: b.Runtime.GetHostsByRole(common.ETCD), + Action: new(EnableBackupETCDService), + Parallel: true, + } + b.Tasks = []task.Interface{ backupETCD, + generateBackupETCDService, + generateBackupETCDTimer, + enable, } } diff --git a/pkg/etcd/tasks.go b/pkg/etcd/tasks.go index a52b3943..d86028ba 100644 --- a/pkg/etcd/tasks.go +++ b/pkg/etcd/tasks.go @@ -18,10 +18,13 @@ package etcd import ( "fmt" - "github.com/kubesphere/kubekey/pkg/files" "path/filepath" "strings" + "github.com/kubesphere/kubekey/pkg/files" + + "github.com/pkg/errors" + kubekeyapiv1alpha2 "github.com/kubesphere/kubekey/apis/kubekey/v1alpha2" "github.com/kubesphere/kubekey/pkg/common" "github.com/kubesphere/kubekey/pkg/core/action" @@ -29,7 +32,6 @@ import ( "github.com/kubesphere/kubekey/pkg/core/util" "github.com/kubesphere/kubekey/pkg/etcd/templates" "github.com/kubesphere/kubekey/pkg/utils" - "github.com/pkg/errors" ) type EtcdNode struct { @@ -390,9 +392,7 @@ func (b *BackupETCD) Execute(runtime connector.Runtime) error { "Etcdendpoint": fmt.Sprintf("https://%s:2379", runtime.RemoteHost().GetInternalAddress()), "Backupdir": b.KubeConf.Cluster.Etcd.BackupDir, "KeepbackupNumber": b.KubeConf.Cluster.Etcd.KeepBackupNumber, - "EtcdBackupPeriod": b.KubeConf.Cluster.Etcd.BackupPeriod, "EtcdBackupScriptDir": b.KubeConf.Cluster.Etcd.BackupScriptDir, - "EtcdBackupHour": templates.BackupTimeInterval(runtime, b.KubeConf), }, } @@ -404,9 +404,17 @@ func (b *BackupETCD) Execute(runtime connector.Runtime) error { if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("chmod +x %s/etcd-backup.sh", b.KubeConf.Cluster.Etcd.BackupScriptDir), false); err != nil { return errors.Wrap(errors.WithStack(err), "chmod etcd backup script failed") } + return nil +} - if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("sh %s/etcd-backup.sh", b.KubeConf.Cluster.Etcd.BackupScriptDir), false); err != nil { - return errors.Wrap(errors.WithStack(err), "Failed to run the etcd-backup.sh") +type EnableBackupETCDService struct { + common.KubeAction +} + +func (e *EnableBackupETCDService) Execute(runtime connector.Runtime) error { + if _, err := runtime.GetRunner().SudoCmd("systemctl enable --now backup-etcd.timer", + false); err != nil { + return errors.Wrap(errors.WithStack(err), "enable backup-etcd.service failed") } return nil } diff --git a/pkg/etcd/templates/backup_script.go b/pkg/etcd/templates/backup_script.go index 2a80eed6..11e889c8 100644 --- a/pkg/etcd/templates/backup_script.go +++ b/pkg/etcd/templates/backup_script.go @@ -17,12 +17,9 @@ package templates import ( - "github.com/kubesphere/kubekey/pkg/common" - "github.com/kubesphere/kubekey/pkg/core/connector" - "github.com/kubesphere/kubekey/pkg/core/logger" - "github.com/lithammer/dedent" - "strconv" "text/template" + + "github.com/lithammer/dedent" ) // EtcdBackupScriptTmpl defines the template of etcd backup script. @@ -34,9 +31,7 @@ ENDPOINTS='{{ .Etcdendpoint }}' ETCD_DATA_DIR="/var/lib/etcd" BACKUP_DIR="{{ .Backupdir }}/etcd-$(date +%Y-%m-%d-%H-%M-%S)" KEEPBACKUPNUMBER='{{ .KeepbackupNumber }}' -ETCDBACKUPPERIOD='{{ .EtcdBackupPeriod }}' ETCDBACKUPSCIPT='{{ .EtcdBackupScriptDir }}' -ETCDBACKUPHOUR='{{ .EtcdBackupHour }}' ETCDCTL_CERT="/etc/ssl/etcd/ssl/admin-{{ .Hostname }}.pem" ETCDCTL_KEY="/etc/ssl/etcd/ssl/admin-{{ .Hostname }}-key.pem" @@ -59,35 +54,4 @@ sleep 3 cd $BACKUP_DIR/../;ls -lt |awk '{if(NR > '$KEEPBACKUPNUMBER'){print "rm -rf "$9}}'|sh -if [[ ! $ETCDBACKUPHOUR ]]; then - time="*/$ETCDBACKUPPERIOD * * * *" -else - if [[ 0 == $ETCDBACKUPPERIOD ]];then - time="* */$ETCDBACKUPHOUR * * *" - else - time="*/$ETCDBACKUPPERIOD */$ETCDBACKUPHOUR * * *" - fi -fi - -crontab -l | grep -v '#' > /tmp/file -echo "$time sh $ETCDBACKUPSCIPT/etcd-backup.sh" >> /tmp/file && awk ' !x[$0]++{print > "/tmp/file"}' /tmp/file -crontab /tmp/file -rm -rf /tmp/file - `))) - -func BackupTimeInterval(runtime connector.Runtime, kubeConf *common.KubeConf) string { - var etcdBackupHour string - if kubeConf.Cluster.Etcd.BackupPeriod != 0 { - period := kubeConf.Cluster.Etcd.BackupPeriod - if period > 60 && period < 1440 { - kubeConf.Cluster.Etcd.BackupPeriod = period % 60 - etcdBackupHour = strconv.Itoa(period / 60) - } - if period > 1440 { - logger.Log.Message(runtime.RemoteHost().GetName(), "etcd backup cannot last more than one day, Please change it to within one day or KubeKey will set it to the default value '24'.") - return "24" - } - } - return etcdBackupHour -} diff --git a/pkg/etcd/templates/backup_service.go b/pkg/etcd/templates/backup_service.go new file mode 100644 index 00000000..a62fb072 --- /dev/null +++ b/pkg/etcd/templates/backup_service.go @@ -0,0 +1,71 @@ +/* + Copyright 2022 The KubeSphere Authors. + + 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 templates + +import ( + "fmt" + "strconv" + "text/template" + + "github.com/lithammer/dedent" +) + +var ( + // BackupETCDService defines the template of backup-etcd service for systemd. + BackupETCDService = template.Must(template.New("backup-etcd.service").Parse( + dedent.Dedent(`[Unit] +Description=Backup ETCD +[Service] +Type=oneshot +ExecStart={{ .ScriptPath }} + `))) + + // BackupETCDTimer defines the template of backup-etcd timer for systemd. + BackupETCDTimer = template.Must(template.New("backup-etcd.timer").Parse( + dedent.Dedent(`[Unit] +Description=Timer to backup ETCD +[Timer] +{{- if .OnCalendarStr }} +OnCalendar={{ .OnCalendarStr }} +{{- else }} +OnCalendar=*-*-* *:*/30:00 +{{- end }} +Unit=backup-etcd.service +[Install] +WantedBy=multi-user.target + `))) +) + +func BackupTimeOnCalendar(period int) string { + var onCalendar string + if period <= 0 { + onCalendar = "*-*-* *:00/30:00" + } else if period < 60 { + onCalendar = fmt.Sprintf("*-*-* *:00/%d:00", period) + } else if period >= 60 && period < 1440 { + hour := strconv.Itoa(period / 60) + minute := strconv.Itoa(period % 60) + if period%60 == 0 { + onCalendar = fmt.Sprintf("*-*-* 00/%s:00:00", hour) + } else { + onCalendar = fmt.Sprintf("*-*-* 00/%s:%s:00", hour, minute) + } + } else { + onCalendar = "*-*-* 00:00:00" + } + return onCalendar +} diff --git a/pkg/etcd/templates/backup_service_test.go b/pkg/etcd/templates/backup_service_test.go new file mode 100644 index 00000000..4abeba21 --- /dev/null +++ b/pkg/etcd/templates/backup_service_test.go @@ -0,0 +1,52 @@ +/* + Copyright 2022 The KubeSphere Authors. + + 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 templates + +import ( + "testing" +) + +func TestBackupTimeOnCalendar(t *testing.T) { + tests := []struct { + period int + want string + }{ + { + 30, + "*-*-* *:00/30:00", + }, + { + 60, + "*-*-* 00/1:00:00", + }, + { + 70, + "*-*-* 00/1:10:00", + }, + { + 1500, + "*-*-* 00:00:00", + }, + } + for _, tt := range tests { + t.Run("", func(t *testing.T) { + if got := BackupTimeOnCalendar(tt.period); got != tt.want { + t.Errorf("BackupTimeOnCalendar() = %v, want %v", got, tt.want) + } + }) + } +}