Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Function BackupDataAllUsingKopiaServer #1968

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
22776b4
Add Function BackupDataAllUsingKopiaServer
r4rajat Mar 21, 2023
4d35878
Sort Imports
r4rajat Mar 21, 2023
b6078ae
Update RequiredArgs
r4rajat Mar 21, 2023
a1462e8
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Mar 22, 2023
673b100
Add Documentation
r4rajat Mar 22, 2023
3a9f7b7
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Mar 23, 2023
3645cb8
Add License
r4rajat Mar 23, 2023
bb3d639
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Mar 29, 2023
2886236
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Apr 10, 2023
0a78314
Merge remote-tracking branch 'origin/master' into backup-data-all-usi…
r4rajat Jun 12, 2023
011ba97
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 12, 2023
b2caa3c
Update documentation
r4rajat Jun 12, 2023
60dc487
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 13, 2023
2f6b5ba
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 19, 2023
0da1e22
Merge branch 'master' into backup-data-all-using-kopia-server
kale-amruta Jun 20, 2023
4386d3a
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 21, 2023
236fbeb
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 23, 2023
4b56db6
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jun 28, 2023
0cd995a
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Jul 13, 2023
455cde7
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Aug 3, 2023
0f793f8
Add repositoryServerUserHostname support for BackupDataAllUsingKopiaS…
r4rajat Aug 3, 2023
5da83db
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Aug 8, 2023
83dea12
Merge branch 'master' into backup-data-all-using-kopia-server
r4rajat Aug 8, 2023
fd1ed6a
Update pkg/function/backup_data_all_using_kopia_server.go
r4rajat Aug 8, 2023
6bf3db2
Update docs/functions.rst
r4rajat Aug 8, 2023
12db6d9
Update docs/functions.rst
r4rajat Aug 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions docs/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,64 @@ For this phase, we will use the ``backupIdentifier`` Artifact provided by backup
backupID: "{{ .ArtifactsIn.backupIdentifier.KeyValue.id }}"
image: ghcr.io/kanisterio/kanister-tools:0.89.0

.. _backupdataallusingkopiaserver:

BackupDataAllUsingKopiaServer
-----------------------------

This function concurrently backs up data from one or more pods into an
object store supported by Kanister using Kopia Repository Server as a data mover.

.. note::
It is important that the application includes a ``kanister-tools``
sidecar container. This sidecar is necessary to run the
tools that backup the volume and store it on the object store.

Additionally, in order to use this function, a RepositoryServer CR is needed
while creating the :ref:`actionsets`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also mention the document that, if we have any, on how to create the repo server CR.

Arguments:

.. csv-table::
:header: "Argument", "Required", "Type", "Description"
:align: left
:widths: 5,5,5,15

`namespace`, Yes, `string`, namespace of the container that you want to backup the data of
`pods`, No, `string`, pods in which you want to backup the data of (by default runs on all the pods)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we specify names of pods separated by comma (,).

`container`, Yes, `string`, name of the kanister sidecar container
`includePath`, Yes, `string`, path of the data to be backed up
`repositoryServerUserHostname`, No, `string`, user's hostname to access the kopia repository server. Hostname would be available in the user access credential secret

Outputs:

.. csv-table::
:header: "Output", "Type", "Description"
:align: left
:widths: 5,5,15

`BackupAllUsingKopiaServerInfo`,`string`, info about snapshot id required for restore

Example:

.. code-block:: yaml
:linenos:

actions:
backup-all:
outputArtifacts:
backupIdentifier:
keyValue:
backupInfo: "{{ .Phases.backupToS3All.Output.BackupAllUsingKopiaServerInfo }}"
phases:
- func: BackupDataAllUsingKopiaServer
name: backupToS3All
args:
namespace: "{{ .Deployment.Namespace }}"
pods: "{{ index .Deployment.Pods 0 }}"
container: kanister-sidecar
includePath: /var/log

Registering Functions
---------------------

Expand Down
167 changes: 167 additions & 0 deletions pkg/function/backup_data_all_using_kopia_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright 2023 The Kanister 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 function

import (
"context"
"encoding/json"
"strings"

"github.com/pkg/errors"
"k8s.io/client-go/kubernetes"

kanister "github.com/kanisterio/kanister/pkg"
"github.com/kanisterio/kanister/pkg/consts"
"github.com/kanisterio/kanister/pkg/field"
kankopia "github.com/kanisterio/kanister/pkg/kopia"
"github.com/kanisterio/kanister/pkg/kube"
"github.com/kanisterio/kanister/pkg/param"
)

