Skip to content

Commit

Permalink
Use Repository data in Recovery CRD (#436)
Browse files Browse the repository at this point in the history
This PR enable user to use Repository data in Recovery CRD so that they don't have to repeat backend info.

Task List:
- [x] Code for using Repository data in Recovery CRD
- [x] Update tests
- [x] Update docs
- [x] Update examples
  • Loading branch information
Md. Emruz Hossain authored and tamalsaha committed Apr 21, 2018
1 parent 1f71dae commit 2af3de2
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 217 deletions.
32 changes: 12 additions & 20 deletions apis/stash/v1alpha1/openapi_generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,17 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"backend": {
"repository": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/appscode/stash/apis/stash/v1alpha1.Backend"),
Type: []string{"string"},
Format: "",
},
},
"snapshot": {
SchemaProps: spec.SchemaProps{
Description: "Snapshot to recover. Default is latest snapshot.",
Type: []string{"string"},
Format: "",
},
},
"paths": {
Expand All @@ -492,23 +500,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
},
},
},
"workload": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/appscode/stash/apis/stash/v1alpha1.LocalTypedReference"),
},
},
"podOrdinal": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"nodeName": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"recoveredVolumes": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Expand All @@ -534,10 +525,11 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
},
},
},
Required: []string{"repository"},
},
},
Dependencies: []string{
"github.com/appscode/stash/apis/stash/v1alpha1.Backend", "github.com/appscode/stash/apis/stash/v1alpha1.LocalSpec", "github.com/appscode/stash/apis/stash/v1alpha1.LocalTypedReference", "k8s.io/api/core/v1.LocalObjectReference"},
"github.com/appscode/stash/apis/stash/v1alpha1.LocalSpec", "k8s.io/api/core/v1.LocalObjectReference"},
},
"github.com/appscode/stash/apis/stash/v1alpha1.RecoveryStatus": {
Schema: spec.Schema{
Expand Down
8 changes: 4 additions & 4 deletions apis/stash/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ type Recovery struct {
}

type RecoverySpec struct {
Backend Backend `json:"backend,omitempty"`
Repository string `json:"repository"`
// Snapshot to recover. Default is latest snapshot.
// +optional
Snapshot string `json:"snapshot,omitempty"`
Paths []string `json:"paths,omitempty"`
Workload LocalTypedReference `json:"workload,omitempty"`
PodOrdinal string `json:"podOrdinal,omitempty"`
NodeName string `json:"nodeName,omitempty"`
RecoveredVolumes []LocalSpec `json:"recoveredVolumes,omitempty"`
ImagePullSecrets []core.LocalObjectReference `json:"imagePullSecrets,omitempty"`
}
Expand Down
38 changes: 14 additions & 24 deletions apis/stash/v1alpha1/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1alpha1

import (
"fmt"
"strings"

"gopkg.in/robfig/cron.v2"
)
Expand Down Expand Up @@ -35,38 +36,27 @@ func (r Restic) IsValid() error {
}

func (r Recovery) IsValid() error {
if r.Spec.Backend.StorageSecretName == "" {
return fmt.Errorf("missing repository secret name")
}
if len(r.Spec.Paths) == 0 {
return fmt.Errorf("missing filegroup paths")
}
if len(r.Spec.RecoveredVolumes) == 0 {
return fmt.Errorf("missing recovery volume")
}

if err := r.Spec.Workload.Canonicalize(); err != nil {
return err
}

switch r.Spec.Workload.Kind {
case KindDeployment, KindReplicaSet, KindReplicationController:
if r.Spec.PodOrdinal != "" || r.Spec.NodeName != "" {
return fmt.Errorf("should not specify podOrdinal/nodeSelector for workload kind %s", r.Spec.Workload.Kind)
if r.Spec.Repository == "" {
return fmt.Errorf("missing repository name")
} else {
if !(strings.HasPrefix(r.Spec.Repository, "deployment.") ||
strings.HasPrefix(r.Spec.Repository, "replicationcontroller.") ||
strings.HasPrefix(r.Spec.Repository, "replicaset.") ||
strings.HasPrefix(r.Spec.Repository, "statefulset.") ||
strings.HasPrefix(r.Spec.Repository, "daemonset.")) {
return fmt.Errorf("invalid repository name")
}
case KindStatefulSet:
if r.Spec.PodOrdinal == "" {
return fmt.Errorf("must specify podOrdinal for workload kind %s", r.Spec.Workload.Kind)
}
if r.Spec.NodeName != "" {
return fmt.Errorf("should not specify nodeSelector for workload kind %s", r.Spec.Workload.Kind)
}
case KindDaemonSet:
if r.Spec.NodeName == "" {
return fmt.Errorf("must specify nodeSelector for workload kind %s", r.Spec.Workload.Kind)
}
if r.Spec.PodOrdinal != "" {
return fmt.Errorf("should not specify podOrdinal for workload kind %s", r.Spec.Workload.Kind)
}
if r.Spec.Snapshot != "" {
if !strings.HasPrefix(r.Spec.Snapshot, r.Spec.Repository+"-") {
return fmt.Errorf("invalid snapshot name")
}
}
return nil
Expand Down
2 changes: 0 additions & 2 deletions apis/stash/v1alpha1/zz_generated.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,11 @@ func (in *RecoveryList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RecoverySpec) DeepCopyInto(out *RecoverySpec) {
*out = *in
in.Backend.DeepCopyInto(&out.Backend)
if in.Paths != nil {
in, out := &in.Paths, &out.Paths
*out = make([]string, len(*in))
copy(*out, *in)
}
out.Workload = in.Workload
if in.RecoveredVolumes != nil {
in, out := &in.RecoveredVolumes, &out.RecoveredVolumes
*out = make([]LocalSpec, len(*in))
Expand Down
102 changes: 19 additions & 83 deletions docs/concepts/crds/recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ section_menu_id: concepts
# Recoveries

## What is Recovery
A `Recovery` is a Kubernetes `CustomResourceDefinition` (CRD). It provides configuration for restoring a backup taken using Stash. You only need to specify the `Restic` that was used for taking backup, the target workload and volume where backup will be restored.
A `Recovery` is a Kubernetes `CustomResourceDefinition` (CRD). It provides configuration for restoring a backup taken using Stash. You only need to specify the `Repository`, `Snapshot` and `path` you want to recover and volume where the backup will be restored.

## Recovery Spec
As with all other Kubernetes objects, a Recovery needs `apiVersion`, `kind`, and `metadata` fields. It also needs a `.spec` section. Below is an example Recovery object.
Expand All @@ -28,15 +28,8 @@ metadata:
name: stash-demo
namespace: default
spec:
workload:
kind: Deployment
name: stash-demo
backend:
local:
mountPath: /safe/data
hostPath:
path: /data/stash-test/restic-repo
storageSecretName: stash-demo
repository: deployment.stash-demo
snapshot: deployment.stash-demo-e0e9c272 # skip this field to recover latest snapshot
paths:
- /source/data
recoveredVolumes:
Expand All @@ -47,92 +40,35 @@ spec:
The `.spec` section has following parts:

### spec.workload
`spec.workload` specifies a target workload that was backed up using `Restic`. A single `Restic` backups all types of workloads that matches the label-selector, but you can only restore a specific workload using a `Recovery`.
### spec.repository

### spec.podOrdinal
For workload kind `Statefulset`, you need to specify pod [index](https://kubernetes.io/docs/guides/stateful-application/basic-stateful-set/#pods-in-a-statefulset) using `spec.podOrdinal`. You must not specify it for other workload kinds. For example:
Indicates the name of the `Repository` CRD that represents respective **restic** repository where the backed up snapshots are stored. To know more about `Repository` CRD, visit [here](/docs/concepts/crds/repository.md).

```yaml
apiVersion: stash.appscode.com/v1alpha1
kind: Recovery
metadata:
name: statefulset-demo
namespace: default
spec:
workload:
kind: Statefulset
name: statefulset-demo
podOrdinal: 0
backend:
local:
mountPath: /safe/data
hostPath:
path: /data/stash-test/restic-repo
storageSecretName: stash-demo
paths:
- /source/data
recoveredVolumes:
- mountPath: /source/data
hostPath:
path: /data/stash-test/restic-restored
```
### spec.snapshot

### spec.nodeName
For workload kind `Daemonset`, you need to specify node name using `spec.nodeName`. You must not specify it for other workload kinds. For example:

```yaml
apiVersion: stash.appscode.com/v1alpha1
kind: Recovery
metadata:
name: daemonset-demo
namespace: default
spec:
restic: daemonset-demo
workload:
kind: Daemonset
name: daemonset-demo
nodeName: minikube
backend:
local:
mountPath: /safe/data
hostPath:
path: /data/stash-test/restic-repo
storageSecretName: stash-demo
paths:
- /source/data
recoveredVolumes:
- mountPath: /source/data
hostPath:
path: /data/stash-test/restic-restored
```

### spec.backend
Specifies the backend that was used in `Restic` to take backups.
To learn how to configure various backends for Restic, please visit [here](/docs/guides/backends.md).
Indicates the name of the `Snapshot` object that represents **restic** backup snapshot. This field allows users to recover specific snapshot. To recover the latest snapshot, skip this field. To know more about `Snapshot`s, visit [here](/docs/concepts/crds/snapshot.md).

### spec.paths
Array of strings specifying the file-group paths that was backed up using `Restic`.

An array of strings specifying the file-group paths that were backed up using `Restic`.

### spec.recoveredVolumes
Indicates an array of volumes where snapshots will be recovered. Here, `path` specifies where the volume will be mounted.
Note that, `Recovery` recovers data in the same paths from where backup was taken (specified in `spec.paths`). So, volumes must be mounted on those paths or their parent paths.
Following parameters are available for `recoveredVolumes`.
Indicates an array of volumes where recovered snapshot data will be stored. Here, `mountPath` specifies where the volume will be mounted in the restore `Job`. Note that, `Recovery` recovers data in the same paths from where the backup was taken (specified in `spec.paths`). So, volumes must be mounted on those paths or their parent paths. Following parameters are available for `recoveredVolumes`.

| Parameter | Description |
|---------------------------------|-----------------------------------------------------------------------------------------------|
| `recoveredVolumes.mountPath` | `Required`. Path where this volume will be mounted in the sidecar container. Example: `/repo` |
| `recoveredVolumes.subPath` | `Optional`. Sub-path inside the referenced volume instead of its root. |
| `recoveredVolumes.VolumeSource` | `Required`. Any Kubernetes volume. Can be specified inlined. Example: `hostPath`
| Parameter | Description |
|---------------------------------|---------------------------------------------------------------------------------------------------|
| `recoveredVolumes.mountPath` | `Required`. The path where this volume will be mounted in the sidecar container. Example: `/repo` |
| `recoveredVolumes.subPath` | `Optional`. Sub-path inside the referenced volume instead of its root. |
| `recoveredVolumes.VolumeSource` | `Required`. Any Kubernetes volume. Can be specified inlined. Example: `hostPath` |

## Recovery Status

Stash operator updates `.status` of a Recovery CRD when recovery operation is completed.
Stash operator updates `.status` of a Recovery CRD when the recovery operation is completed.

- `status.phase` indicates the current phase of overall recovery process. Possible values are `Pending`, `Running`, `Succeeded`, `Failed` and `Unknown`.
- `status.stats` is a array status, each of which indicates the status for individual paths. Each element of the array has following fields:
- `status.phase` indicates the current phase of the overall recovery process. Possible values are `Pending`, `Running`, `Succeeded`, `Failed` and `Unknown`.
- `status.stats` is an array status, each of which indicates the status for individual paths. Each element of the array has following fields:
- `status.stats[].path` indicates a path that was backed up using `Restic` and is selected for recovery.
- `status.stats[].phase` indicates the current phase of recovery process for the particular path. Possible values are `Pending`, `Running`, `Succeeded`, `Failed` and `Unknown`.
- `status.stats[].phase` indicates the current phase of the recovery process for the particular path. Possible values are `Pending`, `Running`, `Succeeded`, `Failed` and `Unknown`.
- `status.stats[].duration` indicates the elapsed time to successfully restore backup for the particular path.

## Next Steps
Expand Down
20 changes: 6 additions & 14 deletions docs/examples/backends/minio/minio-recovery.yaml
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
apiVersion: stash.appscode.com/v1alpha1
kind: Recovery
metadata:
name: stash-demo
name: minio-recovery
namespace: default
spec:
workload:
kind: Deployment
name: stash-demo
backend:
s3:
endpoint: '<your Minio server addres>'
bucket: stash-qa
prefix: demo
storageSecretName: minio-restic-secret
repository: deployment.stash-demo
paths:
- /source/data
recoveredVolumes:
- mountPath: /source/data
hostPath:
path: /data/stash-test/restic-restored

- mountPath: /source/data # where the volume will be mounted
name: stash-recovered-volume
hostPath: # volume source, where the recovered data will be stored.
path: /data/stash-recovered/ # directory in volume source where recovered data will be stored
14 changes: 3 additions & 11 deletions docs/examples/tutorial/recovery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@ metadata:
name: stash-demo
namespace: default
spec:
workload:
kind: Deployment
name: stash-demo
backend:
local:
mountPath: /safe/data
hostPath:
path: /data/stash-test/restic-repo
storageSecretName: stash-demo
repository: deployment.stash-demo
snapshot: deployment.stash-demo-e0e9c272 # skip this field to recover latest snapshot
paths:
- /source/data
recoveredVolumes:
- mountPath: /source/data
hostPath:
path: /data/stash-test/restic-restored

path: /data/stash-test/restic-restored
Loading

0 comments on commit 2af3de2

Please sign in to comment.