Showing preview only (579K chars total). Download the full file or copy to clipboard to get everything.
Repository: kastenhq/kubestr
Branch: master
Commit: e70331629cb5
Files: 67
Total size: 553.9 KB
Directory structure:
gitextract_7zvi4ff5/
├── .github/
│ ├── dependabot.yaml
│ └── workflows/
│ ├── ci.yml
│ ├── dependency-review.yaml
│ ├── docker-publish.yml
│ ├── ossf-scorecard.yml
│ └── release.yaml
├── .goreleaser.yml
├── Dockerfile
├── FIO.md
├── LICENSE
├── README.md
├── _config.yml
├── _posts/
│ └── 2021-02-07-FasterStorage.md
├── cmd/
│ └── rootCmd.go
├── docs/
│ ├── README.md
│ └── _config.yml
├── extra/
│ └── csi-drivers
├── go.mod
├── go.sum
├── index.md
├── main.go
├── pkg/
│ ├── block/
│ │ ├── block_mount.go
│ │ └── block_mount_test.go
│ ├── common/
│ │ └── common.go
│ ├── csi/
│ │ ├── csi.go
│ │ ├── csi_ops.go
│ │ ├── csi_ops_test.go
│ │ ├── file_restore_inspector.go
│ │ ├── file_restore_inspector_steps_test.go
│ │ ├── file_restore_inspector_test.go
│ │ ├── mocks/
│ │ │ ├── mock_api_version_fetcher.go
│ │ │ ├── mock_application_creator.go
│ │ │ ├── mock_argument_validator.go
│ │ │ ├── mock_cleaner.go
│ │ │ ├── mock_data_validator.go
│ │ │ ├── mock_file_restore_stepper.go
│ │ │ ├── mock_kube_executor.go
│ │ │ ├── mock_port_forwarder.go
│ │ │ ├── mock_pvc_browser_stepper.go
│ │ │ ├── mock_snapshot_browser_stepper.go
│ │ │ ├── mock_snapshot_creator.go
│ │ │ └── mock_snapshot_restore_stepper.go
│ │ ├── pvc_inspector.go
│ │ ├── pvc_inspector_steps_test.go
│ │ ├── pvc_inspector_test.go
│ │ ├── snapshot_inspector.go
│ │ ├── snapshot_inspector_steps_test.go
│ │ ├── snapshot_inspector_test.go
│ │ ├── snapshot_restore.go
│ │ ├── snapshot_restore_steps_test.go
│ │ ├── snapshot_restore_test.go
│ │ └── types/
│ │ └── csi_types.go
│ ├── fio/
│ │ ├── _config.yml
│ │ ├── dbench_license
│ │ ├── fio.go
│ │ ├── fio_jobs.go
│ │ ├── fio_test.go
│ │ ├── fio_types.go
│ │ └── parsable_fio_output.go
│ └── kubestr/
│ ├── csi-drivers.go
│ ├── kubernetes_checks.go
│ ├── kubernetes_checks_test.go
│ ├── kubestr.go
│ ├── storage_provisioners.go
│ ├── storage_provisioners_test.go
│ └── utils.go
└── scripts/
└── load_csi_provisioners.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
- package-ecosystem: gomod
commit-message:
prefix: "deps(go):"
directory: "/"
ignore:
# Avoids unnecessarily auto-creating PRs for k8s dependencies, as these
# will be closed since k8s dependencies need to be updated all at once
# starting with kanister and go through additional validation.
- dependency-name: "k8s.io/*"
- dependency-name: "sigs.k8s.io/*"
open-pull-requests-limit: 5
schedule:
interval: daily
- package-ecosystem: github-actions
commit-message:
prefix: "deps(actions):"
directory: "/"
open-pull-requests-limit: 3
schedule:
interval: monthly
groups:
github-actions:
patterns:
- "actions/*"
- "github/codeql-action"
docker:
patterns:
- "docker/*"
- package-ecosystem: docker
commit-message:
prefix: "deps(docker):"
directory: "/"
open-pull-requests-limit: 4
schedule:
interval: monthly
groups:
all:
patterns:
- "*"
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
- master
tags:
- v*
pull_request:
permissions:
contents: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
-
name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: 'go.mod'
id: go
-
name: Build
run: go build -v .
-
name: Test
run: go test -v ./...
lint:
name: Lint
runs-on: ubuntu-latest
steps:
-
name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: 'go.mod'
-
name: golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v2.2.1
args: --timeout=5m --modules-download-mode=mod
skip-cache: true
================================================
FILE: .github/workflows/dependency-review.yaml
================================================
# Dependency Review Action
#
# This workflow scans dependency manifest files that change as part of a pull
# reqest, surfacing known-vulnerable versions of the packages declared or
# updated in the PR.
# If the workflow run is marked as required, PRs introducing known-vulnerable
# packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
#
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 'Dependency Review'
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
================================================
FILE: .github/workflows/docker-publish.yml
================================================
name: Docker
permissions:
contents: read
on:
push:
branches:
- main
- master
# Publish `v1.2.3` tags as releases.
tags:
- v*
pull_request:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
push:
permissions:
packages: write
contents: read
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This action can be useful if you want to add emulation
# support with QEMU to be able to build against more platforms.
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
# This action will create and boot a builder using
# by default the docker-container builder driver.
# Recommended for build multi-platform images, export cache, etc.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Log into ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
with:
platforms: linux/amd64,linux/arm64,linux/ppc64le
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .github/workflows/ossf-scorecard.yml
================================================
name: OSSF Scorecard
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
push:
branches: [ "master" ]
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '25 6 * * 5'
workflow_dispatch:
inputs:
ref:
description: 'branch or git ref to use for the build'
required: true
default: 'master'
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge
id-token: write
steps:
-
name: "Checkout repo"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
-
name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
publish_results: true
-
# Upload the results to GitHub's code scanning dashboard.
name: "Upload to results to dashboard"
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
sarif_file: results.sarif
-
name: "Upload analysis results as 'Job Artifact'"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
================================================
FILE: .github/workflows/release.yaml
================================================
name: Release
permissions:
contents: read
on:
release:
types:
- created
- published
jobs:
goreleaser:
name: Release Go Binary
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: 'go.mod'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .goreleaser.yml
================================================
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod download
builds:
- env:
- CGO_ENABLED=0
- GO_EXTLINK_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
archives:
- name_template: >-
{{ .ProjectName }}_
{{- .Version }}_
{{- if eq .Os "darwin" }}MacOS
{{- else if eq .Os "linux" }}Linux
{{- else if eq .Os "windows" }}Windows
{{- else }}{{ .Os }}{{ end }}_
{{- .Arch }}
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
================================================
FILE: Dockerfile
================================================
ARG BUILDPLATFROM
FROM --platform=$BUILDPLATFORM golang:1.26.1-bookworm@sha256:8e8aa801e8417ef0b5c42b504dd34db3db911bb73dba933bd8bde75ed815fdbb AS builder
ARG TARGETOS
ARG TARGETARCH
ARG TARGETPLATFROM
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=${TARGETOS} \
GOARCH=${TARGETARCH}
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN go build -o /dist/kubestr -ldflags="-w -s" .
FROM alpine:3.23@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
RUN apk --no-cache add fio
COPY --from=builder /dist/kubestr /
ENTRYPOINT ["/kubestr"]
================================================
FILE: FIO.md
================================================
# FIO
[](https://asciinema.org/a/D9EFwlEUVx787hayFapdHljBW)
## More info coming soon
## Examples of FIO files-
Here are some [examples](https://github.com/axboe/fio/tree/master/examples)
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
================================================
FILE: README.md
================================================
# Kubestr
## What is it?
Kubestr is a collection of tools to discover, validate and evaluate your kubernetes storage options.
As adoption of kubernetes grows so have the persistent storage offerings that are available to users. The introduction of [CSI](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/) (Container Storage Interface) has enabled storage providers to develop drivers with ease. In fact there are around a 100 different CSI drivers available today. Along with the existing in-tree providers, these options can make choosing the right storage difficult.
Kubestr can assist in the following ways-
- Identify the various storage options present in a cluster.
- Validate if the storage options are configured correctly.
- Evaluate the storage using common benchmarking tools like FIO.
[](https://asciinema.org/a/7iJTbWKwdhPHNWYV00LIgx7gn)
## Resources
Video
* [Cloud Native Live: Introducing Kubestr – A New Way to Explore your Kubernetes Storage Options](https://youtu.be/N79NY_0aO0w)
* [Introducing Kubestr - A handy tool for Kubernetes Storage](https://youtu.be/U3Rt9vcuQdc)
* [A new way to benchmark your kubernetes storage DoK Talks #71](https://www.youtube.com/watch?v=g64eIOk_Ob4)
Blogs
* [Benchmarking and Evaluating Your Kubernetes Storage with Kubestr](https://blog.kasten.io/benchmarking-kubernetes-storage-with-kubestr)
* [Kubestr: The Easy Button for Validating and Debugging Your Storage in Kubernetes](https://thenewstack.io/kubestr-the-easy-button-for-validating-and-debugging-your-storage-in-kubernetes/)
* [Introducing Kubestr - A handy tool for Kubernetes Storage](https://vzilla.co.uk/vzilla-blog/introducing-kubestr-a-handy-tool-for-kubernetes-storage)
## Using Kubestr
### To install the tool -
- Ensure that the kubernetes context is set and the cluster is accessible through your terminal. (Does [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) work?)
- Download the latest release [here](https://github.com/kastenhq/kubestr/releases/latest).
- Unpack the tool and make it an executable `chmod +x kubestr`.
### To discover available storage options -
- Run `./kubestr`
### To run an FIO test -
- Run `./kubestr fio -s <storage class>`
- Additional options like `--size` and `--fiofile` can be specified.
- For more information visit our [fio](https://github.com/kastenhq/kubestr/blob/master/FIO.md) page.
### To check a CSI drivers snapshot and restore capabilities -
- Run `./kubestr csicheck -s <storage class> -v <volume snapshot class>`
### To check if a StorageClass supports a block mount -
- Run `./kubestr blockmount -s StorageClass`
## Roadmap
- In the future we plan to allow users to post their FIO results and compare to others.
================================================
FILE: _config.yml
================================================
theme: jekyll-theme-cayman
title: Kubestr
description: Explore your kubernetes storage options
================================================
FILE: _posts/2021-02-07-FasterStorage.md
================================================
---
layout: post
title: "Faster Storage"
date: 2021-02-07
published: true
categories: fio storage
---
Some content other
================================================
FILE: cmd/rootCmd.go
================================================
// Copyright 2020 Kubestr Developers
// 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 cmd
import (
"context"
"encoding/json"
"fmt"
"os"
"time"
"github.com/kastenhq/kubestr/pkg/block"
"github.com/kastenhq/kubestr/pkg/csi"
csitypes "github.com/kastenhq/kubestr/pkg/csi/types"
"github.com/kastenhq/kubestr/pkg/fio"
"github.com/kastenhq/kubestr/pkg/kubestr"
"github.com/spf13/cobra"
)
var (
output string
outfile string
rootCmd = &cobra.Command{
Use: "kubestr",
Short: "A tool to validate kubernetes storage",
Long: `kubestr is a tool that will scan your k8s cluster
and validate that the storage systems in place as well as run
performance tests.`,
SilenceUsage: true,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
return Baseline(ctx, output)
},
}
storageClass string
namespace string
containerImage string
fioCheckerSize string
fioNodeSelector map[string]string
fioCheckerFilePath string
fioCheckerTestName string
fioCmd = &cobra.Command{
Use: "fio",
Short: "Runs an fio test",
Long: `Run an fio test`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
return Fio(ctx, output, outfile, storageClass, fioCheckerSize, namespace, fioNodeSelector, fioCheckerTestName, fioCheckerFilePath, containerImage)
},
}
csiCheckVolumeSnapshotClass string
csiCheckRunAsUser int64
csiCheckCleanup bool
csiCheckSkipCFSCheck bool
csiCheckCmd = &cobra.Command{
Use: "csicheck",
Short: "Runs the CSI snapshot restore check",
Long: "Validates a CSI provisioners ability to take a snapshot of an application and restore it",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
return CSICheck(ctx, output, outfile, namespace, storageClass, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, containerImage, csiCheckCleanup, csiCheckSkipCFSCheck)
},
}
browseLocalPort int
browseCmd = &cobra.Command{
Use: "browse",
Short: "Browse the contents of PVC or VolumeSnapshot",
Long: "Browse the contents of a CSI provisioned PVC or a CSI provisioned VolumeSnapshot.",
Deprecated: "use 'browse pvc' instead",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return browsePvcCmd.RunE(cmd, args)
},
}
showTree bool
browsePvcCmd = &cobra.Command{
Use: "pvc [PVC name]",
Short: "Browse the contents of a CSI PVC via file browser",
Long: "Browse the contents of a CSI provisioned PVC by cloning the volume and mounting it with a file browser.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return CsiPvcBrowse(context.Background(), args[0],
namespace,
csiCheckVolumeSnapshotClass,
csiCheckRunAsUser,
browseLocalPort,
showTree,
)
},
}
browseSnapshotCmd = &cobra.Command{
Use: "snapshot [Snapshot name]",
Short: "Browse the contents of a CSI VolumeSnapshot via file browser",
Long: "Browse the contents of a CSI provisioned VolumeSnapshot by cloning the volume and mounting it with a file browser.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return CsiSnapshotBrowse(context.Background(), args[0],
namespace,
csiCheckRunAsUser,
browseLocalPort,
showTree,
)
},
}
fromSnapshot string
fromPVC string
toPVC string
path string
restoreFileCmd = &cobra.Command{
Use: "file-restore",
Short: "Restore file(s) from a Snapshot or PVC to it's source PVC",
Long: "Restore file(s) from a given CSI provisioned VolumeSnapshot or PersistentVolumeClaim to another PersistentVolumeClaim.",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return FileRestore(context.Background(),
fromSnapshot,
fromPVC,
toPVC,
namespace,
csiCheckRunAsUser,
browseLocalPort,
path)
},
}
blockMountRunAsUser int64
blockMountCleanup bool
blockMountCleanupOnly bool
blockMountWaitTimeoutSeconds uint32
blockMountPVCSize string
blockMountCmd = &cobra.Command{
Use: "blockmount",
Short: "Checks if a storage class supports block volumes",
Long: `Checks if volumes provisioned by a storage class can be mounted in block mode.
The checker works as follows:
- It dynamically provisions a volume of the given storage class.
- It then launches a pod with the volume mounted as a block device.
- If the pod is successfully created then the test passes.
- If the pod fails or times out then the test fails.
In case of failure, re-run the checker with the "-c=false" flag and examine the
failed PVC and Pod: it may be necessary to adjust the default values used for
the PVC size, the pod wait timeout, etc. Clean up the failed resources by
running the checker with the "--cleanup-only" flag.
`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
checkerArgs := block.BlockMountCheckerArgs{
StorageClass: storageClass,
Namespace: namespace,
Cleanup: blockMountCleanup,
RunAsUser: blockMountRunAsUser,
ContainerImage: containerImage,
K8sObjectReadyTimeout: (time.Second * time.Duration(blockMountWaitTimeoutSeconds)),
PVCSize: blockMountPVCSize,
}
return BlockMountCheck(ctx, output, outfile, blockMountCleanupOnly, checkerArgs)
},
}
)
func init() {
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Options(json)")
rootCmd.PersistentFlags().StringVarP(&outfile, "outfile", "e", "", "The file where test results will be written")
rootCmd.AddCommand(fioCmd)
fioCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
_ = fioCmd.MarkFlagRequired("storageclass")
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "z", fio.DefaultPVCSize, "The size of the volume used to run FIO. Note that the FIO job definition is not scaled accordingly.")
fioCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run FIO.")
fioCmd.Flags().StringToStringVarP(&fioNodeSelector, "nodeselector", "N", map[string]string{}, "Node selector applied to pod.")
fioCmd.Flags().StringVarP(&fioCheckerFilePath, "fiofile", "f", "", "The path to a an fio config file.")
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test. Options(default-fio)")
fioCmd.Flags().StringVarP(&containerImage, "image", "i", "", "The container image used to create a pod.")
rootCmd.AddCommand(csiCheckCmd)
csiCheckCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
_ = csiCheckCmd.MarkFlagRequired("storageclass")
csiCheckCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
_ = csiCheckCmd.MarkFlagRequired("volumesnapshotclass")
csiCheckCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run the check.")
csiCheckCmd.Flags().StringVarP(&containerImage, "image", "i", "", "The container image used to create a pod.")
csiCheckCmd.Flags().BoolVarP(&csiCheckCleanup, "cleanup", "c", true, "Clean up the objects created by tool")
csiCheckCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the CSI check pod with the specified user ID (int)")
csiCheckCmd.Flags().BoolVarP(&csiCheckSkipCFSCheck, "skipCFScheck", "k", false, "Use this flag to skip validating the ability to clone a snapshot.")
rootCmd.AddCommand(browseCmd)
browseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
_ = browseCmd.MarkFlagRequired("volumesnapshotclass")
browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the resource to browse.")
browseCmd.PersistentFlags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)")
browseCmd.PersistentFlags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector")
browseCmd.PersistentFlags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of given PVC or VolumeSnapshot")
browseCmd.AddCommand(browsePvcCmd)
browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
_ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass")
browseCmd.AddCommand(browseSnapshotCmd)
rootCmd.AddCommand(restoreFileCmd)
restoreFileCmd.Flags().StringVarP(&fromSnapshot, "fromSnapshot", "f", "", "The name of a VolumeSnapshot.")
restoreFileCmd.Flags().StringVarP(&fromPVC, "fromPVC", "v", "", "The name of a PersistentVolumeClaim.")
restoreFileCmd.MarkFlagsMutuallyExclusive("fromSnapshot", "fromPVC")
restoreFileCmd.MarkFlagsOneRequired("fromSnapshot", "fromPVC")
restoreFileCmd.Flags().StringVarP(&toPVC, "toPVC", "t", "", "The name of a PersistentVolumeClaim.")
restoreFileCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of both the given PVC & VS.")
restoreFileCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)")
restoreFileCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector")
restoreFileCmd.Flags().StringVarP(&path, "path", "p", "", "Path of a file or directory that needs to be restored")
rootCmd.AddCommand(blockMountCmd)
blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)")
_ = blockMountCmd.MarkFlagRequired("storageclass")
blockMountCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run the check.")
blockMountCmd.Flags().StringVarP(&containerImage, "image", "i", "", "The container image used to create a pod.")
blockMountCmd.Flags().BoolVarP(&blockMountCleanup, "cleanup", "c", true, "Clean up the objects created by the check.")
blockMountCmd.Flags().BoolVarP(&blockMountCleanupOnly, "cleanup-only", "", false, "Do not run the checker, but just clean up resources left from a previous invocation.")
blockMountCmd.Flags().Int64VarP(&blockMountRunAsUser, "runAsUser", "u", 0, "Runs the block mount check pod with the specified user ID (int)")
blockMountCmd.Flags().Uint32VarP(&blockMountWaitTimeoutSeconds, "wait-timeout", "w", 60, "Max time in seconds to wait for the check pod to become ready")
blockMountCmd.Flags().StringVarP(&blockMountPVCSize, "pvc-size", "", "1Gi", "The size of the provisioned PVC.")
}
// Execute executes the main command
func Execute() error {
return rootCmd.Execute()
}
// Baseline executes the baseline check
func Baseline(ctx context.Context, output string) error {
p, err := kubestr.NewKubestr()
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Print(kubestr.Logo)
result := p.KubernetesChecks()
if PrintAndJsonOutput(result, output, outfile) {
return err
}
for _, retval := range result {
retval.Print()
fmt.Println()
time.Sleep(500 * time.Millisecond)
}
provisionerList, err := p.ValidateProvisioners(ctx)
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Println("Available Storage Provisioners:")
fmt.Println()
time.Sleep(500 * time.Millisecond) // Added to introduce lag.
for _, provisioner := range provisionerList {
provisioner.Print()
fmt.Println()
time.Sleep(500 * time.Millisecond)
}
return err
}
// PrintAndJsonOutput Print JSON output to stdout and to file if arguments say so
// Returns whether we have generated output or JSON
func PrintAndJsonOutput(result []*kubestr.TestOutput, output string, outfile string) bool {
if output == "json" {
jsonRes, _ := json.MarshalIndent(result, "", " ")
if len(outfile) > 0 {
err := os.WriteFile(outfile, jsonRes, 0666)
if err != nil {
fmt.Println("Error writing output:", err.Error())
os.Exit(2)
}
} else {
fmt.Println(string(jsonRes))
}
return true
}
return false
}
// Fio executes the FIO test.
func Fio(ctx context.Context, output, outfile, storageclass, size, namespace string, nodeSelector map[string]string, jobName, fioFilePath string, containerImage string) error {
cli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Println(err.Error())
return err
}
fioRunner := &fio.FIOrunner{
Cli: cli,
}
testName := "FIO test results"
var result *kubestr.TestOutput
fioResult, err := fioRunner.RunFio(ctx, &fio.RunFIOArgs{
StorageClass: storageclass,
Size: size,
Namespace: namespace,
NodeSelector: nodeSelector,
FIOJobName: jobName,
FIOJobFilepath: fioFilePath,
Image: containerImage,
})
if err != nil {
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, err.Error(), fioResult)
} else {
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, fmt.Sprintf("\n%s", fioResult.Result.Print()), fioResult)
}
var wrappedResult = []*kubestr.TestOutput{result}
if !PrintAndJsonOutput(wrappedResult, output, outfile) {
result.Print()
}
return err
}
func CSICheck(ctx context.Context, output, outfile,
namespace string,
storageclass string,
volumesnapshotclass string,
runAsUser int64,
containerImage string,
cleanup bool,
skipCFScheck bool,
) error {
testName := "CSI checker test"
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
csiCheckRunner := &csi.SnapshotRestoreRunner{
KubeCli: kubecli,
DynCli: dyncli,
}
var result *kubestr.TestOutput
csiCheckResult, err := csiCheckRunner.RunSnapshotRestore(ctx, &csitypes.CSISnapshotRestoreArgs{
StorageClass: storageclass,
VolumeSnapshotClass: volumesnapshotclass,
Namespace: namespace,
RunAsUser: runAsUser,
ContainerImage: containerImage,
Cleanup: cleanup,
SkipCFSCheck: skipCFScheck,
})
if err != nil {
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, err.Error(), csiCheckResult)
} else {
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, "CSI application successfully snapshotted and restored.", csiCheckResult)
}
var wrappedResult = []*kubestr.TestOutput{result}
if !PrintAndJsonOutput(wrappedResult, output, outfile) {
result.Print()
}
return err
}
func CsiPvcBrowse(ctx context.Context,
pvcName string,
namespace string,
volumeSnapshotClass string,
runAsUser int64,
localPort int,
showTree bool,
) error {
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
browseRunner := &csi.PVCBrowseRunner{
KubeCli: kubecli,
DynCli: dyncli,
}
err = browseRunner.RunPVCBrowse(ctx, &csitypes.PVCBrowseArgs{
PVCName: pvcName,
Namespace: namespace,
VolumeSnapshotClass: volumeSnapshotClass,
RunAsUser: runAsUser,
LocalPort: localPort,
ShowTree: showTree,
})
if err != nil {
fmt.Printf("Failed to run PVC browser (%s)\n", err.Error())
}
return err
}
func CsiSnapshotBrowse(ctx context.Context,
snapshotName string,
namespace string,
runAsUser int64,
localPort int,
showTree bool,
) error {
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
browseRunner := &csi.SnapshotBrowseRunner{
KubeCli: kubecli,
DynCli: dyncli,
}
err = browseRunner.RunSnapshotBrowse(ctx, &csitypes.SnapshotBrowseArgs{
SnapshotName: snapshotName,
Namespace: namespace,
RunAsUser: runAsUser,
LocalPort: localPort,
ShowTree: showTree,
})
if err != nil {
fmt.Printf("Failed to run Snapshot browser (%s)\n", err.Error())
}
return err
}
func FileRestore(ctx context.Context,
fromSnapshotName string,
fromPVCName string,
toPVCName string,
namespace string,
runAsUser int64,
localPort int,
path string,
) error {
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
fileRestoreRunner := &csi.FileRestoreRunner{
KubeCli: kubecli,
DynCli: dyncli,
}
err = fileRestoreRunner.RunFileRestore(ctx, &csitypes.FileRestoreArgs{
FromSnapshotName: fromSnapshotName,
FromPVCName: fromPVCName,
ToPVCName: toPVCName,
Namespace: namespace,
RunAsUser: runAsUser,
LocalPort: localPort,
Path: path,
})
if err != nil {
fmt.Printf("Failed to run file-restore (%s)\n", err.Error())
}
return err
}
func BlockMountCheck(ctx context.Context, output, outfile string, cleanupOnly bool, checkerArgs block.BlockMountCheckerArgs) error {
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
checkerArgs.KubeCli = kubecli
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
checkerArgs.DynCli = dyncli
blockMountTester, err := block.NewBlockMountChecker(checkerArgs)
if err != nil {
fmt.Printf("Failed to initialize BlockMounter (%s)", err.Error())
return err
}
if cleanupOnly {
blockMountTester.Cleanup()
return nil
}
var (
testName = "Block VolumeMode test"
result *kubestr.TestOutput
)
mountResult, err := blockMountTester.Mount(ctx)
if err != nil {
if !checkerArgs.Cleanup {
fmt.Printf("Warning: Resources may not have been released. Rerun with the additional --cleanup-only flag.\n")
}
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, fmt.Sprintf("StorageClass (%s) does not appear to support Block VolumeMode", checkerArgs.StorageClass), mountResult)
} else {
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, fmt.Sprintf("StorageClass (%s) supports Block VolumeMode", checkerArgs.StorageClass), mountResult)
}
var wrappedResult = []*kubestr.TestOutput{result}
if !PrintAndJsonOutput(wrappedResult, output, outfile) {
result.Print()
}
return err
}
================================================
FILE: docs/README.md
================================================
# Kubestr
Kubestr is a tool that qualifies the storage options present in a cluster.
For more options visit kubestr.io
================================================
FILE: docs/_config.yml
================================================
theme: jekyll-theme-hacker
================================================
FILE: extra/csi-drivers
================================================
# Drivers
The following are a set of CSI driver which can be used with Kubernetes:
> NOTE: If you would like your driver to be added to this table, please open a pull request in [this repo](https://github.com/kubernetes-csi/docs/pulls) updating this file. Other Features is allowed to be filled in Raw Block, Snapshot, Expansion and Cloning. If driver did not implement any Other Features, please leave it blank.
> DISCLAIMER: Information in this table has not been validated by Kubernetes SIG-Storage. Users who want to use these CSI drivers need to contact driver maintainers for driver capabilities.
## Production Drivers
Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persistence (Beyond Pod Lifetime) | Supported Access Modes | Dynamic Provisioning | Other Features
-----|-----------------|--------------------------------|-------------|-----------------------------------|------------------------|----------------------|--------
[Alicloud Disk](https://github.com/AliyunContainerService/csi-plugin) | `diskplugin.csi.alibabacloud.com` | v1.0 | A Container Storage Interface (CSI) Driver for Alicloud Disk | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
[Alicloud NAS](https://github.com/AliyunContainerService/csi-plugin) | `nasplugin.csi.alibabacloud.com` | v1.0 | A Container Storage Interface (CSI) Driver for Alicloud Network Attached Storage (NAS) | Persistent | Read/Write Multiple Pods | No |
[Alicloud OSS](https://github.com/AliyunContainerService/csi-plugin)| `ossplugin.csi.alibabacloud.com` | v1.0 | A Container Storage Interface (CSI) Driver for Alicloud Object Storage Service (OSS) | Persistent | Read/Write Multiple Pods | No |
[ArStor CSI](https://github.com/huayun-docs/csi-driver-arstor) | `arstor.csi.huayun.io` | v1.0 | A Container Storage Interface (CSI) Driver for Huayun Storage Service (ArStor) | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[AWS Elastic Block Storage](https://github.com/kubernetes-sigs/aws-ebs-csi-driver) | `ebs.csi.aws.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for AWS Elastic Block Storage (EBS) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[AWS Elastic File System](https://github.com/aws/aws-efs-csi-driver) | `efs.csi.aws.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for AWS Elastic File System (EFS) | Persistent | Read/Write Multiple Pods | No |
[AWS FSx for Lustre](https://github.com/aws/aws-fsx-csi-driver) | `fsx.csi.aws.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for AWS FSx for Lustre (EBS) | Persistent | Read/Write Multiple Pods | Yes |
[Azure disk](https://github.com/kubernetes-sigs/azuredisk-csi-driver) | `disk.csi.azure.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Azure disk | Persistent | Read/Write Single Pod | Yes |
[Azure file](https://github.com/kubernetes-sigs/azurefile-csi-driver) | `file.csi.azure.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Azure file | Persistent | Read/Write Multiple Pods | Yes |
[BeeGFS](https://github.com/NetApp/beegfs-csi-driver) | `beegfs.csi.netapp.com` | v1.3 | A Container Storage Interface (CSI) Driver for the [BeeGFS](https://www.beegfs.io/) Parallel File System | Persistent | Read/Write Multiple Pods | Yes |
[Bigtera VirtualStor (block)](https://github.com/bigtera-ce/ceph-csi) | `csi.block.bigtera.com` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for Bigtera VirtualStor block storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[Bigtera VirtualStor (filesystem)](https://github.com/bigtera-ce/ceph-csi) | `csi.fs.bigtera.com` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for Bigtera VirtualStor filesystem | Persistent | Read/Write Multiple Pods | Yes | Expansion
[BizFlyCloud Block Storage](https://github.com/bizflycloud/csi-bizflycloud) | `volume.csi.bizflycloud.vn` | v1.2 | A Container Storage Interface (CSI) Driver for BizFly Cloud block storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[CephFS](https://github.com/ceph/ceph-csi) | `cephfs.csi.ceph.com` | v0.3, >=v1.0.0 | A Container Storage Interface (CSI) Driver for CephFS | Persistent | Read/Write Multiple Pods | Yes | Expansion, Snapshot, Cloning
[Ceph RBD](https://github.com/ceph/ceph-csi) | `rbd.csi.ceph.com` | v0.3, >=v1.0.0 | A Container Storage Interface (CSI) Driver for Ceph RBD | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology, Cloning
[ChubaoFS](https://github.com/chubaofs/chubaofs-csi) | `csi.chubaofs.com` | v1.0.0 | A Container Storage Interface (CSI) Driver for ChubaoFS Storage | Persistent | Read/Write Multiple Pods | Yes |
[Cinder](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/cinder) | `cinder.csi.openstack.org` | v0.3, v1.0, v1.1.0, v1.2.0, v1.3.0 | A Container Storage Interface (CSI) Driver for OpenStack Cinder | Persistent and Ephemeral | Depends on the storage backend used | Yes, if storage backend supports it | Raw Block, Snapshot, Expansion, Cloning, Topology
[cloudscale.ch](https://github.com/cloudscale-ch/csi-cloudscale) | `csi.cloudscale.ch` | v1.0 | A Container Storage Interface (CSI) Driver for the [cloudscale.ch](https://www.cloudscale.ch/) IaaS platform | Persistent | Read/Write Single Pod | Yes |Snapshot
[Datatom-InfinityCSI](https://github.com/datatom-infinity/infinity-csi) | `csi-infiblock-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
[Datatom-InfinityCSI (filesystem)](https://github.com/datatom-infinity/infinity-csi) | `csi-infifs-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity filesystem storage | Persistent | Read/Write Multiple Pods | Yes | Expansion
[Datera](https://github.com/Datera/datera-csi) | `dsp.csi.daterainc.io` | v1.0 | A Container Storage Interface (CSI) Driver for Datera Data Services Platform (DSP) | Persistent | Read/Write Single Pod | Yes |Snapshot
[DDN EXAScaler](https://github.com/DDNStorage/exa-csi-driver) | `exa.csi.ddn.com` | v1.0, v1.1 | A Container Storage Interface (CSI) Driver for DDN EXAScaler filesystems | Persistent | Read/Write Multiple Pods | Yes | Expansion
[Dell EMC PowerMax](https://github.com/dell/csi-powermax) | `csi-powermax.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerMax](https://www.delltechnologies.com/en-us/storage/powermax.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[Dell EMC PowerScale](https://github.com/dell/csi-powerscale) | `csi-isilon.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerScale](https://www.delltechnologies.com/en-us/storage/powerscale.htm) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[Dell EMC PowerStore](https://github.com/dell/csi-powerstore) | `csi-powerstore.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerStore](https://www.delltechnologies.com/en-us/storage/powerstore-storage-appliance.htm) | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[Dell EMC Unity](https://github.com/dell/csi-unity) | `csi-unity.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC Unity](https://www.delltechnologies.com/en-us/storage/unity.htm) | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[Dell EMC VxFlexOS](https://github.com/dell/csi-vxflexos) | `csi-vxflexos.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC VxFlexOS](https://www.delltechnologies.com/en-us/hyperconverged-infrastructure/vxflex.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[democratic-csi](https://github.com/democratic-csi/democratic-csi) | `org.democratic-csi.[X]` | v1.0,v1.1,v1.2,v1.3,v1.4,v1.5 | Generic CSI plugin supporting zfs based solutions ([FreeNAS](https://www.freenas.org/) / [TrueNAS](https://www.truenas.com/) and [ZoL](https://zfsonlinux.org/) solutions such as [Ubuntu](https://ubuntu.com/)), [Synology](https://www.synology.com/), and more | Persistent and Ephemeral | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block, Snapshot, Expansion, Cloning
[Diamanti-CSI](https://diamanti.com/use-cases/io-acceleration/#csi) | `dcx.csi.diamanti.com` | v1.0 | A Container Storage Interface (CSI) Driver for Diamanti DCX Platform | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[DigitalOcean Block Storage](https://github.com/digitalocean/csi-digitalocean) | `dobs.csi.digitalocean.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for DigitalOcean Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[Dothill-CSI](https://github.com/enix/dothill-csi) | `dothill.csi.enix.io` | v1.3 | Generic CSI plugin supporting [Seagate AssuredSan](https://www.seagate.com/fr/fr/support/dothill-san/assuredsan-pro-5000-series/) appliances such as [HPE MSA](https://www.hpe.com/us/en/storage/flash-hybrid.html), [Dell EMC PowerVault ME4](https://www.dell.com/fr-fr/work/shop/productdetailstxn/powervault-me4-series) and others ... | Persistent | Read/Write Single Node | Yes | Snapshot, Expansion
[Ember CSI](https://ember-csi.io) | `[x].ember-csi.io` | v0.2, v0.3, v1.0 | Multi-vendor CSI plugin supporting over 80 Drivers to provide block and mount storage to Container Orchestration systems. | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
[Excelero NVMesh](https://github.com/Excelero/nvmesh-csi-driver) | `nvmesh-csi.excelero.com` | v1.0, v1.1 | A Container Storage Interface (CSI) Driver for Excelero NVMesh | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Expansion
[GCE Persistent Disk](https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver) | `pd.csi.storage.gke.io` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Google Compute Engine Persistent Disk (GCE PD) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
[Google Cloud Filestore](https://github.com/kubernetes-sigs/gcp-filestore-csi-driver) | `com.google.csi.filestore` | v0.3 | A Container Storage Interface (CSI) Driver for Google Cloud Filestore | Persistent | Read/Write Multiple Pods | Yes |
[Google Cloud Storage](https://github.com/ofek/csi-gcs) | `gcs.csi.ofek.dev` | v1.0 | A Container Storage Interface (CSI) Driver for Google Cloud Storage | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Expansion
[GlusterFS](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glusterfs` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for GlusterFS | Persistent | Read/Write Multiple Pods | Yes | Snapshot
[Gluster VirtBlock](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glustervirtblock` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Gluster Virtual Block volumes | Persistent | Read/Write Single Pod | Yes |
[Hammerspace CSI](https://github.com/hammer-space/csi-plugin) | `com.hammerspace.csi` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hammerspace Storage | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot
[Hedvig](https://documentation.commvault.com/commvault/hedvig/others/pdf/Hedvig_CSI_User_Guide.pdf) | `io.hedvig.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Hedvig | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion
[Hetzner Cloud Volumes CSI](https://github.com/hetznercloud/csi-driver) | `csi.hetzner.cloud` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hetzner Cloud Volumes | Persistent | Read/Write Single Pod | Yes | Raw Block, Expansion
[Hitachi Vantara](https://knowledge.hitachivantara.com/Documents/Adapters_and_Drivers/Storage_Adapters_and_Drivers/Containers) | `hspc.csi.hitachi.com` | v1.2 | A Container Storage Interface (CSI) Driver for VSP series Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[HPE](https://github.com/hpe-storage/csi-driver) | `csi.hpe.com` | v1.3 | A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Alletra](https://hpe.com/storage/alletra), [Nimble Storage](https://hpe.com/storage/nimble), [Primera](https://hpe.com/storage/primera) and [3PAR](https://hpe.com/storage/3par) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[HPE Ezmeral (MapR)](https://github.com/mapr/mapr-csi) | `com.mapr.csi-kdf` | v1.3 | A Container Storage Interface (CSI) Driver for HPE Ezmeral Data Fabric | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[Huawei Storage CSI](https://github.com/Huawei/eSDK_K8S_Plugin) | `csi.huawei.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for FusionStorage, OceanStor 100D, OceanStor Pacific, OceanStor Dorado V3, OceanStor Dorado V6, OceanStor V3, OceanStor V5 | Persistent | Read/Write Multiple Pod | Yes | Snapshot, Expansion, Cloning
[HyperV CSI](https://github.com/Zetanova/hyperv-csi-driver) | `eu.zetanova.csi.hyperv` | v1.0, v1.1 | A Container Storage Interface (CSI) driver to manage hyperv hosts | Persistent | Read/Write Multiple Pods | Yes |
[IBM Block Storage](https://github.com/ibm/ibm-block-csi-driver) | `block.csi.ibm.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/stg-block-csi-driver) for IBM Spectrum Virtualize Family, IBM FlashSystem A9000 and A9000R, IBM DS8000 Family 8.x and higher. | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[IBM Spectrum Scale](https://github.com/IBM/ibm-spectrum-scale-csi) | `spectrumscale.csi.ibm.com` | v1.0, v1.1 | A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/spectrum-scale-csi) for the IBM Spectrum Scale File System | Persistent | Read/Write Multiple Pod | Yes | Snapshot
[IBM Cloud Block Storage VPC CSI Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block) | `vpc.block.csi.ibm.io` | v1.0 | A Container Storage Interface (CSI) [Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block) for IBM Cloud Kubernetes Service and Red Hat OpenShift on IBM Cloud | Persistent | Read/Write Single Pod | Yes | Raw Block |
[Infinidat](https://github.com/Infinidat/infinibox-csi-driver) | `infinibox-csi-driver` | v1.0, v1.1 | A Container Storage Interface (CSI) Driver for Infinidat [InfiniBox](https://infinidat.com/en/products-technology/infinibox) | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[Inspur InStorage CSI](https://github.com/OpenInspur/instorage-k8s) | `csi-instorage` | v1.0 | A Container Storage Interface (CSI) Driver for inspur AS/HF/CS/CF Series Primary Storage, inspur AS13000 Series SDS Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[Intel PMEM-CSI](https://github.com/intel/pmem-csi) | `pmem-csi.intel.com` | v1.0 | A Container Storage Interface (CSI) driver for [PMEM](https://pmem.io/) from Intel | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block
[Intelliflash Block Storage](https://github.com/DDNStorage/intelliflash-csi-block-driver) | `intelliflash-csi-block-driver.intelliflash.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for Intelliflash Block Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[Intelliflash File Storage](https://github.com/DDNStorage/intelliflash-csi-file-driver) | `intelliflash-csi-file-driver.intelliflash.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for Intelliflash File Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[ionir ](https://github.com/ionir-cloud) | `ionir` | v1.2 | A Container Storage Interface (CSI) Driver for [ionir](https://www.ionir.com/) Kubernetes-Native Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Cloning
[JuiceFS](https://github.com/juicedata/juicefs-csi-driver) | `csi.juicefs.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for JuiceFS File System | Persistent | Read/Write Multiple Pod | Yes |
[kaDalu](https://github.com/kadalu/kadalu) | `org.kadalu.gluster` | v0.3 | A CSI Driver (and operator) for GlusterFS | Persistent | Read/Write Multiple Pods | Yes |
[KumoScale Block Storage](https://github.com/KioxiaAmerica/kumoscale-csi) | `kumoscale.kioxia.com` | v1.0 | A Container Storage Interface (CSI) Driver for KumoScale Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
[Linode Block Storage](https://github.com/linode/linode-blockstorage-csi-driver) | `linodebs.csi.linode.com` | v1.0 | A Container Storage Interface (CSI) Driver for Linode Block Storage | Persistent | Read/Write Single Pod | Yes |
[LINSTOR](https://github.com/piraeusdatastore/linstor-csi) | `linstor.csi.linbit.com` | v1.2 | A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[Longhorn](https://github.com/longhorn/longhorn) | `driver.longhorn.io` | v1.2 | A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes | Persistent | Read/Write Single Node | Yes | Raw Block
[MacroSAN](https://github.com/macrosan-csi/macrosan-csi-driver) | `csi-macrosan` | v1.0 | A Container Storage Interface (CSI) Driver for MacroSAN Block Storage | Persistent | Read/Write Single Pod | Yes |
[Manila](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/manila) | `manila.csi.openstack.org` | v1.1, v1.2 | A Container Storage Interface (CSI) Driver for OpenStack Shared File System Service (Manila) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Topology
[MooseFS](https://github.com/moosefs/moosefs-csi) | `com.tuxera.csi.moosefs` | v1.0 | A Container Storage Interface (CSI) Driver for [MooseFS](https://moosefs.com/) clusters. | Persistent | Read/Write Multiple Pods | Yes |
[NetApp](https://github.com/NetApp/trident) | `csi.trident.netapp.io` | v1.0, v1.1, v1.2, v1.3 | A Container Storage Interface (CSI) Driver for NetApp's [Trident](https://netapp-trident.readthedocs.io/) container storage orchestrator | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[NexentaStor File Storage](https://github.com/Nexenta/nexentastor-csi-driver) | `nexentastor-csi-driver.nexenta.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for NexentaStor File Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[NexentaStor Block Storage](https://github.com/Nexenta/nexentastor-csi-driver-block) | `nexentastor-block-csi-driver.nexenta.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for NexentaStor over iSCSI protocol | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology, Raw block
[Nutanix](https://github.com/nutanix/csi-plugin) | `csi.nutanix.com` | v0.3, v1.0, v1.2 | A Container Storage Interface (CSI) Driver for Nutanix | Persistent | "Read/Write Single Pod" with Nutanix Volumes and "Read/Write Multiple Pods" with Nutanix Files | Yes | Raw Block, Snapshot, Expansion, Cloning
[OpenEBS](https://github.com/openebs/csi)| `cstor.csi.openebs.io` | v1.0 | A Container Storage Interface (CSI) Driver for [OpenEBS](https://www.openebs.io/)| Persistent | Read/Write Single Pod | Yes | Expansion, Snapshot, Cloning
[Open-E](https://github.com/open-e/JovianDSS-KubernetesCSI) | `com.open-e.joviandss.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Open-E JovianDSS Storage | Persistent | Read/Write Single Pod | Yes | Snapshot, Cloning
[Open-Local](https://github.com/alibaba/open-local) | `local.csi.alibaba.com` | v1.0 | A Container Storage Interface (CSI) Driver for Local Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Expansion, Snapshot
[Oracle Cloud Infrastructure(OCI) Block Storage](https://github.com/oracle/oci-cloud-controller-manager/blob/master/container-storage-interface.md) | `blockvolume.csi.oraclecloud.com` | v1.1 | A Container Storage Interface (CSI) Driver for Oracle Cloud Infrastructure (OCI) Block Storage | Persistent | Read/Write Single Pod | Yes | Topology
[oVirt](https://github.com/openshift/ovirt-csi-driver) | `csi.ovirt.org` | v1.0 | A Container Storage Interface (CSI) Driver for [oVirt](https://ovirt.org) | Persistent | Read/Write Single Pod | Yes | Block, File Storage
[Portworx](https://github.com/libopenstorage/openstorage/tree/master/csi) | `pxd.portworx.com` | v1.4 | A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Raw Block, Cloning
[Pure Storage CSI](https://github.com/purestorage/pso-csi)| `pure-csi` | v1.0, v1.1, v1.2, v1.3 | A Container Storage Interface (CSI) Driver for Pure Storage's [Pure Service Orchestrator](https://purestorage.com/containers) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Snapshot, Cloning, Raw Block, Topology, Expansion
[QingCloud CSI](https://github.com/yunify/qingcloud-csi)| `disk.csi.qingcloud.com` | v1.1 | A Container Storage Interface (CSI) Driver for QingCloud Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[QingStor CSI](https://github.com/yunify/qingstor-csi) | `neonsan.csi.qingstor.com` | v0.3, v1.1 | A Container Storage Interface (CSI) Driver for NeonSAN storage system | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[Quobyte](https://github.com/quobyte/quobyte-csi) | `quobyte-csi` | v0.2 | A Container Storage Interface (CSI) Driver for Quobyte | Persistent | Read/Write Multiple Pods | Yes |
[ROBIN](https://get.robin.io/) | `robin` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [ROBIN](https://docs.robin.io) | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[SandStone](https://github.com/sandstone-storage/sandstone-csi-driver) | `csi-sandstone-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SandStone USP | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[Sangfor-EDS-File-Storage](https://github.com/evan37717/sangfor-eds-csi) | `eds.csi.file.sangfor.com` | v1.0 | A Container Storage Interface (CSI) Driver for Sangfor Distributed File Storage(EDS) | Persistent | Read/Write Multiple Pods | Yes |
[Sangfor-EDS-Block-Storage](https://github.com/eds-wzc/sangfor-eds-csi) | `eds.csi.block.sangfor.com` | v1.0 | A Container Storage Interface (CSI) Driver for Sangfor Block Storage(EDS) | Persistent | Read/Write Single Pod | Yes |
[Scaleway CSI](https://github.com/scaleway/scaleway-csi) | `csi.scaleway.com` | v1.2.0 | Container Storage Interface (CSI) Driver for [Scaleway Block Storage](https://www.scaleway.com/block-storage/) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
[Seagate Exos X](https://github.com/Seagate/seagate-exos-x-csi) | `csi-exos-x.seagate.com` | v1.3 | CSI driver for [Seagate Exos X](https://www.seagate.com/products/storage/data-storage-systems/raid/) and OEM systems | Persistent | Read/Write Single Pod | Yes | Snapshot, Expansion, Cloning
[SeaweedFS](https://github.com/seaweedfs/seaweedfs-csi-driver) | `seaweedfs-csi-driver` | v1.0 | A Container Storage Interface (CSI Driver for [SeaweedFS](https://github.com/chrislusf/seaweedfs)) | Persistent | Read/Write Multiple Pods | Yes |
[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) | `secrets-store.csi.k8s.io` | v0.0.10 | A Container Storage Interface (CSI) Driver for mounting secrets, keys, and certs stored in enterprise-grade external secrets stores as volumes. | Ephemeral | N/A | N/A |
[SmartX](http://www.smartx.com/?locale=en) | `csi-smtx-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SmartX ZBS Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
[SODA](https://github.com/sodafoundation/nbp/tree/master/csi) | `csi-soda-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for [SODA](https://sodafoundation.io/) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
[SPDK-CSI](https://github.com/spdk/spdk-csi) | `csi.spdk.io` | v1.1 | A Container Storage Interface (CSI) Driver for [SPDK](https://spdk.io/) | Persistent and Ephemeral | Read/Write Single Pod | Yes |
[StorageOS](https://docs.storageos.com/docs/platforms/kubernetes/install/) | `storageos` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [StorageOS](https://storageos.com/) | Persistent | Read/Write Multiple Pods | Yes |
[Storidge](https://docs.storidge.com/kubernetes_storage/overview.html) | `csi.cio.storidge.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [Storidge CIO](https://storidge.com/) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
[StorPool](https://kb.storpool.com/storpool_integrations/github/kubernetes.html) | `csi-driver.storpool.com` | v1.0 | A Container Storage Interface (CSI) Driver for [StorPool](https://storpool.com/) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Expansion
[Synology](https://github.com/SynologyOpenSource/synology-csi) | `csi.san.synology.com` | v1.0 | A Container Storage Interface (CSI) Driver for Synology NAS | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning
[Tencent Cloud Block Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cbs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud Block Storage | Persistent | Read/Write Single Pod | Yes | Snapshot
[Tencent Cloud File Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cfs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud File Storage | Persistent | Read/Write Multiple Pods | Yes |
[Tencent Cloud Object Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cosfs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud Object Storage | Persistent | Read/Write Multiple Pods | No |
[TopoLVM](https://github.com/cybozu-go/topolvm)| `topolvm.cybozu.com` | v1.1 | A Container Storage Interface (CSI) Driver for LVM | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Expansion, Topology Aware
[VAST Data](https://github.com/vast-data/vast-csi) | `csi.vastdata.com` | v1.0 | A Container Storage Interface (CSI) Driver for VAST Data | Persistent | Read/Write Multiple Pods | Yes |
[XSKY-EBS](https://xsky-storage.github.io/xsky-csi-driver/csi-block.html) | `csi.block.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed Block Storage (X-EBS) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[XSKY-EUS](https://xsky-storage.github.io/xsky-csi-driver/csi-fs.html) | `csi.fs.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed File Storage (X-EUS) | Persistent | Read/Write Multiple Pods | Yes |
[Vault](https://github.com/kubevault/csi-driver) | `secrets.csi.kubevault.com` | v1.0 | A Container Storage Interface (CSI) Driver for mounting HashiCorp Vault secrets as volumes. | Ephemeral | N/A | N/A |
[VDA](https://virtual-disk-array.readthedocs.io/en/latest/Introduction.html) | `csi.vda.io` | v1.0 | An open source block storage system base on SPDK | Persistent | Read/Write Single Pod | N/A |
[Veritas InfoScale Volumes](https://www.veritas.com/solution/virtualization/containers.html) | `org.veritas.infoscale` | v1.2 | A Container Storage Interface (CSI) Driver for Veritas InfoScale volumes | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning
[vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver) | `csi.vsphere.vmware.com` | v1.0 | A Container Storage Interface (CSI) Driver for VMware vSphere | Persistent | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block,<br/><br/>Expansion (Block Volume),<br/><br/>Topology Aware (Block Volume)
[Vultr Block Storage](https://github.com/vultr/vultr-csi) | `block.csi.vultr.com` | v1.2 | A Container Storage Interface (CSI) Driver for Vultr Block Storage | Persistent | Read/Write Single Pod | Yes |
[WekaIO](https://github.com/weka/csi-wekafs) | `csi.weka.io` | v1.0 | A Container Storage Interface (CSI) Driver for mounting WekaIO WekaFS filesystem as volumes | Persistent | Read/Write Multiple Pods | Yes |
[Yandex.Cloud](https://github.com/flant/yandex-csi-driver) | `yandex.csi.flant.com` | v1.2 | A Container Storage Interface (CSI) plugin for Yandex.Cloud Compute Disks | Persistent | Read/Write Single Pod | Yes |
[YanRongYun](http://www.yanrongyun.com/) | ? | v1.0 | A Container Storage Interface (CSI) Driver for YanRong YRCloudFile Storage | Persistent | Read/Write Multiple Pods | Yes |
[Zadara-CSI](https://github.com/zadarastorage/zadara-csi) | `csi.zadara.com` | v1.0, v1.1 | A Container Storage Interface (CSI) plugin for Zadara VPSA Storage Array & VPSA All-Flash | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
## Sample Drivers
Name | Status | More Information
-----|--------|-------
[Flexvolume](https://github.com/kubernetes-csi/csi-driver-flex) | Sample |
[HostPath](https://github.com/kubernetes-csi/csi-driver-host-path) | v1.2.0 | Only use for a single node tests. See the [Example](example.html) page for Kubernetes-specific instructions.
[ImagePopulator](https://github.com/kubernetes-csi/csi-driver-image-populator) | Prototype | Driver that lets you use a container image as an ephemeral volume.
[In-memory Sample Mock Driver](https://github.com/kubernetes-csi/csi-test/tree/master/mock/service) | v0.3.0 | The sample mock driver used for [csi-sanity](https://github.com/kubernetes-csi/csi-test/tree/master/cmd/csi-sanity)
[NFS](https://github.com/kubernetes-csi/csi-driver-nfs) | Sample |
[Synology NAS](https://github.com/jparklab/synology-csi) | v1.0.0 | An unofficial (and unsupported) Container Storage Interface Driver for Synology NAS.
[VFS Driver](https://github.com/thecodeteam/csi-vfs) | Released | A CSI plugin that provides a virtual file system.
================================================
FILE: go.mod
================================================
module github.com/kastenhq/kubestr
go 1.24
replace github.com/graymeta/stow => github.com/kastenhq/stow v0.1.2-kasten
require (
github.com/briandowns/spinner v1.23.2
github.com/frankban/quicktest v1.14.6
github.com/golang/mock v1.6.0
github.com/kanisterio/kanister v0.0.0-20250106180853-0abc731c8242
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.10.2
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
k8s.io/api v0.31.4
k8s.io/apimachinery v0.31.4
k8s.io/client-go v0.31.4
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kanisterio/errkit v0.0.3 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/spdystream v0.5.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/openshift/api v0.0.0-20231222123017-053aee22b4b4 // indirect
github.com/openshift/client-go v0.0.0-20231221125933-2aa81c72f992 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.starlark.net v0.0.0-20240314022150-ee8ed142361c // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.31.4 // indirect
k8s.io/cli-runtime v0.31.4 // indirect
k8s.io/code-generator v0.31.4 // indirect
k8s.io/component-base v0.31.4 // indirect
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/kubectl v0.31.4 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.17.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.3 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
================================================
FILE: go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
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.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kanisterio/errkit v0.0.3 h1:1wHaTqV4DZE0XrN+Nq7Q2M8kyKnV8NhhEF3OB7A/Pd8=
github.com/kanisterio/errkit v0.0.3/go.mod h1:0xesKaif6++1IXFdhb6fywa40J07odjwWq3IKzxWC3A=
github.com/kanisterio/kanister v0.0.0-20250106180853-0abc731c8242 h1:Ubk92hHanqt0lWkw+AJD0HD/kxyNX099XgkLAAfxKQo=
github.com/kanisterio/kanister v0.0.0-20250106180853-0abc731c8242/go.mod h1:GKGelgFnCa/Vc4MDuGlc4DdKGWaN7yIt8oI/Ztsm8V0=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA=
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y=
github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/openshift/api v0.0.0-20231222123017-053aee22b4b4 h1:XHl52N6/q+aE5qvmN3YyHyV2H0xepZTbr/r6Vs5pNjo=
github.com/openshift/api v0.0.0-20231222123017-053aee22b4b4/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
github.com/openshift/client-go v0.0.0-20231221125933-2aa81c72f992 h1:JQ/w7ublPBrPRwknrde4apbTR23PDxKYUmkkfo1Nvws=
github.com/openshift/client-go v0.0.0-20231221125933-2aa81c72f992/go.mod h1:5W+xoimHjRdZ0dI/yeQR0ANRNLK9mPmXMzUWPAIPADo=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.starlark.net v0.0.0-20240314022150-ee8ed142361c h1:roAjH18hZcwI4hHStHbkXjF5b7UUyZ/0SG3hXNN1SjA=
go.starlark.net v0.0.0-20240314022150-ee8ed142361c/go.mod h1:YKMCv9b1WrfWmeqdV5MAuEHWsu5iC+fe6kYl2sQjdI8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM=
k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw=
k8s.io/apiextensions-apiserver v0.31.4 h1:FxbqzSvy92Ca9DIs5jqot883G0Ln/PGXfm/07t39LS0=
k8s.io/apiextensions-apiserver v0.31.4/go.mod h1:hIW9YU8UsqZqIWGG99/gsdIU0Ar45Qd3A12QOe/rvpg=
k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM=
k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/cli-runtime v0.31.4 h1:iczCWiyXaotW+hyF5cWP8RnEYBCzZfJUF6otJ2m9mw0=
k8s.io/cli-runtime v0.31.4/go.mod h1:0/pRzAH7qc0hWx40ut1R4jLqiy2w/KnbqdaAI2eFG8U=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/client-go v0.31.4 h1:t4QEXt4jgHIkKKlx06+W3+1JOwAFU/2OPiOo7H92eRQ=
k8s.io/client-go v0.31.4/go.mod h1:kvuMro4sFYIa8sulL5Gi5GFqUPvfH2O/dXuKstbaaeg=
k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/code-generator v0.31.4 h1:Vu+8fKz+239rKiVDHFVHgjQ162cg5iUQPtTyQbwXeQw=
k8s.io/code-generator v0.31.4/go.mod h1:yMDt13Kn7m4MMZ4LxB1KBzdZjEyxzdT4b4qXq+lnI90=
k8s.io/component-base v0.31.4 h1:wCquJh4ul9O8nNBSB8N/o8+gbfu3BVQkVw9jAUY/Qtw=
k8s.io/component-base v0.31.4/go.mod h1:G4dgtf5BccwiDT9DdejK0qM6zTK0jwDGEKnCmb9+u/s=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kubectl v0.31.4 h1:c8Af8xd1VjyoKyWMW0xHv2+tYxEjne8s6OOziMmaD10=
k8s.io/kubectl v0.31.4/go.mod h1:0E0rpXg40Q57wRE6LB9su+4tmwx1IzZrmIEvhQPk0i4=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0=
sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ=
sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.4.3 h1:sCP7Vv3xx/CWIuTPVN38lUPx0uw0lcLfzaiDa8Ja01A=
sigs.k8s.io/structured-merge-diff/v4 v4.4.3/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
================================================
FILE: index.md
================================================
# Kubestr
## What is it?
Kubestr is a collection of tools to discover, validate and evaluate your kubernetes storage options.
As adoption of kubernetes grows so have the persistent storage offerings that are available to users. The introduction of [CSI](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/)(Container Storage Interface) has enabled storage providers to develop drivers with ease. In fact there are around a 100 different CSI drivers available today. Along with the existing in-tree providers, these options can make choosing the right storage difficult.
Kubestr can assist in the following ways-
- Identify the various storage options present in a cluster.
- Validate if the storage options are configured correctly.
- Evaluate the storage using common benchmarking tools like FIO.
<script id="asciicast-7iJTbWKwdhPHNWYV00LIgx7gn" src="https://asciinema.org/a/7iJTbWKwdhPHNWYV00LIgx7gn.js" async></script>
## Using Kubestr
### To install the tool -
- Ensure that the kubernetes context is set and the cluster is accessible through your terminal. (Does [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) work?)
- Download the latest release [here](https://github.com/kastenhq/kubestr/releases/latest).
- Unpack the tool and make it an executable `chmod +x kubestr`.
### To discover available storage options -
- Run `./kubestr`
### To run an FIO test -
- Run `./kubestr fio -s <storage class>`
- Additional options like `--size` and `--fiofile` can be specified.
- For more information visit our [fio](https://kastenhq.github.io/kubestr/fio) page.
### To check a CSI drivers snapshot and restore capabilities -
- Run `./kubestr csicheck -s <storage class> -v <volume snapshot class>`
## Roadmap
- In the future we plan to allow users to post their FIO results and compare to others.
================================================
FILE: main.go
================================================
// Copyright 2020 Kubestr Developers
// 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 main
//go:generate ./scripts/load_csi_provisioners.sh
import (
"github.com/kastenhq/kubestr/cmd"
"os"
)
func main() {
if err := Execute(); err != nil {
os.Exit(1)
}
}
// Execute executes the main command
func Execute() error {
return cmd.Execute()
}
================================================
FILE: pkg/block/block_mount.go
================================================
package block
import (
"context"
"fmt"
"time"
kankube "github.com/kanisterio/kanister/pkg/kube"
"github.com/kanisterio/kanister/pkg/poll"
"github.com/kastenhq/kubestr/pkg/csi"
"github.com/kastenhq/kubestr/pkg/csi/types"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
)
type BlockMountCheckerArgs struct {
KubeCli kubernetes.Interface
DynCli dynamic.Interface
StorageClass string
Namespace string
Cleanup bool
RunAsUser int64
ContainerImage string
K8sObjectReadyTimeout time.Duration
PVCSize string
}
func (a *BlockMountCheckerArgs) Validate() error {
if a.KubeCli == nil || a.DynCli == nil || a.StorageClass == "" || a.Namespace == "" {
return fmt.Errorf("require fields are missing. (KubeCli, DynCli, StorageClass, Namespace)")
}
return nil
}
// BlockMountChecker tests if a storage class can provision volumes for block mounts.
type BlockMountChecker interface {
Mount(ctx context.Context) (*BlockMountCheckerResult, error)
Cleanup()
}
type BlockMountCheckerResult struct {
StorageClass *sv1.StorageClass
}
const (
blockMountCheckerPVCNameFmt = "kubestr-blockmount-%s-pvc"
blockMountCheckerPodNameFmt = "kubestr-blockmount-%s-pod"
blockModeCheckerPodCleanupTimeout = time.Second * 120
blockModeCheckerPVCCleanupTimeout = time.Second * 120
blockModeCheckerPVCDefaultSize = "1Gi"
)
// blockMountChecker provides BlockMountChecker
type blockMountChecker struct {
args BlockMountCheckerArgs
podName string
pvcName string
validator csi.ArgumentValidator
appCreator csi.ApplicationCreator
cleaner csi.Cleaner
podCleanupTimeout time.Duration
pvcCleanupTimeout time.Duration
}
func NewBlockMountChecker(args BlockMountCheckerArgs) (BlockMountChecker, error) {
if err := args.Validate(); err != nil {
return nil, err
}
b := &blockMountChecker{}
b.args = args
b.podName = fmt.Sprintf(blockMountCheckerPodNameFmt, b.args.StorageClass)
b.pvcName = fmt.Sprintf(blockMountCheckerPVCNameFmt, b.args.StorageClass)
b.validator = csi.NewArgumentValidator(b.args.KubeCli, b.args.DynCli)
b.appCreator = csi.NewApplicationCreator(b.args.KubeCli, args.K8sObjectReadyTimeout)
b.cleaner = csi.NewCleaner(b.args.KubeCli, b.args.DynCli)
b.podCleanupTimeout = blockModeCheckerPodCleanupTimeout
b.pvcCleanupTimeout = blockModeCheckerPVCCleanupTimeout
return b, nil
}
func (b *blockMountChecker) Mount(ctx context.Context) (*BlockMountCheckerResult, error) {
fmt.Printf("Fetching StorageClass %s ...\n", b.args.StorageClass)
sc, err := b.validator.ValidateStorageClass(ctx, b.args.StorageClass)
if err != nil {
fmt.Printf(" -> Failed to fetch StorageClass(%s): (%v)\n", b.args.StorageClass, err)
return nil, err
}
fmt.Printf(" -> Provisioner: %s\n", sc.Provisioner)
if b.args.PVCSize == "" {
b.args.PVCSize = blockModeCheckerPVCDefaultSize
}
restoreSize, err := resource.ParseQuantity(b.args.PVCSize)
if err != nil {
fmt.Printf(" -> Invalid PVC size %s: (%v)\n", b.args.PVCSize, err)
return nil, err
}
blockMode := v1.PersistentVolumeBlock
createPVCArgs := &types.CreatePVCArgs{
Name: b.pvcName,
Namespace: b.args.Namespace,
StorageClass: b.args.StorageClass,
VolumeMode: &blockMode,
RestoreSize: &restoreSize,
}
if b.args.Cleanup {
defer b.Cleanup()
}
fmt.Printf("Provisioning a Volume (%s) for block mode access ...\n", b.args.PVCSize)
tB := time.Now()
_, err = b.appCreator.CreatePVC(ctx, createPVCArgs)
if err != nil {
fmt.Printf(" -> Failed to provision a Volume (%v)\n", err)
return nil, err
}
fmt.Printf(" -> Created PVC %s/%s (%s)\n", b.args.Namespace, b.pvcName, time.Since(tB).Truncate(time.Millisecond).String())
fmt.Println("Creating a Pod with a volumeDevice ...")
tB = time.Now()
_, err = b.appCreator.CreatePod(ctx, &types.CreatePodArgs{
Name: b.podName,
Namespace: b.args.Namespace,
RunAsUser: b.args.RunAsUser,
ContainerImage: b.args.ContainerImage,
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "tail -f /dev/null"},
PVCMap: map[string]types.VolumePath{
b.pvcName: {
DevicePath: "/mnt/block",
},
},
})
if err != nil {
fmt.Printf(" -> Failed to create Pod (%v)\n", err)
return nil, err
}
fmt.Printf(" -> Created Pod %s/%s\n", b.args.Namespace, b.podName)
fmt.Printf(" -> Waiting at most %s for the Pod to become ready ...\n", b.args.K8sObjectReadyTimeout.String())
if err = b.appCreator.WaitForPodReady(ctx, b.args.Namespace, b.podName); err != nil {
fmt.Printf(" -> The Pod timed out (%v)\n", err)
return nil, err
}
fmt.Printf(" -> The Pod is ready (%s)\n", time.Since(tB).Truncate(time.Millisecond).String())
return &BlockMountCheckerResult{
StorageClass: sc,
}, nil
}
func (b *blockMountChecker) Cleanup() {
var (
ctx = context.Background()
err error
)
// delete Pod
fmt.Printf("Deleting Pod %s/%s ...\n", b.args.Namespace, b.podName)
tB := time.Now()
err = b.cleaner.DeletePod(ctx, b.podName, b.args.Namespace)
if err != nil && !apierrors.IsNotFound(err) {
fmt.Printf(" Error deleting Pod %s/%s - (%v)\n", b.args.Namespace, b.podName, err)
}
// Give it a chance to run ...
podWaitCtx, podWaitCancelFn := context.WithTimeout(context.Background(), b.podCleanupTimeout)
defer podWaitCancelFn()
err = kankube.WaitForPodCompletion(podWaitCtx, b.args.KubeCli, b.args.Namespace, b.podName)
if err == nil || (err != nil && apierrors.IsNotFound(err)) {
fmt.Printf(" -> Deleted pod (%s)\n", time.Since(tB).Truncate(time.Millisecond).String())
} else {
fmt.Printf(" -> Failed to delete Pod in %s\n", time.Since(tB).Truncate(time.Millisecond).String())
}
// delete PVC
fmt.Printf("Deleting PVC %s/%s ...\n", b.args.Namespace, b.pvcName)
tB = time.Now()
err = b.cleaner.DeletePVC(ctx, b.pvcName, b.args.Namespace)
if err != nil && !apierrors.IsNotFound(err) {
fmt.Printf(" Error deleting PVC %s/%s - (%v)\n", b.args.Namespace, b.pvcName, err)
}
err = b.pvcWaitForTermination(b.pvcCleanupTimeout)
if err != nil {
fmt.Printf(" -> PVC failed to delete in %s\n", time.Since(tB).Truncate(time.Millisecond).String())
} else {
fmt.Printf(" -> Deleted PVC (%s)\n", time.Since(tB).Truncate(time.Millisecond).String())
}
}
func (b *blockMountChecker) pvcWaitForTermination(timeout time.Duration) error {
pvcWaitCtx, pvcWaitCancelFn := context.WithTimeout(context.Background(), timeout)
defer pvcWaitCancelFn()
return poll.Wait(pvcWaitCtx, func(ctx context.Context) (bool, error) {
_, err := b.validator.ValidatePVC(ctx, b.pvcName, b.args.Namespace)
if err != nil && apierrors.IsNotFound(err) {
return true, nil
}
return false, nil
})
}
================================================
FILE: pkg/block/block_mount_test.go
================================================
package block
import (
"context"
"errors"
"fmt"
"testing"
"time"
qt "github.com/frankban/quicktest"
"github.com/golang/mock/gomock"
"github.com/kastenhq/kubestr/pkg/csi/mocks"
"github.com/kastenhq/kubestr/pkg/csi/types"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes/fake"
)
func TestBlockMountCheckerNew(t *testing.T) {
kubeCli := fake.NewSimpleClientset()
dynCli := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme())
invalidArgs := []struct {
name string
args BlockMountCheckerArgs
}{
{"args:empty", BlockMountCheckerArgs{}},
{"args:KubeCli", BlockMountCheckerArgs{
KubeCli: kubeCli,
}},
{"args:KubeCli-DynCli", BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
}},
{"args:KubeCli-DynCli-StorageClass", BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
StorageClass: "sc",
}},
}
for _, tc := range invalidArgs {
t.Run(tc.name, func(t *testing.T) {
c := qt.New(t)
bmt, err := NewBlockMountChecker(tc.args)
c.Assert(err, qt.IsNotNil)
c.Assert(bmt, qt.IsNil)
})
}
t.Run("success", func(t *testing.T) {
c := qt.New(t)
args := BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
StorageClass: "sc",
Namespace: "namespace",
}
bmt, err := NewBlockMountChecker(args)
c.Assert(err, qt.IsNil)
c.Assert(bmt, qt.IsNotNil)
b, ok := bmt.(*blockMountChecker)
c.Assert(ok, qt.IsTrue)
c.Assert(b.args, qt.Equals, args)
c.Assert(b.validator, qt.IsNotNil)
c.Assert(b.appCreator, qt.IsNotNil)
c.Assert(b.cleaner, qt.IsNotNil)
c.Assert(b.podName, qt.Equals, fmt.Sprintf(blockMountCheckerPodNameFmt, args.StorageClass))
c.Assert(b.pvcName, qt.Equals, fmt.Sprintf(blockMountCheckerPVCNameFmt, args.StorageClass))
c.Assert(b.podCleanupTimeout, qt.Equals, blockModeCheckerPodCleanupTimeout)
c.Assert(b.pvcCleanupTimeout, qt.Equals, blockModeCheckerPVCCleanupTimeout)
})
}
func TestBlockMountCheckerPvcWaitForTermination(t *testing.T) {
type prepareArgs struct {
b *blockMountChecker
mockValidator *mocks.MockArgumentValidator
}
kubeCli := fake.NewSimpleClientset()
dynCli := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme())
tcs := []struct {
name string
pvcTimeout time.Duration
prepare func(*prepareArgs)
expErr error
}{
{
name: "success",
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(nil, apierrors.NewNotFound(schema.GroupResource{}, ""))
},
},
{
name: "timeout",
pvcTimeout: time.Microsecond, // pvc wait will timeout
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(&v1.PersistentVolumeClaim{}, nil).AnyTimes()
},
expErr: context.DeadlineExceeded,
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
c := qt.New(t)
args := BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
StorageClass: "sc",
Namespace: "namespace",
}
bmt, err := NewBlockMountChecker(args)
c.Assert(err, qt.IsNil)
c.Assert(bmt, qt.IsNotNil)
b, ok := bmt.(*blockMountChecker)
c.Assert(ok, qt.IsTrue)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
pa := &prepareArgs{
b: b,
mockValidator: mocks.NewMockArgumentValidator(ctrl),
}
tc.prepare(pa)
b.validator = pa.mockValidator
err = b.pvcWaitForTermination(tc.pvcTimeout)
if tc.expErr != nil {
c.Assert(err, qt.ErrorIs, tc.expErr)
} else {
c.Assert(err, qt.IsNil)
}
})
}
}
func TestBlockMountCheckerCleanup(t *testing.T) {
type prepareArgs struct {
b *blockMountChecker
mockCleaner *mocks.MockCleaner
mockValidator *mocks.MockArgumentValidator
}
errNotFound := apierrors.NewNotFound(schema.GroupResource{}, "")
someError := errors.New("test error")
scName := "sc"
namespace := "namespace"
runningPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf(blockMountCheckerPodNameFmt, scName),
Namespace: namespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{Name: "container-0"},
},
},
Status: v1.PodStatus{
Phase: v1.PodRunning,
},
}
tcs := []struct {
name string
podTimeout time.Duration
pvcTimeout time.Duration
objs []runtime.Object
prepare func(*prepareArgs)
}{
{
name: "nothing-found",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockCleaner.EXPECT().DeletePod(gomock.Any(), pa.b.podName, pa.b.args.Namespace).Return(errNotFound)
pa.mockCleaner.EXPECT().DeletePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(errNotFound)
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(nil, errNotFound)
},
},
{
name: "error-deleting-pod",
podTimeout: time.Microsecond, // pod wait will timeout
pvcTimeout: time.Hour,
objs: []runtime.Object{runningPod},
prepare: func(pa *prepareArgs) {
pa.mockCleaner.EXPECT().DeletePod(gomock.Any(), pa.b.podName, pa.b.args.Namespace).Return(someError)
pa.mockCleaner.EXPECT().DeletePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(errNotFound)
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(nil, errNotFound)
},
},
{
name: "error-deleting-pvc",
podTimeout: time.Hour,
pvcTimeout: time.Microsecond, // timeout
prepare: func(pa *prepareArgs) {
pa.mockCleaner.EXPECT().DeletePod(gomock.Any(), pa.b.podName, pa.b.args.Namespace).Return(errNotFound)
pa.mockCleaner.EXPECT().DeletePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(someError)
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(nil, someError).AnyTimes()
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
c := qt.New(t)
kubeCli := fake.NewSimpleClientset(tc.objs...)
dynCli := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme())
args := BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
StorageClass: scName,
Namespace: namespace,
}
bmt, err := NewBlockMountChecker(args)
c.Assert(err, qt.IsNil)
c.Assert(bmt, qt.IsNotNil)
b, ok := bmt.(*blockMountChecker)
c.Assert(ok, qt.IsTrue)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
pa := &prepareArgs{
b: b,
mockCleaner: mocks.NewMockCleaner(ctrl),
mockValidator: mocks.NewMockArgumentValidator(ctrl),
}
tc.prepare(pa)
b.validator = pa.mockValidator
b.cleaner = pa.mockCleaner
b.podCleanupTimeout = tc.podTimeout
b.pvcCleanupTimeout = tc.pvcTimeout
b.Cleanup()
})
}
}
func TestBlockMountCheckerMount(t *testing.T) {
type prepareArgs struct {
b *blockMountChecker
mockCleaner *mocks.MockCleaner
mockValidator *mocks.MockArgumentValidator
mockAppCreator *mocks.MockApplicationCreator
}
errNotFound := apierrors.NewNotFound(schema.GroupResource{}, "")
someError := errors.New("test error")
scName := "sc"
scProvisioner := "provisioenr"
sc := &sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: scName,
},
Provisioner: scProvisioner,
}
namespace := "namespace"
cleanupCalls := func(pa *prepareArgs) {
pa.mockCleaner.EXPECT().DeletePod(gomock.Any(), pa.b.podName, pa.b.args.Namespace).Return(errNotFound)
pa.mockCleaner.EXPECT().DeletePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(errNotFound)
pa.mockValidator.EXPECT().ValidatePVC(gomock.Any(), pa.b.pvcName, pa.b.args.Namespace).Return(nil, errNotFound)
}
createPVCArgs := func(b *blockMountChecker) *types.CreatePVCArgs {
pvcSize := b.args.PVCSize
if pvcSize == "" {
pvcSize = blockModeCheckerPVCDefaultSize
}
restoreSize := resource.MustParse(pvcSize)
blockMode := v1.PersistentVolumeBlock
return &types.CreatePVCArgs{
Name: b.pvcName,
Namespace: b.args.Namespace,
StorageClass: b.args.StorageClass,
VolumeMode: &blockMode,
RestoreSize: &restoreSize,
}
}
createPVC := func(b *blockMountChecker) *v1.PersistentVolumeClaim {
return &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: b.args.Namespace,
Name: b.pvcName,
},
}
}
createPodArgs := func(b *blockMountChecker) *types.CreatePodArgs {
return &types.CreatePodArgs{
Name: b.podName,
Namespace: b.args.Namespace,
RunAsUser: b.args.RunAsUser,
ContainerImage: b.args.ContainerImage,
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "tail -f /dev/null"},
PVCMap: map[string]types.VolumePath{
b.pvcName: {
DevicePath: "/mnt/block",
},
},
}
}
createPod := func(b *blockMountChecker) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: b.args.Namespace,
Name: b.podName,
},
}
}
tcs := []struct {
name string
podTimeout time.Duration
pvcTimeout time.Duration
noCleanup bool
objs []runtime.Object
prepare func(*prepareArgs)
result *BlockMountCheckerResult
}{
{
name: "no-storage-class",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(nil, apierrors.NewNotFound(schema.GroupResource{}, pa.b.args.StorageClass))
},
},
{
name: "invalid-pvc-size",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.b.args.PVCSize = "10Q"
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(sc, nil)
},
},
{
name: "create-pvc-error",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(sc, nil)
pa.mockAppCreator.EXPECT().CreatePVC(gomock.Any(), createPVCArgs(pa.b)).Return(nil, someError)
cleanupCalls(pa)
},
},
{
name: "create-pod-error",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(sc, nil)
pa.mockAppCreator.EXPECT().CreatePVC(gomock.Any(), createPVCArgs(pa.b)).Return(createPVC(pa.b), nil)
pa.mockAppCreator.EXPECT().CreatePod(gomock.Any(), createPodArgs(pa.b)).Return(nil, someError)
cleanupCalls(pa)
},
},
{
name: "wait-for-pod-error",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(sc, nil)
pa.mockAppCreator.EXPECT().CreatePVC(gomock.Any(), createPVCArgs(pa.b)).Return(createPVC(pa.b), nil)
pa.mockAppCreator.EXPECT().CreatePod(gomock.Any(), createPodArgs(pa.b)).Return(createPod(pa.b), nil)
pa.mockAppCreator.EXPECT().WaitForPodReady(gomock.Any(), pa.b.args.Namespace, pa.b.podName).Return(someError)
cleanupCalls(pa)
},
},
{
name: "success-no-cleanup",
podTimeout: time.Hour,
pvcTimeout: time.Hour,
noCleanup: true,
prepare: func(pa *prepareArgs) {
pa.mockValidator.EXPECT().ValidateStorageClass(gomock.Any(), pa.b.args.StorageClass).Return(sc, nil)
pa.b.args.PVCSize = blockModeCheckerPVCDefaultSize
pa.mockAppCreator.EXPECT().CreatePVC(gomock.Any(), createPVCArgs(pa.b)).Return(createPVC(pa.b), nil)
pa.mockAppCreator.EXPECT().CreatePod(gomock.Any(), createPodArgs(pa.b)).Return(createPod(pa.b), nil)
pa.mockAppCreator.EXPECT().WaitForPodReady(gomock.Any(), pa.b.args.Namespace, pa.b.podName).Return(nil)
},
result: &BlockMountCheckerResult{
StorageClass: sc,
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
c := qt.New(t)
ctx := context.Background()
kubeCli := fake.NewSimpleClientset(tc.objs...)
dynCli := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme())
args := BlockMountCheckerArgs{
KubeCli: kubeCli,
DynCli: dynCli,
StorageClass: scName,
Namespace: namespace,
Cleanup: !tc.noCleanup,
}
bmt, err := NewBlockMountChecker(args)
c.Assert(err, qt.IsNil)
c.Assert(bmt, qt.IsNotNil)
b, ok := bmt.(*blockMountChecker)
c.Assert(ok, qt.IsTrue)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
pa := &prepareArgs{
b: b,
mockCleaner: mocks.NewMockCleaner(ctrl),
mockValidator: mocks.NewMockArgumentValidator(ctrl),
mockAppCreator: mocks.NewMockApplicationCreator(ctrl),
}
tc.prepare(pa)
b.validator = pa.mockValidator
b.cleaner = pa.mockCleaner
b.appCreator = pa.mockAppCreator
b.podCleanupTimeout = tc.podTimeout
b.pvcCleanupTimeout = tc.pvcTimeout
result, err := b.Mount(ctx)
if tc.result != nil {
c.Assert(result, qt.DeepEquals, tc.result)
c.Assert(err, qt.IsNil)
} else {
c.Assert(result, qt.IsNil)
c.Assert(err, qt.IsNotNil)
}
})
}
}
================================================
FILE: pkg/common/common.go
================================================
package common
const (
// VolSnapClassDriverKey describes the driver key in VolumeSnapshotClass resource
VolSnapClassDriverKey = "driver"
// DefaultPodImage the default pod image
DefaultPodImage = "ghcr.io/kastenhq/kubestr:latest"
// SnapGroupName describes the snapshot group name
SnapGroupName = "snapshot.storage.k8s.io"
// VolumeSnapshotClassResourcePlural describes volume snapshot classses
VolumeSnapshotClassResourcePlural = "volumesnapshotclasses"
// VolumeSnapshotResourcePlural is "volumesnapshots"
VolumeSnapshotResourcePlural = "volumesnapshots"
// SnapshotVersion is the apiversion of the VolumeSnapshot resource
SnapshotVersion = "snapshot.storage.k8s.io/v1"
)
================================================
FILE: pkg/csi/csi.go
================================================
package csi
import (
"context"
"github.com/kastenhq/kubestr/pkg/csi/types"
)
type CSI interface {
RunSnapshotRestore(ctx context.Context, args *types.CSISnapshotRestoreArgs) (*types.CSISnapshotRestoreResults, error)
}
================================================
FILE: pkg/csi/csi_ops.go
================================================
package csi
// This file contains general Kubernetes operations, not just CSI related operations.
import (
"context"
"fmt"
"log"
"net/http"
"net/url"
"strings"
"time"
"k8s.io/apimachinery/pkg/runtime"
"github.com/kanisterio/kanister/pkg/kube"
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
"github.com/kanisterio/kanister/pkg/poll"
"github.com/kastenhq/kubestr/pkg/common"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
pf "k8s.io/client-go/tools/portforward"
"k8s.io/client-go/transport/spdy"
)
const (
defaultReadyWaitTimeout = 2 * time.Minute
PVCKind = "PersistentVolumeClaim"
PodKind = "Pod"
// DefaultVolumeSnapshotClassAnnotation is an annotation used to denote a default VolumeSnapshotClass.
DefaultVolumeSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/is-default-class"
)
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_argument_validator.go -package=mocks . ArgumentValidator
type ArgumentValidator interface {
//Rename
ValidatePVC(ctx context.Context, pvcName, namespace string) (*v1.PersistentVolumeClaim, error)
FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error)
ValidateVolumeSnapshot(ctx context.Context, snapshotName, namespace string, groupVersion *metav1.GroupVersionForDiscovery) (*snapv1.VolumeSnapshot, error)
ValidateNamespace(ctx context.Context, namespace string) error
ValidateStorageClass(ctx context.Context, storageClass string) (*sv1.StorageClass, error)
ValidateVolumeSnapshotClass(ctx context.Context, volumeSnapshotClass string, groupVersion *metav1.GroupVersionForDiscovery) (*unstructured.Unstructured, error)
}
type validateOperations struct {
kubeCli kubernetes.Interface
dynCli dynamic.Interface
}
func NewArgumentValidator(kubeCli kubernetes.Interface, dynCli dynamic.Interface) ArgumentValidator {
return &validateOperations{
kubeCli: kubeCli,
dynCli: dynCli,
}
}
func (o *validateOperations) ValidatePVC(ctx context.Context, pvcName, namespace string) (*v1.PersistentVolumeClaim, error) {
if o.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
pvc, err := o.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
if err != nil {
return nil, err
}
return pvc, nil
}
func (o *validateOperations) ValidateVolumeSnapshot(ctx context.Context, snapshotName, namespace string, groupVersion *metav1.GroupVersionForDiscovery) (*snapv1.VolumeSnapshot, error) {
VolSnapGVR := schema.GroupVersionResource{Group: snapv1.GroupName, Version: groupVersion.Version, Resource: common.VolumeSnapshotResourcePlural}
uVS, err := o.dynCli.Resource(VolSnapGVR).Namespace(namespace).Get(ctx, snapshotName, metav1.GetOptions{})
if err != nil {
log.Fatalf("Failed to get VolumeSnapshot: %v", err)
}
volumeSnapshot := &snapv1.VolumeSnapshot{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(uVS.UnstructuredContent(), volumeSnapshot)
return volumeSnapshot, err
}
func (o *validateOperations) FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error) {
if o.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
pv, err := o.kubeCli.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
if err != nil {
return nil, err
}
return pv, nil
}
func (o *validateOperations) ValidateNamespace(ctx context.Context, namespace string) error {
if o.kubeCli == nil {
return fmt.Errorf("kubeCli not initialized")
}
_, err := o.kubeCli.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
return err
}
func (o *validateOperations) ValidateStorageClass(ctx context.Context, storageClass string) (*sv1.StorageClass, error) {
if o.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
sc, err := o.kubeCli.StorageV1().StorageClasses().Get(ctx, storageClass, metav1.GetOptions{})
if err != nil {
return nil, err
}
return sc, nil
}
func (o *validateOperations) ValidateVolumeSnapshotClass(ctx context.Context, volumeSnapshotClass string, groupVersion *metav1.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
if o.dynCli == nil {
return nil, fmt.Errorf("dynCli not initialized")
}
VolSnapClassGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: groupVersion.Version, Resource: common.VolumeSnapshotClassResourcePlural}
return o.dynCli.Resource(VolSnapClassGVR).Get(ctx, volumeSnapshotClass, metav1.GetOptions{})
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_application_creator.go -package=mocks . ApplicationCreator
type ApplicationCreator interface {
CreatePVC(ctx context.Context, args *types.CreatePVCArgs) (*v1.PersistentVolumeClaim, error)
CreatePod(ctx context.Context, args *types.CreatePodArgs) (*v1.Pod, error)
WaitForPVCReady(ctx context.Context, namespace string, pvcName string) error
WaitForPodReady(ctx context.Context, namespace string, podName string) error
}
type applicationCreate struct {
kubeCli kubernetes.Interface
k8sObjectReadyTimeout time.Duration
}
func NewApplicationCreator(kubeCli kubernetes.Interface, k8sObjectReadyTimeout time.Duration) ApplicationCreator {
return &applicationCreate{
kubeCli: kubeCli,
k8sObjectReadyTimeout: k8sObjectReadyTimeout,
}
}
func (c *applicationCreate) CreatePVC(ctx context.Context, args *types.CreatePVCArgs) (*v1.PersistentVolumeClaim, error) {
if c.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
if err := args.Validate(); err != nil {
return nil, err
}
pvc := &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: args.Name,
GenerateName: args.GenerateName,
Namespace: args.Namespace,
Labels: map[string]string{
createdByLabel: "yes",
},
},
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
StorageClassName: &args.StorageClass,
VolumeMode: args.VolumeMode,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: resource.MustParse("1Gi"),
},
},
},
}
if args.DataSource != nil {
pvc.Spec.DataSource = args.DataSource
}
if args.RestoreSize != nil && !args.RestoreSize.IsZero() {
pvc.Spec.Resources.Requests[v1.ResourceStorage] = *args.RestoreSize
}
pvcRes, err := c.kubeCli.CoreV1().PersistentVolumeClaims(args.Namespace).Create(ctx, pvc, metav1.CreateOptions{})
if err != nil {
return pvc, err
}
return pvcRes, nil
}
func (c *applicationCreate) CreatePod(ctx context.Context, args *types.CreatePodArgs) (*v1.Pod, error) {
if c.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
if err := args.Validate(); err != nil {
return nil, err
}
if args.ContainerImage == "" {
args.ContainerImage = common.DefaultPodImage
}
volumeNameInPod := "persistent-storage"
containerName := args.Name
if containerName == "" {
containerName = args.GenerateName
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: args.Name,
GenerateName: args.GenerateName,
Namespace: args.Namespace,
Labels: map[string]string{
createdByLabel: "yes",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: containerName,
Image: args.ContainerImage,
Command: args.Command,
Args: args.ContainerArgs,
}},
},
}
pvcCount := 1
for pvcName, path := range args.PVCMap {
pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{
Name: fmt.Sprintf("%s-%d", volumeNameInPod, pvcCount),
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: pvcName,
},
},
})
if len(path.MountPath) != 0 {
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, v1.VolumeMount{
Name: fmt.Sprintf("%s-%d", volumeNameInPod, pvcCount),
MountPath: path.MountPath,
})
} else {
pod.Spec.Containers[0].VolumeDevices = append(pod.Spec.Containers[0].VolumeDevices, v1.VolumeDevice{
Name: fmt.Sprintf("%s-%d", volumeNameInPod, pvcCount),
DevicePath: path.DevicePath,
})
}
pvcCount++
}
if args.RunAsUser > 0 {
pod.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsUser: &args.RunAsUser,
FSGroup: &args.RunAsUser,
}
}
podRes, err := c.kubeCli.CoreV1().Pods(args.Namespace).Create(ctx, pod, metav1.CreateOptions{})
if err != nil {
return pod, err
}
return podRes, nil
}
func (c *applicationCreate) WaitForPVCReady(ctx context.Context, namespace, name string) error {
if c.kubeCli == nil {
return fmt.Errorf("kubeCli not initialized")
}
err := c.waitForPVCReady(ctx, namespace, name)
if err != nil {
eventErr := c.getErrorFromEvents(ctx, namespace, name, PVCKind)
if eventErr != nil {
return errors.Wrapf(eventErr, "had issues creating PVC")
}
}
return err
}
func (c *applicationCreate) waitForPVCReady(ctx context.Context, namespace string, name string) error {
pvcReadyTimeout := c.k8sObjectReadyTimeout
if pvcReadyTimeout == 0 {
pvcReadyTimeout = defaultReadyWaitTimeout
}
timeoutCtx, waitCancel := context.WithTimeout(ctx, pvcReadyTimeout)
defer waitCancel()
return poll.Wait(timeoutCtx, func(ctx context.Context) (bool, error) {
pvc, err := c.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Get(timeoutCtx, name, metav1.GetOptions{})
if err != nil {
return false, errors.Wrapf(err, "could not find PVC")
}
if pvc.Status.Phase == v1.ClaimLost {
return false, fmt.Errorf("failed to create a PVC, ClaimLost")
}
return pvc.Status.Phase == v1.ClaimBound, nil
})
}
func (c *applicationCreate) WaitForPodReady(ctx context.Context, namespace string, podName string) error {
if c.kubeCli == nil {
return fmt.Errorf("kubeCli not initialized")
}
err := c.waitForPodReady(ctx, namespace, podName)
if err != nil {
eventErr := c.getErrorFromEvents(ctx, namespace, podName, PodKind)
if eventErr != nil {
return errors.Wrapf(eventErr, "had issues creating Pod")
}
}
return err
}
func (c *applicationCreate) waitForPodReady(ctx context.Context, namespace string, podName string) error {
podReadyTimeout := c.k8sObjectReadyTimeout
if podReadyTimeout == 0 {
podReadyTimeout = defaultReadyWaitTimeout
}
timeoutCtx, waitCancel := context.WithTimeout(ctx, podReadyTimeout)
defer waitCancel()
err := kube.WaitForPodReady(timeoutCtx, c.kubeCli, namespace, podName)
return err
}
func (c *applicationCreate) getErrorFromEvents(ctx context.Context, namespace, name, kind string) error {
fieldSelectors := fields.Set{
"involvedObject.kind": kind,
"involvedObject.name": name,
}.AsSelector().String()
listOptions := metav1.ListOptions{
TypeMeta: metav1.TypeMeta{Kind: kind},
FieldSelector: fieldSelectors,
}
events, eventErr := c.kubeCli.CoreV1().Events(namespace).List(ctx, listOptions)
if eventErr != nil {
return errors.Wrapf(eventErr, "failed to retreieve events for %s of kind: %s", name, kind)
}
for _, event := range events.Items {
if event.Type == v1.EventTypeWarning {
return errors.New(event.Message)
}
}
return nil
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_snapshot_creator.go -package=mocks . SnapshotCreator
type SnapshotCreator interface {
NewSnapshotter() (kansnapshot.Snapshotter, error)
CreateSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateSnapshotArgs) (*snapv1.VolumeSnapshot, error)
CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error
}
type snapshotCreate struct {
kubeCli kubernetes.Interface
dynCli dynamic.Interface
}
func (c *snapshotCreate) NewSnapshotter() (kansnapshot.Snapshotter, error) {
if c.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
if c.dynCli == nil {
return nil, fmt.Errorf("dynCli not initialized")
}
return kansnapshot.NewSnapshotter(c.kubeCli, c.dynCli), nil
}
func (c *snapshotCreate) CreateSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateSnapshotArgs) (*snapv1.VolumeSnapshot, error) {
if snapshotter == nil || args == nil {
return nil, fmt.Errorf("snapshotter or args are empty")
}
if err := args.Validate(); err != nil {
return nil, err
}
snapshotMeta := kansnapshot.ObjectMeta{
Name: args.SnapshotName,
Namespace: args.Namespace,
}
err := snapshotter.Create(ctx, args.PVCName, &args.VolumeSnapshotClass, true, snapshotMeta)
if err != nil {
return nil, errors.Wrapf(err, "CSI Driver failed to create snapshot for PVC (%s) in Namespace (%s)", args.PVCName, args.Namespace)
}
snap, err := snapshotter.Get(ctx, args.SnapshotName, args.Namespace)
if err != nil {
return nil, errors.Wrapf(err, "failed to get CSI snapshot (%s) in Namespace (%s)", args.SnapshotName, args.Namespace)
}
return snap, nil
}
func (c *snapshotCreate) CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
if snapshotter == nil || args == nil {
return fmt.Errorf("snapshotter or args are nil")
}
if err := args.Validate(); err != nil {
return err
}
targetSnapClassName := clonePrefix + args.VolumeSnapshotClass
err := snapshotter.CloneVolumeSnapshotClass(ctx, args.VolumeSnapshotClass, targetSnapClassName, kansnapshot.DeletionPolicyRetain, []string{DefaultVolumeSnapshotClassAnnotation})
if err != nil {
return errors.Wrapf(err, "failed to clone a VolumeSnapshotClass to use to restore the snapshot")
}
defer func() {
VolSnapClassGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotClassResourcePlural}
err := c.dynCli.Resource(VolSnapClassGVR).Delete(ctx, targetSnapClassName, metav1.DeleteOptions{})
if err != nil {
fmt.Printf("Delete VSC Error (%s) - (%v)\n", targetSnapClassName, err)
}
}()
snapSrc, err := snapshotter.GetSource(ctx, args.SnapshotName, args.Namespace)
if err != nil {
return errors.Wrapf(err, "failed to get source snapshot source (%s)", args.SnapshotName)
}
snapshotCFSCloneName := clonePrefix + args.SnapshotName
// test the CreateFromSource API
defer func() {
_, _ = snapshotter.Delete(context.Background(), snapshotCFSCloneName, args.Namespace)
}()
src := &kansnapshot.Source{
Handle: snapSrc.Handle,
Driver: snapSrc.Driver,
VolumeSnapshotClassName: targetSnapClassName,
}
snapshotMeta := kansnapshot.ObjectMeta{
Name: snapshotCFSCloneName,
Namespace: args.Namespace,
}
err = snapshotter.CreateFromSource(ctx, src, true, snapshotMeta, kansnapshot.ObjectMeta{})
if err != nil {
return errors.Wrapf(err, "failed to clone snapshot from source (%s)", snapshotCFSCloneName)
}
return nil
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_cleaner.go -package=mocks . Cleaner
type Cleaner interface {
DeletePVC(ctx context.Context, pvcName string, namespace string) error
DeletePod(ctx context.Context, podName string, namespace string) error
DeleteSnapshot(ctx context.Context, snapshotName string, namespace string, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error
}
type cleanse struct {
kubeCli kubernetes.Interface
dynCli dynamic.Interface
}
func NewCleaner(kubeCli kubernetes.Interface, dynCli dynamic.Interface) Cleaner {
return &cleanse{
kubeCli: kubeCli,
dynCli: dynCli,
}
}
func (c *cleanse) DeletePVC(ctx context.Context, pvcName string, namespace string) error {
if c.kubeCli == nil {
return fmt.Errorf("kubeCli not initialized")
}
return c.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Delete(ctx, pvcName, metav1.DeleteOptions{})
}
func (c *cleanse) DeletePod(ctx context.Context, podName string, namespace string) error {
if c.kubeCli == nil {
return fmt.Errorf("kubeCli not initialized")
}
return c.kubeCli.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})
}
func (c *cleanse) DeleteSnapshot(ctx context.Context, snapshotName string, namespace string, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
VolSnapGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotResourcePlural}
return c.dynCli.Resource(VolSnapGVR).Namespace(namespace).Delete(ctx, snapshotName, metav1.DeleteOptions{})
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_api_version_fetcher.go -package=mocks . ApiVersionFetcher
type ApiVersionFetcher interface {
GetCSISnapshotGroupVersion() (*metav1.GroupVersionForDiscovery, error)
}
type apiVersionFetch struct {
kubeCli kubernetes.Interface
}
func (p *apiVersionFetch) GetCSISnapshotGroupVersion() (*metav1.GroupVersionForDiscovery, error) {
if p.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
groups, _, err := p.kubeCli.Discovery().ServerGroupsAndResources()
if err != nil {
return nil, err
}
for _, group := range groups {
if group.Name == common.SnapGroupName {
return &group.PreferredVersion, nil
}
}
return nil, fmt.Errorf("snapshot API group not found")
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_data_validator.go -package=mocks . DataValidator
type DataValidator interface {
FetchPodData(ctx context.Context, podName string, podNamespace string) (string, error)
}
type validateData struct {
kubeCli kubernetes.Interface
}
func (p *validateData) FetchPodData(ctx context.Context, podName string, podNamespace string) (string, error) {
if p.kubeCli == nil {
return "", fmt.Errorf("kubeCli not initialized")
}
stdout, _, err := kube.Exec(ctx, p.kubeCli, podNamespace, podName, "", []string{"sh", "-c", "cat /data/out.txt"}, nil)
return stdout, err
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_port_forwarder.go -package=mocks . PortForwarder
type PortForwarder interface {
FetchRestConfig() (*rest.Config, error)
PortForwardAPod(req *types.PortForwardAPodRequest) error
}
type portforward struct{}
func (p *portforward) PortForwardAPod(req *types.PortForwardAPodRequest) error {
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward",
req.Pod.Namespace, req.Pod.Name)
hostIP := strings.TrimPrefix(req.RestConfig.Host, "https://")
transport, upgrader, err := spdy.RoundTripperFor(req.RestConfig)
if err != nil {
return err
}
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, &url.URL{Scheme: "https", Path: path, Host: hostIP})
fw, err := pf.New(dialer, []string{fmt.Sprintf("%d:%d", req.LocalPort, req.PodPort)}, req.StopCh, req.ReadyCh, &req.OutStream, &req.ErrOutStream)
if err != nil {
return err
}
return fw.ForwardPorts()
}
func (p *portforward) FetchRestConfig() (*rest.Config, error) {
return kube.LoadConfig()
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_kube_executor.go -package=mocks . KubeExecutor
type KubeExecutor interface {
Exec(ctx context.Context, namespace string, podName string, ContainerName string, command []string) (string, error)
}
type kubeExec struct {
kubeCli kubernetes.Interface
}
func (k *kubeExec) Exec(ctx context.Context, namespace string, podName string, ContainerName string, command []string) (string, error) {
if k.kubeCli == nil {
return "", fmt.Errorf("kubeCli not initialized")
}
stdout, _, err := kube.Exec(ctx, k.kubeCli, namespace, podName, ContainerName, command, nil)
return stdout, err
}
================================================
FILE: pkg/csi/csi_ops_test.go
================================================
package csi
import (
"context"
"errors"
"fmt"
"strings"
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
"github.com/kastenhq/kubestr/pkg/common"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
pkgerrors "github.com/pkg/errors"
. "gopkg.in/check.v1"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
discoveryfake "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
k8stesting "k8s.io/client-go/testing"
)
func (s *CSITestSuite) TestGetDriverNameFromUVSC(c *C) {
for _, tc := range []struct {
vsc unstructured.Unstructured
version string
expOut string
}{
{
vsc: unstructured.Unstructured{
Object: map[string]interface{}{
common.VolSnapClassDriverKey: "p2",
},
},
version: common.SnapshotVersion,
expOut: "p2",
},
{
vsc: unstructured.Unstructured{
Object: map[string]interface{}{},
},
version: common.SnapshotVersion,
expOut: "",
},
{
vsc: unstructured.Unstructured{
Object: map[string]interface{}{
common.VolSnapClassDriverKey: map[string]string{},
},
},
version: common.SnapshotVersion,
expOut: "",
},
} {
driverName := getDriverNameFromUVSC(tc.vsc, tc.version)
c.Assert(driverName, Equals, tc.expOut)
}
}
func (s *CSITestSuite) TestGetCSISnapshotGroupVersion(c *C) {
for _, tc := range []struct {
cli kubernetes.Interface
resources []*metav1.APIResourceList
errChecker Checker
gvChecker Checker
}{
{
cli: fake.NewSimpleClientset(),
resources: []*metav1.APIResourceList{
{
GroupVersion: "/////",
},
},
errChecker: NotNil,
gvChecker: IsNil,
},
{
cli: fake.NewSimpleClientset(),
resources: []*metav1.APIResourceList{
{
GroupVersion: "snapshot.storage.k8s.io/v1",
},
},
errChecker: IsNil,
gvChecker: NotNil,
},
{
cli: fake.NewSimpleClientset(),
resources: []*metav1.APIResourceList{
{
GroupVersion: "notrbac.authorization.k8s.io/v1",
},
},
errChecker: NotNil,
gvChecker: IsNil,
},
{
cli: nil,
resources: nil,
errChecker: NotNil,
gvChecker: IsNil,
},
} {
cli := tc.cli
if cli != nil {
cli.Discovery().(*discoveryfake.FakeDiscovery).Resources = tc.resources
}
p := &apiVersionFetch{kubeCli: cli}
gv, err := p.GetCSISnapshotGroupVersion()
c.Check(err, tc.errChecker)
c.Check(gv, tc.gvChecker)
}
}
func (s *CSITestSuite) TestValidatePVC(c *C) {
ctx := context.Background()
ops := NewArgumentValidator(fake.NewSimpleClientset(), nil)
pvc, err := ops.ValidatePVC(ctx, "pvc", "ns")
c.Check(err, NotNil)
c.Check(pvc, IsNil)
ops = NewArgumentValidator(fake.NewSimpleClientset(&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns",
Name: "pvc",
},
}), nil)
pvc, err = ops.ValidatePVC(ctx, "pvc", "ns")
c.Check(err, IsNil)
c.Check(pvc, NotNil)
ops = NewArgumentValidator(nil, nil)
pvc, err = ops.ValidatePVC(ctx, "pvc", "ns")
c.Check(err, NotNil)
c.Check(pvc, IsNil)
}
func (s *CSITestSuite) TestFetchPV(c *C) {
ctx := context.Background()
ops := NewArgumentValidator(fake.NewSimpleClientset(), nil)
pv, err := ops.FetchPV(ctx, "pv")
c.Check(err, NotNil)
c.Check(pv, IsNil)
ops = NewArgumentValidator(fake.NewSimpleClientset(&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
},
}), nil)
pv, err = ops.FetchPV(ctx, "pv")
c.Check(err, IsNil)
c.Check(pv, NotNil)
ops = NewArgumentValidator(nil, nil)
pv, err = ops.FetchPV(ctx, "pv")
c.Check(err, NotNil)
c.Check(pv, IsNil)
}
func (s *CSITestSuite) TestValidateNamespace(c *C) {
ctx := context.Background()
ops := NewArgumentValidator(fake.NewSimpleClientset(), nil)
err := ops.ValidateNamespace(ctx, "ns")
c.Check(err, NotNil)
ops = NewArgumentValidator(fake.NewSimpleClientset(&v1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "ns",
},
}), nil)
err = ops.ValidateNamespace(ctx, "ns")
c.Check(err, IsNil)
ops = NewArgumentValidator(nil, nil)
err = ops.ValidateNamespace(ctx, "ns")
c.Check(err, NotNil)
}
func (s *CSITestSuite) TestValidateStorageClass(c *C) {
ctx := context.Background()
ops := &validateOperations{
kubeCli: fake.NewSimpleClientset(),
}
sc, err := ops.ValidateStorageClass(ctx, "sc")
c.Check(err, NotNil)
c.Check(sc, IsNil)
ops = &validateOperations{
kubeCli: fake.NewSimpleClientset(&sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc",
},
}),
}
sc, err = ops.ValidateStorageClass(ctx, "sc")
c.Check(err, IsNil)
c.Check(sc, NotNil)
ops = &validateOperations{
kubeCli: nil,
}
sc, err = ops.ValidateStorageClass(ctx, "sc")
c.Check(err, NotNil)
c.Check(sc, IsNil)
}
func (s *CSITestSuite) TestValidateVolumeSnapshotClass(c *C) {
ctx := context.Background()
for _, tc := range []struct {
ops *validateOperations
groupVersion string
version string
errChecker Checker
uVCSChecker Checker
}{
{
ops: &validateOperations{
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
},
groupVersion: common.SnapshotVersion,
errChecker: NotNil,
uVCSChecker: IsNil,
},
{
ops: &validateOperations{
dynCli: fakedynamic.NewSimpleDynamicClient(
runtime.NewScheme(),
&unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": fmt.Sprintf("%s/%s", kansnapshot.GroupName, kansnapshot.Version),
"kind": "VolumeSnapshotClass",
"metadata": map[string]interface{}{
"name": "vsc",
},
"driver": "somesnapshotter",
"deletionPolicy": "Delete",
},
},
),
},
groupVersion: common.SnapshotVersion,
version: kansnapshot.Version,
errChecker: IsNil,
uVCSChecker: NotNil,
},
} {
uVSC, err := tc.ops.ValidateVolumeSnapshotClass(ctx, "vsc", &metav1.GroupVersionForDiscovery{GroupVersion: tc.groupVersion, Version: tc.version})
c.Check(err, tc.errChecker)
c.Check(uVSC, tc.uVCSChecker)
}
}
func (s *CSITestSuite) TestCreatePVC(c *C) {
ctx := context.Background()
resourceQuantity := resource.MustParse("1Gi")
for _, tc := range []struct {
cli kubernetes.Interface
args *types.CreatePVCArgs
failCreates bool
errChecker Checker
pvcChecker Checker
}{
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "genName",
StorageClass: "sc",
Namespace: "ns",
DataSource: &v1.TypedLocalObjectReference{
Name: "ds",
},
RestoreSize: &resourceQuantity,
},
errChecker: IsNil,
pvcChecker: NotNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "genName",
StorageClass: "sc",
Namespace: "ns",
DataSource: &v1.TypedLocalObjectReference{
Name: "ds",
},
},
errChecker: IsNil,
pvcChecker: NotNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "genName",
StorageClass: "sc",
Namespace: "ns",
},
errChecker: IsNil,
pvcChecker: NotNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "genName",
StorageClass: "sc",
Namespace: "ns",
},
failCreates: true,
errChecker: NotNil,
pvcChecker: NotNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "",
StorageClass: "sc",
Namespace: "ns",
},
errChecker: NotNil,
pvcChecker: IsNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "something",
StorageClass: "",
Namespace: "ns",
},
errChecker: NotNil,
pvcChecker: IsNil,
},
{
cli: fake.NewSimpleClientset(),
args: &types.CreatePVCArgs{
GenerateName: "Something",
StorageClass: "sc",
Namespace:
gitextract_7zvi4ff5/
├── .github/
│ ├── dependabot.yaml
│ └── workflows/
│ ├── ci.yml
│ ├── dependency-review.yaml
│ ├── docker-publish.yml
│ ├── ossf-scorecard.yml
│ └── release.yaml
├── .goreleaser.yml
├── Dockerfile
├── FIO.md
├── LICENSE
├── README.md
├── _config.yml
├── _posts/
│ └── 2021-02-07-FasterStorage.md
├── cmd/
│ └── rootCmd.go
├── docs/
│ ├── README.md
│ └── _config.yml
├── extra/
│ └── csi-drivers
├── go.mod
├── go.sum
├── index.md
├── main.go
├── pkg/
│ ├── block/
│ │ ├── block_mount.go
│ │ └── block_mount_test.go
│ ├── common/
│ │ └── common.go
│ ├── csi/
│ │ ├── csi.go
│ │ ├── csi_ops.go
│ │ ├── csi_ops_test.go
│ │ ├── file_restore_inspector.go
│ │ ├── file_restore_inspector_steps_test.go
│ │ ├── file_restore_inspector_test.go
│ │ ├── mocks/
│ │ │ ├── mock_api_version_fetcher.go
│ │ │ ├── mock_application_creator.go
│ │ │ ├── mock_argument_validator.go
│ │ │ ├── mock_cleaner.go
│ │ │ ├── mock_data_validator.go
│ │ │ ├── mock_file_restore_stepper.go
│ │ │ ├── mock_kube_executor.go
│ │ │ ├── mock_port_forwarder.go
│ │ │ ├── mock_pvc_browser_stepper.go
│ │ │ ├── mock_snapshot_browser_stepper.go
│ │ │ ├── mock_snapshot_creator.go
│ │ │ └── mock_snapshot_restore_stepper.go
│ │ ├── pvc_inspector.go
│ │ ├── pvc_inspector_steps_test.go
│ │ ├── pvc_inspector_test.go
│ │ ├── snapshot_inspector.go
│ │ ├── snapshot_inspector_steps_test.go
│ │ ├── snapshot_inspector_test.go
│ │ ├── snapshot_restore.go
│ │ ├── snapshot_restore_steps_test.go
│ │ ├── snapshot_restore_test.go
│ │ └── types/
│ │ └── csi_types.go
│ ├── fio/
│ │ ├── _config.yml
│ │ ├── dbench_license
│ │ ├── fio.go
│ │ ├── fio_jobs.go
│ │ ├── fio_test.go
│ │ ├── fio_types.go
│ │ └── parsable_fio_output.go
│ └── kubestr/
│ ├── csi-drivers.go
│ ├── kubernetes_checks.go
│ ├── kubernetes_checks_test.go
│ ├── kubestr.go
│ ├── storage_provisioners.go
│ ├── storage_provisioners_test.go
│ └── utils.go
└── scripts/
└── load_csi_provisioners.sh
SYMBOL INDEX (512 symbols across 43 files)
FILE: cmd/rootCmd.go
function init (line 192) | func init() {
function Execute (line 255) | func Execute() error {
function Baseline (line 260) | func Baseline(ctx context.Context, output string) error {
function PrintAndJsonOutput (line 298) | func PrintAndJsonOutput(result []*kubestr.TestOutput, output string, out...
function Fio (line 316) | func Fio(ctx context.Context, output, outfile, storageclass, size, names...
function CSICheck (line 348) | func CSICheck(ctx context.Context, output, outfile,
function CsiPvcBrowse (line 395) | func CsiPvcBrowse(ctx context.Context,
function CsiSnapshotBrowse (line 431) | func CsiSnapshotBrowse(ctx context.Context,
function FileRestore (line 465) | func FileRestore(ctx context.Context,
function BlockMountCheck (line 503) | func BlockMountCheck(ctx context.Context, output, outfile string, cleanu...
FILE: main.go
function main (line 24) | func main() {
function Execute (line 31) | func Execute() error {
FILE: pkg/block/block_mount.go
type BlockMountCheckerArgs (line 20) | type BlockMountCheckerArgs struct
method Validate (line 33) | func (a *BlockMountCheckerArgs) Validate() error {
type BlockMountChecker (line 42) | type BlockMountChecker interface
type BlockMountCheckerResult (line 47) | type BlockMountCheckerResult struct
constant blockMountCheckerPVCNameFmt (line 52) | blockMountCheckerPVCNameFmt = "kubestr-blockmount-%s-pvc"
constant blockMountCheckerPodNameFmt (line 53) | blockMountCheckerPodNameFmt = "kubestr-blockmount-%s-pod"
constant blockModeCheckerPodCleanupTimeout (line 55) | blockModeCheckerPodCleanupTimeout = time.Second * 120
constant blockModeCheckerPVCCleanupTimeout (line 56) | blockModeCheckerPVCCleanupTimeout = time.Second * 120
constant blockModeCheckerPVCDefaultSize (line 57) | blockModeCheckerPVCDefaultSize = "1Gi"
type blockMountChecker (line 61) | type blockMountChecker struct
method Mount (line 90) | func (b *blockMountChecker) Mount(ctx context.Context) (*BlockMountChe...
method Cleanup (line 165) | func (b *blockMountChecker) Cleanup() {
method pvcWaitForTermination (line 205) | func (b *blockMountChecker) pvcWaitForTermination(timeout time.Duratio...
function NewBlockMountChecker (line 72) | func NewBlockMountChecker(args BlockMountCheckerArgs) (BlockMountChecker...
FILE: pkg/block/block_mount_test.go
function TestBlockMountCheckerNew (line 25) | func TestBlockMountCheckerNew(t *testing.T) {
function TestBlockMountCheckerPvcWaitForTermination (line 82) | func TestBlockMountCheckerPvcWaitForTermination(t *testing.T) {
function TestBlockMountCheckerCleanup (line 150) | func TestBlockMountCheckerCleanup(t *testing.T) {
function TestBlockMountCheckerMount (line 252) | func TestBlockMountCheckerMount(t *testing.T) {
FILE: pkg/common/common.go
constant VolSnapClassDriverKey (line 5) | VolSnapClassDriverKey = "driver"
constant DefaultPodImage (line 7) | DefaultPodImage = "ghcr.io/kastenhq/kubestr:latest"
constant SnapGroupName (line 9) | SnapGroupName = "snapshot.storage.k8s.io"
constant VolumeSnapshotClassResourcePlural (line 11) | VolumeSnapshotClassResourcePlural = "volumesnapshotclasses"
constant VolumeSnapshotResourcePlural (line 13) | VolumeSnapshotResourcePlural = "volumesnapshots"
constant SnapshotVersion (line 15) | SnapshotVersion = "snapshot.storage.k8s.io/v1"
FILE: pkg/csi/csi.go
type CSI (line 9) | type CSI interface
FILE: pkg/csi/csi_ops.go
constant defaultReadyWaitTimeout (line 38) | defaultReadyWaitTimeout = 2 * time.Minute
constant PVCKind (line 40) | PVCKind = "PersistentVolumeClaim"
constant PodKind (line 41) | PodKind = "Pod"
constant DefaultVolumeSnapshotClassAnnotation (line 44) | DefaultVolumeSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/i...
type ArgumentValidator (line 48) | type ArgumentValidator interface
type validateOperations (line 58) | type validateOperations struct
method ValidatePVC (line 70) | func (o *validateOperations) ValidatePVC(ctx context.Context, pvcName,...
method ValidateVolumeSnapshot (line 81) | func (o *validateOperations) ValidateVolumeSnapshot(ctx context.Contex...
method FetchPV (line 92) | func (o *validateOperations) FetchPV(ctx context.Context, pvName strin...
method ValidateNamespace (line 103) | func (o *validateOperations) ValidateNamespace(ctx context.Context, na...
method ValidateStorageClass (line 111) | func (o *validateOperations) ValidateStorageClass(ctx context.Context,...
method ValidateVolumeSnapshotClass (line 122) | func (o *validateOperations) ValidateVolumeSnapshotClass(ctx context.C...
function NewArgumentValidator (line 63) | func NewArgumentValidator(kubeCli kubernetes.Interface, dynCli dynamic.I...
type ApplicationCreator (line 131) | type ApplicationCreator interface
type applicationCreate (line 138) | type applicationCreate struct
method CreatePVC (line 150) | func (c *applicationCreate) CreatePVC(ctx context.Context, args *types...
method CreatePod (line 194) | func (c *applicationCreate) CreatePod(ctx context.Context, args *types...
method WaitForPVCReady (line 266) | func (c *applicationCreate) WaitForPVCReady(ctx context.Context, names...
method waitForPVCReady (line 281) | func (c *applicationCreate) waitForPVCReady(ctx context.Context, names...
method WaitForPodReady (line 303) | func (c *applicationCreate) WaitForPodReady(ctx context.Context, names...
method waitForPodReady (line 317) | func (c *applicationCreate) waitForPodReady(ctx context.Context, names...
method getErrorFromEvents (line 329) | func (c *applicationCreate) getErrorFromEvents(ctx context.Context, na...
function NewApplicationCreator (line 143) | func NewApplicationCreator(kubeCli kubernetes.Interface, k8sObjectReadyT...
type SnapshotCreator (line 353) | type SnapshotCreator interface
type snapshotCreate (line 359) | type snapshotCreate struct
method NewSnapshotter (line 364) | func (c *snapshotCreate) NewSnapshotter() (kansnapshot.Snapshotter, er...
method CreateSnapshot (line 374) | func (c *snapshotCreate) CreateSnapshot(ctx context.Context, snapshott...
method CreateFromSourceCheck (line 396) | func (c *snapshotCreate) CreateFromSourceCheck(ctx context.Context, sn...
type Cleaner (line 448) | type Cleaner interface
type cleanse (line 454) | type cleanse struct
method DeletePVC (line 466) | func (c *cleanse) DeletePVC(ctx context.Context, pvcName string, names...
method DeletePod (line 473) | func (c *cleanse) DeletePod(ctx context.Context, podName string, names...
method DeleteSnapshot (line 480) | func (c *cleanse) DeleteSnapshot(ctx context.Context, snapshotName str...
function NewCleaner (line 459) | func NewCleaner(kubeCli kubernetes.Interface, dynCli dynamic.Interface) ...
type ApiVersionFetcher (line 492) | type ApiVersionFetcher interface
type apiVersionFetch (line 496) | type apiVersionFetch struct
method GetCSISnapshotGroupVersion (line 500) | func (p *apiVersionFetch) GetCSISnapshotGroupVersion() (*metav1.GroupV...
type DataValidator (line 517) | type DataValidator interface
type validateData (line 521) | type validateData struct
method FetchPodData (line 525) | func (p *validateData) FetchPodData(ctx context.Context, podName strin...
type PortForwarder (line 534) | type PortForwarder interface
type portforward (line 539) | type portforward struct
method PortForwardAPod (line 541) | func (p *portforward) PortForwardAPod(req *types.PortForwardAPodReques...
method FetchRestConfig (line 559) | func (p *portforward) FetchRestConfig() (*rest.Config, error) {
type KubeExecutor (line 564) | type KubeExecutor interface
type kubeExec (line 568) | type kubeExec struct
method Exec (line 572) | func (k *kubeExec) Exec(ctx context.Context, namespace string, podName...
FILE: pkg/csi/csi_ops_test.go
method TestGetDriverNameFromUVSC (line 30) | func (s *CSITestSuite) TestGetDriverNameFromUVSC(c *C) {
method TestGetCSISnapshotGroupVersion (line 69) | func (s *CSITestSuite) TestGetCSISnapshotGroupVersion(c *C) {
method TestValidatePVC (line 124) | func (s *CSITestSuite) TestValidatePVC(c *C) {
method TestFetchPV (line 147) | func (s *CSITestSuite) TestFetchPV(c *C) {
method TestValidateNamespace (line 169) | func (s *CSITestSuite) TestValidateNamespace(c *C) {
method TestValidateStorageClass (line 188) | func (s *CSITestSuite) TestValidateStorageClass(c *C) {
method TestValidateVolumeSnapshotClass (line 216) | func (s *CSITestSuite) TestValidateVolumeSnapshotClass(c *C) {
method TestCreatePVC (line 262) | func (s *CSITestSuite) TestCreatePVC(c *C) {
method TestCreatePod (line 392) | func (s *CSITestSuite) TestCreatePod(c *C) {
method TestCreateSnapshot (line 635) | func (s *CSITestSuite) TestCreateSnapshot(c *C) {
method TestCreateFromSourceCheck (line 755) | func (s *CSITestSuite) TestCreateFromSourceCheck(c *C) {
type fakeSnapshotter (line 886) | type fakeSnapshotter struct
method GroupVersion (line 902) | func (f *fakeSnapshotter) GroupVersion(ctx context.Context) schema.Gro...
method GetVolumeSnapshotClass (line 909) | func (f *fakeSnapshotter) GetVolumeSnapshotClass(ctx context.Context, ...
method CloneVolumeSnapshotClass (line 912) | func (f *fakeSnapshotter) CloneVolumeSnapshotClass(ctx context.Context...
method Create (line 915) | func (f *fakeSnapshotter) Create(ctx context.Context, pvcName string, ...
method Get (line 918) | func (f *fakeSnapshotter) Get(ctx context.Context, name, namespace str...
method Delete (line 921) | func (f *fakeSnapshotter) Delete(ctx context.Context, name, namespace ...
method DeleteContent (line 924) | func (f *fakeSnapshotter) DeleteContent(ctx context.Context, name stri...
method Clone (line 925) | func (f *fakeSnapshotter) Clone(ctx context.Context, name, namespace s...
method GetSource (line 928) | func (f *fakeSnapshotter) GetSource(ctx context.Context, snapshotName,...
method CreateFromSource (line 931) | func (f *fakeSnapshotter) CreateFromSource(ctx context.Context, source...
method CreateContentFromSource (line 934) | func (f *fakeSnapshotter) CreateContentFromSource(ctx context.Context,...
method WaitOnReadyToUse (line 937) | func (f *fakeSnapshotter) WaitOnReadyToUse(ctx context.Context, snapsh...
method List (line 941) | func (f *fakeSnapshotter) List(ctx context.Context, namespace string, ...
method TestDeletePVC (line 945) | func (s *CSITestSuite) TestDeletePVC(c *C) {
method TestDeletePod (line 994) | func (s *CSITestSuite) TestDeletePod(c *C) {
method TestDeleteSnapshot (line 1045) | func (s *CSITestSuite) TestDeleteSnapshot(c *C) {
method TestWaitForPVCReady (line 1120) | func (s *CSITestSuite) TestWaitForPVCReady(c *C) {
method getPVC (line 1196) | func (s *CSITestSuite) getPVC(ns, pvc string, phase v1.PersistentVolumeC...
method TestWaitForPodReady (line 1208) | func (s *CSITestSuite) TestWaitForPodReady(c *C) {
FILE: pkg/csi/file_restore_inspector.go
type FileRestoreRunner (line 22) | type FileRestoreRunner struct
method RunFileRestore (line 31) | func (f *FileRestoreRunner) RunFileRestore(ctx context.Context, args *...
method RunFileRestoreHelper (line 55) | func (f *FileRestoreRunner) RunFileRestoreHelper(ctx context.Context, ...
type FileRestoreStepper (line 102) | type FileRestoreStepper interface
type fileRestoreSteps (line 110) | type fileRestoreSteps struct
method ValidateArgs (line 120) | func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *typ...
method CreateInspectorApplication (line 198) | func (f *fileRestoreSteps) CreateInspectorApplication(ctx context.Cont...
method ExecuteCopyCommand (line 265) | func (f *fileRestoreSteps) ExecuteCopyCommand(ctx context.Context, arg...
method PortForwardAPod (line 274) | func (f *fileRestoreSteps) PortForwardAPod(pod *v1.Pod, localPort int)...
method Cleanup (line 322) | func (f *fileRestoreSteps) Cleanup(ctx context.Context, args *types.Fi...
FILE: pkg/csi/file_restore_inspector_steps_test.go
method TestFileRestoreValidateArgs (line 21) | func (s *CSITestSuite) TestFileRestoreValidateArgs(c *C) {
method TestCreateInspectorApplicationForFileRestore (line 343) | func (s *CSITestSuite) TestCreateInspectorApplicationForFileRestore(c *C) {
method TestFileRestoreCleanup (line 629) | func (s *CSITestSuite) TestFileRestoreCleanup(c *C) {
FILE: pkg/csi/file_restore_inspector_test.go
method TestRunFileRestoreHelper (line 22) | func (s *CSITestSuite) TestRunFileRestoreHelper(c *C) {
method TestFileRestoreRunner (line 186) | func (s *CSITestSuite) TestFileRestoreRunner(c *C) {
FILE: pkg/csi/mocks/mock_api_version_fetcher.go
type MockApiVersionFetcher (line 15) | type MockApiVersionFetcher struct
method EXPECT (line 33) | func (m *MockApiVersionFetcher) EXPECT() *MockApiVersionFetcherMockRec...
method GetCSISnapshotGroupVersion (line 38) | func (m *MockApiVersionFetcher) GetCSISnapshotGroupVersion() (*v1.Grou...
type MockApiVersionFetcherMockRecorder (line 21) | type MockApiVersionFetcherMockRecorder struct
method GetCSISnapshotGroupVersion (line 47) | func (mr *MockApiVersionFetcherMockRecorder) GetCSISnapshotGroupVersio...
function NewMockApiVersionFetcher (line 26) | func NewMockApiVersionFetcher(ctrl *gomock.Controller) *MockApiVersionFe...
FILE: pkg/csi/mocks/mock_application_creator.go
type MockApplicationCreator (line 17) | type MockApplicationCreator struct
method EXPECT (line 35) | func (m *MockApplicationCreator) EXPECT() *MockApplicationCreatorMockR...
method CreatePVC (line 40) | func (m *MockApplicationCreator) CreatePVC(arg0 context.Context, arg1 ...
method CreatePod (line 55) | func (m *MockApplicationCreator) CreatePod(arg0 context.Context, arg1 ...
method WaitForPodReady (line 70) | func (m *MockApplicationCreator) WaitForPodReady(arg0 context.Context,...
method WaitForPVCReady (line 83) | func (m *MockApplicationCreator) WaitForPVCReady(ctx context.Context, ...
type MockApplicationCreatorMockRecorder (line 23) | type MockApplicationCreatorMockRecorder struct
method CreatePVC (line 49) | func (mr *MockApplicationCreatorMockRecorder) CreatePVC(arg0, arg1 int...
method CreatePod (line 64) | func (mr *MockApplicationCreatorMockRecorder) CreatePod(arg0, arg1 int...
method WaitForPodReady (line 78) | func (mr *MockApplicationCreatorMockRecorder) WaitForPodReady(arg0, ar...
method WaitForPVCReady (line 91) | func (mr *MockApplicationCreatorMockRecorder) WaitForPVCReady(ctx, nam...
function NewMockApplicationCreator (line 28) | func NewMockApplicationCreator(ctrl *gomock.Controller) *MockApplication...
FILE: pkg/csi/mocks/mock_argument_validator.go
type MockArgumentValidator (line 20) | type MockArgumentValidator struct
method EXPECT (line 38) | func (m *MockArgumentValidator) EXPECT() *MockArgumentValidatorMockRec...
method FetchPV (line 43) | func (m *MockArgumentValidator) FetchPV(arg0 context.Context, arg1 str...
method ValidateNamespace (line 58) | func (m *MockArgumentValidator) ValidateNamespace(arg0 context.Context...
method ValidatePVC (line 72) | func (m *MockArgumentValidator) ValidatePVC(arg0 context.Context, arg1...
method ValidateStorageClass (line 87) | func (m *MockArgumentValidator) ValidateStorageClass(arg0 context.Cont...
method ValidateVolumeSnapshot (line 102) | func (m *MockArgumentValidator) ValidateVolumeSnapshot(arg0 context.Co...
method ValidateVolumeSnapshotClass (line 117) | func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 conte...
type MockArgumentValidatorMockRecorder (line 26) | type MockArgumentValidatorMockRecorder struct
method FetchPV (line 52) | func (mr *MockArgumentValidatorMockRecorder) FetchPV(arg0, arg1 interf...
method ValidateNamespace (line 66) | func (mr *MockArgumentValidatorMockRecorder) ValidateNamespace(arg0, a...
method ValidatePVC (line 81) | func (mr *MockArgumentValidatorMockRecorder) ValidatePVC(arg0, arg1, a...
method ValidateStorageClass (line 96) | func (mr *MockArgumentValidatorMockRecorder) ValidateStorageClass(arg0...
method ValidateVolumeSnapshot (line 111) | func (mr *MockArgumentValidatorMockRecorder) ValidateVolumeSnapshot(ar...
method ValidateVolumeSnapshotClass (line 126) | func (mr *MockArgumentValidatorMockRecorder) ValidateVolumeSnapshotCla...
function NewMockArgumentValidator (line 31) | func NewMockArgumentValidator(ctrl *gomock.Controller) *MockArgumentVali...
FILE: pkg/csi/mocks/mock_cleaner.go
type MockCleaner (line 16) | type MockCleaner struct
method EXPECT (line 34) | func (m *MockCleaner) EXPECT() *MockCleanerMockRecorder {
method DeletePVC (line 39) | func (m *MockCleaner) DeletePVC(arg0 context.Context, arg1, arg2 strin...
method DeletePod (line 53) | func (m *MockCleaner) DeletePod(arg0 context.Context, arg1, arg2 strin...
method DeleteSnapshot (line 67) | func (m *MockCleaner) DeleteSnapshot(arg0 context.Context, arg1, arg2 ...
type MockCleanerMockRecorder (line 22) | type MockCleanerMockRecorder struct
method DeletePVC (line 47) | func (mr *MockCleanerMockRecorder) DeletePVC(arg0, arg1, arg2 interfac...
method DeletePod (line 61) | func (mr *MockCleanerMockRecorder) DeletePod(arg0, arg1, arg2 interfac...
method DeleteSnapshot (line 75) | func (mr *MockCleanerMockRecorder) DeleteSnapshot(arg0, arg1, arg2, ar...
function NewMockCleaner (line 27) | func NewMockCleaner(ctrl *gomock.Controller) *MockCleaner {
FILE: pkg/csi/mocks/mock_data_validator.go
type MockDataValidator (line 15) | type MockDataValidator struct
method EXPECT (line 33) | func (m *MockDataValidator) EXPECT() *MockDataValidatorMockRecorder {
method FetchPodData (line 38) | func (m *MockDataValidator) FetchPodData(arg0 context.Context, arg1, a...
type MockDataValidatorMockRecorder (line 21) | type MockDataValidatorMockRecorder struct
method FetchPodData (line 47) | func (mr *MockDataValidatorMockRecorder) FetchPodData(arg0, arg1, arg2...
function NewMockDataValidator (line 26) | func NewMockDataValidator(ctrl *gomock.Controller) *MockDataValidator {
FILE: pkg/csi/mocks/mock_file_restore_stepper.go
type MockFileRestoreStepper (line 19) | type MockFileRestoreStepper struct
method EXPECT (line 37) | func (m *MockFileRestoreStepper) EXPECT() *MockFileRestoreStepperMockR...
method Cleanup (line 42) | func (m *MockFileRestoreStepper) Cleanup(arg0 context.Context, arg1 *t...
method CreateInspectorApplication (line 54) | func (m *MockFileRestoreStepper) CreateInspectorApplication(arg0 conte...
method ExecuteCopyCommand (line 71) | func (m *MockFileRestoreStepper) ExecuteCopyCommand(arg0 context.Conte...
method PortForwardAPod (line 86) | func (m *MockFileRestoreStepper) PortForwardAPod(arg0 *v10.Pod, arg1 i...
method ValidateArgs (line 100) | func (m *MockFileRestoreStepper) ValidateArgs(arg0 context.Context, ar...
type MockFileRestoreStepperMockRecorder (line 25) | type MockFileRestoreStepperMockRecorder struct
method Cleanup (line 48) | func (mr *MockFileRestoreStepperMockRecorder) Cleanup(arg0, arg1, arg2...
method CreateInspectorApplication (line 65) | func (mr *MockFileRestoreStepperMockRecorder) CreateInspectorApplicati...
method ExecuteCopyCommand (line 80) | func (mr *MockFileRestoreStepperMockRecorder) ExecuteCopyCommand(arg0,...
method PortForwardAPod (line 94) | func (mr *MockFileRestoreStepperMockRecorder) PortForwardAPod(arg0, ar...
method ValidateArgs (line 112) | func (mr *MockFileRestoreStepperMockRecorder) ValidateArgs(arg0, arg1 ...
function NewMockFileRestoreStepper (line 30) | func NewMockFileRestoreStepper(ctrl *gomock.Controller) *MockFileRestore...
FILE: pkg/csi/mocks/mock_kube_executor.go
type MockKubeExecutor (line 15) | type MockKubeExecutor struct
method EXPECT (line 33) | func (m *MockKubeExecutor) EXPECT() *MockKubeExecutorMockRecorder {
method Exec (line 38) | func (m *MockKubeExecutor) Exec(arg0 context.Context, arg1, arg2, arg3...
type MockKubeExecutorMockRecorder (line 21) | type MockKubeExecutorMockRecorder struct
method Exec (line 47) | func (mr *MockKubeExecutorMockRecorder) Exec(arg0, arg1, arg2, arg3, a...
function NewMockKubeExecutor (line 26) | func NewMockKubeExecutor(ctrl *gomock.Controller) *MockKubeExecutor {
FILE: pkg/csi/mocks/mock_port_forwarder.go
type MockPortForwarder (line 16) | type MockPortForwarder struct
method EXPECT (line 34) | func (m *MockPortForwarder) EXPECT() *MockPortForwarderMockRecorder {
method FetchRestConfig (line 39) | func (m *MockPortForwarder) FetchRestConfig() (*rest.Config, error) {
method PortForwardAPod (line 54) | func (m *MockPortForwarder) PortForwardAPod(arg0 *types.PortForwardAPo...
type MockPortForwarderMockRecorder (line 22) | type MockPortForwarderMockRecorder struct
method FetchRestConfig (line 48) | func (mr *MockPortForwarderMockRecorder) FetchRestConfig() *gomock.Call {
method PortForwardAPod (line 62) | func (mr *MockPortForwarderMockRecorder) PortForwardAPod(arg0 interfac...
function NewMockPortForwarder (line 27) | func NewMockPortForwarder(ctrl *gomock.Controller) *MockPortForwarder {
FILE: pkg/csi/mocks/mock_pvc_browser_stepper.go
type MockPVCBrowserStepper (line 19) | type MockPVCBrowserStepper struct
method EXPECT (line 37) | func (m *MockPVCBrowserStepper) EXPECT() *MockPVCBrowserStepperMockRec...
method Cleanup (line 42) | func (m *MockPVCBrowserStepper) Cleanup(arg0 context.Context, arg1 *v1...
method CreateInspectorApplication (line 54) | func (m *MockPVCBrowserStepper) CreateInspectorApplication(arg0 contex...
method ExecuteTreeCommand (line 70) | func (m *MockPVCBrowserStepper) ExecuteTreeCommand(arg0 context.Contex...
method PortForwardAPod (line 85) | func (m *MockPVCBrowserStepper) PortForwardAPod(arg0 context.Context, ...
method SnapshotPVC (line 99) | func (m *MockPVCBrowserStepper) SnapshotPVC(arg0 context.Context, arg1...
method ValidateArgs (line 114) | func (m *MockPVCBrowserStepper) ValidateArgs(arg0 context.Context, arg...
type MockPVCBrowserStepperMockRecorder (line 25) | type MockPVCBrowserStepperMockRecorder struct
method Cleanup (line 48) | func (mr *MockPVCBrowserStepperMockRecorder) Cleanup(arg0, arg1, arg2,...
method CreateInspectorApplication (line 64) | func (mr *MockPVCBrowserStepperMockRecorder) CreateInspectorApplicatio...
method ExecuteTreeCommand (line 79) | func (mr *MockPVCBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, ...
method PortForwardAPod (line 93) | func (mr *MockPVCBrowserStepperMockRecorder) PortForwardAPod(arg0, arg...
method SnapshotPVC (line 108) | func (mr *MockPVCBrowserStepperMockRecorder) SnapshotPVC(arg0, arg1, a...
method ValidateArgs (line 123) | func (mr *MockPVCBrowserStepperMockRecorder) ValidateArgs(arg0, arg1 i...
function NewMockPVCBrowserStepper (line 30) | func NewMockPVCBrowserStepper(ctrl *gomock.Controller) *MockPVCBrowserSt...
FILE: pkg/csi/mocks/mock_snapshot_browser_stepper.go
type MockSnapshotBrowserStepper (line 19) | type MockSnapshotBrowserStepper struct
method EXPECT (line 37) | func (m *MockSnapshotBrowserStepper) EXPECT() *MockSnapshotBrowserStep...
method Cleanup (line 42) | func (m *MockSnapshotBrowserStepper) Cleanup(arg0 context.Context, arg...
method CreateInspectorApplication (line 54) | func (m *MockSnapshotBrowserStepper) CreateInspectorApplication(arg0 c...
method ExecuteTreeCommand (line 70) | func (m *MockSnapshotBrowserStepper) ExecuteTreeCommand(arg0 context.C...
method PortForwardAPod (line 85) | func (m *MockSnapshotBrowserStepper) PortForwardAPod(arg0 context.Cont...
method ValidateArgs (line 99) | func (m *MockSnapshotBrowserStepper) ValidateArgs(arg0 context.Context...
type MockSnapshotBrowserStepperMockRecorder (line 25) | type MockSnapshotBrowserStepperMockRecorder struct
method Cleanup (line 48) | func (mr *MockSnapshotBrowserStepperMockRecorder) Cleanup(arg0, arg1, ...
method CreateInspectorApplication (line 64) | func (mr *MockSnapshotBrowserStepperMockRecorder) CreateInspectorAppli...
method ExecuteTreeCommand (line 79) | func (mr *MockSnapshotBrowserStepperMockRecorder) ExecuteTreeCommand(a...
method PortForwardAPod (line 93) | func (mr *MockSnapshotBrowserStepperMockRecorder) PortForwardAPod(arg0...
method ValidateArgs (line 109) | func (mr *MockSnapshotBrowserStepperMockRecorder) ValidateArgs(arg0, a...
function NewMockSnapshotBrowserStepper (line 30) | func NewMockSnapshotBrowserStepper(ctrl *gomock.Controller) *MockSnapsho...
FILE: pkg/csi/mocks/mock_snapshot_creator.go
type MockSnapshotCreator (line 19) | type MockSnapshotCreator struct
method EXPECT (line 37) | func (m *MockSnapshotCreator) EXPECT() *MockSnapshotCreatorMockRecorder {
method CreateFromSourceCheck (line 42) | func (m *MockSnapshotCreator) CreateFromSourceCheck(arg0 context.Conte...
method CreateSnapshot (line 56) | func (m *MockSnapshotCreator) CreateSnapshot(arg0 context.Context, arg...
method NewSnapshotter (line 71) | func (m *MockSnapshotCreator) NewSnapshotter() (snapshot.Snapshotter, ...
type MockSnapshotCreatorMockRecorder (line 25) | type MockSnapshotCreatorMockRecorder struct
method CreateFromSourceCheck (line 50) | func (mr *MockSnapshotCreatorMockRecorder) CreateFromSourceCheck(arg0,...
method CreateSnapshot (line 65) | func (mr *MockSnapshotCreatorMockRecorder) CreateSnapshot(arg0, arg1, ...
method NewSnapshotter (line 80) | func (mr *MockSnapshotCreatorMockRecorder) NewSnapshotter() *gomock.Ca...
function NewMockSnapshotCreator (line 30) | func NewMockSnapshotCreator(ctrl *gomock.Controller) *MockSnapshotCreator {
FILE: pkg/csi/mocks/mock_snapshot_restore_stepper.go
type MockSnapshotRestoreStepper (line 18) | type MockSnapshotRestoreStepper struct
method EXPECT (line 36) | func (m *MockSnapshotRestoreStepper) EXPECT() *MockSnapshotRestoreStep...
method Cleanup (line 41) | func (m *MockSnapshotRestoreStepper) Cleanup(arg0 context.Context, arg...
method CreateApplication (line 53) | func (m *MockSnapshotRestoreStepper) CreateApplication(arg0 context.Co...
method RestoreApplication (line 69) | func (m *MockSnapshotRestoreStepper) RestoreApplication(arg0 context.C...
method SnapshotApplication (line 85) | func (m *MockSnapshotRestoreStepper) SnapshotApplication(arg0 context....
method ValidateArgs (line 100) | func (m *MockSnapshotRestoreStepper) ValidateArgs(arg0 context.Context...
method ValidateData (line 114) | func (m *MockSnapshotRestoreStepper) ValidateData(arg0 context.Context...
type MockSnapshotRestoreStepperMockRecorder (line 24) | type MockSnapshotRestoreStepperMockRecorder struct
method Cleanup (line 47) | func (mr *MockSnapshotRestoreStepperMockRecorder) Cleanup(arg0, arg1 i...
method CreateApplication (line 63) | func (mr *MockSnapshotRestoreStepperMockRecorder) CreateApplication(ar...
method RestoreApplication (line 79) | func (mr *MockSnapshotRestoreStepperMockRecorder) RestoreApplication(a...
method SnapshotApplication (line 94) | func (mr *MockSnapshotRestoreStepperMockRecorder) SnapshotApplication(...
method ValidateArgs (line 108) | func (mr *MockSnapshotRestoreStepperMockRecorder) ValidateArgs(arg0, a...
method ValidateData (line 122) | func (mr *MockSnapshotRestoreStepperMockRecorder) ValidateData(arg0, a...
function NewMockSnapshotRestoreStepper (line 29) | func NewMockSnapshotRestoreStepper(ctrl *gomock.Controller) *MockSnapsho...
FILE: pkg/csi/pvc_inspector.go
type PVCBrowseRunner (line 26) | type PVCBrowseRunner struct
method RunPVCBrowse (line 35) | func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *type...
method RunPVCBrowseHelper (line 67) | func (r *PVCBrowseRunner) RunPVCBrowseHelper(ctx context.Context, args...
type PVCBrowserStepper (line 113) | type PVCBrowserStepper interface
type pvcBrowserSteps (line 122) | type pvcBrowserSteps struct
method ValidateArgs (line 133) | func (p *pvcBrowserSteps) ValidateArgs(ctx context.Context, args *type...
method SnapshotPVC (line 177) | func (p *pvcBrowserSteps) SnapshotPVC(ctx context.Context, args *types...
method CreateInspectorApplication (line 191) | func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Conte...
method ExecuteTreeCommand (line 247) | func (p *pvcBrowserSteps) ExecuteTreeCommand(ctx context.Context, args...
method PortForwardAPod (line 256) | func (p *pvcBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1...
method Cleanup (line 304) | func (p *pvcBrowserSteps) Cleanup(ctx context.Context, pvc *v1.Persist...
function openbrowser (line 325) | func openbrowser(url string) {
FILE: pkg/csi/pvc_inspector_steps_test.go
method TestPvcBrowseValidateArgs (line 20) | func (s *CSITestSuite) TestPvcBrowseValidateArgs(c *C) {
method TestPvcBrowseSnapshotPVC (line 392) | func (s *CSITestSuite) TestPvcBrowseSnapshotPVC(c *C) {
method TestCreateInspectorApplicationForPVC (line 489) | func (s *CSITestSuite) TestCreateInspectorApplicationForPVC(c *C) {
method TestPVCBrowseCleanup (line 699) | func (s *CSITestSuite) TestPVCBrowseCleanup(c *C) {
FILE: pkg/csi/pvc_inspector_test.go
method TestRunPVCBrowseHelper (line 22) | func (s *CSITestSuite) TestRunPVCBrowseHelper(c *C) {
method TestPVCBrowseRunner (line 206) | func (s *CSITestSuite) TestPVCBrowseRunner(c *C) {
FILE: pkg/csi/snapshot_inspector.go
type SnapshotBrowseRunner (line 22) | type SnapshotBrowseRunner struct
method RunSnapshotBrowse (line 31) | func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, ...
method RunSnapshotBrowseHelper (line 55) | func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Con...
type SnapshotBrowserStepper (line 98) | type SnapshotBrowserStepper interface
type snapshotBrowserSteps (line 106) | type snapshotBrowserSteps struct
method ValidateArgs (line 116) | func (s *snapshotBrowserSteps) ValidateArgs(ctx context.Context, args ...
method CreateInspectorApplication (line 151) | func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context....
method ExecuteTreeCommand (line 207) | func (s *snapshotBrowserSteps) ExecuteTreeCommand(ctx context.Context,...
method PortForwardAPod (line 216) | func (s *snapshotBrowserSteps) PortForwardAPod(ctx context.Context, po...
method Cleanup (line 264) | func (s *snapshotBrowserSteps) Cleanup(ctx context.Context, pvc *v1.Pe...
FILE: pkg/csi/snapshot_inspector_steps_test.go
method TestSnapshotBrowseValidateArgs (line 20) | func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) {
method TestCreateInspectorApplicationForSnapshot (line 293) | func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) {
method TestSnapshotBrowseCleanup (line 503) | func (s *CSITestSuite) TestSnapshotBrowseCleanup(c *C) {
FILE: pkg/csi/snapshot_inspector_test.go
method TestRunSnapshotBrowseHelper (line 22) | func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) {
method TestSnapshotBrowseRunner (line 185) | func (s *CSITestSuite) TestSnapshotBrowseRunner(c *C) {
FILE: pkg/csi/snapshot_restore.go
constant originalPVCGenerateName (line 20) | originalPVCGenerateName = "kubestr-csi-original-pvc"
constant originalPodGenerateName (line 21) | originalPodGenerateName = "kubestr-csi-original-pod"
constant clonedPVCGenerateName (line 22) | clonedPVCGenerateName = "kubestr-csi-cloned-pvc"
constant clonedPodGenerateName (line 23) | clonedPodGenerateName = "kubestr-csi-cloned-pod"
constant createdByLabel (line 24) | createdByLabel = "created-by-kubestr-csi"
constant clonePrefix (line 25) | clonePrefix = "kubestr-clone-"
constant snapshotPrefix (line 26) | snapshotPrefix = "kubestr-snapshot-"
type SnapshotRestoreRunner (line 29) | type SnapshotRestoreRunner struct
method RunSnapshotRestore (line 35) | func (r *SnapshotRestoreRunner) RunSnapshotRestore(ctx context.Context...
method RunSnapshotRestoreHelper (line 69) | func (r *SnapshotRestoreRunner) RunSnapshotRestoreHelper(ctx context.C...
type SnapshotRestoreStepper (line 121) | type SnapshotRestoreStepper interface
type snapshotRestoreSteps (line 130) | type snapshotRestoreSteps struct
method ValidateArgs (line 140) | func (s *snapshotRestoreSteps) ValidateArgs(ctx context.Context, args ...
method CreateApplication (line 170) | func (s *snapshotRestoreSteps) CreateApplication(ctx context.Context, ...
method ValidateData (line 208) | func (s *snapshotRestoreSteps) ValidateData(ctx context.Context, pod *...
method SnapshotApplication (line 219) | func (s *snapshotRestoreSteps) SnapshotApplication(ctx context.Context...
method RestoreApplication (line 247) | func (s *snapshotRestoreSteps) RestoreApplication(ctx context.Context,...
method Cleanup (line 294) | func (s *snapshotRestoreSteps) Cleanup(ctx context.Context, results *t...
function getDriverNameFromUVSC (line 330) | func getDriverNameFromUVSC(vsc unstructured.Unstructured, version string...
FILE: pkg/csi/snapshot_restore_steps_test.go
method TestValidateArgs (line 20) | func (s *CSITestSuite) TestValidateArgs(c *C) {
method TestCreateApplication (line 199) | func (s *CSITestSuite) TestCreateApplication(c *C) {
method TestSnapshotApplication (line 402) | func (s *CSITestSuite) TestSnapshotApplication(c *C) {
method TestRestoreApplication (line 581) | func (s *CSITestSuite) TestRestoreApplication(c *C) {
method TestCleanup (line 782) | func (s *CSITestSuite) TestCleanup(c *C) {
method TestValidateData (line 901) | func (s *CSITestSuite) TestValidateData(c *C) {
FILE: pkg/csi/snapshot_restore_test.go
function Test (line 22) | func Test(t *testing.T) { TestingT(t) }
type CSITestSuite (line 24) | type CSITestSuite struct
method TestRunSnapshotRestoreHelper (line 28) | func (s *CSITestSuite) TestRunSnapshotRestoreHelper(c *C) {
method TestRunSnapshotRestoreRunner (line 416) | func (s *CSITestSuite) TestRunSnapshotRestoreRunner(c *C) {
FILE: pkg/csi/types/csi_types.go
type CSISnapshotRestoreArgs (line 14) | type CSISnapshotRestoreArgs struct
method Validate (line 25) | func (a *CSISnapshotRestoreArgs) Validate() error {
type CSISnapshotRestoreResults (line 32) | type CSISnapshotRestoreResults struct
type CreatePVCArgs (line 40) | type CreatePVCArgs struct
method Validate (line 50) | func (c *CreatePVCArgs) Validate() error {
type VolumePath (line 59) | type VolumePath struct
type CreatePodArgs (line 64) | type CreatePodArgs struct
method Validate (line 75) | func (c *CreatePodArgs) Validate() error {
type CreateSnapshotArgs (line 95) | type CreateSnapshotArgs struct
method Validate (line 102) | func (c *CreateSnapshotArgs) Validate() error {
type FetchSnapshotArgs (line 109) | type FetchSnapshotArgs struct
method Validate (line 114) | func (c *FetchSnapshotArgs) Validate() error {
type CreateFromSourceCheckArgs (line 121) | type CreateFromSourceCheckArgs struct
method Validate (line 127) | func (c *CreateFromSourceCheckArgs) Validate() error {
type PVCBrowseArgs (line 134) | type PVCBrowseArgs struct
method Validate (line 143) | func (p *PVCBrowseArgs) Validate() error {
type SnapshotBrowseArgs (line 150) | type SnapshotBrowseArgs struct
method Validate (line 158) | func (p *SnapshotBrowseArgs) Validate() error {
type FileRestoreArgs (line 165) | type FileRestoreArgs struct
method Validate (line 175) | func (f *FileRestoreArgs) Validate() error {
type PortForwardAPodRequest (line 188) | type PortForwardAPodRequest struct
FILE: pkg/fio/fio.go
constant DefaultNS (line 25) | DefaultNS = "default"
constant PodNamespaceEnvKey (line 27) | PodNamespaceEnvKey = "POD_NAMESPACE"
constant DefaultFIOJob (line 29) | DefaultFIOJob = "default-fio"
constant KubestrFIOJobGenName (line 31) | KubestrFIOJobGenName = "kubestr-fio"
constant ConfigMapJobKey (line 33) | ConfigMapJobKey = "fiojob"
constant DefaultPVCSize (line 35) | DefaultPVCSize = "100Gi"
constant PVCGenerateName (line 37) | PVCGenerateName = "kubestr-fio-pvc-"
constant PodGenerateName (line 39) | PodGenerateName = "kubestr-fio-pod-"
constant ContainerName (line 41) | ContainerName = "kubestr-fio"
constant PodNameEnvKey (line 43) | PodNameEnvKey = "HOSTNAME"
constant ConfigMapMountPath (line 45) | ConfigMapMountPath = "/etc/fio-config"
constant VolumeMountPath (line 47) | VolumeMountPath = "/dataset"
constant CreatedByFIOLabel (line 49) | CreatedByFIOLabel = "createdbyfio"
type FIO (line 53) | type FIO interface
type FIOrunner (line 58) | type FIOrunner struct
method RunFio (line 87) | func (f *FIOrunner) RunFio(ctx context.Context, args *RunFIOArgs) (*Ru...
method RunFioHelper (line 97) | func (f *FIOrunner) RunFioHelper(ctx context.Context, args *RunFIOArgs...
type RunFIOArgs (line 63) | type RunFIOArgs struct
method Validate (line 73) | func (a *RunFIOArgs) Validate() error {
type RunFIOResult (line 80) | type RunFIOResult struct
type fioSteps (line 163) | type fioSteps interface
type fioStepper (line 176) | type fioStepper struct
method validateNamespace (line 182) | func (s *fioStepper) validateNamespace(ctx context.Context, namespace ...
method validateNodeSelector (line 189) | func (s *fioStepper) validateNodeSelector(ctx context.Context, selecto...
method storageClassExists (line 204) | func (s *fioStepper) storageClassExists(ctx context.Context, storageCl...
method loadConfigMap (line 208) | func (s *fioStepper) loadConfigMap(ctx context.Context, args *RunFIOAr...
method createPVC (line 237) | func (s *fioStepper) createPVC(ctx context.Context, storageclass, size...
method deletePVC (line 263) | func (s *fioStepper) deletePVC(ctx context.Context, pvcName, namespace...
method createPod (line 267) | func (s *fioStepper) createPod(ctx context.Context, pvcName, configMap...
method deletePod (line 331) | func (s *fioStepper) deletePod(ctx context.Context, podName, namespace...
method runFIOCommand (line 335) | func (s *fioStepper) runFIOCommand(ctx context.Context, podName, conta...
method deleteConfigMap (line 373) | func (s *fioStepper) deleteConfigMap(ctx context.Context, configMap *v...
function fioTestFilename (line 380) | func fioTestFilename(configMap map[string]string) (string, error) {
type waitForPodReadyInterface (line 391) | type waitForPodReadyInterface interface
type podReadyChecker (line 395) | type podReadyChecker struct
method waitForPodReady (line 399) | func (p *podReadyChecker) waitForPodReady(ctx context.Context, namespa...
type kubeExecInterface (line 403) | type kubeExecInterface interface
type kubeExecutor (line 407) | type kubeExecutor struct
method exec (line 411) | func (k *kubeExecutor) exec(ctx context.Context, namespace, podName, c...
FILE: pkg/fio/fio_test.go
function Test (line 22) | func Test(t *testing.T) { TestingT(t) }
type FIOTestSuite (line 24) | type FIOTestSuite struct
method TestRunner (line 28) | func (s *FIOTestSuite) TestRunner(c *C) {
method TestRunFioHelper (line 37) | func (s *FIOTestSuite) TestRunFioHelper(c *C) {
method TestStorageClassExists (line 362) | func (s *FIOTestSuite) TestStorageClassExists(c *C) {
method TestValidateNamespace (line 386) | func (s *FIOTestSuite) TestValidateNamespace(c *C) {
method TestValidateNodeSelector (line 400) | func (s *FIOTestSuite) TestValidateNodeSelector(c *C) {
method TestLoadConfigMap (line 450) | func (s *FIOTestSuite) TestLoadConfigMap(c *C) {
method TestCreatePVC (line 532) | func (s *FIOTestSuite) TestCreatePVC(c *C) {
method TestDeletePVC (line 584) | func (s *FIOTestSuite) TestDeletePVC(c *C) {
method TestCreatPod (line 597) | func (s *FIOTestSuite) TestCreatPod(c *C) {
method TestDeletePod (line 730) | func (s *FIOTestSuite) TestDeletePod(c *C) {
method TestFioTestFileName (line 743) | func (s *FIOTestSuite) TestFioTestFileName(c *C) {
method TestRunFioCommand (line 772) | func (s *FIOTestSuite) TestRunFioCommand(c *C) {
method TestDeleteConfigMap (line 852) | func (s *FIOTestSuite) TestDeleteConfigMap(c *C) {
method TestWaitForPodReady (line 923) | func (s *FIOTestSuite) TestWaitForPodReady(c *C) {
type fakeFioStepper (line 284) | type fakeFioStepper struct
method validateNamespace (line 316) | func (f *fakeFioStepper) validateNamespace(ctx context.Context, namesp...
method validateNodeSelector (line 320) | func (f *fakeFioStepper) validateNodeSelector(ctx context.Context, sel...
method storageClassExists (line 324) | func (f *fakeFioStepper) storageClassExists(ctx context.Context, stora...
method loadConfigMap (line 328) | func (f *fakeFioStepper) loadConfigMap(ctx context.Context, args *RunF...
method createPVC (line 332) | func (f *fakeFioStepper) createPVC(ctx context.Context, storageclass, ...
method deletePVC (line 338) | func (f *fakeFioStepper) deletePVC(ctx context.Context, pvcName, names...
method createPod (line 342) | func (f *fakeFioStepper) createPod(ctx context.Context, pvcName, confi...
method deletePod (line 349) | func (f *fakeFioStepper) deletePod(ctx context.Context, podName, names...
method runFIOCommand (line 353) | func (f *fakeFioStepper) runFIOCommand(ctx context.Context, podName, c...
method deleteConfigMap (line 357) | func (f *fakeFioStepper) deleteConfigMap(ctx context.Context, configMa...
type fakePodReadyChecker (line 941) | type fakePodReadyChecker struct
method waitForPodReady (line 945) | func (f *fakePodReadyChecker) waitForPodReady(ctx context.Context, nam...
type fakeKubeExecutor (line 949) | type fakeKubeExecutor struct
method exec (line 959) | func (fk *fakeKubeExecutor) exec(_ context.Context, namespace, podName...
FILE: pkg/fio/fio_types.go
type FioResult (line 5) | type FioResult struct
method Print (line 15) | func (f FioResult) Print() string {
type FioGlobalOptions (line 30) | type FioGlobalOptions struct
method Print (line 39) | func (g FioGlobalOptions) Print() string {
type FioJobs (line 43) | type FioJobs struct
method Print (line 72) | func (j FioJobs) Print() string {
type FioJobOptions (line 84) | type FioJobOptions struct
method Print (line 94) | func (o FioJobOptions) Print() string {
type FioStats (line 98) | type FioStats struct
method Print (line 124) | func (s FioStats) Print() string {
type FioNS (line 132) | type FioNS struct
type FioDepth (line 140) | type FioDepth struct
type FioLatency (line 152) | type FioLatency struct
type FioDiskUtil (line 167) | type FioDiskUtil struct
method Print (line 179) | func (d FioDiskUtil) Print() string {
FILE: pkg/fio/parsable_fio_output.go
constant parsableFioOutput (line 3) | parsableFioOutput = `{
FILE: pkg/kubestr/kubernetes_checks.go
constant MinK8sMajorVersion (line 14) | MinK8sMajorVersion = 1
constant MinK8sMinorVersion (line 16) | MinK8sMinorVersion = 12
constant MinK8sGitVersion (line 18) | MinK8sGitVersion = "v1.12.0"
constant RbacGroupName (line 20) | RbacGroupName = "rbac.authorization.k8s.io"
method KubernetesChecks (line 24) | func (p *Kubestr) KubernetesChecks() []*TestOutput {
method validateK8sVersion (line 33) | func (p *Kubestr) validateK8sVersion() *TestOutput {
method validateK8sVersionHelper (line 43) | func (p *Kubestr) validateK8sVersionHelper() (*version.Info, error) {
method validateRBAC (line 73) | func (p *Kubestr) validateRBAC() *TestOutput {
method validateRBACHelper (line 84) | func (p *Kubestr) validateRBACHelper() (*v1.APIGroup, error) {
method validateAggregatedLayer (line 97) | func (p *Kubestr) validateAggregatedLayer() *TestOutput {
method validateAggregatedLayerHelper (line 107) | func (p *Kubestr) validateAggregatedLayerHelper() (*v1.APIResourceList, ...
FILE: pkg/kubestr/kubernetes_checks_test.go
function Test (line 14) | func Test(t *testing.T) { TestingT(t) }
type K8sChecksTestSuite (line 16) | type K8sChecksTestSuite struct
method TestGetK8sVersion (line 20) | func (s *K8sChecksTestSuite) TestGetK8sVersion(c *C) {
method TestValidateRBAC (line 56) | func (s *K8sChecksTestSuite) TestValidateRBAC(c *C) {
method TestValidateAggregatedLayer (line 105) | func (s *K8sChecksTestSuite) TestValidateAggregatedLayer(c *C) {
FILE: pkg/kubestr/kubestr.go
type Kubestr (line 15) | type Kubestr struct
constant Logo (line 24) | Logo = `
function NewKubestr (line 41) | func NewKubestr() (*Kubestr, error) {
function LoadDynCli (line 64) | func LoadDynCli() (dynamic.Interface, error) {
function LoadKubeCli (line 78) | func LoadKubeCli() (kubernetes.Interface, error) {
FILE: pkg/kubestr/storage_provisioners.go
constant APIVersionKey (line 25) | APIVersionKey = "apiVersion"
constant FeatureGateTestPVCName (line 28) | FeatureGateTestPVCName = "kubestr-featuregate-test"
constant DefaultNS (line 30) | DefaultNS = "default"
constant PodNamespaceEnvKey (line 32) | PodNamespaceEnvKey = "POD_NAMESPACE"
type Provisioner (line 36) | type Provisioner struct
method Print (line 105) | func (v *Provisioner) Print() {
type CSIDriver (line 45) | type CSIDriver struct
method Provider (line 56) | func (c *CSIDriver) Provider() string {
method URL (line 65) | func (c *CSIDriver) URL() string {
method Print (line 78) | func (c *CSIDriver) Print(prefix string) {
method SupportsSnapshots (line 85) | func (c *CSIDriver) SupportsSnapshots() bool {
type SCInfo (line 90) | type SCInfo struct
type VSCInfo (line 97) | type VSCInfo struct
method ValidateProvisioners (line 163) | func (p *Kubestr) ValidateProvisioners(ctx context.Context) ([]*Provisio...
method processProvisioner (line 179) | func (p *Kubestr) processProvisioner(ctx context.Context, provisioner st...
method hasCSIDriverObject (line 233) | func (p *Kubestr) hasCSIDriverObject(ctx context.Context, provisioner st...
method isK8sVersionCSISnapshotCapable (line 246) | func (p *Kubestr) isK8sVersionCSISnapshotCapable(ctx context.Context) (b...
method validateStorageClass (line 266) | func (p *Kubestr) validateStorageClass(provisioner string, storageClass ...
method validateVolumeSnapshotClass (line 275) | func (p *Kubestr) validateVolumeSnapshotClass(vsc unstructured.Unstructu...
method provisionerList (line 293) | func (p *Kubestr) provisionerList(ctx context.Context) ([]string, error) {
method loadStorageClasses (line 305) | func (p *Kubestr) loadStorageClasses(ctx context.Context) (*sv1.StorageC...
method loadVolumeSnapshotClasses (line 316) | func (p *Kubestr) loadVolumeSnapshotClasses(ctx context.Context, version...
method getDriverNameFromUVSC (line 329) | func (p *Kubestr) getDriverNameFromUVSC(vsc unstructured.Unstructured, v...
method getCSIGroupVersion (line 347) | func (p *Kubestr) getCSIGroupVersion() *metav1.GroupVersionForDiscovery {
type snapshotDataSourceFG (line 360) | type snapshotDataSourceFG interface
type snapshotDataSourceFGValidator (line 364) | type snapshotDataSourceFGValidator struct
method validate (line 369) | func (s *snapshotDataSourceFGValidator) validate(ctx context.Context) ...
FILE: pkg/kubestr/storage_provisioners_test.go
type ProvisionerTestSuite (line 22) | type ProvisionerTestSuite struct
method TestHasCSIDriverObject (line 26) | func (s *ProvisionerTestSuite) TestHasCSIDriverObject(c *C) {
method TestIsK8sVersionCSISnapshotCapable (line 57) | func (s *ProvisionerTestSuite) TestIsK8sVersionCSISnapshotCapable(c *C) {
method TestValidateVolumeSnapshotClass (line 106) | func (s *ProvisionerTestSuite) TestValidateVolumeSnapshotClass(c *C) {
method TestLoadStorageClassesAndProvisioners (line 151) | func (s *ProvisionerTestSuite) TestLoadStorageClassesAndProvisioners(c...
method TestLoadVolumeSnaphsotClasses (line 175) | func (s *ProvisionerTestSuite) TestLoadVolumeSnaphsotClasses(c *C) {
method TestGetCSIGroupVersion (line 203) | func (s *ProvisionerTestSuite) TestGetCSIGroupVersion(c *C) {
method TestGetDriverNameFromUVSC (line 247) | func (s *ProvisionerTestSuite) TestGetDriverNameFromUVSC(c *C) {
type fakeSDSFGValidator (line 97) | type fakeSDSFGValidator struct
method validate (line 102) | func (f *fakeSDSFGValidator) validate(ctx context.Context) (bool, erro...
FILE: pkg/kubestr/utils.go
constant ErrorColor (line 10) | ErrorColor = "\033[1;31m%s\033[0m"
constant SuccessColor (line 12) | SuccessColor = "\033[1;32m%s\033[0m"
constant YellowColor (line 14) | YellowColor = "\033[1;33m%s\033[0m"
type Status (line 18) | type Status struct
method Print (line 39) | func (s *Status) Print(prefix string) {
type StatusCode (line 25) | type StatusCode
constant StatusOK (line 29) | StatusOK = StatusCode("OK")
constant StatusWarning (line 31) | StatusWarning = StatusCode("Warning")
constant StatusError (line 33) | StatusError = StatusCode("Error")
constant StatusInfo (line 35) | StatusInfo = StatusCode("Info")
function printErrorMessage (line 53) | func printErrorMessage(errorMesg string) {
function printSuccessMessage (line 60) | func printSuccessMessage(message string) {
function printSuccessColor (line 66) | func printSuccessColor(message string) {
function printInfoMessage (line 72) | func printInfoMessage(message string) {
function printWarningMessage (line 77) | func printWarningMessage(message string) {
type TestOutput (line 82) | type TestOutput struct
method Print (line 89) | func (t *TestOutput) Print() {
function MakeTestOutput (line 96) | func MakeTestOutput(testname string, code StatusCode, mesg string, raw i...
function makeStatus (line 104) | func makeStatus(code StatusCode, mesg string, raw interface{}) Status {
function convertSetToSlice (line 112) | func convertSetToSlice(set map[string]struct{}) []string {
function getPodNamespace (line 121) | func getPodNamespace() string {
Condensed preview — 67 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (625K chars).
[
{
"path": ".github/dependabot.yaml",
"chars": 1057,
"preview": "version: 2\nupdates:\n - package-ecosystem: gomod\n commit-message:\n prefix: \"deps(go):\"\n directory: \"/\"\n ig"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1320,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n - master\n tags:\n - v*\n\n pull_request:\n\npermissions:\n contents"
},
{
"path": ".github/workflows/dependency-review.yaml",
"chars": 970,
"preview": "# Dependency Review Action\n#\n# This workflow scans dependency manifest files that change as part of a pull\n# reqest, sur"
},
{
"path": ".github/workflows/docker-publish.yml",
"chars": 2189,
"preview": "name: Docker\n\npermissions:\n contents: read\n\non:\n push:\n branches:\n - main\n - master\n\n # Publish `v1.2.3` t"
},
{
"path": ".github/workflows/ossf-scorecard.yml",
"chars": 1840,
"preview": "name: OSSF Scorecard\non:\n # For Branch-Protection check. Only the default branch is supported. See\n # https://github.c"
},
{
"path": ".github/workflows/release.yaml",
"chars": 792,
"preview": "name: Release\n\npermissions:\n contents: read\n\non:\n release:\n types:\n - created\n - published\n\njobs:\n gorel"
},
{
"path": ".goreleaser.yml",
"chars": 818,
"preview": "# This is an example goreleaser.yaml file with some sane defaults.\n# Make sure to check the documentation at http://gore"
},
{
"path": "Dockerfile",
"chars": 599,
"preview": "ARG BUILDPLATFROM\n\nFROM --platform=$BUILDPLATFORM golang:1.26.1-bookworm@sha256:8e8aa801e8417ef0b5c42b504dd34db3db911bb7"
},
{
"path": "FIO.md",
"chars": 259,
"preview": "# FIO\n\n[](https://asciinema.org/a/D9EFwlEUVx787hayFap"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 2800,
"preview": "# Kubestr\n\n## What is it?\n\nKubestr is a collection of tools to discover, validate and evaluate your kubernetes storage o"
},
{
"path": "_config.yml",
"chars": 95,
"preview": "theme: jekyll-theme-cayman\ntitle: Kubestr\ndescription: Explore your kubernetes storage options\n"
},
{
"path": "_posts/2021-02-07-FasterStorage.md",
"chars": 123,
"preview": "---\nlayout: post\ntitle: \"Faster Storage\"\ndate: 2021-02-07 \npublished: true\ncategories: fio storage\n---\n\nSome content oth"
},
{
"path": "cmd/rootCmd.go",
"chars": 19699,
"preview": "// Copyright 2020 Kubestr Developers\n\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "docs/README.md",
"chars": 122,
"preview": "# Kubestr\n\nKubestr is a tool that qualifies the storage options present in a cluster. \nFor more options visit kubestr.i"
},
{
"path": "docs/_config.yml",
"chars": 26,
"preview": "theme: jekyll-theme-hacker"
},
{
"path": "extra/csi-drivers",
"chars": 31136,
"preview": "# Drivers\nThe following are a set of CSI driver which can be used with Kubernetes:\n\n> NOTE: If you would like your drive"
},
{
"path": "go.mod",
"chars": 5347,
"preview": "module github.com/kastenhq/kubestr\n\ngo 1.24\n\nreplace github.com/graymeta/stow => github.com/kastenhq/stow v0.1.2-kasten\n"
},
{
"path": "go.sum",
"chars": 63167,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1"
},
{
"path": "index.md",
"chars": 1847,
"preview": "# Kubestr\n\n## What is it?\n\nKubestr is a collection of tools to discover, validate and evaluate your kubernetes storage o"
},
{
"path": "main.go",
"chars": 856,
"preview": "// Copyright 2020 Kubestr Developers\n\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
},
{
"path": "pkg/block/block_mount.go",
"chars": 6884,
"preview": "package block\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\tkankube \"github.com/kanisterio/kanister/pkg/kube\"\n\t\"github.com/kanis"
},
{
"path": "pkg/block/block_mount_test.go",
"chars": 13582,
"preview": "package block\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\tqt \"github.com/frankban/quicktest\"\n\t\"github.com"
},
{
"path": "pkg/common/common.go",
"chars": 689,
"preview": "package common\n\nconst (\n\t// VolSnapClassDriverKey describes the driver key in VolumeSnapshotClass resource\n\tVolSnapClass"
},
{
"path": "pkg/csi/csi.go",
"chars": 224,
"preview": "package csi\n\nimport (\n\t\"context\"\n\n\t\"github.com/kastenhq/kubestr/pkg/csi/types\"\n)\n\ntype CSI interface {\n\tRunSnapshotResto"
},
{
"path": "pkg/csi/csi_ops.go",
"chars": 20574,
"preview": "package csi\n\n// This file contains general Kubernetes operations, not just CSI related operations.\n\nimport (\n\t\"context\"\n"
},
{
"path": "pkg/csi/csi_ops_test.go",
"chars": 33708,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tkansnapshot \"github.com/kanisterio/kanister/pkg/kube/snap"
},
{
"path": "pkg/csi/file_restore_inspector.go",
"chars": 12354,
"preview": "package csi\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/kastenhq/kubestr/pk"
},
{
"path": "pkg/csi/file_restore_inspector_steps_test.go",
"chars": 20543,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"k8s.io/apimachinery/pkg/api/resource\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"git"
},
{
"path": "pkg/csi/file_restore_inspector_test.go",
"chars": 6021,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/csi/mocks\"\n\t"
},
{
"path": "pkg/csi/mocks/mock_api_version_fetcher.go",
"chars": 1762,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ApiVersionFetcher"
},
{
"path": "pkg/csi/mocks/mock_application_creator.go",
"chars": 3611,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ApplicationCreato"
},
{
"path": "pkg/csi/mocks/mock_argument_validator.go",
"chars": 5476,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ArgumentValidator"
},
{
"path": "pkg/csi/mocks/mock_cleaner.go",
"chars": 2631,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: Cleaner)\n\n// Pack"
},
{
"path": "pkg/csi/mocks/mock_data_validator.go",
"chars": 1609,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: DataValidator)\n\n/"
},
{
"path": "pkg/csi/mocks/mock_file_restore_stepper.go",
"chars": 5061,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: FileRestoreSteppe"
},
{
"path": "pkg/csi/mocks/mock_kube_executor.go",
"chars": 1583,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: KubeExecutor)\n\n//"
},
{
"path": "pkg/csi/mocks/mock_port_forwarder.go",
"chars": 2163,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: PortForwarder)\n\n/"
},
{
"path": "pkg/csi/mocks/mock_pvc_browser_stepper.go",
"chars": 5351,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: PVCBrowserStepper"
},
{
"path": "pkg/csi/mocks/mock_snapshot_browser_stepper.go",
"chars": 4875,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotBrowserSt"
},
{
"path": "pkg/csi/mocks/mock_snapshot_creator.go",
"chars": 3291,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotCreator)\n"
},
{
"path": "pkg/csi/mocks/mock_snapshot_restore_stepper.go",
"chars": 5456,
"preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotRestoreSt"
},
{
"path": "pkg/csi/pvc_inspector.go",
"chars": 10762,
"preview": "package csi\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"runtime\"\n\t\"sync\"\n\t\"syscall\"\n\t\"ti"
},
{
"path": "pkg/csi/pvc_inspector_steps_test.go",
"chars": 21686,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/common\"\n\t\"gi"
},
{
"path": "pkg/csi/pvc_inspector_test.go",
"chars": 6465,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/csi/mocks\"\n\t"
},
{
"path": "pkg/csi/snapshot_inspector.go",
"chars": 9136,
"preview": "package csi\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/kastenhq/kubestr/pk"
},
{
"path": "pkg/csi/snapshot_inspector_steps_test.go",
"chars": 15957,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/common\"\n\t\"gi"
},
{
"path": "pkg/csi/snapshot_inspector_test.go",
"chars": 5739,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/csi/mocks\"\n\t"
},
{
"path": "pkg/csi/snapshot_restore.go",
"chars": 12278,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/kastenhq/kubestr/pkg/common\"\n\t\"github.com/kastenhq/kubestr"
},
{
"path": "pkg/csi/snapshot_restore_steps_test.go",
"chars": 27144,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/common\"\n\t\"gi"
},
{
"path": "pkg/csi/snapshot_restore_test.go",
"chars": 13656,
"preview": "package csi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/golang/mock/gomock\"\n\t\"github.com/kastenhq/kubestr/pkg/c"
},
{
"path": "pkg/csi/types/csi_types.go",
"chars": 5585,
"preview": "package types\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"time\"\n\n\tsnapv1 \"github.com/kubernetes-csi/external-snapshotter/client/v4/apis/"
},
{
"path": "pkg/fio/_config.yml",
"chars": 16,
"preview": "baseurl: \"/fio\"\n"
},
{
"path": "pkg/fio/dbench_license",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2018 LogDNA\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "pkg/fio/fio.go",
"chars": 13351,
"preview": "package fio\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/briandowns/spinner"
},
{
"path": "pkg/fio/fio_jobs.go",
"chars": 818,
"preview": "package fio\n\nvar fioJobs = map[string]string{\n\tDefaultFIOJob: testJob1,\n\t\"randrw\": randReadWrite,\n}\n\nvar testJob1 ="
},
{
"path": "pkg/fio/fio_test.go",
"chars": 24853,
"preview": "package fio\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/kastenhq/kubestr/pkg/common\"\n\t\"g"
},
{
"path": "pkg/fio/fio_types.go",
"chars": 7284,
"preview": "package fio\n\nimport \"fmt\"\n\ntype FioResult struct {\n\tFioVersion string `json:\"fio version,omitempty\"`\n\tTimes"
},
{
"path": "pkg/fio/parsable_fio_output.go",
"chars": 9258,
"preview": "package fio\n\nconst parsableFioOutput = `{\n\t\"fio version\" : \"fio-3.20\",\n\t\"timestamp\" : 1611952282,\n\t\"timestamp_ms\" : 1611"
},
{
"path": "pkg/kubestr/csi-drivers.go",
"chars": 41687,
"preview": "package kubestr\n\n// THIS FILE IS AUTO_GENERATED.\n// To generate file run \"go generate\" at the top level\n// This file mus"
},
{
"path": "pkg/kubestr/kubernetes_checks.go",
"chars": 3838,
"preview": "package kubestr\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/pkg/errors\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\tversio"
},
{
"path": "pkg/kubestr/kubernetes_checks_test.go",
"chars": 3704,
"preview": "package kubestr\n\nimport (\n\t\"testing\"\n\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\tversion \"k8s.io/apimachinery/pkg/v"
},
{
"path": "pkg/kubestr/kubestr.go",
"chars": 2317,
"preview": "package kubestr\n\nimport (\n\t\"github.com/kanisterio/kanister/pkg/kube\"\n\t\"github.com/kastenhq/kubestr/pkg/fio\"\n\t\"github.com"
},
{
"path": "pkg/kubestr/storage_provisioners.go",
"chars": 12782,
"preview": "package kubestr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\tkanvolume \"github.com/kanisterio/kanister/"
},
{
"path": "pkg/kubestr/storage_provisioners_test.go",
"chars": 8364,
"preview": "package kubestr\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tkansnapshot \"github.com/kanisterio/kanister/pkg/kube/snapshot\"\n\t. \"gopkg.i"
},
{
"path": "pkg/kubestr/utils.go",
"chars": 2906,
"preview": "package kubestr\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nconst (\n\t// ErrorColor formatted color red\n\tErrorColor = \"\\033[1;31m%s\\033[0m\""
},
{
"path": "scripts/load_csi_provisioners.sh",
"chars": 2629,
"preview": "#!/usr/bin/env bash\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nCLEANSED_STR=\"\"\ncleanse_str() {\n case \"$1\" in\n \""
}
]
About this extraction
This page contains the full source code of the kastenhq/kubestr GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 67 files (553.9 KB), approximately 179.9k tokens, and a symbol index with 512 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.