Skip to content
This repository has been archived by the owner on Jun 29, 2022. It is now read-only.

Commit

Permalink
velero: add velero restic plugin for Packet
Browse files Browse the repository at this point in the history
This commit adds support for using restic plugin for Packet platform
when the underlying storage is Rook or node storage.

Signed-off-by: Imran Pochi <imran@kinvolk.io>
  • Loading branch information
ipochi committed Sep 21, 2020
1 parent de45743 commit 3fb6e97
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 3 deletions.
19 changes: 17 additions & 2 deletions docs/configuration-reference/components/velero.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ component "velero" {
# }
# }
# restic {
# credentials = file("cloud-credentials-file")
#
# backup_storage_location {
# provider = "aws"
# bucket = "my-bucket"
# name = "my-backup-location"
# }
# }
# Optional.
metrics {
enabled = false
Expand All @@ -101,7 +111,7 @@ Table of all the arguments accepted by the component.
Example:

| Argument | Description | Default | Type | Required |
|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|-:-:-----------------------------------------------|-:-:----|-:-:------|
|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|--:-:----------------------------------------------|--:-:---|--:-:-----|
| `namespace` | Namespace to install Velero. | "velero" | string | false |
| `metrics` | Configure Prometheus to scrape Velero metrics. Needs the [Prometheus Operator component](prometheus-operator.md) installed. | - | object | false |
| `metrics.enabled` | Adds Prometheus annotations to Velero deployment if enabled. | false | bool | false |
Expand Down Expand Up @@ -129,13 +139,18 @@ Example:
| `openebs.backup_storage_location.name` | Name for backup location object on the cluster. | - | string | false |
| `openebs.volume_snapshot_location` | Configure volume snapshot location. | - | object | true |
| `openebs.volume_snapshot_location.bucket` | Cloud storage bucket name for storing volume snapshots. | - | string | true |
| `openebs.volume_snapshot_location.region` | Cloud provider region for | | | |
| `openebs.volume_snapshot_location.region` | Cloud provider region for storing snapshots. | | string | true |
| `openebs.volume_snapshot_location.provider` | Cloud provider name for storing snapshots. Overrides `openebs.provider` field for backup storage. | - | string | false |
| `openebs.volume_snapshot_location.name` | Name for snapshot location object on the cluster. | - | string | false |
| `openebs.volume_snapshot_location.prefix` | Prefix for snapshot names. | - | string | false |
| `openebs.volume_snapshot_location.local` | If `true`, backups won't be copied to cloud storage. | false | bool | false |
| `openebs.volume_snapshot_location.openebs_namespace` | Name of the namespace where OpenEBS runs. | - | string | true |
| `openebs.volume_snapshot_location.s3_url` | S3 API URL. | - | string | false |
| `restic` | Configure Restic provider for Velero. | - | object | false |
| `restic.credentials` | Content of cloud provider credentials. | - | string | true |
| `restic.backup_storage_location.provider` | Cloud provider name for storing backups. | - | string | false |
| `restic.backup_storage_location.bucket` | Cloud storage bucket name for storing backups. | - | string | true |
| `restic.backup_storage_location.name` | Name for backup location object on the cluster. | - | string | false |

## Applying

Expand Down
9 changes: 8 additions & 1 deletion pkg/components/velero/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/kinvolk/lokomotive/pkg/components/util"
"github.com/kinvolk/lokomotive/pkg/components/velero/azure"
"github.com/kinvolk/lokomotive/pkg/components/velero/openebs"
"github.com/kinvolk/lokomotive/pkg/components/velero/restic"
"github.com/kinvolk/lokomotive/pkg/k8sutil"
)

Expand All @@ -47,6 +48,8 @@ type component struct {
Azure *azure.Configuration `hcl:"azure,block"`
// OpenEBS specific parameters.
OpenEBS *openebs.Configuration `hcl:"openebs,block"`
// Restic specific parameters.
Restic *restic.Configuration `hcl:"restic,block"`
}

// Metrics represents prometheus specific parameters
Expand Down Expand Up @@ -176,7 +179,7 @@ func (c *component) validate() hcl.Diagnostics {

// getSupportedProviders returns a list of supported providers.
func (c *component) getSupportedProviders() []string {
return []string{"azure", "openebs"}
return []string{"azure", "openebs", "restic"}
}

// getProvider returns correct provider interface based on component configuration.
Expand All @@ -194,6 +197,10 @@ func (c *component) getProvider() (provider, error) {
providers = append(providers, c.OpenEBS)
}

if c.Restic != nil {
providers = append(providers, c.Restic)
}

if len(providers) > 1 {
return nil, fmt.Errorf("more than one provider configured")
}
Expand Down
36 changes: 36 additions & 0 deletions pkg/components/velero/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,39 @@ component "velero" {}
t.Fatalf("Loading configuration should fail if there is no provider configured")
}
}

