Full Code of kastenhq/kubestr for AI

master e70331629cb5 cached
67 files
553.9 KB
179.9k tokens
512 symbols
1 requests
Download .txt
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

[![asciicast](https://asciinema.org/a/D9EFwlEUVx787hayFapdHljBW.svg)](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.

[![asciicast](https://asciinema.org/a/7iJTbWKwdhPHNWYV00LIgx7gn.svg)](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:  
Download .txt
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
Download .txt
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[![asciicast](https://asciinema.org/a/D9EFwlEUVx787hayFapdHljBW.svg)](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.

Copied to clipboard!