Velero analyzer (#1366)

* feat: add velero analyzer (#806)

  * updated schema
  * analyzer without collector
  * tests
  * covers deprecated Restic repository type
  * velero version from deployment image to check deprecated type
  * read for both velero pod kinds (velero*, node-agent*)

---------

Signed-off-by: Archit Sharma <archit@pm.me>
This commit is contained in:
Archit Sharma
2023-11-03 18:41:17 +05:30
committed by GitHub
parent a7bb9ea31e
commit 7038da85b1
17 changed files with 1987 additions and 37 deletions
@@ -1489,6 +1489,19 @@ spec:
required:
- outcomes
type: object
velero:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
exclude:
type: BoolString
strict:
type: BoolString
type: object
weaveReport:
properties:
annotations:
@@ -1489,6 +1489,19 @@ spec:
required:
- outcomes
type: object
velero:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
exclude:
type: BoolString
strict:
type: BoolString
type: object
weaveReport:
properties:
annotations:
@@ -1520,6 +1520,19 @@ spec:
required:
- outcomes
type: object
velero:
properties:
annotations:
additionalProperties:
type: string
type: object
checkName:
type: string
exclude:
type: BoolString
strict:
type: BoolString
type: object
weaveReport:
properties:
annotations:
+3 -3
View File
@@ -35,9 +35,11 @@ require (
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/tj/go-spin v1.1.0
github.com/vmware-tanzu/velero v1.12.0
go.opentelemetry.io/otel v1.19.0
go.opentelemetry.io/otel/sdk v1.19.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/mod v0.12.0
golang.org/x/sync v0.4.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.28.2
@@ -86,7 +88,6 @@ require (
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/onsi/ginkgo v1.14.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rubenv/sql-migrate v1.3.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -105,7 +106,6 @@ require (
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
@@ -123,7 +123,7 @@ require (
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
github.com/andybalholm/brotli v1.0.1 // indirect
github.com/aws/aws-sdk-go v1.44.198 // indirect
github.com/aws/aws-sdk-go v1.44.253 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect
+7 -14
View File
@@ -192,7 +192,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0=
github.com/Azure/azure-sdk-for-go v56.3.0+incompatible h1:DmhwMrUIvpeoTDiWRDtNHqelNUd3Og8JCkrLHQK795c=
github.com/Azure/azure-sdk-for-go v67.2.0+incompatible h1:Uu/Ww6ernvPTrpq31kITVTIm/I5jlJ1wjtEH/bmSB2k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
@@ -244,8 +244,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.198 h1:kgnvxQv4/kP5M0nbxBx0Ac0so9ndr9f8Ti0g+NmPQF8=
github.com/aws/aws-sdk-go v1.44.198/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.253 h1:iqDd0okcH4ShfFexz2zzf4VmeDFf6NOMm07pHnEb8iY=
github.com/aws/aws-sdk-go v1.44.253/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -800,19 +800,14 @@ github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M=
github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@@ -1001,6 +996,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY=
github.com/vladimirvivien/gexe v0.2.0/go.mod h1:LHQL00w/7gDUKIak24n801ABp8C+ni6eBht9vGVst8w=
github.com/vmware-tanzu/velero v1.12.0 h1:gN8PbQMYOAMv8OYE3RkIvxr6s6IoMS0Daxc+IQN0X5U=
github.com/vmware-tanzu/velero v1.12.0/go.mod h1:rY2UfdC2K9je9jtjnSBsZr8Zmg8hzePjG2W00Oe/CT4=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1149,7 +1146,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -1246,14 +1242,12 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1268,7 +1262,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+21
View File
@@ -1,16 +1,20 @@
package analyzer
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
"github.com/replicatedhq/troubleshoot/pkg/redact"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
@@ -226,6 +230,8 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
return &AnalyzeRedis{analyzer: analyzer.Redis}
case analyzer.CephStatus != nil:
return &AnalyzeCephStatus{analyzer: analyzer.CephStatus}
case analyzer.Velero != nil:
return &AnalyzeVelero{analyzer: analyzer.Velero}
case analyzer.Longhorn != nil:
return &AnalyzeLonghorn{analyzer: analyzer.Longhorn}
case analyzer.RegistryImages != nil:
@@ -265,3 +271,18 @@ func DedupAnalyzers(allAnalyzers []*troubleshootv1beta2.Analyze) []*troubleshoot
}
return finalAnalyzers
}
func stripRedactedLines(yaml []byte) []byte {
buf := bytes.NewBuffer(yaml)
scanner := bufio.NewScanner(buf)
out := []byte{}
for scanner.Scan() {
line := strings.ReplaceAll(scanner.Text(), redact.MASK_TEXT, "HIDDEN")
out = append(out, []byte(line)...)
out = append(out, '\n')
}
return out
}
-19
View File
@@ -1,19 +1,15 @@
package analyzer
import (
"bufio"
"bytes"
"fmt"
"path/filepath"
"reflect"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
longhornv1beta1 "github.com/replicatedhq/troubleshoot/pkg/longhorn/apis/longhorn/v1beta1"
longhorntypes "github.com/replicatedhq/troubleshoot/pkg/longhorn/types"
"github.com/replicatedhq/troubleshoot/pkg/redact"
"gopkg.in/yaml.v2"
)
@@ -241,21 +237,6 @@ func analyzeLonghornEngine(engine *longhornv1beta1.Engine) *AnalyzeResult {
return result
}
func stripRedactedLines(yaml []byte) []byte {
buf := bytes.NewBuffer(yaml)
scanner := bufio.NewScanner(buf)
out := []byte{}
for scanner.Scan() {
line := strings.ReplaceAll(scanner.Text(), redact.MASK_TEXT, "HIDDEN")
out = append(out, []byte(line)...)
out = append(out, '\n')
}
return out
}
func analyzeLonghornReplicaChecksums(volumeName string, checksums []map[string]string) *AnalyzeResult {
result := &AnalyzeResult{
Title: fmt.Sprintf("Longhorn Volume Replica Corruption: %s", volumeName),
+62
View File
@@ -0,0 +1,62 @@
package analyzer
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ResticRepositorySpec is the specification for a ResticRepository.
type ResticRepositorySpec struct {
// VolumeNamespace is the namespace this restic repository contains
// pod volume backups for.
VolumeNamespace string `json:"volumeNamespace"`
// BackupStorageLocation is the name of the BackupStorageLocation
// that should contain this repository.
BackupStorageLocation string `json:"backupStorageLocation"`
// ResticIdentifier is the full restic-compatible string for identifying
// this repository.
ResticIdentifier string `json:"resticIdentifier"`
// MaintenanceFrequency is how often maintenance should be run.
MaintenanceFrequency metav1.Duration `json:"maintenanceFrequency"`
}
// ResticRepositoryPhase represents the lifecycle phase of a ResticRepository.
// +kubebuilder:validation:Enum=New;Ready;NotReady
type ResticRepositoryPhase string
const (
ResticRepositoryPhaseNew ResticRepositoryPhase = "New"
ResticRepositoryPhaseReady ResticRepositoryPhase = "Ready"
ResticRepositoryPhaseNotReady ResticRepositoryPhase = "NotReady"
)
// ResticRepositoryStatus is the current status of a ResticRepository.
type ResticRepositoryStatus struct {
// Phase is the current state of the ResticRepository.
// +optional
Phase ResticRepositoryPhase `json:"phase,omitempty"`
// Message is a message about the current status of the ResticRepository.
// +optional
Message string `json:"message,omitempty"`
// LastMaintenanceTime is the last time maintenance was run.
// +optional
// +nullable
LastMaintenanceTime *metav1.Time `json:"lastMaintenanceTime,omitempty"`
}
type ResticRepository struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
Spec ResticRepositorySpec `json:"spec,omitempty"`
// +optional
Status ResticRepositoryStatus `json:"status,omitempty"`
}
+735
View File
@@ -0,0 +1,735 @@
package analyzer
import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
appsV1 "k8s.io/api/apps/v1"
restic_types "github.com/replicatedhq/troubleshoot/pkg/analyze/types"
"golang.org/x/mod/semver"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)
type AnalyzeVelero struct {
analyzer *troubleshootv1beta2.VeleroAnalyze
}
func (a *AnalyzeVelero) Title() string {
title := a.analyzer.CheckName
if title == "" {
title = "Velero"
}
return title
}
func (a *AnalyzeVelero) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeVelero) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
results, err := a.veleroStatus(a.analyzer, getFile, findFiles)
if err != nil {
return nil, err
}
for i := range results {
results[i].Strict = a.analyzer.Strict.BoolOrDefaultFalse()
}
return results, nil
}
func (a *AnalyzeVelero) veleroStatus(analyzer *troubleshootv1beta2.VeleroAnalyze, getFileContents getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
excludeFiles := []string{}
results := []*AnalyzeResult{}
oldVeleroRepoType := false
veleroVersion, err := getVeleroVersion(excludeFiles, findFiles)
if err != nil {
return nil, errors.Wrap(err, "Unable to find velero deployment")
}
// Check if the version string is valid erer
if !semver.IsValid(veleroVersion) {
return nil, errors.Errorf("Invalid velero semver: %s", veleroVersion)
}
// check if veleroVersion is less than 1.10.x
compareResult := semver.Compare(veleroVersion, "1.10.0")
if compareResult < 0 {
oldVeleroRepoType = true
}
if oldVeleroRepoType == true {
// old velero (v1.9.x) has a BackupRepositoryTypeRestic
// get resticrepositories.velero.io
resticRepositoriesDir := GetVeleroResticRepositoriesDirectory()
resticRepositoriesGlob := filepath.Join(resticRepositoriesDir, "*.json")
resticRepositoriesJson, err := findFiles(resticRepositoriesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero restic repositories files under %s", resticRepositoriesDir)
}
resticRepositories := []*restic_types.ResticRepository{}
for key, resticRepositoryJson := range resticRepositoriesJson {
var resticRepositoryArray []*restic_types.ResticRepository
err := json.Unmarshal(resticRepositoryJson, &resticRepositories)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal restic repository json from %s", key)
}
resticRepositories = append(resticRepositories, resticRepositoryArray...)
}
results = append(results, analyzeResticRepositories(resticRepositories)...)
} else {
// velerov1.Version
// get backuprepositories.velero.io
backupRepositoriesDir := GetVeleroBackupRepositoriesDirectory()
backupRepositoriesGlob := filepath.Join(backupRepositoriesDir, "*.json")
backupRepositoriesJson, err := findFiles(backupRepositoriesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup repositories files under %s", backupRepositoriesDir)
}
backupRepositories := []*velerov1.BackupRepository{}
for key, backupRepositoryJson := range backupRepositoriesJson {
var backupRepositoryArray []*velerov1.BackupRepository
err := json.Unmarshal(backupRepositoryJson, &backupRepositoryArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal backup repository json from %s", key)
}
backupRepositories = append(backupRepositories, backupRepositoryArray...)
}
results = append(results, analyzeBackupRepositories(backupRepositories)...)
}
// get backups.velero.io
backupsDir := GetVeleroBackupsDirectory()
backupsGlob := filepath.Join(backupsDir, "*.json")
veleroJSONs, err := findFiles(backupsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup files")
}
backups := []*velerov1.Backup{}
for _, veleroJSON := range veleroJSONs {
if err != nil {
return nil, errors.Wrapf(err, "failed to read velero backup file %s", veleroJSON)
}
var veleroBackups []*velerov1.Backup
err = json.Unmarshal(veleroJSON, &veleroBackups)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal velero backup file %s", veleroJSON)
}
backups = append(backups, veleroBackups...)
}
// get backupstoragelocations.velero.io
backupStorageLocationsDir := GetVeleroBackupStorageLocationsDirectory()
backupStorageLocationsGlob := filepath.Join(backupStorageLocationsDir, "*.json")
backupStorageLocationsJson, err := findFiles(backupStorageLocationsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero backup storage locations files under %s", backupStorageLocationsDir)
}
backupStorageLocations := []*velerov1.BackupStorageLocation{}
for key, backupStorageLocationJson := range backupStorageLocationsJson {
var backupStorageLocationArray []*velerov1.BackupStorageLocation
err := json.Unmarshal(backupStorageLocationJson, &backupStorageLocationArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal backup storage location json from %s", key)
}
backupStorageLocations = append(backupStorageLocations, backupStorageLocationArray...)
}
// get deletebackuprequests.velero.io
deleteBackupRequestsDir := GetVeleroDeleteBackupRequestsDirectory()
deleteBackupRequestsGlob := filepath.Join(deleteBackupRequestsDir, "*.json")
deleteBackupRequestsJson, err := findFiles(deleteBackupRequestsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero delete backup requests files under %s", deleteBackupRequestsDir)
}
deleteBackupRequests := []*velerov1.DeleteBackupRequest{}
for key, deleteBackupRequestJson := range deleteBackupRequestsJson {
var deleteBackupRequestArray []*velerov1.DeleteBackupRequest
err := json.Unmarshal(deleteBackupRequestJson, &deleteBackupRequestArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal delete backup request json from %s", key)
}
deleteBackupRequests = append(deleteBackupRequests, deleteBackupRequestArray...)
}
// get podvolumebackups.velero.io
podVolumeBackupsDir := GetVeleroPodVolumeBackupsDirectory()
podVolumeBackupsGlob := filepath.Join(podVolumeBackupsDir, "*.json")
podVolumeBackupsJson, err := findFiles(podVolumeBackupsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero pod volume backups files under %s", podVolumeBackupsDir)
}
podVolumeBackups := []*velerov1.PodVolumeBackup{}
for key, podVolumeBackupJson := range podVolumeBackupsJson {
var podVolumeBackupArray []*velerov1.PodVolumeBackup
err := json.Unmarshal(podVolumeBackupJson, &podVolumeBackupArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pod volume backup json from %s", key)
}
podVolumeBackups = append(podVolumeBackups, podVolumeBackupArray...)
}
// get podvolumerestores.velero.io
podVolumeRestoresDir := GetVeleroPodVolumeRestoresDirectory()
podVolumeRestoresGlob := filepath.Join(podVolumeRestoresDir, "*.json")
podVolumeRestoresJson, err := findFiles(podVolumeRestoresGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero pod volume restores files under %s", podVolumeRestoresDir)
}
podVolumeRestores := []*velerov1.PodVolumeRestore{}
for key, podVolumeRestoreJson := range podVolumeRestoresJson {
var podVolumeRestoreArray []*velerov1.PodVolumeRestore
err := json.Unmarshal(podVolumeRestoreJson, &podVolumeRestoreArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal pod volume restore json from %s", key)
}
podVolumeRestores = append(podVolumeRestores, podVolumeRestoreArray...)
}
// get restores.velero.io
restoresDir := GetVeleroRestoresDirectory()
restoresGlob := filepath.Join(restoresDir, "*.json")
restoresJson, err := findFiles(restoresGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero restores files under %s", restoresDir)
}
restores := []*velerov1.Restore{}
for key, restoreJson := range restoresJson {
var restoreArray []*velerov1.Restore
err := json.Unmarshal(restoreJson, &restoreArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal restore json from %s", key)
}
restores = append(restores, restoreArray...)
}
// get schedules.velero.io
schedulesDir := GetVeleroSchedulesDirectory()
schedulesGlob := filepath.Join(schedulesDir, "*.json")
schedulesJson, err := findFiles(schedulesGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero schedules files under %s", schedulesDir)
}
schedules := []*velerov1.Schedule{}
for key, scheduleJson := range schedulesJson {
var scheduleArray []*velerov1.Schedule
err := json.Unmarshal(scheduleJson, &scheduleArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal schedule json from %s", key)
}
schedules = append(schedules, scheduleArray...)
}
// get serverstatusrequests.velero.io
serverStatusRequestsDir := GetVeleroServerStatusRequestsDirectory()
serverStatusRequestsGlob := filepath.Join(serverStatusRequestsDir, "*.json")
serverStatusRequestsJson, err := findFiles(serverStatusRequestsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero server status requests files under %s", serverStatusRequestsDir)
}
serverStatusRequests := []*velerov1.ServerStatusRequest{}
for key, serverStatusRequestJson := range serverStatusRequestsJson {
var serverStatusRequestArray []*velerov1.ServerStatusRequest
err := json.Unmarshal(serverStatusRequestJson, &serverStatusRequestArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal server status request json from %s", key)
}
serverStatusRequests = append(serverStatusRequests, serverStatusRequestArray...)
}
// get volumesnapshotlocations.velero.io
volumeSnapshotLocationsDir := GetVeleroVolumeSnapshotLocationsDirectory()
volumeSnapshotLocationsGlob := filepath.Join(volumeSnapshotLocationsDir, "*.json")
volumeSnapshotLocationsJson, err := findFiles(volumeSnapshotLocationsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero volume snapshot locations files under %s", volumeSnapshotLocationsDir)
}
volumeSnapshotLocations := []*velerov1.VolumeSnapshotLocation{}
for key, volumeSnapshotLocationJson := range volumeSnapshotLocationsJson {
var volumeSnapshotLocationArray []*velerov1.VolumeSnapshotLocation
err := json.Unmarshal(volumeSnapshotLocationJson, &volumeSnapshotLocationArray)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal volume snapshot location json from %s", key)
}
volumeSnapshotLocations = append(volumeSnapshotLocations, volumeSnapshotLocationArray...)
}
logsDir := GetVeleroLogsDirectory()
logsGlob := filepath.Join(logsDir, "node-agent*", "*.log")
nodeAgentlogs, err := findFiles(logsGlob, excludeFiles)
if err != nil {
return nil, errors.Wrapf(err, "failed to find velero logs files under %s", logsDir)
}
veleroLogsGlob := filepath.Join(logsDir, "velero*", "*.log")
veleroLogs, err := findFiles(veleroLogsGlob, excludeFiles)
results = append(results, analyzeLogs(nodeAgentlogs, "node-agent*")...)
results = append(results, analyzeLogs(veleroLogs, "velero*")...)
results = append(results, analyzeBackups(backups)...)
results = append(results, analyzeBackupStorageLocations(backupStorageLocations)...)
results = append(results, analyzeDeleteBackupRequests(deleteBackupRequests)...)
results = append(results, analyzePodVolumeBackups(podVolumeBackups)...)
results = append(results, analyzePodVolumeRestores(podVolumeRestores)...)
results = append(results, analyzeRestores(restores)...)
results = append(results, analyzeSchedules(schedules)...)
results = append(results, analyzeVolumeSnapshotLocations(volumeSnapshotLocations)...)
return aggregateResults(results), nil
}
func analyzeBackupRepositories(backupRepositories []*velerov1.BackupRepository) []*AnalyzeResult {
results := []*AnalyzeResult{}
readyCount := 0
backupRepositoriesResult := &AnalyzeResult{
Title: "At least 1 Backup Repository configured",
}
if len(backupRepositories) == 0 {
backupRepositoriesResult.IsFail = true
backupRepositoriesResult.Message = "No backup repositories configured"
} else {
for _, backupRepository := range backupRepositories {
if backupRepository.Status.Phase != velerov1.BackupRepositoryPhaseReady {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup Repository %s", backupRepository.Name),
}
result.Message = fmt.Sprintf("Backup Repository [%s] is in phase %s", backupRepository.Name, backupRepository.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
readyCount++
}
}
if readyCount > 0 {
backupRepositoriesResult.IsPass = true
backupRepositoriesResult.Message = fmt.Sprintf("Found %d backup repositories configured and %d Ready", len(backupRepositories), readyCount)
} else {
backupRepositoriesResult.IsWarn = true
backupRepositoriesResult.Message = fmt.Sprintf("Found %d configured backup repositories, but none are ready", len(backupRepositories))
}
}
results = append(results, backupRepositoriesResult)
return results
}
func analyzeResticRepositories(resticRepositories []*restic_types.ResticRepository) []*AnalyzeResult {
results := []*AnalyzeResult{}
readyCount := 0
resticRepositoriesResult := &AnalyzeResult{
Title: "At least 1 Restic Repository configured",
}
if len(resticRepositories) == 0 {
resticRepositoriesResult.IsFail = true
resticRepositoriesResult.Message = "No restic repositories configured"
} else {
for _, resticRepository := range resticRepositories {
if resticRepository.Status.Phase != restic_types.ResticRepositoryPhaseReady {
result := &AnalyzeResult{
Title: fmt.Sprintf("Restic Repository %s", resticRepository.GetName()),
}
result.Message = fmt.Sprintf("Restic Repository [%s] is in phase %s", resticRepository.Name, resticRepository.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
readyCount++
}
}
if readyCount > 0 {
resticRepositoriesResult.IsPass = true
resticRepositoriesResult.Message = fmt.Sprintf("Found %d restic repositories configured and %d Ready", len(resticRepositories), readyCount)
} else {
resticRepositoriesResult.IsWarn = true
resticRepositoriesResult.Message = fmt.Sprintf("Found %d configured restic repositories, but none are ready", len(resticRepositories))
}
}
results = append(results, resticRepositoriesResult)
return results
}
func analyzeBackups(backups []*velerov1.Backup) []*AnalyzeResult {
results := []*AnalyzeResult{}
failedPhases := map[velerov1.BackupPhase]bool{
velerov1.BackupPhaseFailed: true,
velerov1.BackupPhasePartiallyFailed: true,
velerov1.BackupPhaseFailedValidation: true,
velerov1.BackupPhaseFinalizingPartiallyFailed: true,
velerov1.BackupPhaseWaitingForPluginOperationsPartiallyFailed: true,
}
for _, backup := range backups {
if failedPhases[backup.Status.Phase] {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup %s", backup.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Backup %s phase is %s", backup.Name, backup.Status.Phase)
results = append(results, result)
}
}
if len(backups) > 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Backups",
IsPass: true,
Message: fmt.Sprintf("Found %d backups", len(backups)),
})
}
return results
}
func analyzeBackupStorageLocations(backupStorageLocations []*velerov1.BackupStorageLocation) []*AnalyzeResult {
results := []*AnalyzeResult{}
availableCount := 0
bslResult := &AnalyzeResult{
Title: "At least 1 Backup Storage Location configured",
}
if len(backupStorageLocations) == 0 {
bslResult.IsFail = true
bslResult.Message = "No backup storage locations configured"
} else {
for _, backupStorageLocation := range backupStorageLocations {
if backupStorageLocation.Status.Phase != velerov1.BackupStorageLocationPhaseAvailable {
result := &AnalyzeResult{
Title: fmt.Sprintf("Backup Storage Location %s", backupStorageLocation.Name),
}
result.Message = fmt.Sprintf("Backup Storage Location [%s] is in phase %s", backupStorageLocation.Name, backupStorageLocation.Status.Phase)
result.IsWarn = true
results = append(results, result)
} else {
availableCount++
}
}
if availableCount > 0 {
bslResult.IsPass = true
bslResult.Message = fmt.Sprintf("Found %d backup storage locations configured and %d Available", len(backupStorageLocations), availableCount)
} else {
bslResult.IsWarn = true
bslResult.Message = fmt.Sprintf("Found %d configured backup storage locations, but none are available", len(backupStorageLocations))
}
}
results = append(results, bslResult)
return results
}
func analyzeDeleteBackupRequests(deleteBackupRequests []*velerov1.DeleteBackupRequest) []*AnalyzeResult {
results := []*AnalyzeResult{}
inProgressCount := 0
if len(deleteBackupRequests) > 0 {
for _, deleteBackupRequest := range deleteBackupRequests {
if deleteBackupRequest.Status.Phase == velerov1.DeleteBackupRequestPhaseInProgress {
inProgressCount++
}
}
if inProgressCount > 0 {
deleteBackupRequestsResult := &AnalyzeResult{
Title: "Delete Backup Requests summary",
}
deleteBackupRequestsResult.IsWarn = true
deleteBackupRequestsResult.Message = fmt.Sprintf("Found %d delete backup requests in progress", inProgressCount)
results = append(results, deleteBackupRequestsResult)
}
}
return results
}
func analyzePodVolumeBackups(podVolumeBackups []*velerov1.PodVolumeBackup) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(podVolumeBackups) > 0 {
for _, podVolumeBackup := range podVolumeBackups {
if podVolumeBackup.Status.Phase == velerov1.PodVolumeBackupPhaseFailed {
result := &AnalyzeResult{
Title: fmt.Sprintf("Pod Volume Backup %s", podVolumeBackup.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Pod Volume Backup %s phase is %s", podVolumeBackup.Name, podVolumeBackup.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Pod Volume Backups",
IsPass: true,
Message: fmt.Sprintf("Found %d pod volume backups", len(podVolumeBackups)),
})
}
}
return results
}
func analyzePodVolumeRestores(podVolumeRestores []*velerov1.PodVolumeRestore) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(podVolumeRestores) > 0 {
for _, podVolumeRestore := range podVolumeRestores {
if podVolumeRestore.Status.Phase == velerov1.PodVolumeRestorePhaseFailed {
result := &AnalyzeResult{
Title: fmt.Sprintf("Pod Volume Restore %s", podVolumeRestore.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Pod Volume Restore %s phase is %s", podVolumeRestore.Name, podVolumeRestore.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Pod Volume Restores",
IsPass: true,
Message: fmt.Sprintf("Found %d pod volume restores", len(podVolumeRestores)),
})
}
}
return results
}
func analyzeRestores(restores []*velerov1.Restore) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(restores) > 0 {
failedPhases := map[velerov1.RestorePhase]bool{
velerov1.RestorePhaseFailed: true,
velerov1.RestorePhasePartiallyFailed: true,
velerov1.RestorePhaseFailedValidation: true,
velerov1.RestorePhaseWaitingForPluginOperationsPartiallyFailed: true,
}
for _, restore := range restores {
if failedPhases[restore.Status.Phase] {
result := &AnalyzeResult{
Title: fmt.Sprintf("Restore %s", restore.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Restore %s phase is %s", restore.Name, restore.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Restores",
IsPass: true,
Message: fmt.Sprintf("Found %d restores", len(restores)),
})
}
}
return results
}
func analyzeSchedules(schedules []*velerov1.Schedule) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(schedules) > 0 {
for _, schedule := range schedules {
if schedule.Status.Phase == velerov1.SchedulePhaseFailedValidation {
result := &AnalyzeResult{
Title: fmt.Sprintf("Schedule %s", schedule.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Schedule %s phase is %s", schedule.Name, schedule.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Schedules",
IsPass: true,
Message: fmt.Sprintf("Found %d schedules", len(schedules)),
})
}
}
return results
}
func analyzeVolumeSnapshotLocations(volumeSnapshotLocations []*velerov1.VolumeSnapshotLocation) []*AnalyzeResult {
results := []*AnalyzeResult{}
failures := 0
if len(volumeSnapshotLocations) > 0 {
for _, volumeSnapshotLocation := range volumeSnapshotLocations {
if volumeSnapshotLocation.Status.Phase == velerov1.VolumeSnapshotLocationPhaseUnavailable {
result := &AnalyzeResult{
Title: fmt.Sprintf("Volume Snapshot Location %s", volumeSnapshotLocation.Name),
}
result.IsFail = true
result.Message = fmt.Sprintf("Volume Snapshot Location %s phase is %s", volumeSnapshotLocation.Name, volumeSnapshotLocation.Status.Phase)
results = append(results, result)
failures++
}
}
if failures == 0 {
results = append(results, &AnalyzeResult{
Title: "Velero Volume Snapshot Locations",
IsPass: true,
Message: fmt.Sprintf("Found %d volume snapshot locations", len(volumeSnapshotLocations)),
})
}
}
return results
}
func analyzeLogs(logs map[string][]byte, kind string) []*AnalyzeResult {
results := []*AnalyzeResult{}
if len(logs) > 0 {
for key, logBytes := range logs {
logContent := string(logBytes)
result := &AnalyzeResult{
Title: fmt.Sprintf("Velero logs for pod [%s]", key),
}
if strings.Contains(logContent, "permission denied") {
result.IsWarn = true
result.Message = fmt.Sprintf("Found 'permission denied' in %s pod log file(s)", kind)
results = append(results, result)
continue
}
if strings.Contains(logContent, "error") || strings.Contains(logContent, "panic") || strings.Contains(logContent, "fatal") {
result.IsWarn = true
result.Message = fmt.Sprintf("Found error|panic|fatal in %s pod log file(s)", kind)
results = append(results, result)
}
}
results = append(results, &AnalyzeResult{
Title: fmt.Sprintf("Velero Logs analysis for kind [%s]", kind),
IsPass: true,
Message: fmt.Sprintf("Found %d log files", len(logs)),
})
}
return results
}
func aggregateResults(results []*AnalyzeResult) []*AnalyzeResult {
out := []*AnalyzeResult{}
resultFailed := false
for _, result := range results {
if result.IsFail {
resultFailed = true
}
out = append(out, result)
}
if len(results) > 0 {
if resultFailed == false {
out = append(out, &AnalyzeResult{
Title: "Velero Status",
IsPass: true,
Message: "Velero setup is healthy",
})
}
if resultFailed == true {
out = append(out, &AnalyzeResult{
Title: "Velero Status",
IsWarn: true,
Message: "Velero setup is not entirely healthy",
})
}
}
return out
}
func getVeleroVersion(excludedFiles []string, findFiles getChildCollectedFileContents) (string, error) {
veleroDeploymentDir := "cluster-resources/deployments"
veleroVersion := ""
veleroDeploymentGlob := filepath.Join(veleroDeploymentDir, "velero.json")
veleroDeploymentJson, err := findFiles(veleroDeploymentGlob, excludedFiles)
if err != nil {
return "", errors.Wrapf(err, "failed to find velero deployment file under %s", veleroDeploymentDir)
}
var deploymentList *appsV1.DeploymentList
// should run only once
for key, veleroDeploymentJsonBytes := range veleroDeploymentJson {
err := json.Unmarshal(veleroDeploymentJsonBytes, &deploymentList)
if err != nil {
return "", errors.Wrapf(err, "failed to unmarshal velero deployment json from %s", key)
}
break
}
for _, deployment := range deploymentList.Items {
for _, container := range deployment.Spec.Template.Spec.Containers {
if container.Name == "velero" {
container_image := container.Image
veleroVersion = strings.Split(container_image, ":")[1]
return veleroVersion, nil
}
}
}
return "", errors.Errorf("Unable to get velero version. Could not find velero container in deployment!")
}
func GetVeleroBackupsDirectory() string {
return "cluster-resources/custom-resources/backups.velero.io"
}
func GetVeleroBackupRepositoriesDirectory() string {
return "cluster-resources/custom-resources/backuprepositories.velero.io"
}
func GetVeleroBackupStorageLocationsDirectory() string {
return "cluster-resources/custom-resources/backupstoragelocations.velero.io"
}
func GetVeleroDeleteBackupRequestsDirectory() string {
return "cluster-resources/custom-resources/deletebackuprequests.velero.io"
}
func GetVeleroDownloadRequestsDirectory() string {
return "cluster-resources/custom-resources/downloadrequests.velero.io"
}
func GetVeleroLogsDirectory() string {
return "velero/logs"
}
func GetVeleroPodVolumeBackupsDirectory() string {
return "cluster-resources/custom-resources/podvolumebackups.velero.io"
}
func GetVeleroPodVolumeRestoresDirectory() string {
return "cluster-resources/custom-resources/podvolumerestores.velero.io"
}
func GetVeleroRestoresDirectory() string {
return "cluster-resources/custom-resources/restores.velero.io"
}
func GetVeleroSchedulesDirectory() string {
return "cluster-resources/custom-resources/schedules.velero.io"
}
func GetVeleroServerStatusRequestsDirectory() string {
return "cluster-resources/custom-resources/serverstatusrequests.velero.io"
}
func GetVeleroVolumeSnapshotLocationsDirectory() string {
return "cluster-resources/custom-resources/volumesnapshotlocations.velero.io"
}
func GetVeleroResticRepositoriesDirectory() string {
return "cluster-resources/custom-resources/resticrepositories.velero.io"
}
File diff suppressed because it is too large Load Diff
@@ -186,6 +186,10 @@ type CephStatusAnalyze struct {
Namespace string `json:"namespace" yaml:"namespace"`
}
type VeleroAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
}
type LonghornAnalyze struct {
AnalyzeMeta `json:",inline" yaml:",inline"`
Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"`
@@ -245,6 +249,7 @@ type Analyze struct {
Mysql *DatabaseAnalyze `json:"mysql,omitempty" yaml:"mysql,omitempty"`
Redis *DatabaseAnalyze `json:"redis,omitempty" yaml:"redis,omitempty"`
CephStatus *CephStatusAnalyze `json:"cephStatus,omitempty" yaml:"cephStatus,omitempty"`
Velero *VeleroAnalyze `json:"velero,omitempty" yaml:"velero,omitempty"`
Longhorn *LonghornAnalyze `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
RegistryImages *RegistryImagesAnalyze `json:"registryImages,omitempty" yaml:"registryImages,omitempty"`
WeaveReport *WeaveReportAnalyze `json:"weaveReport,omitempty" yaml:"weaveReport,omitempty"`
@@ -169,6 +169,11 @@ func (in *Analyze) DeepCopyInto(out *Analyze) {
*out = new(CephStatusAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Velero != nil {
in, out := &in.Velero, &out.Velero
*out = new(VeleroAnalyze)
(*in).DeepCopyInto(*out)
}
if in.Longhorn != nil {
in, out := &in.Longhorn, &out.Longhorn
*out = new(LonghornAnalyze)
@@ -4654,6 +4659,22 @@ func (in *UDPPortStatusAnalyze) DeepCopy() *UDPPortStatusAnalyze {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VeleroAnalyze) DeepCopyInto(out *VeleroAnalyze) {
*out = *in
in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroAnalyze.
func (in *VeleroAnalyze) DeepCopy() *VeleroAnalyze {
if in == nil {
return nil
}
out := new(VeleroAnalyze)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WeaveReportAnalyze) DeepCopyInto(out *WeaveReportAnalyze) {
*out = *in
+11
View File
@@ -0,0 +1,11 @@
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: velero
spec:
collectors:
- logs:
namespace: velero
name: velero/logs
analyzers:
- velero: {}
+3 -1
View File
@@ -1,6 +1,8 @@
package types
import "fmt"
import (
"fmt"
)
type NotFoundError struct {
Name string
@@ -2255,6 +2255,26 @@
}
}
},
"velero": {
"type": "object",
"properties": {
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"checkName": {
"type": "string"
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"weaveReport": {
"type": "object",
"required": [
@@ -2255,6 +2255,26 @@
}
}
},
"velero": {
"type": "object",
"properties": {
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"checkName": {
"type": "string"
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"weaveReport": {
"type": "object",
"required": [
@@ -2301,6 +2301,26 @@
}
}
},
"velero": {
"type": "object",
"properties": {
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"checkName": {
"type": "string"
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"strict": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
}
}
},
"weaveReport": {
"type": "object",
"required": [