const (
// BackupDataAllUsingKopiaServerFuncName gives the name of the function
BackupDataAllUsingKopiaServerFuncName = "BackupDataAllUsingKopiaServer"
BackupDataAllUsingKopiaServerOutput = "BackupAllUsingKopiaServerInfo"
)

type backupDataAllUsingKopiaServerFunc struct{}

func init() {
_ = kanister.Register(&backupDataAllUsingKopiaServerFunc{})
}

func (*backupDataAllUsingKopiaServerFunc) Name() string {
return BackupDataAllUsingKopiaServerFuncName
}

func (*backupDataAllUsingKopiaServerFunc) RequiredArgs() []string {
return []string{
BackupDataAllNamespaceArg,
BackupDataAllContainerArg,
BackupDataAllIncludePathArg,
}
}

func (*backupDataAllUsingKopiaServerFunc) Arguments() []string {
return []string{
BackupDataAllNamespaceArg,
BackupDataAllContainerArg,
BackupDataAllIncludePathArg,
BackupDataAllPodsArg,
KopiaRepositoryServerUserHostname,
}
}

func (*backupDataAllUsingKopiaServerFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) {
var namespace, pods, container, includePath, userHostname string
var err error
if err = Arg(args, BackupDataAllNamespaceArg, &namespace); err != nil {
return nil, err
}
if err = Arg(args, BackupDataAllContainerArg, &container); err != nil {
return nil, err
}
if err = Arg(args, BackupDataAllIncludePathArg, &includePath); err != nil {
return nil, err
}
if err = OptArg(args, BackupDataAllPodsArg, &pods, ""); err != nil {
return nil, err
}
if err = OptArg(args, KopiaRepositoryServerUserHostname, &userHostname, ""); err != nil {
return nil, err
}

cli, err := kube.NewClient()
if err != nil {
return nil, errors.Wrapf(err, "Failed to create Kubernetes client")
}
var ps []string
if pods == "" {
switch {
case tp.Deployment != nil:
ps = tp.Deployment.Pods
case tp.StatefulSet != nil:
ps = tp.StatefulSet.Pods
default:
return nil, errors.New("Failed to get pods")
}
} else {
ps = strings.Fields(pods)
}
ctx = field.Context(ctx, consts.ContainerNameKey, container)
userPassphrase, cert, err := userCredentialsAndServerTLS(&tp)
if err != nil {
return nil, errors.Wrap(err, "Failed to fetch User Credentials/Certificate Data from Template Params")
}

fingerprint, err := kankopia.ExtractFingerprintFromCertificateJSON(cert)
if err != nil {
return nil, errors.Wrap(err, "Failed to fetch Kopia API Server Certificate Secret Data from Certificate")
}

hostname, userAccessPassphrase, err := hostNameAndUserPassPhraseFromRepoServer(userPassphrase, userHostname)
if err != nil {
return nil, errors.Wrap(err, "Failed to fetch Hostname/User Passphrase from Secret")
}
return backupDataAllUsingKopiaServer(cli, ctx, namespace, container, includePath, hostname, tp.RepositoryServer.Address, fingerprint, tp.RepositoryServer.Username, userAccessPassphrase, ps)
}

func backupDataAllUsingKopiaServer(
cli kubernetes.Interface,
ctx context.Context,
namespace,
container,
includePath,
hostname,
serverAddress,
fingerprint,
username,
userPassphrase string,
ps []string,
) (map[string]interface{}, error) {
errChan := make(chan error, len(ps))
outChan := make(chan BackupInfo, len(ps))
Output := make(map[string]BackupInfo)

for _, pod := range ps {
go func(pod string, container string) {
ctx = field.Context(ctx, consts.PodNameKey, pod)
backupOutputs, err := backupDataUsingKopiaServer(cli, container, hostname, includePath, namespace, pod, serverAddress, fingerprint, username, userPassphrase, nil)
errChan <- errors.Wrapf(err, "Failed to backup data for pod %s", pod)
outChan <- BackupInfo{PodName: pod, BackupID: backupOutputs.SnapshotID}
}(pod, container)
}
errs := make([]string, 0, len(ps))
for i := 0; i < len(ps); i++ {
err := <-errChan
output := <-outChan
if err != nil {
errs = append(errs, err.Error())
} else {
Output[output.PodName] = output
}
}
if len(errs) != 0 {
return nil, errors.New(strings.Join(errs, "\n"))
}
manifestData, err := json.Marshal(Output)
if err != nil {
return nil, errors.Wrapf(err, "Failed to encode JSON data")
}
return map[string]interface{}{
BackupDataAllUsingKopiaServerOutput: string(manifestData),
FunctionOutputVersion: kanister.DefaultVersion,
}, nil
}
Loading