Skip to content

Commit

Permalink
Merge pull request #820 from k8up-io/add/container-annotation
Browse files Browse the repository at this point in the history
adding new annotation to specify in which container inside pod backup should run
  • Loading branch information
wejdross authored Mar 8, 2023
2 parents 79dbeb2 + b57c97d commit 13973b7
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 25 deletions.
4 changes: 3 additions & 1 deletion cmd/restic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ var (

&cli.StringFlag{Destination: &cfg.Config.BackupCommandAnnotation, Name: "backupCommandAnnotation", EnvVars: []string{"BACKUPCOMMAND_ANNOTATION"}, Usage: "Defines the command to invoke when doing a backup via STDOUT."},
&cli.StringFlag{Destination: &cfg.Config.BackupFileExtensionAnnotation, Name: "fileExtensionAnnotation", EnvVars: []string{"FILEEXTENSION_ANNOTATION"}, Usage: "Defines the file extension to use for STDOUT backups."},
&cli.StringFlag{Destination: &cfg.Config.BackupContainerAnnotation, Name: "backucontainerannotation", EnvVars: []string{"BACKUP_CONTAINERANNOTATION"}, Value: "k8up.io/backupcommand-container", Usage: "set the annotation name that specify the backup container inside the Pod"},

&cli.StringFlag{Destination: &cfg.Config.PromURL, Name: "promURL", EnvVars: []string{"PROM_URL"}, Usage: "Sets the URL of a prometheus push gateway to report metrics."},
&cli.StringFlag{Destination: &cfg.Config.WebhookURL, Name: "webhookURL", Aliases: []string{"statsURL"}, EnvVars: []string{"STATS_URL"}, Usage: "Sets the URL of a server which will retrieve a webhook after the action completes."},

Expand Down Expand Up @@ -245,7 +247,7 @@ func backupAnnotatedPods(ctx context.Context, resticCLI *resticCli.Restic, mainL
return nil
}

podLister := kubernetes.NewPodLister(ctx, cfg.Config.BackupCommandAnnotation, cfg.Config.BackupFileExtensionAnnotation, cfg.Config.Hostname, cfg.Config.TargetPods, mainLogger)
podLister := kubernetes.NewPodLister(ctx, cfg.Config.BackupCommandAnnotation, cfg.Config.BackupFileExtensionAnnotation, cfg.Config.BackupContainerAnnotation, cfg.Config.Hostname, cfg.Config.TargetPods, mainLogger)
podList, err := podLister.ListPods()
if err != nil {
mainLogger.Error(err, "could not list pods", "namespace", cfg.Config.Hostname)
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/examples/usage/restic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ OPTIONS:
--tag value [ --tag value ] List of tags to consider for given operation
--backupCommandAnnotation value Defines the command to invoke when doing a backup via STDOUT. [$BACKUPCOMMAND_ANNOTATION]
--fileExtensionAnnotation value Defines the file extension to use for STDOUT backups. [$FILEEXTENSION_ANNOTATION]
--backucontainerannotation value set the annotation name that specify the backup container inside the Pod (default: "k8up.io/backupcommand-container") [$BACKUP_CONTAINERANNOTATION]
--promURL value Sets the URL of a prometheus push gateway to report metrics. [$PROM_URL]
--webhookURL value, --statsURL value Sets the URL of a server which will retrieve a webhook after the action completes. [$STATS_URL]
--backupDir value Set from which directory the backup should be performed. (default: "/data") [$BACKUP_DIR]
Expand Down
25 changes: 25 additions & 0 deletions docs/modules/ROOT/pages/how-tos/application-aware-backups.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,28 @@ $ ./mongorestore -u root -p <root-pw> mongodb://localhost:27017 --archive=mydata
2022-02-14T18:07:21.711+0100 79020 document(s) restored successfully. 0 document(s) failed to restore.
----
====

== Pick a specific container

Using `k8up.io/backupcommand-container` annotation You can specify the container name inside the pod where the command will get executed.

[source,yaml]
----
<SNIP>
template:
metadata:
labels:
app: my-db
annotations:
k8up.io/backupcommand: sh -c 'PGDATABASE="$POSTGRES_DB" PGUSER="$POSTGRES_USER" PGPASSWORD="$POSTGRES_PASSWORD" pg_dump --clean'
k8up.io/file-extension: .sql
k8up.io/backupcommand-container: postgres
spec:
containers:
- name: pgbouncer
- name: postgres
- name: prometheus-exporter
...
<SNIP>
----
6 changes: 6 additions & 0 deletions docs/modules/ROOT/pages/references/annotations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ See xref:references/operator-config-reference.adoc[Operator Configuration refere
|A string which is valid file-extension on the source system, e.g. `.sql`.
|`Pod`
|`BACKUP_FILEEXTENSIONANNOTATION`

|`k8up.io/backupcommand-container`
|Specify in which container inside pod backup should be done
|A string which is valid pod name.
|`Pod`
|`BACKUP_CONTAINERANNOTATION`
|===
7 changes: 7 additions & 0 deletions e2e/definitions/annotated-subject/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ spec:
annotations:
k8up.io/backupcommand: '/tmp/test.sh'
k8up.io/file-extension: '.txt'
k8up.io/backupcommand-container: subject-container
spec:
containers:
- image: busybox
name: dummy-container-blocking-first-position
command:
- "/bin/sh"
- "-c"
- "sleep infinity"
- name: subject-container
image: quay.io/prometheus/busybox:latest
imagePullPolicy: IfNotPresent
Expand Down
2 changes: 1 addition & 1 deletion e2e/lib/k8up.bash
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ given_an_annotated_subject() {
export BACKUP_FILE_CONTENT=${2}

kubectl apply -f definitions/pv/pvc.yaml
yq e '.spec.template.spec.containers[0].securityContext.runAsUser='$(id -u)' | .spec.template.spec.containers[0].env[0].value=strenv(BACKUP_FILE_CONTENT) | .spec.template.spec.containers[0].env[1].value=strenv(BACKUP_FILE_NAME)' definitions/annotated-subject/deployment.yaml | kubectl apply -f -
yq e '.spec.template.spec.containers[1].securityContext.runAsUser='$(id -u)' | .spec.template.spec.containers[1].env[0].value=strenv(BACKUP_FILE_CONTENT) | .spec.template.spec.containers[1].env[1].value=strenv(BACKUP_FILE_NAME)' definitions/annotated-subject/deployment.yaml | kubectl apply -f -

echo "✅ The annotated subject is ready"
}
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions operator/cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var (
type Configuration struct {
MountPath string
BackupAnnotation string
BackupContainerAnnotation string
BackupCommandAnnotation string
FileExtensionAnnotation string
ServiceAccount string
Expand Down
1 change: 1 addition & 0 deletions restic/cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Configuration struct {

BackupCommandAnnotation string
BackupFileExtensionAnnotation string
BackupContainerAnnotation string
BackupDir string

PromURL string
Expand Down
3 changes: 0 additions & 3 deletions restic/kubernetes/pod_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ type ExecData struct {
// PodExec sends the command to the specified pod
// and returns a bytes buffer with the stdout
func PodExec(pod BackupPod, log logr.Logger) (*ExecData, error) {

execLogger := log.WithName("k8sExec")

config, _ := getClientConfig()
k8sclient, err := kubernetes.NewForConfig(config)
if err != nil {
Expand All @@ -44,7 +42,6 @@ func PodExec(pod BackupPod, log logr.Logger) (*ExecData, error) {

command := qsplit.ToStrings([]byte(pod.Command))
execLogger.Info("executing command", "command", strings.Join(command, ", "), "namespace", pod.Namespace, "pod", pod.PodName)

parameterCodec := runtime.NewParameterCodec(scheme)
req.VersionedParams(&apiv1.PodExecOptions{
Command: command,
Expand Down
47 changes: 27 additions & 20 deletions restic/kubernetes/pod_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import (

// PodLister holds the state for listing the pods.
type PodLister struct {
backupCommandAnnotation string
fileExtensionAnnotation string
k8scli *kube.Clientset
err error
namespace string
targetPods map[string]struct{}
log logr.Logger
ctx context.Context
backupCommandAnnotation string
backupContainerAnnotation string
fileExtensionAnnotation string
k8scli *kube.Clientset
err error
namespace string
targetPods map[string]struct{}
log logr.Logger
ctx context.Context
}

// BackupPod contains all information nessecary to execute the backupcommands.
Expand All @@ -33,7 +34,7 @@ type BackupPod struct {
}

// NewPodLister returns a PodLister configured to find the defined annotations.
func NewPodLister(ctx context.Context, backupCommandAnnotation, fileExtensionAnnotation, namespace string, targetPods []string, log logr.Logger) *PodLister {
func NewPodLister(ctx context.Context, backupCommandAnnotation, fileExtensionAnnotation, backupContainerAnnotation, namespace string, targetPods []string, log logr.Logger) *PodLister {
k8cli, err := newk8sClient()
if err != nil {
err = fmt.Errorf("can't create podLister: %v", err)
Expand All @@ -45,14 +46,15 @@ func NewPodLister(ctx context.Context, backupCommandAnnotation, fileExtensionAnn
}

return &PodLister{
backupCommandAnnotation: backupCommandAnnotation,
fileExtensionAnnotation: fileExtensionAnnotation,
k8scli: k8cli,
err: err,
namespace: namespace,
targetPods: tp,
log: log.WithName("k8sClient"),
ctx: ctx,
backupCommandAnnotation: backupCommandAnnotation,
backupContainerAnnotation: backupContainerAnnotation,
fileExtensionAnnotation: fileExtensionAnnotation,
k8scli: k8cli,
err: err,
namespace: namespace,
targetPods: tp,
log: log.WithName("k8sClient"),
ctx: ctx,
}
}

Expand All @@ -72,6 +74,13 @@ func (p *PodLister) ListPods() ([]BackupPod, error) {
foundPods := make([]BackupPod, 0)
sameOwner := make(map[string]bool)
for _, pod := range pods.Items {
var execInContainer = pod.Spec.Containers[0].Name
annotations := pod.GetAnnotations()

if execInContainerName, ok := annotations[p.backupContainerAnnotation]; ok {
execInContainer = execInContainerName
}

if pod.Status.Phase != corev1.PodRunning {
continue
}
Expand All @@ -83,8 +92,6 @@ func (p *PodLister) ListPods() ([]BackupPod, error) {
continue
}

annotations := pod.GetAnnotations()

if command, ok := annotations[p.backupCommandAnnotation]; ok {

fileExtension := annotations[p.fileExtensionAnnotation]
Expand All @@ -98,7 +105,7 @@ func (p *PodLister) ListPods() ([]BackupPod, error) {
foundPods = append(foundPods, BackupPod{
Command: command,
PodName: pod.Name,
ContainerName: pod.Spec.Containers[0].Name,
ContainerName: execInContainer,
Namespace: p.namespace,
FileExtension: fileExtension,
})
Expand Down

0 comments on commit 13973b7

Please sign in to comment.