mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-02 22:27:06 +00:00
disable GC and backup deletion if Kubernetes is less than v1.7.5
Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
Generated
+2
-2
@@ -419,13 +419,13 @@
|
||||
|
||||
[[projects]]
|
||||
name = "k8s.io/kubernetes"
|
||||
packages = ["pkg/printers"]
|
||||
packages = ["pkg/printers","pkg/util/version"]
|
||||
revision = "bdaeafa71f6c7c04636251031f93464384d54963"
|
||||
version = "v1.8.2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "c3cd1b703421685e5b2343ced6eaa6ec958b9c44d62277322f4c93de164c2d04"
|
||||
inputs-digest = "cd582891fb7e89c2ea28ea41e52687e2902b6105c6c3bf989bd628ebb4b72208"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@@ -20,11 +20,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
api "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
"github.com/heptio/ark/pkg/client"
|
||||
"github.com/heptio/ark/pkg/cmd"
|
||||
"github.com/heptio/ark/pkg/controller"
|
||||
kubeutil "github.com/heptio/ark/pkg/util/kube"
|
||||
)
|
||||
|
||||
func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
|
||||
@@ -37,6 +40,16 @@ func NewDeleteCommand(f client.Factory, use string) *cobra.Command {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
kubeClient, err := f.KubeClient()
|
||||
cmd.CheckError(err)
|
||||
|
||||
serverVersion, err := kubeutil.ServerVersion(kubeClient.Discovery())
|
||||
cmd.CheckError(err)
|
||||
|
||||
if !serverVersion.AtLeast(controller.MinVersionForDelete) {
|
||||
cmd.CheckError(errors.Errorf("this command requires the Kubernetes server version to be at least %s", controller.MinVersionForDelete))
|
||||
}
|
||||
|
||||
arkClient, err := f.Client()
|
||||
cmd.CheckError(err)
|
||||
|
||||
|
||||
+26
-16
@@ -55,6 +55,7 @@ import (
|
||||
"github.com/heptio/ark/pkg/plugin"
|
||||
"github.com/heptio/ark/pkg/restore"
|
||||
"github.com/heptio/ark/pkg/util/kube"
|
||||
kubeutil "github.com/heptio/ark/pkg/util/kube"
|
||||
"github.com/heptio/ark/pkg/util/logging"
|
||||
)
|
||||
|
||||
@@ -470,22 +471,31 @@ func (s *server) runControllers(config *api.Config) error {
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
gcController := controller.NewGCController(
|
||||
s.backupService,
|
||||
s.snapshotService,
|
||||
config.BackupStorageProvider.Bucket,
|
||||
config.GCSyncPeriod.Duration,
|
||||
s.sharedInformerFactory.Ark().V1().Backups(),
|
||||
s.arkClient.ArkV1(),
|
||||
s.sharedInformerFactory.Ark().V1().Restores(),
|
||||
s.arkClient.ArkV1(),
|
||||
s.logger,
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
gcController.Run(ctx, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
serverVersion, err := kubeutil.ServerVersion(s.kubeClient.Discovery())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !serverVersion.AtLeast(controller.MinVersionForDelete) {
|
||||
s.logger.Errorf("Garbage-collection is disabled because it requires the Kubernetes server version to be at least %s", controller.MinVersionForDelete)
|
||||
} else {
|
||||
gcController := controller.NewGCController(
|
||||
s.backupService,
|
||||
s.snapshotService,
|
||||
config.BackupStorageProvider.Bucket,
|
||||
config.GCSyncPeriod.Duration,
|
||||
s.sharedInformerFactory.Ark().V1().Backups(),
|
||||
s.arkClient.ArkV1(),
|
||||
s.sharedInformerFactory.Ark().V1().Restores(),
|
||||
s.arkClient.ArkV1(),
|
||||
s.logger,
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
gcController.Run(ctx, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
restorer, err := newRestorer(
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/util/version"
|
||||
|
||||
api "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
"github.com/heptio/ark/pkg/cloudprovider"
|
||||
@@ -42,6 +43,13 @@ import (
|
||||
|
||||
const gcFinalizer = "gc.ark.heptio.com"
|
||||
|
||||
// MinVersionForDelete is the minimum Kubernetes server version that Ark
|
||||
// requires in order to be able to properly delete backups (including
|
||||
// the associated snapshots and object storage files). This is because
|
||||
// Ark uses finalizers on the backup CRD to implement garbage-collection
|
||||
// and deletion.
|
||||
var MinVersionForDelete = version.MustParseSemantic("1.7.5")
|
||||
|
||||
// gcController removes expired backup content from object storage.
|
||||
type gcController struct {
|
||||
backupService cloudprovider.BackupService
|
||||
|
||||
@@ -24,7 +24,9 @@ import (
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/discovery"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
// NamespaceAndName returns a string in the format <namespace>/<name>
|
||||
@@ -48,3 +50,17 @@ func EnsureNamespaceExists(namespace *v1.Namespace, client corev1.NamespaceInter
|
||||
return false, errors.Wrapf(err, "error creating namespace %s", namespace.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func ServerVersion(client discovery.DiscoveryInterface) (*version.Version, error) {
|
||||
versionInfo, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting server version")
|
||||
}
|
||||
|
||||
semVer, err := version.ParseSemantic(versionInfo.String())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error parsing server version")
|
||||
}
|
||||
|
||||
return semVer, err
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2017 The Kubernetes 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.
|
||||
|
||||
# verify-util-pkg.sh checks whether *.go except doc.go in pkg/util have been moved into
|
||||
# sub-pkgs, see issue #15634.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
BASH_DIR=$(dirname "${BASH_SOURCE}")
|
||||
|
||||
find_go_files() {
|
||||
find . -maxdepth 1 -not \( \
|
||||
\( \
|
||||
-wholename './doc.go' \
|
||||
\) -prune \
|
||||
\) -name '*.go'
|
||||
}
|
||||
|
||||
ret=0
|
||||
|
||||
pushd "${BASH_DIR}" > /dev/null
|
||||
for path in `find_go_files`; do
|
||||
file=$(basename $path)
|
||||
echo "Found pkg/util/${file}, but should be moved into util sub-pkgs." 1>&2
|
||||
ret=1
|
||||
done
|
||||
popd > /dev/null
|
||||
|
||||
if [[ ${ret} > 0 ]]; then
|
||||
exit ${ret}
|
||||
fi
|
||||
|
||||
echo "Util Package Verified."
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 version provides utilities for version number comparisons
|
||||
package version // import "k8s.io/kubernetes/pkg/util/version"
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes 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 version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is an opqaue representation of a version number
|
||||
type Version struct {
|
||||
components []uint
|
||||
semver bool
|
||||
preRelease string
|
||||
buildMetadata string
|
||||
}
|
||||
|
||||
var (
|
||||
// versionMatchRE splits a version string into numeric and "extra" parts
|
||||
versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
|
||||
// extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
|
||||
extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
|
||||
)
|
||||
|
||||
func parse(str string, semver bool) (*Version, error) {
|
||||
parts := versionMatchRE.FindStringSubmatch(str)
|
||||
if parts == nil {
|
||||
return nil, fmt.Errorf("could not parse %q as version", str)
|
||||
}
|
||||
numbers, extra := parts[1], parts[2]
|
||||
|
||||
components := strings.Split(numbers, ".")
|
||||
if (semver && len(components) != 3) || (!semver && len(components) < 2) {
|
||||
return nil, fmt.Errorf("illegal version string %q", str)
|
||||
}
|
||||
|
||||
v := &Version{
|
||||
components: make([]uint, len(components)),
|
||||
semver: semver,
|
||||
}
|
||||
for i, comp := range components {
|
||||
if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
num, err := strconv.ParseUint(comp, 10, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
|
||||
}
|
||||
v.components[i] = uint(num)
|
||||
}
|
||||
|
||||
if semver && extra != "" {
|
||||
extraParts := extraMatchRE.FindStringSubmatch(extra)
|
||||
if extraParts == nil {
|
||||
return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
|
||||
}
|
||||
v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
|
||||
|
||||
for _, comp := range strings.Split(v.preRelease, ".") {
|
||||
if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
|
||||
if strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ParseGeneric parses a "generic" version string. The version string must consist of two
|
||||
// or more dot-separated numeric fields (the first of which can't have leading zeroes),
|
||||
// followed by arbitrary uninterpreted data (which need not be separated from the final
|
||||
// numeric field by punctuation). For convenience, leading and trailing whitespace is
|
||||
// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
|
||||
func ParseGeneric(str string) (*Version, error) {
|
||||
return parse(str, false)
|
||||
}
|
||||
|
||||
// MustParseGeneric is like ParseGeneric except that it panics on error
|
||||
func MustParseGeneric(str string) *Version {
|
||||
v, err := ParseGeneric(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
|
||||
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
|
||||
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
|
||||
// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
|
||||
// ParseGeneric.
|
||||
func ParseSemantic(str string) (*Version, error) {
|
||||
return parse(str, true)
|
||||
}
|
||||
|
||||
// MustParseSemantic is like ParseSemantic except that it panics on error
|
||||
func MustParseSemantic(str string) *Version {
|
||||
v, err := ParseSemantic(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Major returns the major release number
|
||||
func (v *Version) Major() uint {
|
||||
return v.components[0]
|
||||
}
|
||||
|
||||
// Minor returns the minor release number
|
||||
func (v *Version) Minor() uint {
|
||||
return v.components[1]
|
||||
}
|
||||
|
||||
// Patch returns the patch release number if v is a Semantic Version, or 0
|
||||
func (v *Version) Patch() uint {
|
||||
if len(v.components) < 3 {
|
||||
return 0
|
||||
}
|
||||
return v.components[2]
|
||||
}
|
||||
|
||||
// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) BuildMetadata() string {
|
||||
return v.buildMetadata
|
||||
}
|
||||
|
||||
// PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) PreRelease() string {
|
||||
return v.preRelease
|
||||
}
|
||||
|
||||
// Components returns the version number components
|
||||
func (v *Version) Components() []uint {
|
||||
return v.components
|
||||
}
|
||||
|
||||
// String converts a Version back to a string; note that for versions parsed with
|
||||
// ParseGeneric, this will not include the trailing uninterpreted portion of the version
|
||||
// number.
|
||||
func (v *Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for i, comp := range v.components {
|
||||
if i > 0 {
|
||||
buffer.WriteString(".")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d", comp))
|
||||
}
|
||||
if v.preRelease != "" {
|
||||
buffer.WriteString("-")
|
||||
buffer.WriteString(v.preRelease)
|
||||
}
|
||||
if v.buildMetadata != "" {
|
||||
buffer.WriteString("+")
|
||||
buffer.WriteString(v.buildMetadata)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
|
||||
// if they are equal
|
||||
func (v *Version) compareInternal(other *Version) int {
|
||||
for i := range v.components {
|
||||
switch {
|
||||
case i >= len(other.components):
|
||||
if v.components[i] != 0 {
|
||||
return 1
|
||||
}
|
||||
case other.components[i] < v.components[i]:
|
||||
return 1
|
||||
case other.components[i] > v.components[i]:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
if !v.semver || !other.semver {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.preRelease == "" && other.preRelease != "":
|
||||
return 1
|
||||
case v.preRelease != "" && other.preRelease == "":
|
||||
return -1
|
||||
case v.preRelease == other.preRelease: // includes case where both are ""
|
||||
return 0
|
||||
}
|
||||
|
||||
vPR := strings.Split(v.preRelease, ".")
|
||||
oPR := strings.Split(other.preRelease, ".")
|
||||
for i := range vPR {
|
||||
if i >= len(oPR) {
|
||||
return 1
|
||||
}
|
||||
vNum, err := strconv.ParseUint(vPR[i], 10, 0)
|
||||
if err == nil {
|
||||
oNum, err := strconv.ParseUint(oPR[i], 10, 0)
|
||||
if err == nil {
|
||||
switch {
|
||||
case oNum < vNum:
|
||||
return 1
|
||||
case oNum > vNum:
|
||||
return -1
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if oPR[i] < vPR[i] {
|
||||
return 1
|
||||
} else if oPR[i] > vPR[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// AtLeast tests if a version is at least equal to a given minimum version. If both
|
||||
// Versions are Semantic Versions, this will use the Semantic Version comparison
|
||||
// algorithm. Otherwise, it will compare only the numeric components, with non-present
|
||||
// components being considered "0" (ie, "1.4" is equal to "1.4.0").
|
||||
func (v *Version) AtLeast(min *Version) bool {
|
||||
return v.compareInternal(min) != -1
|
||||
}
|
||||
|
||||
// LessThan tests if a version is less than a given version. (It is exactly the opposite
|
||||
// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
|
||||
// "is v new enough?".)
|
||||
func (v *Version) LessThan(other *Version) bool {
|
||||
return v.compareInternal(other) == -1
|
||||
}
|
||||
|
||||
// Compare compares v against a version string (which will be parsed as either Semantic
|
||||
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
|
||||
// it is greater than other, or 0 if they are equal.
|
||||
func (v *Version) Compare(other string) (int, error) {
|
||||
ov, err := parse(other, v.semver)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v.compareInternal(ov), nil
|
||||
}
|
||||
Reference in New Issue
Block a user