func TestRenderManifestRestic(t *testing.T) {
configHCL := `
component "velero" {
restic {
credentials = "foo"
backup_storage_location {
bucket = "foo"
provider = "aws"
}
}
}
`

component := newComponent()

body, d := util.GetComponentBody(configHCL, name)
if d != nil {
t.Fatalf("Error getting component body: %v", d)
}

d = component.LoadConfig(body, &hcl.EvalContext{})
if d.HasErrors() {
t.Fatalf("Valid config should not return error, got: %s", d)
}

m, err := component.RenderManifests()
if err != nil {
t.Fatalf("Rendering manifests with valid config should succeed, got: %s", err)
}

if len(m) == 0 {
t.Fatalf("Rendered manifests shouldn't be empty")
}
}
122 changes: 122 additions & 0 deletions pkg/components/velero/restic/restic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2020 The Lokomotive 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 restic deals with configuring restic plugin.
package restic

import (
"bytes"
"fmt"
"text/template"

"github.com/hashicorp/hcl/v2"

"github.com/kinvolk/lokomotive/internal"
)

const indentation = 6

// Configuration contains Restic specific parameters.
type Configuration struct {
Credentials string `hcl:"credentials"`
BackupStorageLocation *BackupStorageLocation `hcl:"backup_storage_location,block"`
}

// BackupStorageLocation configures the backup storage location.
type BackupStorageLocation struct {
Provider string `hcl:"provider"`
Bucket string `hcl:"bucket"`
Name string `hcl:"name,optional"`
}

// NewConfiguration returns the default restic configuration.
func NewConfiguration() *Configuration {
return &Configuration{
BackupStorageLocation: &BackupStorageLocation{
Provider: "aws",
Name: "default",
},
}
}

// Values returns plugin-specific value for Velero Helm chart.
func (c *Configuration) Values() (string, error) {
t := template.Must(template.New("values").Parse(chartValuesTmpl))

var buf bytes.Buffer

v := struct {
Configuration *Configuration
CredentialsIndented string
}{
Configuration: c,
CredentialsIndented: internal.Indent(c.Credentials, indentation),
}

if err := t.Execute(&buf, v); err != nil {
return "", fmt.Errorf("executing values template: %w", err)
}

return buf.String(), nil
}

// Validate validates the restic configuration.
func (c *Configuration) Validate() hcl.Diagnostics {
var diagnostics hcl.Diagnostics

if c.BackupStorageLocation == nil {
c.BackupStorageLocation = &BackupStorageLocation{}

diagnostics = append(diagnostics, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "'restic.backup_storage_location' block must be specified",
Detail: "Make sure to the set the field to valid non-empty value",
})
}

if c.BackupStorageLocation.Bucket == "" {
diagnostics = append(diagnostics, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "restic.backup_storage_location.bucket must not be empty",
Detail: "Make sure `bucket` value is set",
})
}

if !isSupportedProvider(c.BackupStorageLocation.Provider) {
diagnostics = append(diagnostics, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("restic.backup_storage_location.provider must be one of: '%s'",
resticSupportedProviders()),
Detail: "Make sure to set provider to one of supported values",
})
}

return diagnostics
}

// isSupportedProvider checks if the provider is supported or not.
func isSupportedProvider(provider string) bool {
for _, p := range resticSupportedProviders() {
if provider == p {
return true
}
}

return false
}

// resticSupportedProviders returns the list of supported providers.
func resticSupportedProviders() []string {
return []string{"aws", "gcp", "azure"}
}
74 changes: 74 additions & 0 deletions pkg/components/velero/restic/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2020 The Lokomotive 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 restic

const chartValuesTmpl = `
configuration:
provider: {{ .Configuration.BackupStorageLocation.Provider }}
backupStorageLocation:
{{- if .Configuration.BackupStorageLocation.Provider}}
provider: {{ .Configuration.BackupStorageLocation.Provider }}
{{- end }}
{{- if .Configuration.BackupStorageLocation.Name}}
name: {{ .Configuration.BackupStorageLocation.Name }}
{{- end }}
bucket: {{ .Configuration.BackupStorageLocation.Bucket }}
config:
region: eu-west-1
deployRestic: true
snapshotsEnabled: false
restic:
privileged: true
credentials:
secretContents:
{{- if .Configuration.Credentials }}
cloud: |
{{ .CredentialsIndented }}
{{- end }}
initContainers:
{{- if eq .Configuration.BackupStorageLocation.Provider "aws" }}
- image: velero/velero-plugin-for-aws:v1.1.0
imagePullPolicy: IfNotPresent
name: velero-plugin-for-aws
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /target
name: plugins
{{- end }}
{{- if eq .Configuration.BackupStorageLocation.Provider "gcp" }}
- image: velero/velero-plugin-for-gcp:v1.1.0
imagePullPolicy: IfNotPresent
name: velero-plugin-for-gcp
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /target
name: plugins
{{- end }}
{{- if eq .Configuration.BackupStorageLocation.Provider "azure" }}
- image: velero/velero-plugin-for-microsoft-azure:v1.1.0
imagePullPolicy: IfNotPresent
name: velero-plugin-for-azure
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /target
name: plugins
{{- end }}
`

0 comments on commit 3fb6e97

Please sign in to comment.