Skip to content

Commit

Permalink
[cherry-pick] Dump PVC storage information (#1379) (#1385)
Browse files Browse the repository at this point in the history
Signed-off-by: hmsayem <hmsayem@appscode.com>
  • Loading branch information
1gtm committed May 29, 2023
1 parent 1a05c3a commit 54525e6
Show file tree
Hide file tree
Showing 24 changed files with 6,695 additions and 2,856 deletions.
8 changes: 4 additions & 4 deletions go.mod
Expand Up @@ -13,10 +13,10 @@ require (
k8s.io/apimachinery v0.25.3
k8s.io/client-go v0.25.1
k8s.io/klog/v2 v2.80.1
kmodules.xyz/client-go v0.25.19
kmodules.xyz/client-go v0.25.23
kmodules.xyz/custom-resources v0.25.0
kmodules.xyz/offshoot-api v0.25.0
stash.appscode.dev/apimachinery v0.29.0
kmodules.xyz/offshoot-api v0.25.3
stash.appscode.dev/apimachinery v0.29.1-0.20230529131221-1e979c48da10
)

require (
Expand Down Expand Up @@ -69,7 +69,7 @@ require (
golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.1.0 // indirect
gomodules.xyz/clock v0.0.0-20200817085942-06523dba733f // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
gomodules.xyz/mergo v0.3.13 // indirect
gomodules.xyz/pointer v0.1.0 // indirect
gomodules.xyz/sets v0.2.1 // indirect
Expand Down
17 changes: 8 additions & 9 deletions go.sum
Expand Up @@ -117,7 +117,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
Expand Down Expand Up @@ -672,8 +671,8 @@ gomodules.xyz/flags v0.1.3 h1:jQ06+EfmoMv5NvjXvJon03dOhLU+FF0TQMWN7I6qpzs=
gomodules.xyz/flags v0.1.3/go.mod h1:e+kvBLnqdEWGG670SKOYag1CXStM2Slrxq01OIK3tFs=
gomodules.xyz/go-sh v0.1.0 h1:1BJAuGREh2RhePt7HRrpmjnkbgfpXlCzc42SiyZ5dkc=
gomodules.xyz/go-sh v0.1.0/go.mod h1:N8IrjNiYppUI/rxENYrWD6FOrSxSyEZnIekPEWM7LP0=
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc=
gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
gomodules.xyz/logs v0.0.6 h1:8+9Wkud5yBPtIvkVszubyTeFxNII30lWODom0+GZD8U=
gomodules.xyz/logs v0.0.6/go.mod h1:Q+fFtZFLEB5q86KmDehXCGuMP72Rv+Rwz0KuVxK+Gi4=
gomodules.xyz/mergo v0.3.13 h1:q6cL/MMXZH/MrR2+yjSihFFq6UifXqjwaqI48B6cMEM=
Expand Down Expand Up @@ -874,14 +873,14 @@ k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsM
k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
kmodules.xyz/apiversion v0.2.0 h1:vAQYqZFm4xu4pbB1cAdHbFEPES6EQkcR4wc06xdTOWk=
kmodules.xyz/apiversion v0.2.0/go.mod h1:oPX8g8LvlPdPX3Yc5YvCzJHQnw3YF/X4/jdW0b1am80=
kmodules.xyz/client-go v0.25.19 h1:IYWixMIGMqS17LHYyiDxw8U/3ml6igI8QsYAdToYBGc=
kmodules.xyz/client-go v0.25.19/go.mod h1:KWVpgjAj/VNzCzj/BihpJkY39aWIqe8qMp5RuPsuSJA=
kmodules.xyz/client-go v0.25.23 h1:qz5XJYHLVZUowqfRXEJD7JQ4iaLLzQ1O1zPMmsdrkJw=
kmodules.xyz/client-go v0.25.23/go.mod h1:wbdzLEoDYiCPI6dTW0mIAGNwkwFV4lC5BN1FJxiDsbw=
kmodules.xyz/custom-resources v0.25.0 h1:5JQTEuiv6lC/+VVFNKqzfX4YtJCmN5E7mcNtGnHGVQM=
kmodules.xyz/custom-resources v0.25.0/go.mod h1:ULwzvLmOqZJcPSXKI7iLclYL5eYRlKx8Nbex28Ht19E=
kmodules.xyz/objectstore-api v0.25.1-0.20221104003322-f0289b5b6ca2 h1:efc0glYeBw+ok5s5ZecKdB9zgnRo/IvsLlSaQUPQjZE=
kmodules.xyz/objectstore-api v0.25.1-0.20221104003322-f0289b5b6ca2/go.mod h1:X5aCkyU91p9TOn4jcWw0cfcJL0HCKd/Z6FJHdzKz1ZU=
kmodules.xyz/offshoot-api v0.25.0 h1:Svq9da/+sg5afOjpgo9vx2J/Lu90Mo0aFxkdQmgKnGI=
kmodules.xyz/offshoot-api v0.25.0/go.mod h1:ysEBn7LJuT3+s8ynAQA/OG0BSsJugXa6KGtDLMRjlKo=
kmodules.xyz/offshoot-api v0.25.3 h1:KOGzW+TRHJvZ/KauvVQfKHztHF3HTHQ0ibbCHB3sWRg=
kmodules.xyz/offshoot-api v0.25.3/go.mod h1:PUk4EuJFhhyQykCflHj7EgXcljGIqs9vi0IN0RpxtY4=
kmodules.xyz/prober v0.25.0 h1:R5uRLHJEvEtEoogj+vaTAob0Btph6+PX5IlS6hPh8PA=
kmodules.xyz/prober v0.25.0/go.mod h1:z4RTnjaajNQa/vPltsiOnO3xI716I/ziD2ac2Exm+1M=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
Expand All @@ -895,5 +894,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kF
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
stash.appscode.dev/apimachinery v0.29.0 h1:36ijvg31s2UDQIAN2lnYY76E9dydUADutTkkErqokAQ=
stash.appscode.dev/apimachinery v0.29.0/go.mod h1:7ao2U0Jgntc10uRf9KRmzXuabO12Nm7+2WpcRE/ZXWo=
stash.appscode.dev/apimachinery v0.29.1-0.20230529131221-1e979c48da10 h1:QOMoITdH8rqfDD1biUw334dpWieMXl4hzBcgkd1//gk=
stash.appscode.dev/apimachinery v0.29.1-0.20230529131221-1e979c48da10/go.mod h1:7ao2U0Jgntc10uRf9KRmzXuabO12Nm7+2WpcRE/ZXWo=
44 changes: 43 additions & 1 deletion pkg/backup.go
Expand Up @@ -19,11 +19,14 @@ package pkg
import (
"context"
"fmt"
"os"
"path/filepath"
"time"

"stash.appscode.dev/apimachinery/apis"
api_v1beta1 "stash.appscode.dev/apimachinery/apis/stash/v1beta1"
stash "stash.appscode.dev/apimachinery/client/clientset/versioned"
"stash.appscode.dev/apimachinery/pkg/invoker"
"stash.appscode.dev/apimachinery/pkg/restic"
api_util "stash.appscode.dev/apimachinery/pkg/util"

Expand All @@ -34,6 +37,7 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
meta_util "kmodules.xyz/client-go/meta"
appcatalog "kmodules.xyz/custom-resources/apis/appcatalog/v1alpha1"
appcatalog_cs "kmodules.xyz/custom-resources/client/clientset/versioned"
v1 "kmodules.xyz/offshoot-api/api/v1"
Expand Down Expand Up @@ -235,6 +239,9 @@ func (opt *esOptions) backupElasticsearch(targetRef api_v1beta1.TargetRef) (*res
return nil, err
}

if err := opt.dumpPVCStorageLimit(targetRef); err != nil {
return nil, fmt.Errorf("failed to dump pvc storage limit info %w", err)
}
// dumped data has been stored in the interim data dir. Now, we will backup this directory using Stash.
opt.backupOptions.BackupPaths = []string{opt.interimDataDir}

Expand All @@ -243,6 +250,41 @@ func (opt *esOptions) backupElasticsearch(targetRef api_v1beta1.TargetRef) (*res
if err != nil {
return nil, err
}

return resticWrapper.RunBackup(opt.backupOptions, targetRef)
}

func (opt *esOptions) dumpPVCStorageLimit(targetRef api_v1beta1.TargetRef) error {
bs, err := opt.stashClient.StashV1beta1().BackupSessions(opt.namespace).Get(context.TODO(), opt.backupSessionName, metav1.GetOptions{})
if err != nil {
return err
}

bi, err := invoker.NewBackupInvoker(opt.stashClient, bs.Spec.Invoker.Kind, bs.Spec.Invoker.Name, opt.namespace)
if err != nil {
return err
}

var pvcName string
for _, info := range bi.GetTargetInfo() {
if info.Target != nil && targetMatched(info.Target.Ref, targetRef.Kind, targetRef.Name, targetRef.Namespace) {
if info.InterimVolumeTemplate == nil {
return nil
}
pvcName = meta_util.ValidNameWithPrefix(opt.backupSessionName, info.InterimVolumeTemplate.Name)
}
}

klog.Infoln("Dumping pvc storage limit info")
pvc, err := opt.kubeClient.CoreV1().PersistentVolumeClaims(opt.namespace).Get(context.TODO(), pvcName, metav1.GetOptions{})
if err != nil {
return err
}
storageSize := pvc.Spec.Resources.Requests.Storage().String()

// Write PVC storage limit info to file
return os.WriteFile(filepath.Join(opt.interimDataDir, apis.ESMetaFile), []byte(storageSize), os.ModePerm)
}

func targetMatched(tref api_v1beta1.TargetRef, expectedKind, expectedName, expectedNamespace string) bool {
return tref.Kind == expectedKind && tref.Namespace == expectedNamespace && tref.Name == expectedName
}
17 changes: 16 additions & 1 deletion pkg/restore.go
Expand Up @@ -19,8 +19,10 @@ package pkg
import (
"context"
"fmt"
"os"
"path/filepath"

"stash.appscode.dev/apimachinery/apis"
api_v1beta1 "stash.appscode.dev/apimachinery/apis/stash/v1beta1"
"stash.appscode.dev/apimachinery/pkg/restic"

Expand Down Expand Up @@ -211,9 +213,13 @@ func (opt *esOptions) restoreElasticsearch(targetRef api_v1beta1.TargetRef) (*re
return nil, err
}

// delete the metadata file as it is not required for restoring the dumps
if err := clearFile(filepath.Join(opt.interimDataDir, apis.ESMetaFile)); err != nil {
return nil, err
}

// run separate shell to restore indices
// klog.Infoln("Performing multielasticdump on", hostname)

session.sh.ShowCMD = false
session.setUserArgs(opt.esArgs)
session.sh.Command(session.cmd.Name, session.cmd.Args...) // xref: multielasticdump: https://github.com/taskrabbit/elasticsearch-dump#multielasticdump
Expand All @@ -223,3 +229,12 @@ func (opt *esOptions) restoreElasticsearch(targetRef api_v1beta1.TargetRef) (*re
}
return restoreOutput, nil
}

func clearFile(filepath string) error {
if _, err := os.Stat(filepath); err == nil {
if err := os.Remove(filepath); err != nil {
return fmt.Errorf("unable to clean file: %v. Reason: %v", filepath, err)
}
}
return nil
}
1 change: 1 addition & 0 deletions pkg/utils.go
Expand Up @@ -148,6 +148,7 @@ func clearDir(dir string) error {
if err := os.RemoveAll(dir); err != nil {
return fmt.Errorf("unable to clean datadir: %v. Reason: %v", dir, err)
}

return os.MkdirAll(dir, os.ModePerm)
}

Expand Down
160 changes: 34 additions & 126 deletions vendor/gomodules.xyz/jsonpatch/v2/jsonpatch.go
@@ -1,7 +1,6 @@
package jsonpatch

import (
"bytes"
"encoding/json"
"fmt"
"reflect"
Expand All @@ -24,21 +23,28 @@ func (j *Operation) Json() string {
}

func (j *Operation) MarshalJSON() ([]byte, error) {
var b bytes.Buffer
b.WriteString("{")
b.WriteString(fmt.Sprintf(`"op":"%s"`, j.Operation))
b.WriteString(fmt.Sprintf(`,"path":"%s"`, j.Path))
// Consider omitting Value for non-nullable operations.
if j.Value != nil || j.Operation == "replace" || j.Operation == "add" {
v, err := json.Marshal(j.Value)
if err != nil {
return nil, err
}
b.WriteString(`,"value":`)
b.Write(v)
}
b.WriteString("}")
return b.Bytes(), nil
// Ensure for add and replace we emit `value: null`
if j.Value == nil && (j.Operation == "replace" || j.Operation == "add") {
return json.Marshal(struct {
Operation string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value"`
}{
Operation: j.Operation,
Path: j.Path,
})
}
// otherwise just marshal normally. We cannot literally do json.Marshal(j) as it would be recursively
// calling this function.
return json.Marshal(struct {
Operation string `json:"op"`
Path string `json:"path"`
Value interface{} `json:"value,omitempty"`
}{
Operation: j.Operation,
Path: j.Path,
Value: j.Value,
})
}

type ByPath []Operation
Expand Down Expand Up @@ -149,9 +155,6 @@ func makePath(path string, newPart interface{}) string {
if path == "" {
return "/" + key
}
if strings.HasSuffix(path, "/") {
return path + key
}
return path + "/" + key
}

Expand Down Expand Up @@ -211,22 +214,18 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
}
case []interface{}:
bt := bv.([]interface{})
if isSimpleArray(at) && isSimpleArray(bt) {
patch = append(patch, compareEditDistance(at, bt, p)...)
} else {
n := min(len(at), len(bt))
for i := len(at) - 1; i >= n; i-- {
patch = append(patch, NewOperation("remove", makePath(p, i), nil))
}
for i := n; i < len(bt); i++ {
patch = append(patch, NewOperation("add", makePath(p, i), bt[i]))
}
for i := 0; i < n; i++ {
var err error
patch, err = handleValues(at[i], bt[i], makePath(p, i), patch)
if err != nil {
return nil, err
}
n := min(len(at), len(bt))
for i := len(at) - 1; i >= n; i-- {
patch = append(patch, NewOperation("remove", makePath(p, i), nil))
}
for i := n; i < len(bt); i++ {
patch = append(patch, NewOperation("add", makePath(p, i), bt[i]))
}
for i := 0; i < n; i++ {
var err error
patch, err = handleValues(at[i], bt[i], makePath(p, i), patch)
if err != nil {
return nil, err
}
}
default:
Expand All @@ -235,100 +234,9 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
return patch, nil
}

func isBasicType(a interface{}) bool {
switch a.(type) {
case string, float64, bool:
default:
return false
}
return true
}

func isSimpleArray(a []interface{}) bool {
for i := range a {
switch a[i].(type) {
case string, float64, bool:
default:
val := reflect.ValueOf(a[i])
if val.Kind() == reflect.Map {
for _, k := range val.MapKeys() {
av := val.MapIndex(k)
if av.Kind() == reflect.Ptr || av.Kind() == reflect.Interface {
if av.IsNil() {
continue
}
av = av.Elem()
}
if av.Kind() != reflect.String && av.Kind() != reflect.Float64 && av.Kind() != reflect.Bool {
return false
}
}
return true
}
return false
}
}
return true
}

// https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm
// Adapted from https://github.com/texttheater/golang-levenshtein
func compareEditDistance(s, t []interface{}, p string) []Operation {
m := len(s)
n := len(t)

d := make([][]int, m+1)
for i := 0; i <= m; i++ {
d[i] = make([]int, n+1)
d[i][0] = i
}
for j := 0; j <= n; j++ {
d[0][j] = j
}

for j := 1; j <= n; j++ {
for i := 1; i <= m; i++ {
if reflect.DeepEqual(s[i-1], t[j-1]) {
d[i][j] = d[i-1][j-1] // no op required
} else {
del := d[i-1][j] + 1
add := d[i][j-1] + 1
rep := d[i-1][j-1] + 1
d[i][j] = min(rep, min(add, del))
}
}
}

return backtrace(s, t, p, m, n, d)
}

func min(x int, y int) int {
if y < x {
return y
}
return x
}

func backtrace(s, t []interface{}, p string, i int, j int, matrix [][]int) []Operation {
if i > 0 && matrix[i-1][j]+1 == matrix[i][j] {
op := NewOperation("remove", makePath(p, i-1), nil)
return append([]Operation{op}, backtrace(s, t, p, i-1, j, matrix)...)
}
if j > 0 && matrix[i][j-1]+1 == matrix[i][j] {
op := NewOperation("add", makePath(p, i), t[j-1])
return append([]Operation{op}, backtrace(s, t, p, i, j-1, matrix)...)
}
if i > 0 && j > 0 && matrix[i-1][j-1]+1 == matrix[i][j] {
if isBasicType(s[0]) {
op := NewOperation("replace", makePath(p, i-1), t[j-1])
return append([]Operation{op}, backtrace(s, t, p, i-1, j-1, matrix)...)
}

p2, _ := handleValues(s[i-1], t[j-1], makePath(p, i-1), []Operation{})
return append(p2, backtrace(s, t, p, i-1, j-1, matrix)...)
}
if i > 0 && j > 0 && matrix[i-1][j-1] == matrix[i][j] {
return backtrace(s, t, p, i-1, j-1, matrix)
}
return []Operation{}
}

0 comments on commit 54525e6

Please sign in to comment.