Skip to content

Commit

Permalink
Merge pull request #484 from acekingke/BackupStatus
Browse files Browse the repository at this point in the history
*: Add backup Name  into Backup status
  • Loading branch information
andyli029 authored May 30, 2022
2 parents 8538f05 + 2f98bfc commit 171acda
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 29 deletions.
19 changes: 12 additions & 7 deletions api/v1alpha1/backup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// This is the backup Job CRD.
// BackupSpec defines the desired state of Backup
type BackupSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Expand Down Expand Up @@ -59,14 +57,21 @@ type BackupStatus struct {
// Completed indicates whether the backup is in a final state,
// no matter whether its' corresponding job failed or succeeded
Completed bool `json:"completed,omitempty"`

// Get the backup path.
BackupName string `json:"backupName,omitempty"`
// Get the backup Date
BackupDate string `json:"backupDate,omitempty"`
// Get the backup Type
BackupType string `json:"backupType,omitempty"`
// Conditions represents the backup resource conditions list.
Conditions []BackupCondition `json:"conditions,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="BackupName",type="string",JSONPath=".status.backupName",description="The Backup name"
// +kubebuilder:printcolumn:name="BackupDate",type="string",JSONPath=".status.backupDate",description="The Backup Date time"
// +kubebuilder:printcolumn:name="Type",type="string",JSONPath=".status.backupType",description="The Backup Type"
// Backup is the Schema for the backups API
type Backup struct {
metav1.TypeMeta `json:",inline"`
Expand Down
29 changes: 26 additions & 3 deletions backup/syncer/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type jobSyncer struct {
backup *backup.Backup
}

// Owner returns the object owner or nil if object does not have one.
func (s *jobSyncer) ObjectOwner() runtime.Object { return s.backup.Unwrap() }

// NewJobSyncer returns a syncer for backup jobs
func NewJobSyncer(c client.Client, s *runtime.Scheme, backup *backup.Backup) syncer.Interface {
obj := &batchv1.Job{
Expand Down Expand Up @@ -83,6 +86,15 @@ func (s *jobSyncer) updateStatus(job *batchv1.Job) {
if cond.Status == corev1.ConditionTrue {
s.backup.Status.Completed = true
}
if backupName := s.job.Annotations[utils.JobAnonationName]; backupName != "" {
s.backup.Status.BackupName = backupName
}
if backDate := s.job.Annotations[utils.JobAnonationDate]; backDate != "" {
s.backup.Status.BackupDate = backDate
}
if backType := s.job.Annotations[utils.JobAnonationType]; backType != "" {
s.backup.Status.BackupType = backType
}
}

// check for failed condition
Expand All @@ -92,6 +104,7 @@ func (s *jobSyncer) updateStatus(job *batchv1.Job) {
s.backup.Status.Completed = true
}
}

}

func jobCondition(condType batchv1.JobConditionType, job *batchv1.Job) *batchv1.JobCondition {
Expand All @@ -113,7 +126,7 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec {
sctName := fmt.Sprintf("%s-secret", s.backup.Spec.ClusterName)
in.Containers[0].Name = utils.ContainerBackupName
in.Containers[0].Image = fmt.Sprintf("%s%s", mysqlcluster.GetPrefixFromEnv(), s.backup.Spec.Image)

in.ServiceAccountName = s.backup.Spec.ClusterName
if len(s.backup.Spec.NFSServerAddress) != 0 {
// add volumn about pvc
in.Volumes = []corev1.Volume{
Expand All @@ -131,10 +144,15 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec {
in.Containers[0].Command = []string{
"/bin/bash", "-c", "--",
}
var backupToDir string = utils.BuildBackupName(s.backup.Spec.ClusterName)
backupToDir, DateTime := utils.BuildBackupName(s.backup.Spec.ClusterName)
strAnnonations := fmt.Sprintf(`curl -X PATCH -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -H "Content-Type: application/json-patch+json" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/batch/v1/namespaces/%s/jobs/%s \
-d '[{"op": "add", "path": "/metadata/annotations/backupName", "value": "%s"}, {"op": "add", "path": "/metadata/annotations/backupDate", "value": "%s"}, {"op": "add", "path": "/metadata/annotations/backupType", "value": "NFS"}]';`,
s.backup.Namespace, s.backup.GetNameForJob(), backupToDir, DateTime)
in.Containers[0].Args = []string{
fmt.Sprintf("mkdir -p /backup/%s;"+
"curl --user $BACKUP_USER:$BACKUP_PASSWORD %s/download|xbstream -x -C /backup/%s; exit ${PIPESTATUS[0]}",
"curl --user $BACKUP_USER:$BACKUP_PASSWORD %s/download|xbstream -x -C /backup/%s;"+
strAnnonations+"exit ${PIPESTATUS[0]}",
backupToDir,
s.backup.GetBackupURL(s.backup.Spec.ClusterName, s.backup.Spec.HostName), backupToDir),
}
Expand Down Expand Up @@ -200,6 +218,11 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec {
},
},
},
// Cluster Name for set Anotations.
{
Name: "JOB_NAME",
Value: s.job.Name,
},
}
return in
}
27 changes: 25 additions & 2 deletions charts/mysql-operator/crds/mysql.radondb.com_backups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,20 @@ spec:
singular: backup
scope: Namespaced
versions:
- name: v1alpha1
- additionalPrinterColumns:
- description: The Backup name
jsonPath: .status.backupName
name: BackupName
type: string
- description: The Backup Date time
jsonPath: .status.backupDate
name: BackupDate
type: string
- description: The Backup Type
jsonPath: .status.backupType
name: Type
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: Backup is the Schema for the backups API
Expand All @@ -34,7 +47,8 @@ spec:
metadata:
type: object
spec:
description: BackupSpec defines the desired state of Backup
description: This is the backup Job CRD. BackupSpec defines the desired
state of Backup
properties:
clusterName:
description: ClusterName represents the cluster name to backup
Expand All @@ -61,6 +75,15 @@ spec:
status:
description: BackupStatus defines the observed state of Backup
properties:
backupDate:
description: Get the backup Date
type: string
backupName:
description: Get the backup path.
type: string
backupType:
description: Get the backup Type
type: string
completed:
description: Completed indicates whether the backup is in a final
state, no matter whether its' corresponding job failed or succeeded
Expand Down
27 changes: 25 additions & 2 deletions config/crd/bases/mysql.radondb.com_backups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,20 @@ spec:
singular: backup
scope: Namespaced
versions:
- name: v1alpha1
- additionalPrinterColumns:
- description: The Backup name
jsonPath: .status.backupName
name: BackupName
type: string
- description: The Backup Date time
jsonPath: .status.backupDate
name: BackupDate
type: string
- description: The Backup Type
jsonPath: .status.backupType
name: Type
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: Backup is the Schema for the backups API
Expand All @@ -34,7 +47,8 @@ spec:
metadata:
type: object
spec:
description: BackupSpec defines the desired state of Backup
description: This is the backup Job CRD. BackupSpec defines the desired
state of Backup
properties:
clusterName:
description: ClusterName represents the cluster name to backup
Expand All @@ -61,6 +75,15 @@ spec:
status:
description: BackupStatus defines the observed state of Backup
properties:
backupDate:
description: Get the backup Date
type: string
backupName:
description: Get the backup path.
type: string
backupType:
description: Get the backup Type
type: string
completed:
description: Completed indicates whether the backup is in a final
state, no matter whether its' corresponding job failed or succeeded
Expand Down
5 changes: 5 additions & 0 deletions mysqlcluster/syncer/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ func NewRoleSyncer(cli client.Client, c *mysqlcluster.MysqlCluster) syncer.Inter
APIGroups: []string{""},
Resources: []string{"pods"},
},
{
Verbs: []string{"get", "update", "patch"},
APIGroups: []string{"batch"},
Resources: []string{"jobs"},
},
}
return nil
})
Expand Down
13 changes: 10 additions & 3 deletions sidecar/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ type Config struct {
// for mysql backup
// backup user and password for http endpoint
ClusterName string

// Job name if is backup Job
JobName string
// Backup user name to http Server
BackupUser string

Expand Down Expand Up @@ -232,6 +233,7 @@ func NewReqBackupConfig() *Config {

BackupUser: getEnvValue("BACKUP_USER"),
BackupPassword: getEnvValue("BACKUP_PASSWORD"),
JobName: getEnvValue("JOB_NAME"),
}
}

Expand Down Expand Up @@ -261,7 +263,7 @@ func (cfg *Config) XtrabackupArgs() []string {
}

// Build xbcloud arguments
func (cfg *Config) XCloudArgs() []string {
func (cfg *Config) XCloudArgs(backupName string) []string {
xcloudArgs := []string{
"put",
"--storage=S3",
Expand All @@ -270,12 +272,17 @@ func (cfg *Config) XCloudArgs() []string {
fmt.Sprintf("--s3-secret-key=%s", cfg.XCloudS3SecretKey),
fmt.Sprintf("--s3-bucket=%s", cfg.XCloudS3Bucket),
"--parallel=10",
utils.BuildBackupName(cfg.ClusterName),
// utils.BuildBackupName(cfg.ClusterName),
backupName,
"--insecure",
}
return xcloudArgs
}

func (cfg *Config) XBackupName() (string, string) {
return utils.BuildBackupName(cfg.ClusterName)
}

// buildExtraConfig build a ini file for mysql.
func (cfg *Config) buildExtraConfig(filePath string) (*ini.File, error) {
conf := ini.Empty()
Expand Down
46 changes: 44 additions & 2 deletions sidecar/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package sidecar

import (
"context"
"encoding/json"
"fmt"
"io"
"net"
Expand All @@ -27,6 +28,10 @@ import (
"strings"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

"github.com/radondb/radondb-mysql-kubernetes/utils"
)

Expand Down Expand Up @@ -93,15 +98,17 @@ func (s *server) healthHandler(w http.ResponseWriter, r *http.Request) {

func (s *server) backupHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "keep-alive")
w.Header().Set("content-type", "text/json")
if !s.isAuthenticated(r) {
http.Error(w, "Not authenticated!", http.StatusForbidden)
return
}
err := RunTakeBackupCommand(s.cfg)
backName, Datetime, err := RunTakeBackupCommand(s.cfg)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
w.Write([]byte("OK"))
msg, _ := json.Marshal(utils.JsonResult{Status: backupSuccessful, BackupName: backName, Date: Datetime})
w.Write(msg)
}
}

Expand Down Expand Up @@ -204,6 +211,34 @@ func transportWithTimeout(connectTimeout time.Duration) http.RoundTripper {
}
}

func setAnnonations(cfg *Config, backname string, DateTime string, BackupType string) error {
config, err := rest.InClusterConfig()
if err != nil {
return err
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return err
}

job, err := clientset.BatchV1().Jobs(cfg.NameSpace).Get(context.TODO(), cfg.JobName, metav1.GetOptions{})
if err != nil {
return err
}
if job.Annotations == nil {
job.Annotations = make(map[string]string)
}
job.Annotations[utils.JobAnonationName] = backname
job.Annotations[utils.JobAnonationDate] = DateTime
job.Annotations[utils.JobAnonationType] = BackupType
_, err = clientset.BatchV1().Jobs(cfg.NameSpace).Update(context.TODO(), job, metav1.UpdateOptions{})
if err != nil {
return err
}
return nil
}

// requestABackup connects to specified host and endpoint and gets the backup.
func requestABackup(cfg *Config, host string, endpoint string) (*http.Response, error) {
log.Info("initialize a backup", "host", host, "endpoint", endpoint)
Expand All @@ -227,6 +262,13 @@ func requestABackup(cfg *Config, host string, endpoint string) (*http.Response,
}
return nil, fmt.Errorf("fail to get backup: %s, code: %s", err, status)
}
defer resp.Body.Close()
var result utils.JsonResult
json.NewDecoder(resp.Body).Decode(&result)

err = setAnnonations(cfg, result.BackupName, result.Date, "S3") // set annotation
if err != nil {
return nil, fmt.Errorf("fail to set annotation: %s", err)
}
return resp, nil
}
17 changes: 9 additions & 8 deletions sidecar/takebackup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,28 @@ import (
)

// RunTakeBackupCommand starts a backup command
func RunTakeBackupCommand(cfg *Config) error {
func RunTakeBackupCommand(cfg *Config) (string, string, error) {
// cfg->XtrabackupArgs()
xtrabackup := exec.Command(xtrabackupCommand, cfg.XtrabackupArgs()...)

var err error
xcloud := exec.Command(xcloudCommand, cfg.XCloudArgs()...)
log.Info("xargs ", "xargs", strings.Join(cfg.XCloudArgs(), " "))
backupName, DateTime := cfg.XBackupName()
xcloud := exec.Command(xcloudCommand, cfg.XCloudArgs(backupName)...)
log.Info("xargs ", "xargs", strings.Join(cfg.XCloudArgs(backupName), " "))
if xcloud.Stdin, err = xtrabackup.StdoutPipe(); err != nil {
log.Error(err, "failed to pipline")
return err
return "", "", err
}
xtrabackup.Stderr = os.Stderr
xcloud.Stderr = os.Stderr

if err := xtrabackup.Start(); err != nil {
log.Error(err, "failed to start xtrabackup command")
return err
return "", "", err
}
if err := xcloud.Start(); err != nil {
log.Error(err, "fail start xcloud ")
return err
return "", "", err
}

// pipe command fail one, whole things fail
Expand All @@ -57,8 +58,8 @@ func RunTakeBackupCommand(cfg *Config) error {

for i := 0; i < 2; i++ {
if err = <-errorChannel; err != nil {
return err
return "", "", err
}
}
return nil
return backupName, DateTime, nil
}
Loading

0 comments on commit 171acda

Please sign in to comment.