├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── build-test.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── COPYING-LGPLV3 ├── README.md ├── build.sh ├── cmd └── glusterfs │ ├── main.go │ └── main_suite_test.go ├── docs ├── ci-infrastructure.md └── releases.md ├── examples └── kubernetes │ └── glusterfs │ ├── app-with-clone-pvc.yaml │ ├── app.yaml │ ├── csi-deployment.yaml │ ├── glusterd-ds.yaml │ ├── glusterfs-lite-pvc.yaml │ ├── glusterfs-lite-storage-class.yaml │ ├── pvc-restore.yaml │ ├── pvc.yaml │ ├── snapshot-class.yaml │ ├── storage-class.yaml │ ├── thin-arbiter-pod.yaml │ ├── thin-arbiter-pvc.yaml │ ├── thin-arbiter-storageclass.yaml │ └── volume-snapshot.yaml ├── extras └── centos-ci.sh ├── go.mod ├── go.sum ├── pkg ├── glusterfs │ ├── Dockerfile │ ├── glusterfs.go │ ├── glusterfs_test.go │ ├── identityserver.go │ ├── identityserver_test.go │ ├── nodeserver.go │ ├── server.go │ ├── utils.go │ └── version.go └── utils │ └── common-utils.go └── scripts ├── install-go-tools.sh ├── lint-go.sh ├── lint-text.sh ├── mdl-style.rb └── test-go.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | vendor 3 | profile.cov 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **Steps to reproduce** 11 | Steps to reproduce the behavior: 12 | 13 | 1. Go to '...' 14 | 1. Click on '....' 15 | 1. Scroll down to '....' 16 | 1. See error 17 | 18 | **Actual results** 19 | Describe what happened 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Describe the feature you'd like to have.** 8 | What new functionality do you want? 9 | 10 | **What is the value to the end user? (why is it a priority?)** 11 | How would the end user gain value from having this feature? 12 | 13 | **How will we know we have a good solution? (acceptance criteria)** 14 | Add a list of criteria that should be met for this feature to be useful 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Describe what this PR does** 2 | Provide some context for the reviewer 3 | 4 | **Is there anything that requires special attention?** 5 | Do you have any questions? Did you do something clever? 6 | 7 | **Related issues:** 8 | Mention any github issues relevant to this PR 9 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: build-test 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | - 'release-**' 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout source code 17 | uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # pin@v3.2.0 18 | - name: Setup Go 19 | uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@3.5.0 20 | with: 21 | go-version: '1.21' 22 | cache: false 23 | - name: golangci-lint 24 | uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 25 | with: 26 | version: v1.54 27 | - name: Install ginkgo 28 | run: go install github.com/onsi/ginkgo/v2/ginkgo@v2.12.0 29 | - name: unit tests 30 | run: ginkgo ./... -race -v 31 | - name: Test build 32 | run: CGO_ENABLED=0 go build -trimpath ./cmd/glusterfs 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # go coverage data 2 | profile.cov 3 | 4 | # Remove any tarfiles in the repo 5 | *.gz 6 | 7 | # We are not keeping vendor in the repo 8 | vendor 9 | 10 | # Ignore patches. 11 | 12 | *.patch 13 | 14 | 15 | # OSX leaves these everywhere on SMB shares 16 | ._* 17 | 18 | # OSX trash 19 | .DS_Store 20 | 21 | # Eclipse files 22 | .classpath 23 | .project 24 | .settings/** 25 | 26 | # Files generated by JetBrains IDEs, e.g. IntelliJ IDEA 27 | .idea/ 28 | *.iml 29 | 30 | # Vscode files 31 | .vscode 32 | 33 | 34 | # Emacs save files 35 | *~ 36 | \#*\# 37 | .\#* 38 | 39 | # Vim-related files 40 | [._]*.s[a-w][a-z] 41 | [._]s[a-w][a-z] 42 | *.un~ 43 | Session.vim 44 | .netrwhist 45 | 46 | # cscope-related files 47 | cscope.* 48 | 49 | # Go test binaries 50 | *.test 51 | 52 | # JUnit test output from ginkgo e2e tests 53 | /junit*.xml 54 | 55 | 56 | # Vagrant 57 | .vagrant 58 | 59 | # direnv .envrc files 60 | .envrc 61 | 62 | # make-related metadata 63 | /.make/ 64 | 65 | # Godeps workspace 66 | /Godeps/_workspace 67 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: true 3 | 4 | addons: 5 | apt: 6 | packages: 7 | - docker-ce 8 | - python 9 | - realpath 10 | - ruby 11 | 12 | jobs: 13 | include: 14 | - name: Linter 15 | install: 16 | - gem install asciidoctor mdl 17 | - pip install --user --upgrade pip 18 | - pip install --user yamllint 19 | script: 20 | - scripts/lint-text.sh --require-all 21 | - name: glusterfs-csi-driver 22 | script: 23 | - ./build.sh glusterfs 24 | deploy: 25 | # Master branch will push the container to :latest 26 | - provider: script 27 | on: # yamllint disable-line rule:truthy 28 | branch: master 29 | script: >- 30 | .travis/push_container.sh 31 | gluster/glusterfs-csi-driver 32 | verbatim latest 33 | # Tags of the form v + SEMVER (e.g., v1.2.3) will push to the 34 | # corresponding container version number (e.g., :1.2.3). 35 | - provider: script 36 | on: # yamllint disable-line rule:truthy 37 | tags: true 38 | condition: $TRAVIS_TAG =~ ^v[0-9]+ 39 | script: >- 40 | .travis/push_container.sh 41 | gluster/glusterfs-csi-driver 42 | version "$TRAVIS_TAG" 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.0.0-pre.0 4 | 5 | * Support for CSI 1.0.0. 6 | * Kube 1.13.1 support. 7 | * Added support for Replica Volume Type. 8 | * Many build enhancements. 9 | * Log level support for CSI driver deployment. 10 | * Make gluster rest timeout configurable. 11 | * Many other bug fixes and code enhancements. 12 | Madhu Rajanna 13 | 14 | ## v0.0.9 15 | 16 | * Added snapshot functionality. 17 | * Replaced volumestatus api with volumeinfo. 18 | * Fully contenarized build. 19 | * Updated vendor to point to k8s stable release. 20 | * Other build enhancements. 21 | Humble Chirammal 22 | 23 | ## v0.0.8 24 | 25 | * Address review comments. 26 | Madhu Rajanna 27 | 28 | ## v0.0.7 29 | 30 | * Address review comments. 31 | Humble Chirammal 32 | 33 | ## v0.0.6 34 | 35 | * Update deployment artifacts to v0.0.6 tag. 36 | Humble Chirammal 37 | 38 | ## v0.0.1 39 | 40 | * Initial drop of GlusterFS CSI driver with GD2 API. 41 | Humble Chirammal 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Want to help the gluster-csi-driver project? This document covers some of the 4 | policies and preferences contributors to the project need to 5 | know about. 6 | 7 | - [The Basics](#the-basics) 8 | - [Contributor's Workflow](#contributors-workflow) 9 | 10 | ## The Basics 11 | 12 | ### New to Go programming 13 | 14 | gluster-csi-driver is primarily written in Go and if you are new to the 15 | language, it is *highly* encouraged you take [A Tour of 16 | Go](http://tour.golang.org/welcome/1). 17 | 18 | ### New to GitHub 19 | 20 | If you are new to the GitHub process, please see 21 | [Understanding the GitHub flow](https://guides.github.com/introduction/flow/index.html). 22 | 23 | ### Getting Started 24 | 25 | 1. Fork the gluster-csi-project GitHub project 26 | 1. Download latest Go to your system 27 | 1. Setup your 28 | [GOPATH](http://www.g33knotes.org/2014/07/60-second-count-down-to-go.html) 29 | environment 30 | 1. Type: `mkdir -p $GOPATH/src/github.com/gluster` 31 | 1. Type: `cd $GOPATH/src/github.com/gluster` 32 | 1. Type: `git clone https://github.com/gluster/gluster-csi-driver.git` 33 | 1. Type: `cd gluster-csi-driver` 34 | 35 | Now you need to setup your repo where you will be pushing your changes into: 36 | 37 | 1. `git remote add ` 38 | 1. `git fetch ` 39 | 40 | Where `` is a remote name of your choosing and `` 41 | is a git URL that you can both pull from and push to. 42 | 43 | For example if you called your remote "github", you can verify your 44 | configuration like so: 45 | 46 | ```sh 47 | $ git remote -v 48 | github git@github.com:jdoe1234/gluster-csi-driver.git (fetch) 49 | github git@github.com:jdoe1234/gluster-csi-driver.git (push) 50 | origin https://gluster/gluster-csi-driver (fetch) 51 | origin https://gluster/gluster-csi-driver (push) 52 | ``` 53 | 54 | ### Building and Testing 55 | 56 | To build the gluster-csi-driver, type `make` 57 | from the top of the gluster-csi-driver source tree. 58 | 59 | gluster-csi-driver comes with a suite of module and unit tests. To run the 60 | suite of code quality checks, unit tests run `make test` from the top of the 61 | gluster-csi-driver source tree. 62 | 63 | ## Contributor's Workflow 64 | 65 | Here is a guide on how to work on a new patch and get it included 66 | in the official gluster-csi-driver sources. 67 | 68 | ### Preparatory work 69 | 70 | Before you start working on a change, you should check the existing 71 | issues and pull requests for related content. Maybe someone has 72 | already done some analysis or even started a patch for your topic... 73 | 74 | ### Working on the code and creating patches 75 | 76 | In this example, we will work on a patch called *hellopatch*: 77 | 78 | 1. `git checkout master` 79 | 1. `git pull` 80 | 1. `git checkout -b hellopatch` 81 | 82 | Do your work here and then commit it. For example, run `git commit -as`, 83 | to automatically include all your outstanding changes into a new 84 | patch. 85 | 86 | #### Splitting your change into commits 87 | 88 | Generally, you will not just commit all your changes into a single 89 | patch but split them up into multiple commits. It is perfectly 90 | okay to have multiple patches in one pull request to achieve 91 | the higher level goal of the pull request. (For example one patch 92 | fix a bug and one patch to add a regression test.) 93 | 94 | You can use `git add -i` to select which hunks of your change to 95 | commit. `git rebase -i` can be used to polish up a sequence of 96 | work-in-progress patches into a sequence of patches of merge quality. 97 | 98 | gluster-csi-driver's guidelines for the contents of commits are: 99 | 100 | - Commits should usually be as minimal and atomic as possible. 101 | - I.e. a patch should only contain one logical change but achieve it completely. 102 | - If the commit does X and Y, you should probably split it into two patches. 103 | - Each patch should compile and pass `make test` 104 | 105 | #### Good commit messages 106 | 107 | Each commit has a commit message. The gluster-csi-driver project prefers 108 | commit messages roughly of the following form: 109 | 110 | ``` 111 | component(or topic)[:component]: Short description of what the patch does 112 | 113 | Optionally longer explanation of the why and how. 114 | 115 | Signed-off-by: Author Name 116 | ``` 117 | 118 | #### Linking to issues 119 | 120 | If you are working on an existing issue you should make sure to use 121 | the appropriate [keywords](https://help.github.com/articles/closing-issues-via-commit-messages/) 122 | in your commit message (e.g. `Fixes #`). 123 | Doing so will allow GitHub to automatically 124 | create references between your changes and the issue. 125 | 126 | ### Testing the Change 127 | 128 | Each pull request needs to pass the basic test suite in order 129 | to qualify for merging. It is hence highly recommended that you 130 | run at least the basic test suite on your branch, preferably 131 | even on each individual commit and make sure it passes 132 | before submitting your changes for review. 133 | 134 | #### Basic Tests 135 | 136 | As mentioned in the section above gluster-csi-driver has a suite of quality 137 | checks and unit tests that should always be run before submitting a change to 138 | the project. The simplest way to run this suite is to run `make test` in the 139 | top of the source tree. 140 | 141 | Sometimes it may not make sense to run the entire suite, especially if you 142 | are iterating on changes in a narrow area of the code. In this case, you 143 | can execute the [Go language test tool](https://golang.org/cmd/go/#hdr-Test_packages) 144 | directly. When using `go test` you can specify a package (sub-directory) 145 | and the tool will only run tests in that directory. For example: 146 | 147 | ``` 148 | go test -v github.com/gluster/gluster-csi-driver/pkg/glusterfs 149 | ``` 150 | 151 | ### Pull Requests 152 | 153 | Once you are satisfied with your changes you can push them to your 154 | gluster-csi-driver fork on GitHub. For example, `git push github hellopatch` 155 | will push the contents of your hellopatch branch to your fork. 156 | 157 | Now that the patch or patches are available on GitHub you can use the GitHub 158 | interface to create a pull request (PR). If you are submitting a single patch 159 | GitHub will automatically populate the PR description with the content of 160 | the change's commit message. Otherwise provide a brief summary of your 161 | changes and complete the PR. 162 | 163 | Usually, a PR should concentrate on one topic like a fix for a 164 | bug, or the implementation of a feature. This can be achieved 165 | with multiple commits in the patchset for this PR, but if your 166 | patchset accomplishes multiple independent things, you should 167 | probably split it up and create multiple PRs. 168 | 169 | *NOTE*: The PR description is not a replacement for writing good commit messages. 170 | Remember that your commit messages may be needed by someone in the future 171 | who is trying to learn why a particular change was made. 172 | 173 | ### Patch Review 174 | 175 | Now other developers involved in the project will provide feedback on your 176 | PR. If a maintainer decides the changes are good as-is they will merge 177 | the PR into the main gluster-csi-driver repository. However, it is likely that some 178 | discussion will occur and changes may be requested. 179 | 180 | ### Iterating on Feedback 181 | 182 | You will need to return to your local clone and work through the changes 183 | as requested. You may end up with multiple changes across multiple commits. 184 | The gluster-csi-driver project developers prefer a linear history where each 185 | change is a clear logical unit. This means you will generally expect to rebase 186 | your changes. 187 | 188 | Run `git rebase -i master` and you will be presented with output something 189 | like the following: 190 | 191 | ``` 192 | pick e405b76 my first change 193 | pick ac78522 my second change 194 | ... 195 | pick bf34223 my twentythird change 196 | 197 | # Rebase d03eaf9..bf34223 onto d03eaf9 198 | # 199 | # Commands: 200 | # p, pick = use commit 201 | # r, reword = use commit, but edit the commit message 202 | # e, edit = use commit, but stop for amending 203 | # s, squash = use commit, but meld into previous commit 204 | # f, fixup = like "squash", but discard this commit's log message 205 | # x, exec = run command (the rest of the line) using shell 206 | ... 207 | ``` 208 | 209 | What you do here is highly dependent on what changes were requested but let's 210 | imagine you need to combine the change "my twentythird change" with 211 | "my first change". In that case you could alter the file such that it 212 | looks like: 213 | 214 | ``` 215 | pick e405b76 my first change 216 | fixup bf34223 my twentythird change 217 | pick ac78522 my second change 218 | ... 219 | ``` 220 | 221 | This will instruct git to combine the two changes, throwing away the 222 | latter change's commit message. 223 | 224 | Once done you need to re-submit your branch to GitHub. Since you've altered 225 | the existing content of the branch you will likely need to force push, 226 | like so: `git push -f github hellopatch`. 227 | 228 | After iterating on the feedback you have got from other developers the 229 | maintainers may accept your patch. It will be merged into the project 230 | and you can delete your branch. Congratulations! 231 | -------------------------------------------------------------------------------- /COPYING-LGPLV3: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gluster-csi-driver 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/gluster/gluster-csi-driver)](https://goreportcard.com/report/github.com/gluster/gluster-csi-driver) 4 | [![Build Status](https://ci.centos.org/view/Gluster/job/gluster_csi-driver-master/badge/icon)](https://ci.centos.org/view/Gluster/job/gluster_csi-driver-master/) 5 | 6 | This repo contains CSI driver for Gluster. The Container Storage Interface 7 | (CSI) is a proposed new industry standard for cluster-wide volume plugins. 8 | “Container Storage Interface” (CSI) enables storage vendors (SP) to develop a 9 | plugin once and have it work across a number of container orchestration (CO) 10 | systems. 11 | 12 | ## Demo of GlusterFS CSI driver to create and delete volumes on GD2 Cluster 13 | 14 | [![GlusterFS CSI driver Demo](https://asciinema.org/a/195024.png)](https://asciinema.org/a/195024) 15 | 16 | ## Building GlusterFS CSI driver 17 | 18 | This repository contains the source and a Dockerfile to build the GlusterFS CSI 19 | driver. The driver is built as a multi-stage container build. This requires a 20 | relatively recent version of Docker or Buildah. 21 | 22 | Docker packages can be obtained for 23 | [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/), 24 | [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/) or other 25 | distributions. 26 | 27 | To build, ensure docker is installed, and run: 28 | 29 | 1. Get inside the repository directory 30 | 31 | ``` 32 | [root@localhost]# cd gluster-csi-driver 33 | ``` 34 | 35 | 1. Build the glusterfs-csi-driver container 36 | 37 | ``` 38 | [root@localhost]# ./build.sh 39 | ``` 40 | 41 | ## Testing GlusterFS CSI driver 42 | 43 | ### Deploy kubernetes Cluster 44 | 45 | ### Deploy a GD2 gluster cluster 46 | 47 | ### Create a glusterfs storage class (RWX) 48 | 49 | ``` 50 | [root@localhost]# cat storage-class.yaml 51 | apiVersion: storage.k8s.io/v1 52 | kind: StorageClass 53 | metadata: 54 | name: glusterfs-csi 55 | annotations: 56 | storageclass.kubernetes.io/is-default-class: "true" 57 | provisioner: org.gluster.glusterfs 58 | ``` 59 | 60 | ``` 61 | [root@localhost]# kubectl create -f storage-class.yaml 62 | storageclass.storage.k8s.io/glusterfs-csi created 63 | ``` 64 | 65 | Verify glusterfs storage class (RWX) 66 | 67 | ``` 68 | [root@localhost]# kubectl get storageclass 69 | NAME PROVISIONER AGE 70 | glusterfs-csi (default) org.gluster.glusterfs 105s 71 | [root@localhost]# kubectl describe storageclass/glusterfs-csi 72 | Name: glusterfs-csi 73 | IsDefaultClass: Yes 74 | Annotations: storageclass.kubernetes.io/is-default-class=true 75 | Provisioner: org.gluster.glusterfs 76 | Parameters: 77 | AllowVolumeExpansion: 78 | MountOptions: 79 | ReclaimPolicy: Delete 80 | VolumeBindingMode: Immediate 81 | Events: 82 | ``` 83 | 84 | ### Create RWX PersistentVolumeClaim 85 | 86 | ``` 87 | [root@localhost]# cat pvc.yaml 88 | --- 89 | kind: PersistentVolumeClaim 90 | apiVersion: v1 91 | metadata: 92 | name: glusterfs-csi-pv 93 | spec: 94 | storageClassName: glusterfs-csi 95 | accessModes: 96 | - ReadWriteMany 97 | resources: 98 | requests: 99 | storage: 5Gi 100 | 101 | [root@localhost]# kubectl create -f pvc.yaml 102 | persistentvolumeclaim/glusterfs-csi-pv created 103 | ``` 104 | 105 | Validate the RWX claim creation 106 | 107 | ``` 108 | [root@localhost]# kubectl get pvc 109 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 110 | glusterfs-csi-pv Bound pvc-953d21f5a51311e8 5Gi RWX glusterfs-csi 3s 111 | ``` 112 | 113 | ``` 114 | [root@localhost]# kubectl describe pvc 115 | Name: glusterfs-csi-pv 116 | Namespace: default 117 | StorageClass: glusterfs-csi 118 | Status: Bound 119 | Volume: pvc-953d21f5a51311e8 120 | Labels: 121 | Annotations: control-plane.alpha.kubernetes.io/leader={"holderIdentity":"874a6cc9-a511-11e8-bae2-0a580af40202","leaseDurationSeconds":15,"acquireTime":"2018-08-21T07:26:58Z","renewTime":"2018-08-21T07:27:00Z","lea... 122 | pv.kubernetes.io/bind-completed=yes 123 | pv.kubernetes.io/bound-by-controller=yes 124 | storageClassName=glusterfs-csi 125 | volume.kubernetes.io/storage-provisioner=org.gluster.glusterfs 126 | Finalizers: [kubernetes.io/pvc-protection] 127 | Capacity: 5Gi 128 | Access Modes: RWX 129 | Events: 130 | Type Reason Age From Message 131 | ---- ------ ---- ---- ------- 132 | Normal ExternalProvisioning 30s (x2 over 30s) persistentvolume-controller waiting for a volume to be created, either by external provisioner "org.gluster.glusterfs" or manually created by system administrator 133 | Normal Provisioning 30s org.gluster.glusterfs csi-provisioner-glusterfsplugin-0 874a6cc9-a511-11e8-bae2-0a580af40202 External provisioner is provisioning volume for claim "default/glusterfs-csi-pv" 134 | Normal ProvisioningSucceeded 29s org.gluster.glusterfs csi-provisioner-glusterfsplugin-0 874a6cc9-a511-11e8-bae2-0a580af40202 Successfully provisioned volume pvc-953d21f5a51311e8 135 | ``` 136 | 137 | Verify PV details: 138 | 139 | ``` 140 | [root@localhost]# kubectl describe pv 141 | Name: pvc-953d21f5a51311e8 142 | Labels: 143 | Annotations: pv.kubernetes.io/provisioned-by=org.gluster.glusterfs 144 | Finalizers: [kubernetes.io/pv-protection] 145 | StorageClass: glusterfs-csi 146 | Status: Bound 147 | Claim: default/glusterfs-csi-pv 148 | Reclaim Policy: Delete 149 | Access Modes: RWX 150 | Capacity: 5Gi 151 | Node Affinity: 152 | Message: 153 | Source: 154 | Type: CSI (a Container Storage Interface (CSI) volume source) 155 | Driver: org.gluster.glusterfs 156 | VolumeHandle: pvc-953d21f5a51311e8 157 | ReadOnly: false 158 | Events: 159 | ``` 160 | 161 | ### Create a pod with RWX pvc claim 162 | 163 | ``` 164 | [root@localhost]# cat app.yaml 165 | --- 166 | apiVersion: v1 167 | kind: Pod 168 | metadata: 169 | name: gluster 170 | labels: 171 | name: gluster 172 | spec: 173 | containers: 174 | - name: gluster 175 | image: redis 176 | imagePullPolicy: IfNotPresent 177 | volumeMounts: 178 | - mountPath: "/mnt/gluster" 179 | name: glustercsivol 180 | volumes: 181 | - name: glustercsivol 182 | persistentVolumeClaim: 183 | claimName: glusterfs-csi-pv 184 | 185 | [root@localhost]# kubectl create -f app.yaml 186 | ``` 187 | 188 | Check mount output and validate. 189 | 190 | ``` 191 | [root@localhost]# mount |grep glusterfs 192 | 192.168.121.158:pvc-953d21f5a51311e8 on /var/lib/kubelet/pods/2a563343-a514-11e8-a324-525400a04cb4/volumes/kubernetes.io~csi/pvc-953d21f5a51311e8/mount type fuse.glusterfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other,max_read=131072) 193 | 194 | [root@localhost]# kubectl delete pod gluster 195 | pod "gluster" deleted 196 | [root@localhost]# mount |grep glusterfs 197 | [root@localhost]# 198 | ``` 199 | 200 | ### Support for Snapshot 201 | 202 | Kubernetes v1.12 introduces alpha support for volume snapshotting. 203 | This feature allows creating/deleting volume snapshots, and the ability 204 | to create new volumes from a snapshot natively using the Kubernetes API. 205 | 206 | To verify clone functionality work as intended, 207 | lets start with writing some data into already created application with PVC. 208 | 209 | ``` 210 | [root@localhost]# kubectl exec -it redis /bin/bash 211 | root@redis:/data# cd /mnt/gluster/ 212 | root@redis:/mnt/gluster# echo "glusterfs csi clone test" > clone_data 213 | ``` 214 | 215 | ### Create a snapshot class 216 | 217 | ``` 218 | [root@localhost]# cat snapshot-class.yaml 219 | --- 220 | apiVersion: snapshot.storage.k8s.io/v1alpha1 221 | kind: VolumeSnapshotClass 222 | metadata: 223 | name: glusterfs-csi-snap 224 | snapshotter: org.gluster.glusterfs 225 | ``` 226 | 227 | ``` 228 | [root@localhost]# kubectl create -f snapshot-class.yaml 229 | volumesnapshotclass.snapshot.storage.k8s.io/glusterfs-csi-snap created 230 | ``` 231 | 232 | Verify snapshot class 233 | 234 | ``` 235 | [root@localhost]# kubectl get volumesnapshotclass 236 | NAME AGE 237 | glusterfs-csi-snap 1h 238 | [root@localhost]# kubectl describe volumesnapshotclass/glusterfs-csi-snap 239 | Name: glusterfs-csi-snap 240 | Namespace: 241 | Labels: 242 | Annotations: 243 | API Version: snapshot.storage.k8s.io/v1alpha1 244 | Kind: VolumeSnapshotClass 245 | Metadata: 246 | Creation Timestamp: 2018-10-24T04:57:34Z 247 | Generation: 1 248 | Resource Version: 3215 249 | Self Link: /apis/snapshot.storage.k8s.io/v1alpha1/volumesnapshotclasses/glusterfs-csi-snap 250 | UID: 51de83df-d749-11e8-892a-525400d84c47 251 | Snapshotter: org.gluster.glusterfs 252 | Events: 253 | ``` 254 | 255 | ### Create a snapshot from RWX pvc 256 | 257 | ``` 258 | [root@localhost]# cat volume-snapshot.yaml 259 | --- 260 | apiVersion: snapshot.storage.k8s.io/v1alpha1 261 | kind: VolumeSnapshot 262 | metadata: 263 | name: glusterfs-csi-ss 264 | spec: 265 | snapshotClassName: glusterfs-csi-ss 266 | source: 267 | name: glusterfs-csi-pv 268 | kind: PersistentVolumeClaim 269 | 270 | ``` 271 | 272 | ``` 273 | [root@localhost]# kubectl create -f volume-snapshot.yaml 274 | volumesnapshot.snapshot.storage.k8s.io/glusterfs-csi-ss created 275 | ``` 276 | 277 | Verify volume snapshot 278 | 279 | ``` 280 | [root@localhost]# kubectl get volumesnapshot 281 | NAME AGE 282 | glusterfs-csi-ss 13s 283 | [root@localhost]# kubectl describe volumesnapshot/glusterfs-csi-ss 284 | Name: glusterfs-csi-ss 285 | Namespace: default 286 | Labels: 287 | Annotations: 288 | API Version: snapshot.storage.k8s.io/v1alpha1 289 | Kind: VolumeSnapshot 290 | Metadata: 291 | Creation Timestamp: 2018-10-24T06:39:35Z 292 | Generation: 1 293 | Resource Version: 12567 294 | Self Link: /apis/snapshot.storage.k8s.io/v1alpha1/namespaces/default/volumesnapshots/glusterfs-csi-ss 295 | UID: 929722b7-d757-11e8-892a-525400d84c47 296 | Spec: 297 | Snapshot Class Name: glusterfs-csi-snap 298 | Snapshot Content Name: snapcontent-929722b7-d757-11e8-892a-525400d84c47 299 | Source: 300 | Kind: PersistentVolumeClaim 301 | Name: glusterfs-csi-pv 302 | Status: 303 | Creation Time: 1970-01-01T00:00:01Z 304 | Ready: true 305 | Restore Size: 306 | Events: 307 | ``` 308 | 309 | ### Provision new volume from snapshot 310 | 311 | ``` 312 | [root@localhost]# cat pvc-restore.yaml 313 | --- 314 | apiVersion: v1 315 | kind: PersistentVolumeClaim 316 | metadata: 317 | name: glusterfs-pv-restore 318 | spec: 319 | storageClassName: glusterfs-csi 320 | dataSource: 321 | name: glusterfs-csi-ss 322 | kind: VolumeSnapshot 323 | apiGroup: snapshot.storage.k8s.io 324 | accessModes: 325 | - ReadWriteMany 326 | resources: 327 | requests: 328 | storage: 5Gi 329 | ``` 330 | 331 | ``` 332 | [root@localhost]# kubectl create -f pvc-restore.yaml 333 | persistentvolumeclaim/glusterfs-pv-restore created 334 | ``` 335 | 336 | Verify newly created claim 337 | 338 | ``` 339 | [root@localhost]# kubectl get pvc 340 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 341 | glusterfs-csi-pv Bound pvc-712278b0-d749-11e8-892a-525400d84c47 5Gi RWX glusterfs-csi 103m 342 | glusterfs-pv-restore Bound pvc-dfcc36f0-d757-11e8-892a-525400d84c47 5Gi RWO glusterfs-csi 14s 343 | ``` 344 | 345 | ``` 346 | [root@localhost]# kubectl describe pvc/glusterfs-pv-restore 347 | Name: glusterfs-pv-restore 348 | Namespace: default 349 | StorageClass: glusterfs-csi 350 | Status: Bound 351 | Volume: pvc-dfcc36f0-d757-11e8-892a-525400d84c47 352 | Labels: 353 | Annotations: pv.kubernetes.io/bind-completed: yes 354 | pv.kubernetes.io/bound-by-controller: yes 355 | volume.kubernetes.io/storage-provisioner: org.gluster.glusterfs 356 | Finalizers: [kubernetes.io/pvc-protection] 357 | Capacity: 5Gi 358 | Access Modes: RWO 359 | Events: 360 | Type Reason Age From Message 361 | ---- ------ ---- ---- ------- 362 | Normal ExternalProvisioning 41s persistentvolume-controller waiting for a volume to be created, either by external provisioner "org.gluster.glusterfs" or manually created by system administrator 363 | Normal Provisioning 41s org.gluster.glusterfs_csi-provisioner-glusterfsplugin-0_1e7821cb-d749-11e8-9935-0a580af40303 External provisioner is provisioning volume for claim "default/glusterfs-pv-restore" 364 | Normal ProvisioningSucceeded 41s org.gluster.glusterfs_csi-provisioner-glusterfsplugin-0_1e7821cb-d749-11e8-9935-0a580af40303 Successfully provisioned volume pvc-dfcc36f0-d757-11e8-892a-525400d84c47 365 | Mounted By: 366 | ``` 367 | 368 | ### Create an app with New claim 369 | 370 | ``` 371 | [root@localhost]# cat app-with-clone.yaml 372 | --- 373 | apiVersion: v1 374 | kind: Pod 375 | metadata: 376 | name: redis-pvc-restore 377 | labels: 378 | name: redis-pvc-restore 379 | spec: 380 | containers: 381 | - name: redis-pvc-restore 382 | image: redis:latest 383 | imagePullPolicy: IfNotPresent 384 | volumeMounts: 385 | - mountPath: "/mnt/gluster" 386 | name: glusterfscsivol 387 | volumes: 388 | - name: glusterfscsivol 389 | persistentVolumeClaim: 390 | claimName: glusterfs-pv-restore 391 | ``` 392 | 393 | ``` 394 | [root@localhost]# kubectl create -f app-with-clone.yaml 395 | pod/redis-pvc-restore created 396 | ``` 397 | 398 | Verify cloned data is present in newly created application 399 | 400 | ``` 401 | [root@localhost]# kubectl get po 402 | NAME READY STATUS RESTARTS AGE 403 | csi-attacher-glusterfsplugin-0 2/2 Running 0 112m 404 | csi-nodeplugin-glusterfsplugin-dl7pp 2/2 Running 0 112m 405 | csi-nodeplugin-glusterfsplugin-khrtd 2/2 Running 0 112m 406 | csi-nodeplugin-glusterfsplugin-kqcsw 2/2 Running 0 112m 407 | csi-provisioner-glusterfsplugin-0 3/3 Running 0 112m 408 | glusterfs-55v7v 1/1 Running 0 128m 409 | glusterfs-qbvgv 1/1 Running 0 128m 410 | glusterfs-vclr4 1/1 Running 0 128m 411 | redis 1/1 Running 0 109m 412 | redis-pvc-restore 1/1 Running 0 26s 413 | [root@localhost]# kubectl exec -it redis-pvc-restore /bin/bash 414 | root@redis-pvc-restore:/data# cd /mnt/gluster/ 415 | root@redis-pvc-restore:/mnt/gluster# ls 416 | clone_data 417 | root@redis-pvc-restore:/mnt/gluster# cat clone_data 418 | glusterfs csi clone test 419 | ``` 420 | 421 | ### Create a glusterfs lite storage class to use loopback bricks (RWX) 422 | 423 | ``` 424 | [root@localhost]# cat glusterfs-lite-storage-class.yaml 425 | apiVersion: storage.k8s.io/v1 426 | kind: StorageClass 427 | metadata: 428 | name: glusterfs-lite-csi 429 | provisioner: org.gluster.glusterfs 430 | parameters: 431 | brickType: "loop" 432 | ``` 433 | 434 | ``` 435 | [root@localhost]# kubectl create -f glusterfs-lite-storage-class.yaml 436 | storageclass.storage.k8s.io/glusterfs-lite-csi created 437 | ``` 438 | 439 | Verify glusterfs storage class (RWX) 440 | 441 | ``` 442 | [root@localhost]# kubectl get storageclass 443 | NAME PROVISIONER AGE 444 | glusterfs-lite-csi org.gluster.glusterfs 105s 445 | ``` 446 | 447 | ### Create RWX PersistentVolumeClaim using glusterfs-lite-csi storage class 448 | 449 | ``` 450 | [root@localhost]# cat pvc.yaml 451 | --- 452 | kind: PersistentVolumeClaim 453 | apiVersion: v1 454 | metadata: 455 | name: glusterfs-lite-csi-pv 456 | spec: 457 | storageClassName: glusterfs-lite-csi 458 | accessModes: 459 | - ReadWriteMany 460 | resources: 461 | requests: 462 | storage: 5Gi 463 | 464 | [root@localhost]# kubectl create -f pvc.yaml 465 | persistentvolumeclaim/glusterfs-lite-csi-pv created 466 | ``` 467 | 468 | Validate the RWX claim creation 469 | 470 | ``` 471 | [root@localhost]# kubectl get pvc 472 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 473 | glusterfs-lite-csi-pv Bound pvc-943d21f5a51312e7 5Gi RWX glusterfs-lite-csi 5s 474 | ``` 475 | 476 | #### Create PVC with thin arbiter support 477 | 478 | follow [guide]( 479 | https://docs.gluster.org/en/latest/Administrator%20Guide/Thin-Arbiter-Volumes/) 480 | to setup thin arbiter 481 | 482 | ### Create Thin-Arbiter storage class 483 | 484 | ``` 485 | $ cat thin-arbiter-storageclass.yaml 486 | --- 487 | apiVersion: storage.k8s.io/v1 488 | kind: StorageClass 489 | metadata: 490 | name: glusterfs-csi-thin-arbiter 491 | provisioner: org.gluster.glusterfs 492 | parameters: 493 | arbiterType: "thin" 494 | arbiterPath: "192.168.10.90:24007/mnt/arbiter-path" 495 | ``` 496 | 497 | ``` 498 | $ kubectl create -f thin-arbiter-storageclass.yaml 499 | storageclass.storage.k8s.io/glusterfs-csi-thin-arbiter created 500 | ``` 501 | 502 | ### Create Thin-Arbiter PersistentVolumeClaim 503 | 504 | ``` 505 | $ cat thin-arbiter-pvc.yaml 506 | --- 507 | kind: PersistentVolumeClaim 508 | apiVersion: v1 509 | metadata: 510 | name: glusterfs-csi-thin-pv 511 | spec: 512 | storageClassName: glusterfs-csi-thin-arbiter 513 | accessModes: 514 | - ReadWriteMany 515 | resources: 516 | requests: 517 | storage: 5Gi 518 | ``` 519 | 520 | ``` 521 | $ kube create -f thin-arbiter-pvc.yaml 522 | persistentvolumeclaim/glusterfs-csi-thin-pv created 523 | ``` 524 | 525 | Verify PVC is in Bound state 526 | 527 | ``` 528 | $ kube get pvc 529 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 530 | glusterfs-csi-thin-pv Bound pvc-86b3b70b-1fa0-11e9-9232-525400ea010d 5Gi RWX glusterfs-csi-arbiter 13m 531 | 532 | ``` 533 | 534 | ### Create an app with claim 535 | 536 | ``` 537 | $ cat thin-arbiter-pod.yaml 538 | --- 539 | apiVersion: v1 540 | kind: Pod 541 | metadata: 542 | name: ta-redis 543 | labels: 544 | name: redis 545 | spec: 546 | containers: 547 | - name: redis 548 | image: redis 549 | imagePullPolicy: IfNotPresent 550 | volumeMounts: 551 | - mountPath: "/mnt/gluster" 552 | name: glusterfscsivol 553 | volumes: 554 | - name: glusterfscsivol 555 | persistentVolumeClaim: 556 | claimName: glusterfs-csi-thin-pv 557 | ``` 558 | 559 | ``` 560 | $ kube create -f thin-arbiter-pod.yaml 561 | pod/ta-redis created 562 | ``` 563 | 564 | Verify app is in running state 565 | 566 | ``` 567 | $ kube get po 568 | NAME READY STATUS RESTARTS AGE 569 | ta-redis 1/1 Running 0 6m54s 570 | ``` 571 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | # Set which docker repo to use 6 | REPO="${REPO:-gluster}" 7 | 8 | # Allow overriding default docker command 9 | RUNTIME_CMD=${RUNTIME_CMD:-docker} 10 | 11 | GOLANGCILINT_VERSION="${GOLANGCILINT_VERSION:-1.52.2}" 12 | 13 | build="build --load" 14 | if [[ "${RUNTIME_CMD}" == "buildah" ]]; then 15 | build="bud" 16 | fi 17 | 18 | # Allow disabling tests during build 19 | RUN_TESTS=${RUN_TESTS:-1} 20 | 21 | DRIVER=glusterfs-csi-driver 22 | DOCKERFILE=pkg/glusterfs/Dockerfile 23 | 24 | 25 | # This sets the version variable to (hopefully) a semver compatible string. We 26 | # expect released versions to have a tag of vX.Y.Z (with Y & Z optional), so we 27 | # only look for those tags. For version info on non-release commits, we want to 28 | # include the git commit info as a "build" suffix ("+stuff" at the end). There 29 | # is also special casing here for when no tags match. 30 | VERSION_GLOB="v[0-9]*" 31 | # Get the nearest "version" tag if one exists. If not, this returns the full 32 | # git hash 33 | NEAREST_TAG="$(git describe --always --tags --match "$VERSION_GLOB" --abbrev=0)" 34 | # Full output of git describe for us to parse: TAG--g- 35 | FULL_DESCRIBE="$(git describe --always --tags --match "$VERSION_GLOB" --dirty)" 36 | # If full matches against nearest, we found a valid tag earlier 37 | if [[ $FULL_DESCRIBE =~ ${NEAREST_TAG}-(.*) ]]; then 38 | # Build suffix is the last part of describe w/ "-" replaced by "." 39 | VERSION="$NEAREST_TAG+${BASH_REMATCH[1]//-/.}" 40 | else 41 | # We didn't find a valid tag, so assume version 0 and everything ends up 42 | # in build suffix. 43 | VERSION="0.0.0+g${FULL_DESCRIBE//-/.}" 44 | fi 45 | 46 | BUILDDATE="$(date -u '+%Y-%m-%dT%H:%M:%S.%NZ')" 47 | 48 | 49 | build_args=() 50 | build_args+=(--build-arg "RUN_TESTS=$RUN_TESTS") 51 | build_args+=(--build-arg "GOLANGCILINT_VERSION=$GOLANGCILINT_VERSION") 52 | build_args+=(--build-arg "version=$VERSION") 53 | build_args+=(--build-arg "builddate=$BUILDDATE") 54 | 55 | # Print Docker version 56 | echo "=== $RUNTIME_CMD version ===" 57 | $RUNTIME_CMD version 58 | 59 | #-- Build glusterfs csi driver container 60 | $RUNTIME_CMD $build \ 61 | -t "${REPO}/${DRIVER}" \ 62 | "${build_args[@]}" \ 63 | -f "$DOCKERFILE" \ 64 | . || 65 | exit 1 66 | 67 | # If running tests, extract profile data 68 | if [ "$RUN_TESTS" -ne 0 ]; then 69 | rm -f profile.cov 70 | $RUNTIME_CMD run --entrypoint cat "${REPO}/${DRIVER}" \ 71 | /profile.cov > profile.cov 72 | fi 73 | -------------------------------------------------------------------------------- /cmd/glusterfs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "os" 10 | "strings" 11 | 12 | gfd "github.com/gluster/gluster-csi-driver/pkg/glusterfs" 13 | "k8s.io/component-base/metrics/legacyregistry" 14 | "k8s.io/klog/v2" 15 | 16 | "github.com/spf13/cobra" 17 | ) 18 | 19 | func init() { 20 | _ = flag.Set("logtostderr", "true") 21 | } 22 | 23 | func main() { 24 | _ = flag.CommandLine.Parse([]string{}) 25 | var options = &gfd.DriverOptions{} 26 | 27 | cmd := &cobra.Command{ 28 | Use: "glusterfs-csi-driver", 29 | Short: "GlusterFS CSI driver", 30 | Run: func(cmd *cobra.Command, args []string) { 31 | exportMetrics(options) 32 | handle(options) 33 | }, 34 | } 35 | 36 | cmd.Flags().AddGoFlagSet(flag.CommandLine) 37 | cmd.PersistentFlags().StringVar(&options.NodeID, "nodeid", "", "CSI node id") 38 | _ = cmd.MarkPersistentFlagRequired("nodeid") 39 | cmd.PersistentFlags().StringVar(&options.Endpoint, "endpoint", "", "CSI endpoint to connect to") 40 | cmd.PersistentFlags().StringVar(&options.DriverName, "driver-name", "", "CSI driver name") 41 | cmd.PersistentFlags().StringVar(&options.Kubeconfig, "kubeconfig", "", "Absolute path to the kubeconfig file. Required only when running out of cluster.") 42 | cmd.PersistentFlags().StringVar(&options.MetricsAddress, "metrics-address", "", "metrics address") 43 | 44 | if err := cmd.Execute(); err != nil { 45 | _, _ = fmt.Fprintf(os.Stderr, "%s", err.Error()) 46 | os.Exit(1) 47 | } 48 | } 49 | 50 | func handle(options *gfd.DriverOptions) { 51 | if options.Endpoint == "" { 52 | options.Endpoint = os.Getenv("CSI_ENDPOINT") 53 | } 54 | d := gfd.NewDriver(options) 55 | if d == nil { 56 | klog.Fatalln("Failed to initialize GlusterFS CSI driver") 57 | } 58 | testingMock := false 59 | d.Run(options.Endpoint, options.Kubeconfig, testingMock) 60 | } 61 | 62 | func exportMetrics(options *gfd.DriverOptions) { 63 | if options.MetricsAddress == "" { 64 | return 65 | } 66 | l, err := net.Listen("tcp", options.MetricsAddress) 67 | if err != nil { 68 | klog.Warningf("failed to get listener for metrics endpoint: %v", err) 69 | return 70 | } 71 | serve(context.Background(), l, serveMetrics) 72 | } 73 | 74 | func serve(ctx context.Context, l net.Listener, serveFunc func(net.Listener) error) { 75 | path := l.Addr().String() 76 | klog.V(2).Infof("set up prometheus server on %v", path) 77 | go func() { 78 | defer l.Close() 79 | if err := serveFunc(l); err != nil { 80 | klog.Fatalf("serve failure(%v), address(%v)", err, path) 81 | } 82 | }() 83 | } 84 | 85 | func serveMetrics(l net.Listener) error { 86 | m := http.NewServeMux() 87 | m.Handle("/metrics", legacyregistry.Handler()) 88 | return trapClosedConnErr(http.Serve(l, m)) 89 | } 90 | 91 | func trapClosedConnErr(err error) error { 92 | if err == nil { 93 | return nil 94 | } 95 | if strings.Contains(err.Error(), "use of closed network connection") { 96 | return nil 97 | } 98 | return err 99 | } 100 | -------------------------------------------------------------------------------- /cmd/glusterfs/main_suite_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/kubernetes-csi/csi-test/v5/pkg/sanity" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/credentials/insecure" 12 | "k8s.io/klog/v2" 13 | 14 | "github.com/gluster/gluster-csi-driver/pkg/glusterfs" 15 | ) 16 | 17 | const ( 18 | endpointSchema = "unix:" 19 | endpointPath = "/tmp/glustercsi.socket" 20 | ) 21 | 22 | func TestGlusterCSIDriver(t *testing.T) { 23 | RegisterFailHandler(Fail) 24 | RunSpecs(t, "Glusterfs Suite") 25 | } 26 | 27 | var _ = Describe("GlusterFS CSI driver", func() { 28 | Context("Normal Config", func() { 29 | config := &sanity.TestConfig{ 30 | Address: fmt.Sprintf("%s%s", endpointSchema, endpointPath), 31 | DialOptions: []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}, 32 | } 33 | options := &glusterfs.DriverOptions{ 34 | Endpoint: fmt.Sprintf("%s/%s", endpointSchema, endpointPath), 35 | NodeID: "1", 36 | DriverName: "glusterfs", 37 | } 38 | 39 | var driver *glusterfs.Driver 40 | 41 | BeforeEach(func() { 42 | driver = glusterfs.NewDriver(options) 43 | if driver == nil { 44 | klog.Fatalln("Failed to initialize GlusterFS CSI driver") 45 | } 46 | testingMock := true 47 | driver.Run(options.Endpoint, options.Kubeconfig, testingMock) 48 | }) 49 | 50 | AfterEach(func() { 51 | driver.Stop() 52 | }) 53 | 54 | Describe("CSI Sanity", func() { 55 | sanity.GinkgoTest(config) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /docs/ci-infrastructure.md: -------------------------------------------------------------------------------- 1 | # Automated testing 2 | 3 | This document provides an overview of the automated testing for the Gluster-CSI 4 | repository. 5 | 6 | ## End-to-end testing 7 | 8 | (Full e2e testing is coming soon...) 9 | 10 | ## Testing of PRs 11 | 12 | Pull requests are automatically tested by [Travis CI](https://travis-ci.com). 13 | The tests that are run include: 14 | 15 | - Running linters over the text-like files in the repo (bash, md, yaml, etc.) 16 | - Building the code with recent versions of golang 17 | - Running a number of code linters over the source (via [gometalinter](https://github.com/alecthomas/gometalinter)) 18 | - Building the container images via Docker 19 | 20 | ### Configuration 21 | 22 | The configuration for Travis is controlled by the 23 | [.travis.yml](https://github.com/gluster/gluster-csi-driver/blob/master/.travis.yml) 24 | file in the main directory of the repo. Briefly, there is one job for each type 25 | of CSI driver. Within each job, the "install" steps are executed in order, 26 | followed by the "script" steps. If the CI job is running against `master` or a 27 | tag of the form 'v[0-9]+', the deploy step will execute, pushing the built 28 | containers to both Quay and Docker hub. If any command fails, the job will 29 | register a failure. See the main Travis documentation for details on the 30 | configuration file. 31 | 32 | ### Troubleshooting 33 | 34 | If your PR fails to build, the best place to start is the logs from Travis. 35 | Head over to the [list of PR 36 | jobs](https://travis-ci.com/gluster/gluster-csi-driver/pull_requests) and find 37 | yours. Once you locate your job, you can examine the individual test jobs and 38 | see which one(s) failed. By clicking on it, you can view the job output and 39 | diagnose the failure. 40 | 41 | When looking for failures, remember that the job will continue to run even 42 | after the first failure is detected. A failed command may actually be the 43 | result of an earlier command failing, so check through the entire output, not 44 | just the last couple lines. 45 | -------------------------------------------------------------------------------- /docs/releases.md: -------------------------------------------------------------------------------- 1 | # Gluster CSI driver Release Process 2 | 3 | - [Gluster CSI driver Release Process](#gluster-csi-driver-release-process) 4 | - [Introduction](#introduction) 5 | - [Versioning](#versioning) 6 | - [Tagging repositories](#tagging-repositories) 7 | - [Release process](#release-process) 8 | 9 | ## Introduction 10 | 11 | This document provides details about Gluster CSI driver releases. 12 | 13 | ## Versioning 14 | 15 | The Gluster CSI driver project uses 16 | [semantic versioning](http://semver.org/) 17 | for all releases. 18 | Semantic versions are comprised of three 19 | fields in the form: 20 | 21 | ```MAJOR.MINOR.PATCH``` 22 | 23 | For examples: `1.0.0`, `1.0.0-rc.2`. 24 | 25 | Semantic versioning is used since the version 26 | number is able to convey clear information about 27 | how a new version relates to the previous version. 28 | For example, semantic versioning can also provide 29 | assurances to allow users to know when they must 30 | upgrade compared with when they might want to upgrade: 31 | 32 | - When `PATCH` increases, the new release contains important **security fixes**, 33 | general bug fixes and an upgrade is recommended. 34 | 35 | The patch field can contain extra details after the number. 36 | Dashes denote pre-release versions.`1.0.0-rc.2` in the example 37 | denotes the second release candidate for release `1.0.0`. 38 | 39 | - When `MINOR` increases, the new release adds **new features** 40 | and it must be backward compatible. 41 | 42 | - When `MAJOR` increases, the new release adds **new features, 43 | bug fixes, or both** and which *changes the behavior from 44 | the previous release* (may be backward incompatible). 45 | 46 | ## Tagging repositories 47 | 48 | The tag name must begin with "v" followed by the version number, conforming to 49 | the [versioning](#versioning) requirements (e.g. a tag of `v1.0.0-rc2` for 50 | version `1.0.0-rc2`). This tag format is used by the Travis CI infrastructure to 51 | properly upload and tag releases to Quay and Docker Hub. 52 | 53 | ## Release process 54 | 55 | The Release Owner must follow the following process, which is 56 | designed to ensure clarity, quality, stability, and auditability 57 | of each release: 58 | 59 | - Raise an issue and assign to themselves. 60 | 61 | [new issue in this repository](https://github.com/gluster/gluster-csi-driver/issues/new) 62 | 63 | This issue is used to track the progress of the release with maximum visibility. 64 | 65 | - Paste the release checklist into the issue. 66 | 67 | This is useful for tracking so that the stage of the release is visible 68 | to all interested parties. 69 | 70 | - Once all steps are complete, close the issue. 71 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/app-with-clone-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: redis-pvc-restore 6 | labels: 7 | name: redis-pvc-restore 8 | spec: 9 | containers: 10 | - name: redis-pvc-restore 11 | image: redis:latest 12 | imagePullPolicy: IfNotPresent 13 | volumeMounts: 14 | - mountPath: "/mnt/gluster" 15 | name: glusterfscsivol 16 | volumes: 17 | - name: glusterfscsivol 18 | persistentVolumeClaim: 19 | claimName: glusterfs-pv-restore 20 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/app.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: redis 6 | labels: 7 | name: redis 8 | spec: 9 | containers: 10 | - name: redis 11 | image: redis 12 | imagePullPolicy: IfNotPresent 13 | volumeMounts: 14 | - mountPath: "/mnt/gluster" 15 | name: glusterfscsivol 16 | volumes: 17 | - name: glusterfscsivol 18 | persistentVolumeClaim: 19 | claimName: glusterfs-csi-pv 20 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/csi-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Gluster CSI Authors. 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | --- 16 | 17 | kind: Service 18 | apiVersion: v1 19 | metadata: 20 | name: csi-attacher-glusterfsplugin 21 | labels: 22 | app: csi-attacher-glusterfsplugin 23 | spec: 24 | selector: 25 | app: csi-attacher-glusterfsplugin 26 | ports: 27 | - name: dummy 28 | port: 12345 29 | 30 | --- 31 | kind: StatefulSet 32 | apiVersion: apps/v1beta1 33 | metadata: 34 | name: csi-attacher-glusterfsplugin 35 | spec: 36 | serviceName: "csi-attacher" 37 | replicas: 1 38 | template: 39 | metadata: 40 | labels: 41 | app: csi-attacher-glusterfsplugin 42 | spec: 43 | serviceAccount: glusterfs-csi 44 | containers: 45 | - name: csi-attacher 46 | image: quay.io/k8scsi/csi-attacher:v1.0.1 47 | args: 48 | - "--v=5" 49 | - "--csi-address=$(ADDRESS)" 50 | env: 51 | - name: ADDRESS 52 | value: /var/lib/csi/sockets/pluginproxy/csi.sock 53 | imagePullPolicy: "IfNotPresent" 54 | volumeMounts: 55 | - name: socket-dir 56 | mountPath: /var/lib/csi/sockets/pluginproxy/ 57 | 58 | - name: glusterfs 59 | image: docker.io/gluster/glusterfs-csi-driver 60 | args: 61 | - "--nodeid=$(NODE_ID)" 62 | - "--v=5" 63 | - "--endpoint=$(CSI_ENDPOINT)" 64 | - "--resturl=$(REST_URL)" 65 | - "--resttimeout=120" 66 | - "--restsecret=$(REST_SECRET)" 67 | env: 68 | - name: NODE_ID 69 | valueFrom: 70 | fieldRef: 71 | fieldPath: spec.nodeName 72 | - name: CSI_ENDPOINT 73 | value: unix://plugin/csi.sock 74 | - name: REST_URL 75 | value: http://192.168.121.182:24007 76 | - name: REST_SECRET 77 | value: b03045b7988258557ecd3e136cd37ba3f928ea0831b3b1b7ed8ae238d36d9071 78 | imagePullPolicy: "IfNotPresent" 79 | volumeMounts: 80 | - name: socket-dir 81 | mountPath: /plugin 82 | volumes: 83 | - name: socket-dir 84 | emptyDir: 85 | 86 | --- 87 | kind: DaemonSet 88 | apiVersion: apps/v1beta2 89 | metadata: 90 | name: csi-nodeplugin-glusterfsplugin 91 | spec: 92 | selector: 93 | matchLabels: 94 | app: csi-nodeplugin-glusterfsplugin 95 | template: 96 | metadata: 97 | labels: 98 | app: csi-nodeplugin-glusterfsplugin 99 | spec: 100 | serviceAccount: glusterfs-csi 101 | hostNetwork: true 102 | containers: 103 | - name: csi-node-driver-registrar 104 | image: quay.io/k8scsi/csi-node-driver-registrar:v1.0.1 105 | args: 106 | - "--v=5" 107 | - "--csi-address=$(ADDRESS)" 108 | - "--kubelet-registration-path=$(DRIVER_REG_SOCK_PATH)" 109 | lifecycle: 110 | preStop: 111 | exec: 112 | command: [ 113 | "/bin/sh", "-c", 114 | "rm -rf /registration/org.gluster.glusterfs \ 115 | /registration/org.gluster.glusterfs-reg.sock" 116 | ] 117 | env: 118 | - name: ADDRESS 119 | value: /plugin/csi.sock 120 | - name: DRIVER_REG_SOCK_PATH 121 | value: /var/lib/kubelet/plugins_registry/org.gluster.glusterfs/csi.sock 122 | - name: KUBE_NODE_NAME 123 | valueFrom: 124 | fieldRef: 125 | fieldPath: spec.nodeName 126 | volumeMounts: 127 | - name: plugin-dir 128 | mountPath: /plugin 129 | - name: registration-dir 130 | mountPath: /registration 131 | - name: glusterfs 132 | securityContext: 133 | privileged: true 134 | capabilities: 135 | add: ["SYS_ADMIN"] 136 | allowPrivilegeEscalation: true 137 | image: docker.io/gluster/glusterfs-csi-driver 138 | args: 139 | - "--nodeid=$(NODE_ID)" 140 | - "--v=5" 141 | - "--endpoint=$(CSI_ENDPOINT)" 142 | - "--resturl=$(REST_URL)" 143 | - "--resttimeout=120" 144 | - "--restsecret=$(REST_SECRET)" 145 | env: 146 | - name: NODE_ID 147 | valueFrom: 148 | fieldRef: 149 | fieldPath: spec.nodeName 150 | - name: CSI_ENDPOINT 151 | value: unix://plugin/csi.sock 152 | - name: REST_URL 153 | value: http://192.168.121.182:24007 154 | - name: REST_SECRET 155 | value: b03045b7988258557ecd3e136cd37ba3f928ea0831b3b1b7ed8ae238d36d9071 156 | imagePullPolicy: "IfNotPresent" 157 | volumeMounts: 158 | - name: plugin-dir 159 | mountPath: /plugin 160 | - name: pods-mount-dir 161 | mountPath: /var/lib/kubelet/pods 162 | mountPropagation: "Bidirectional" 163 | volumes: 164 | - name: plugin-dir 165 | hostPath: 166 | path: /var/lib/kubelet/plugins_registry/org.gluster.glusterfs 167 | type: DirectoryOrCreate 168 | - name: registration-dir 169 | hostPath: 170 | path: /var/lib/kubelet/plugins_registry/ 171 | type: Directory 172 | - name: pods-mount-dir 173 | hostPath: 174 | path: /var/lib/kubelet/pods 175 | type: Directory 176 | 177 | --- 178 | kind: Service 179 | apiVersion: v1 180 | metadata: 181 | name: csi-provisioner-glusterfsplugin 182 | labels: 183 | app: csi-provisioner-glusterfsplugin 184 | spec: 185 | selector: 186 | app: csi-provisioner-glusterfsplugin 187 | ports: 188 | - name: dummy 189 | port: 12345 190 | 191 | --- 192 | kind: StatefulSet 193 | apiVersion: apps/v1beta1 194 | metadata: 195 | name: csi-provisioner-glusterfsplugin 196 | spec: 197 | serviceName: "csi-provisioner-glusterfsplugin" 198 | replicas: 1 199 | template: 200 | metadata: 201 | labels: 202 | app: csi-provisioner-glusterfsplugin 203 | spec: 204 | serviceAccount: glusterfs-csi 205 | containers: 206 | - name: csi-provisioner 207 | image: quay.io/k8scsi/csi-provisioner:v1.0.1 208 | args: 209 | - "--provisioner=org.gluster.glusterfs" 210 | - "--csi-address=$(ADDRESS)" 211 | env: 212 | - name: ADDRESS 213 | value: /var/lib/csi/sockets/pluginproxy/csi.sock 214 | volumeMounts: 215 | - name: socket-dir 216 | mountPath: /var/lib/csi/sockets/pluginproxy/ 217 | imagePullPolicy: "IfNotPresent" 218 | - name: csi-cluster-driver-registrar 219 | image: quay.io/k8scsi/csi-cluster-driver-registrar:v1.0.1 220 | args: 221 | - "--v=5" 222 | - "--pod-info-mount-version=\"v1\"" 223 | - "--csi-address=$(ADDRESS)" 224 | env: 225 | - name: ADDRESS 226 | value: /var/lib/csi/sockets/pluginproxy/csi.sock 227 | volumeMounts: 228 | - name: socket-dir 229 | mountPath: /var/lib/csi/sockets/pluginproxy/ 230 | - name: csi-snapshotter 231 | image: quay.io/k8scsi/csi-snapshotter:v1.0.1 232 | args: 233 | - "--csi-address=$(ADDRESS)" 234 | - "--connection-timeout=15s" 235 | env: 236 | - name: ADDRESS 237 | value: /var/lib/csi/sockets/pluginproxy/csi.sock 238 | imagePullPolicy: "IfNotPresent" 239 | volumeMounts: 240 | - name: socket-dir 241 | mountPath: /var/lib/csi/sockets/pluginproxy/ 242 | - name: glusterfs 243 | image: docker.io/gluster/glusterfs-csi-driver 244 | args: 245 | - "--nodeid=$(NODE_ID)" 246 | - "--v=5" 247 | - "--endpoint=$(CSI_ENDPOINT)" 248 | - "--resturl=$(REST_URL)" 249 | - "--resttimeout=120" 250 | - "--restsecret=$(REST_SECRET)" 251 | env: 252 | - name: NODE_ID 253 | valueFrom: 254 | fieldRef: 255 | fieldPath: spec.nodeName 256 | - name: CSI_ENDPOINT 257 | value: unix://plugin/csi.sock 258 | - name: REST_URL 259 | value: http://192.168.121.182:24007 260 | - name: REST_SECRET 261 | value: b03045b7988258557ecd3e136cd37ba3f928ea0831b3b1b7ed8ae238d36d9071 262 | imagePullPolicy: "IfNotPresent" 263 | volumeMounts: 264 | - name: socket-dir 265 | mountPath: /plugin 266 | volumes: 267 | - name: socket-dir 268 | emptyDir: 269 | 270 | --- 271 | apiVersion: v1 272 | kind: ServiceAccount 273 | metadata: 274 | name: glusterfs-csi 275 | 276 | --- 277 | kind: ClusterRole 278 | apiVersion: rbac.authorization.k8s.io/v1 279 | metadata: 280 | name: glusterfs-csi 281 | rules: 282 | - apiGroups: [""] 283 | resources: ["persistentvolumes"] 284 | verbs: ["get", "list", "watch", "update", "create", "delete"] 285 | - apiGroups: [""] 286 | resources: ["nodes"] 287 | verbs: ["get", "list", "watch"] 288 | - apiGroups: [""] 289 | resources: ["persistentvolumeclaims"] 290 | verbs: ["get", "list", "watch", "update"] 291 | - apiGroups: ["storage.k8s.io"] 292 | resources: ["storageclasses"] 293 | verbs: ["get", "list", "watch"] 294 | - apiGroups: ["storage.k8s.io"] 295 | resources: ["volumeattachments"] 296 | verbs: ["get", "list", "watch", "update"] 297 | - apiGroups: [""] 298 | resources: ["events"] 299 | verbs: ["list", "watch", "create", "update", "patch", "delete", "get"] 300 | - apiGroups: ["csi.storage.k8s.io"] 301 | resources: ["csinodeinfos"] 302 | verbs: ["get", "list", "watch"] 303 | - apiGroups: [""] 304 | resources: ["secrets"] 305 | verbs: ["get", "list"] 306 | - apiGroups: [""] 307 | resources: ["endpoints"] 308 | verbs: ["get", "list", "watch", "create", "update"] 309 | - apiGroups: ["snapshot.storage.k8s.io"] 310 | resources: ["volumesnapshotclasses"] 311 | verbs: ["get", "list", "watch"] 312 | - apiGroups: ["snapshot.storage.k8s.io"] 313 | resources: ["volumesnapshotcontents"] 314 | verbs: ["create", "get", "list", "watch", "update", "delete"] 315 | - apiGroups: ["snapshot.storage.k8s.io"] 316 | resources: ["volumesnapshots"] 317 | verbs: ["get", "list", "watch", "update"] 318 | - apiGroups: ["apiextensions.k8s.io"] 319 | resources: ["customresourcedefinitions"] 320 | verbs: ["create", "list", "watch", "delete"] 321 | - apiGroups: ["csi.storage.k8s.io"] 322 | resources: ["csidrivers"] 323 | verbs: ["create", "delete"] 324 | 325 | --- 326 | kind: ClusterRoleBinding 327 | apiVersion: rbac.authorization.k8s.io/v1 328 | metadata: 329 | name: glusterfs-csi-role 330 | subjects: 331 | - kind: ServiceAccount 332 | name: glusterfs-csi 333 | namespace: default 334 | roleRef: 335 | kind: ClusterRole 336 | name: glusterfs-csi 337 | apiGroup: rbac.authorization.k8s.io 338 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/glusterd-ds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: DaemonSet 3 | apiVersion: extensions/v1beta1 4 | metadata: 5 | name: glusterfs 6 | labels: 7 | glusterfs: daemonset 8 | annotations: 9 | description: GlusterFS DaemonSet 10 | tags: glusterfs 11 | spec: 12 | template: 13 | metadata: 14 | name: glusterfs 15 | labels: 16 | glusterfs: pod 17 | glusterfs-node: pod 18 | spec: 19 | hostNetwork: true 20 | containers: 21 | - image: docker.io/gluster/glusterd2-nightly 22 | imagePullPolicy: IfNotPresent 23 | name: glusterfs 24 | resources: 25 | requests: 26 | memory: 100Mi 27 | cpu: 100m 28 | volumeMounts: 29 | - name: glusterfs-run 30 | mountPath: "/run" 31 | - name: glusterfs-lvm 32 | mountPath: "/run/lvm" 33 | - mountPath: /var/lib/glusterd2 34 | name: glusterd2-config 35 | - name: glusterfs-etc 36 | mountPath: "/etc/glusterfs" 37 | - name: glusterfs-logs 38 | mountPath: "/var/log/glusterfs" 39 | - name: glusterfs-config 40 | mountPath: "/var/lib/glusterd" 41 | - name: glusterfs-dev 42 | mountPath: "/dev" 43 | - name: glusterfs-misc 44 | mountPath: "/var/lib/misc/glusterfsd" 45 | - name: glusterfs-cgroup 46 | mountPath: "/sys/fs/cgroup" 47 | readOnly: true 48 | - name: glusterfs-ssl 49 | mountPath: "/etc/ssl" 50 | readOnly: true 51 | - name: kernel-modules 52 | mountPath: "/usr/lib/modules" 53 | readOnly: true 54 | securityContext: 55 | capabilities: {} 56 | privileged: true 57 | volumes: 58 | - name: glusterfs-run 59 | - name: glusterd2-config 60 | - name: glusterfs-lvm 61 | hostPath: 62 | path: "/run/lvm" 63 | - name: glusterfs-etc 64 | hostPath: 65 | path: "/etc/glusterfs" 66 | - name: glusterfs-logs 67 | hostPath: 68 | path: "/var/log/glusterfs" 69 | - name: glusterfs-config 70 | hostPath: 71 | path: "/var/lib/glusterd" 72 | - name: glusterfs-dev 73 | hostPath: 74 | path: "/dev" 75 | - name: glusterfs-misc 76 | hostPath: 77 | path: "/var/lib/misc/glusterfsd" 78 | - name: glusterfs-cgroup 79 | hostPath: 80 | path: "/sys/fs/cgroup" 81 | - name: glusterfs-ssl 82 | hostPath: 83 | path: "/etc/ssl" 84 | - name: kernel-modules 85 | hostPath: 86 | path: "/usr/lib/modules" 87 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/glusterfs-lite-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: glusterfs-lite-csi-pv 6 | spec: 7 | storageClassName: glusterfs-lite-csi 8 | accessModes: 9 | - ReadWriteMany 10 | resources: 11 | requests: 12 | storage: 5Gi 13 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/glusterfs-lite-storage-class.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: storage.k8s.io/v1 3 | kind: StorageClass 4 | metadata: 5 | name: glusterfs-lite-csi 6 | provisioner: org.gluster.glusterfs 7 | parameters: 8 | brickType: "loop" 9 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/pvc-restore.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: glusterfs-pv-restore 6 | spec: 7 | storageClassName: glusterfs-csi 8 | dataSource: 9 | name: glusterfs-csi-ss 10 | kind: VolumeSnapshot 11 | apiGroup: snapshot.storage.k8s.io 12 | accessModes: 13 | - ReadWriteMany 14 | resources: 15 | requests: 16 | storage: 5Gi 17 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: glusterfs-csi-pv 6 | spec: 7 | storageClassName: glusterfs-csi 8 | accessModes: 9 | - ReadWriteMany 10 | resources: 11 | requests: 12 | storage: 5Gi 13 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/snapshot-class.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: snapshot.storage.k8s.io/v1alpha1 3 | kind: VolumeSnapshotClass 4 | metadata: 5 | name: glusterfs-csi-snap 6 | snapshotter: org.gluster.glusterfs 7 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/storage-class.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: storage.k8s.io/v1 3 | kind: StorageClass 4 | metadata: 5 | name: glusterfs-csi 6 | annotations: 7 | storageclass.kubernetes.io/is-default-class: "true" 8 | provisioner: org.gluster.glusterfs 9 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/thin-arbiter-pod.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: ta-redis 6 | labels: 7 | name: redis 8 | spec: 9 | containers: 10 | - name: redis 11 | image: redis 12 | imagePullPolicy: IfNotPresent 13 | volumeMounts: 14 | - mountPath: "/mnt/gluster" 15 | name: glusterfscsivol 16 | volumes: 17 | - name: glusterfscsivol 18 | persistentVolumeClaim: 19 | claimName: glusterfs-csi-thin-pv 20 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/thin-arbiter-pvc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: glusterfs-csi-thin-pv 6 | spec: 7 | storageClassName: glusterfs-csi-arbiter 8 | accessModes: 9 | - ReadWriteMany 10 | resources: 11 | requests: 12 | storage: 5Gi 13 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/thin-arbiter-storageclass.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: storage.k8s.io/v1 3 | kind: StorageClass 4 | metadata: 5 | name: glusterfs-csi-arbiter 6 | provisioner: org.gluster.glusterfs 7 | parameters: 8 | replicas: "2" 9 | arbiterType: "thin" 10 | arbiterPath: "192.168.10.90:/mnt/arbiter-path:24007" 11 | -------------------------------------------------------------------------------- /examples/kubernetes/glusterfs/volume-snapshot.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: snapshot.storage.k8s.io/v1alpha1 3 | kind: VolumeSnapshot 4 | metadata: 5 | name: glusterfs-csi-ss 6 | spec: 7 | snapshotClassName: glusterfs-csi-snap 8 | source: 9 | name: glusterfs-csi-pv 10 | kind: PersistentVolumeClaim 11 | -------------------------------------------------------------------------------- /extras/centos-ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sets up the centos-ci environment and runs the PR tests for gluster-csi-driver. 4 | 5 | # if anything fails, we'll abort 6 | set -e 7 | 8 | # Install buildah & Docker 9 | yum-config-manager \ 10 | --add-repo \ 11 | https://download.docker.com/linux/centos/docker-ce.repo 12 | yum -y install buildah docker-ce 13 | systemctl start docker 14 | 15 | # Build glusterfs container 16 | ./build.sh glusterfs 17 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gluster/gluster-csi-driver 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/container-storage-interface/spec v1.8.0 7 | github.com/gluster/glusterd2 v5.0.0-rc0.0.20190228134612-994aaa048955+incompatible 8 | github.com/golang/protobuf v1.5.3 9 | github.com/kubernetes-csi/csi-lib-utils v0.2.0 10 | github.com/kubernetes-csi/csi-test/v5 v5.0.0 11 | github.com/onsi/ginkgo/v2 v2.12.0 12 | github.com/onsi/gomega v1.27.10 13 | github.com/spf13/cobra v1.7.0 14 | github.com/stretchr/testify v1.8.2 15 | golang.org/x/net v0.14.0 16 | google.golang.org/grpc v1.54.0 17 | k8s.io/component-base v0.28.1 18 | k8s.io/klog/v2 v2.100.1 19 | k8s.io/mount-utils v0.27.1 20 | sigs.k8s.io/yaml v1.3.0 21 | ) 22 | 23 | require ( 24 | github.com/BurntSushi/toml v1.2.1 // indirect 25 | github.com/beorn7/perks v1.0.1 // indirect 26 | github.com/blang/semver/v4 v4.0.0 // indirect 27 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 28 | github.com/davecgh/go-spew v1.1.1 // indirect 29 | github.com/dgrijalva/jwt-go v3.1.0+incompatible // indirect 30 | github.com/fsnotify/fsnotify v1.6.0 // indirect 31 | github.com/go-logr/logr v1.2.4 // indirect 32 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 33 | github.com/google/go-cmp v0.5.9 // indirect 34 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect 35 | github.com/google/uuid v1.3.0 // indirect 36 | github.com/hashicorp/hcl v1.0.0 // indirect 37 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 38 | github.com/kr/text v0.2.0 // indirect 39 | github.com/magiconair/properties v1.8.0 // indirect 40 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 41 | github.com/mitchellh/mapstructure v1.4.1 // indirect 42 | github.com/moby/sys/mountinfo v0.6.2 // indirect 43 | github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect 44 | github.com/pelletier/go-toml v1.2.0 // indirect 45 | github.com/pmezard/go-difflib v1.0.0 // indirect 46 | github.com/prometheus/client_golang v1.16.0 // indirect 47 | github.com/prometheus/client_model v0.4.0 // indirect 48 | github.com/prometheus/common v0.44.0 // indirect 49 | github.com/prometheus/procfs v0.10.1 // indirect 50 | github.com/sirupsen/logrus v1.9.0 // indirect 51 | github.com/spf13/afero v1.9.5 // indirect 52 | github.com/spf13/cast v1.3.0 // indirect 53 | github.com/spf13/jwalterweatherman v1.0.0 // indirect 54 | github.com/spf13/pflag v1.0.5 // indirect 55 | github.com/spf13/viper v1.0.3 // indirect 56 | golang.org/x/sys v0.11.0 // indirect 57 | golang.org/x/text v0.12.0 // indirect 58 | golang.org/x/tools v0.12.0 // indirect 59 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect 60 | google.golang.org/protobuf v1.30.0 // indirect 61 | gopkg.in/yaml.v2 v2.4.0 // indirect 62 | gopkg.in/yaml.v3 v3.0.1 // indirect 63 | k8s.io/apimachinery v0.28.1 // indirect 64 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect 65 | ) 66 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 39 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 40 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 41 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 42 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 43 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 44 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 45 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 46 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 47 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 48 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 49 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 50 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 51 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 52 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 53 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 54 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 55 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 56 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 57 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 58 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 59 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 60 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 61 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 62 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 63 | github.com/container-storage-interface/spec v1.6.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s= 64 | github.com/container-storage-interface/spec v1.8.0 h1:D0vhF3PLIZwlwZEf2eNbpujGCNwspwTYf2idJRJx4xI= 65 | github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= 66 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 67 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 68 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 69 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 70 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 71 | github.com/dgrijalva/jwt-go v3.1.0+incompatible h1:FFziAwDQQ2dz1XClWMkwvukur3evtZx7x/wMHKM1i20= 72 | github.com/dgrijalva/jwt-go v3.1.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 73 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 74 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 75 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 76 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 77 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 78 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 79 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 80 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 81 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 82 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 83 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 84 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 85 | github.com/gluster/glusterd2 v5.0.0-rc0.0.20190228134612-994aaa048955+incompatible h1:hFkz5/5RID7W89+anrsQskJLrTibz/CycLaku1G+sWE= 86 | github.com/gluster/glusterd2 v5.0.0-rc0.0.20190228134612-994aaa048955+incompatible/go.mod h1:/YFY3NEiOkrDWoOyPP5v3joOtJi0DuBrg5LsqU3tlVE= 87 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 88 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 89 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 90 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 91 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 92 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 93 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 94 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 95 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 96 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 97 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 98 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 99 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 100 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 101 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 102 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 103 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 104 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 105 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 106 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 107 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 108 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 109 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 111 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 112 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 113 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 114 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 115 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 116 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 117 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 118 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 119 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 120 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 121 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 122 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 123 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 124 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 125 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 126 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 127 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 128 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 129 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 130 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 131 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 132 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 133 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 134 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 135 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 136 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 137 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 138 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 139 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 140 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 141 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 142 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 143 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 144 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 145 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 146 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 147 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 148 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 149 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 150 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 151 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 152 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 153 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 154 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 155 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 156 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= 157 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 158 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 159 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 160 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 161 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 162 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 163 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 164 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 165 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 166 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 167 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 168 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 169 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 170 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 171 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 172 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 173 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 174 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 175 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 176 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 177 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 178 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 179 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 180 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 181 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 182 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 183 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 184 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 185 | github.com/kubernetes-csi/csi-lib-utils v0.2.0 h1:JPYmgBuogmlJWHUazSFH4Q36Cl9RGFPOasAcZ/FIX5A= 186 | github.com/kubernetes-csi/csi-lib-utils v0.2.0/go.mod h1:GVmlUmxZ+SUjVLXicRFjqWUUvWez0g0Y78zNV9t7KfQ= 187 | github.com/kubernetes-csi/csi-test/v5 v5.0.0 h1:GJ0M+ppcKgWhafXH3B2Ssfw1Egzly9GlMx3JOQApekM= 188 | github.com/kubernetes-csi/csi-test/v5 v5.0.0/go.mod h1:jVEIqf8Nv1roo/4zhl/r6Tc68MAgRX/OQSQK0azTHyo= 189 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= 190 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 191 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 192 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 193 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 194 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 195 | github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= 196 | github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= 197 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 198 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 199 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 200 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 201 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 202 | github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 203 | github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= 204 | github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= 205 | github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= 206 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 207 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 208 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 209 | github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= 210 | github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= 211 | github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 212 | github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 213 | github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE= 214 | github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= 215 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 216 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 217 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 218 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 219 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 220 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 221 | github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= 222 | github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= 223 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 224 | github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= 225 | github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= 226 | github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= 227 | github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= 228 | github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= 229 | github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 230 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 231 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 232 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 233 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 234 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 235 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 236 | github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= 237 | github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 238 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 239 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 240 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 241 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 242 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 243 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 244 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 245 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 246 | github.com/spf13/viper v1.0.3 h1:z5LPUc2iz8VLT5Cw1UyrESG6FUUnOGecYGY08BLKSuc= 247 | github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= 248 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 249 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 250 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 251 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 252 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 253 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 254 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 255 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 256 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 257 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 258 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 259 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 260 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 261 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 262 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 263 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 264 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 265 | github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 266 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 267 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 268 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 269 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 270 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 271 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 272 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 273 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 274 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 275 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 276 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 277 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 278 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 279 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 280 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 281 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 282 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 283 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 284 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 285 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 286 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 287 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 288 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 289 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 290 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 291 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 292 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 293 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 294 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 295 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 296 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 297 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 298 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 299 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 300 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 301 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 302 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 303 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 304 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 305 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 306 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 307 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 308 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 309 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 310 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 311 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 312 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 313 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 314 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 315 | golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 316 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 317 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 318 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 319 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 320 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 321 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 322 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 323 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 324 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 325 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 326 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 327 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 328 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 329 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 330 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 331 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 332 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 333 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 334 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 335 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 336 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 337 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 338 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 339 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 340 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 341 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 342 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 343 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 344 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 345 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 346 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 347 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 348 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 349 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 350 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 351 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 352 | golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 353 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 354 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 355 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 356 | golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 357 | golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= 358 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 359 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 360 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 361 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 362 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 363 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 364 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 365 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 366 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 367 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 368 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 369 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 370 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 371 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 372 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 373 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 374 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 375 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 376 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 377 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 378 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 379 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 380 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 381 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 382 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 401 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 402 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 403 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 404 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 410 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 411 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 412 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 413 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 414 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 415 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 416 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 417 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 418 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 419 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 420 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 421 | golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 422 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 423 | golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 424 | golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 425 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 426 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 427 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 428 | golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 429 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 430 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= 431 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 432 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 433 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 434 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 435 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 436 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 437 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 438 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 439 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 440 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 441 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 442 | golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= 443 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 444 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 445 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 446 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 447 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 448 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 449 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 450 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 451 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 452 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 453 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 454 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 455 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 456 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 457 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 458 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 459 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 460 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 461 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 462 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 463 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 464 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 465 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 466 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 467 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 468 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 469 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 470 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 471 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 472 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 473 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 474 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 475 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 476 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 477 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 478 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 479 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 480 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 481 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 482 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 483 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 484 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 485 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 486 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 487 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 488 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 489 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 490 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 491 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 492 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 493 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 494 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 495 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 496 | golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 497 | golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= 498 | golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 499 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 500 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 501 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 502 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 503 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 504 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 505 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 506 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 507 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 508 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 509 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 510 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 511 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 512 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 513 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 514 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 515 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 516 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 517 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 518 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 519 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 520 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 521 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 522 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 523 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 524 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 525 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 526 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 527 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 528 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 529 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 530 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 531 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 532 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 533 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 534 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 535 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 536 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 537 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 538 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 539 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 540 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 541 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 542 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 543 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 544 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 545 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 546 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 547 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 548 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 549 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 550 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 551 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 552 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 553 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 554 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 555 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 556 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 557 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 558 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 559 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 560 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 561 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 562 | google.golang.org/genproto v0.0.0-20201209185603-f92720507ed4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 563 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 564 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 565 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 566 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 567 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= 568 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= 569 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 570 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 571 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 572 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 573 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 574 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 575 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 576 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 577 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 578 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 579 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 580 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 581 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 582 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 583 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 584 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 585 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 586 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 587 | google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 588 | google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= 589 | google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= 590 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 591 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 592 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 593 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 594 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 595 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 596 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 597 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 598 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 599 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 600 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 601 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 602 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 603 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 604 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 605 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 606 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 607 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 608 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 609 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 610 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 611 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 612 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 613 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 614 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 615 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 616 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 617 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 618 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 619 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 620 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 621 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 622 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 623 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 624 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 625 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 626 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 627 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 628 | k8s.io/apimachinery v0.28.1 h1:EJD40og3GizBSV3mkIoXQBsws32okPOy+MkRyzh6nPY= 629 | k8s.io/apimachinery v0.28.1/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw= 630 | k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg= 631 | k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU= 632 | k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 633 | k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= 634 | k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 635 | k8s.io/mount-utils v0.27.1 h1:RSd0wslbIuwLRaGGNAGMZ3m9FLcvukxJ3FWlOm76W2A= 636 | k8s.io/mount-utils v0.27.1/go.mod h1:vmcjYdi2Vg1VTWY7KkhvwJVY6WDHxb/QQhiQKkR8iNs= 637 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= 638 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 639 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 640 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 641 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 642 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 643 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 644 | -------------------------------------------------------------------------------- /pkg/glusterfs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Gluster CSI Authors. 2 | 3 | # Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 4 | # You may obtain a copy of the License at 5 | # https://opensource.org/licenses/lgpl-3.0.html 6 | 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #-- Create build environment 14 | 15 | FROM golang:1.20 as build 16 | 17 | ENV GOPATH="/go/" \ 18 | SRCDIR="/go/src/github.com/gluster/gluster-csi-driver/" 19 | ENV SCRIPTSDIR="${SRCDIR}scripts/" 20 | 21 | # Install go tools 22 | ARG GOLANGCILINT_VERSION=latest 23 | COPY scripts/install-go-tools.sh "${SCRIPTSDIR}" 24 | RUN mkdir -p /go/bin; ${SCRIPTSDIR}/install-go-tools.sh 25 | 26 | # Vendor dependencies 27 | COPY go.mod go.sum "${SRCDIR}/" 28 | WORKDIR "${SRCDIR}" 29 | RUN go mod download 30 | 31 | # Copy source directories 32 | COPY cmd/ "${SRCDIR}/cmd" 33 | COPY pkg/ "${SRCDIR}/pkg" 34 | COPY scripts/ "${SCRIPTSDIR}" 35 | 36 | #-- Test phase 37 | 38 | ARG RUN_TESTS=1 39 | ARG GO_METALINTER_THREADS=1 40 | ENV TEST_COVERAGE=stdout \ 41 | GO_COVER_DIR=/build/ 42 | 43 | RUN mkdir /build && \ 44 | [ $RUN_TESTS -eq 0 ] || ( ${SCRIPTSDIR}/lint-go.sh && \ 45 | ${SCRIPTSDIR}/test-go.sh ) && \ 46 | CGO_ENABLED=0 GOOS=linux go build -ldflags '-extldflags "-static"' -o /build/glusterfs-csi-driver cmd/glusterfs/main.go 47 | 48 | #-- Final container 49 | 50 | FROM fedora:37 as final 51 | 52 | # Install dependencies 53 | RUN yum update -y && \ 54 | yum -y install glusterfs-fuse && \ 55 | yum clean all -y && \ 56 | rm -rf /var/cache/yum && \ 57 | rpm -qa | grep gluster | tee /gluster-rpm-versions.txt 58 | 59 | # Copy glusterfs-csi-driver from build phase 60 | COPY --from=build /build / 61 | 62 | # The version of the driver (git describe --dirty --always --tags | sed 's/-/./2' | sed 's/-/./2') 63 | ARG version="(unknown)" 64 | # Container build time (date -u '+%Y-%m-%dT%H:%M:%S.%NZ') 65 | ARG builddate="(unknown)" 66 | 67 | LABEL build-date="${builddate}" 68 | LABEL io.k8s.description="FUSE-based CSI driver for Gluster file access" 69 | LABEL name="glusterfs-csi-driver" 70 | LABEL summary="FUSE-based CSI driver for Gluster file access" 71 | LABEL vcs-type="git" 72 | LABEL vcs-url="https://github.com/gluster/gluster-csi-driver" 73 | LABEL vendor="gluster.org" 74 | LABEL version="${version}" 75 | 76 | ENTRYPOINT ["/glusterfs-csi-driver"] 77 | -------------------------------------------------------------------------------- /pkg/glusterfs/glusterfs.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "runtime" 5 | "strings" 6 | 7 | "github.com/container-storage-interface/spec/lib/go/csi" 8 | "k8s.io/klog/v2" 9 | "k8s.io/mount-utils" 10 | ) 11 | 12 | const ( 13 | glusterfsCSIDriverName = "org.gluster.glusterfs" 14 | glusterfsCSIDriverVersion = "2.0.0" 15 | 16 | pvcNameKey = "csi.storage.k8s.io/pvc/name" 17 | pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace" 18 | pvNameKey = "csi.storage.k8s.io/pv/name" 19 | pvcNameMetadata = "${pvc.metadata.name}" 20 | pvcNamespaceMetadata = "${pvc.metadata.namespace}" 21 | pvNameMetadata = "${pv.metadata.name}" 22 | ) 23 | 24 | type CSIDriver interface { 25 | csi.ControllerServer 26 | csi.NodeServer 27 | csi.IdentityServer 28 | 29 | Run(endpoint, kubeconfig string, testMode bool) 30 | } 31 | 32 | // Driver is the struct embedding information about the connection to gluster 33 | // cluster and configuration of CSI driver. 34 | type Driver struct { 35 | endpoint string 36 | name string 37 | nodeID string 38 | version string 39 | 40 | ns *NodeServer 41 | nscap []*csi.NodeServiceCapability 42 | } 43 | 44 | type DriverOptions struct { 45 | Endpoint string 46 | NodeID string 47 | DriverName string 48 | Kubeconfig string 49 | MetricsAddress string 50 | } 51 | 52 | // New returns CSI driver 53 | func NewDriver(options *DriverOptions) *Driver { 54 | klog.V(2).Infof("Driver: %#v version: %v", options, driverVersion) 55 | 56 | if options == nil { 57 | klog.Errorf("GlusterFS CSI driver initialization failed") 58 | return nil 59 | } 60 | 61 | gfd := &Driver{ 62 | endpoint: options.Endpoint, 63 | name: options.DriverName, 64 | version: driverVersion, 65 | nodeID: options.NodeID, 66 | } 67 | 68 | gfd.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{ 69 | csi.NodeServiceCapability_RPC_GET_VOLUME_STATS, 70 | csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER, 71 | csi.NodeServiceCapability_RPC_UNKNOWN, 72 | }) 73 | 74 | klog.V(1).Infof("GlusterFS CSI driver initialized") 75 | 76 | return gfd 77 | } 78 | 79 | // NewNodeServer initialize a node server for GlusterFS CSI driver. 80 | func NewNodeServer(g *Driver, mounter mount.Interface) *NodeServer { 81 | return &NodeServer{ 82 | Driver: g, 83 | mounter: mounter, 84 | } 85 | } 86 | 87 | // Run start a non-blocking grpc controller,node and identityserver for 88 | // GlusterFS CSI driver which can serve multiple parallel requests 89 | func (g *Driver) Run(endpoint, kubeconfig string, testMode bool) { 90 | versionMeta, err := GetVersionYAML(g.name) 91 | if err != nil { 92 | klog.Fatalf("%v", err) 93 | } 94 | klog.V(2).Infof("\nDRIVER INFORMATION:\n-------------------\n%s\n", versionMeta) 95 | 96 | mounter := mount.New("") 97 | if runtime.GOOS == "linux" { 98 | // MounterForceUnmounter is only implemented on Linux now 99 | mounter = mounter.(mount.MounterForceUnmounter) 100 | } 101 | g.ns = NewNodeServer(g, mounter) 102 | srv := NewNonBlockingGRPCServer() 103 | srv.Start(g.endpoint, NewIdentityServer(g), nil, g.ns, testMode) 104 | srv.Wait() 105 | } 106 | 107 | func (n *Driver) AddNodeServiceCapabilities(nl []csi.NodeServiceCapability_RPC_Type) { 108 | var nsc []*csi.NodeServiceCapability 109 | for _, n := range nl { 110 | nsc = append(nsc, NewNodeServiceCapability(n)) 111 | } 112 | n.nscap = nsc 113 | } 114 | 115 | // replaceWithMap replace key with value for str 116 | func replaceWithMap(str string, m map[string]string) string { 117 | for k, v := range m { 118 | if k != "" { 119 | str = strings.ReplaceAll(str, k, v) 120 | } 121 | } 122 | return str 123 | } 124 | 125 | func IsCorruptedDir(dir string) bool { 126 | _, pathErr := mount.PathExists(dir) 127 | return pathErr != nil && mount.IsCorruptedMnt(pathErr) 128 | } 129 | -------------------------------------------------------------------------------- /pkg/glusterfs/glusterfs_test.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/container-storage-interface/spec/lib/go/csi" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | const ( 13 | fakeNodeID = "fakeNodeID" 14 | DefaultDriverName = "glusterfs.csi.gluster.io" 15 | ) 16 | 17 | func NewEmptyDriver(emptyField string) *Driver { 18 | var d *Driver 19 | switch emptyField { 20 | case "version": 21 | d = &Driver{ 22 | name: DefaultDriverName, 23 | version: "", 24 | nodeID: fakeNodeID, 25 | } 26 | case "name": 27 | d = &Driver{ 28 | name: "", 29 | version: driverVersion, 30 | nodeID: fakeNodeID, 31 | } 32 | default: 33 | d = &Driver{ 34 | name: DefaultDriverName, 35 | version: driverVersion, 36 | nodeID: fakeNodeID, 37 | } 38 | } 39 | return d 40 | } 41 | 42 | func TestNewFakeDriver(t *testing.T) { 43 | d := NewEmptyDriver("version") 44 | assert.Empty(t, d.version) 45 | 46 | d = NewEmptyDriver("name") 47 | assert.Empty(t, d.name) 48 | } 49 | 50 | func TestIsCorruptedDir(t *testing.T) { 51 | existingMountPath, err := os.MkdirTemp(os.TempDir(), "csi-mount-test") 52 | if err != nil { 53 | t.Fatalf("failed to create tmp dir: %v", err) 54 | } 55 | defer os.RemoveAll(existingMountPath) 56 | 57 | curruptedPath := filepath.Join(existingMountPath, "curruptedPath") 58 | if err := os.Symlink(existingMountPath, curruptedPath); err != nil { 59 | t.Fatalf("failed to create curruptedPath: %v", err) 60 | } 61 | 62 | tests := []struct { 63 | desc string 64 | dir string 65 | expectedResult bool 66 | }{ 67 | { 68 | desc: "NotExist dir", 69 | dir: "/tmp/NotExist", 70 | expectedResult: false, 71 | }, 72 | { 73 | desc: "Existing dir", 74 | dir: existingMountPath, 75 | expectedResult: false, 76 | }, 77 | } 78 | 79 | for i, test := range tests { 80 | isCorruptedDir := IsCorruptedDir(test.dir) 81 | assert.Equal(t, test.expectedResult, isCorruptedDir, "TestCase[%d]: %s", i, test.desc) 82 | } 83 | } 84 | 85 | func TestRun(t *testing.T) { 86 | testCases := []struct { 87 | name string 88 | testFunc func(t *testing.T) 89 | }{ 90 | { 91 | name: "Successful run", 92 | testFunc: func(t *testing.T) { 93 | d := NewEmptyDriver("") 94 | d.endpoint = "tcp://127.0.0.1:0" 95 | d.Run("tcp://127.0.0.1:0", "", true) 96 | }, 97 | }, 98 | { 99 | name: "Successful run with node ID missing", 100 | testFunc: func(t *testing.T) { 101 | d := NewEmptyDriver("") 102 | d.endpoint = "tcp://127.0.0.1:0" 103 | d.nodeID = "" 104 | d.Run("tcp://127.0.0.1:0", "", true) 105 | }, 106 | }, 107 | } 108 | 109 | for _, tc := range testCases { 110 | t.Run(tc.name, tc.testFunc) 111 | } 112 | } 113 | 114 | func TestNewNodeServiceCapability(t *testing.T) { 115 | tests := []struct { 116 | cap csi.NodeServiceCapability_RPC_Type 117 | }{ 118 | { 119 | cap: csi.NodeServiceCapability_RPC_UNKNOWN, 120 | }, 121 | { 122 | cap: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME, 123 | }, 124 | { 125 | cap: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS, 126 | }, 127 | { 128 | cap: csi.NodeServiceCapability_RPC_EXPAND_VOLUME, 129 | }, 130 | } 131 | for _, test := range tests { 132 | resp := NewNodeServiceCapability(test.cap) 133 | assert.NotNil(t, resp) 134 | assert.Equal(t, resp.XXX_sizecache, int32(0)) 135 | } 136 | } 137 | 138 | func TestReplaceWithMap(t *testing.T) { 139 | tests := []struct { 140 | desc string 141 | str string 142 | m map[string]string 143 | expected string 144 | }{ 145 | { 146 | desc: "empty string", 147 | str: "", 148 | expected: "", 149 | }, 150 | { 151 | desc: "empty map", 152 | str: "", 153 | m: map[string]string{}, 154 | expected: "", 155 | }, 156 | { 157 | desc: "empty key", 158 | str: "prefix-" + pvNameMetadata, 159 | m: map[string]string{"": "pv"}, 160 | expected: "prefix-" + pvNameMetadata, 161 | }, 162 | { 163 | desc: "empty value", 164 | str: "prefix-" + pvNameMetadata, 165 | m: map[string]string{pvNameMetadata: ""}, 166 | expected: "prefix-", 167 | }, 168 | { 169 | desc: "one replacement", 170 | str: "prefix-" + pvNameMetadata, 171 | m: map[string]string{pvNameMetadata: "pv"}, 172 | expected: "prefix-pv", 173 | }, 174 | { 175 | desc: "multiple replacements", 176 | str: pvcNamespaceMetadata + pvcNameMetadata, 177 | m: map[string]string{pvcNamespaceMetadata: "namespace", pvcNameMetadata: "pvcname"}, 178 | expected: "namespacepvcname", 179 | }, 180 | } 181 | 182 | for _, test := range tests { 183 | result := replaceWithMap(test.str, test.m) 184 | if result != test.expected { 185 | t.Errorf("test[%s]: unexpected output: %v, expected result: %v", test.desc, result, test.expected) 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /pkg/glusterfs/identityserver.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/container-storage-interface/spec/lib/go/csi" 7 | "github.com/golang/protobuf/ptypes/wrappers" 8 | "google.golang.org/grpc/codes" 9 | "google.golang.org/grpc/status" 10 | "k8s.io/klog/v2" 11 | ) 12 | 13 | // IdentityServer struct of Glusterfs CSI driver with supported methods of CSI 14 | // identity server spec. 15 | type IdentityServer struct { 16 | *Driver 17 | } 18 | 19 | // NewIdentityServer initialize an identity server for GlusterFS CSI driver. 20 | func NewIdentityServer(g *Driver) *IdentityServer { 21 | return &IdentityServer{ 22 | Driver: g, 23 | } 24 | } 25 | 26 | // GetPluginInfo returns metadata of the plugin 27 | func (ids *IdentityServer) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { 28 | if ids.Driver.name == "" { 29 | return nil, status.Error(codes.Unavailable, "Driver name not configured") 30 | } 31 | 32 | if ids.Driver.version == "" { 33 | return nil, status.Error(codes.Unavailable, "Driver is missing version") 34 | } 35 | 36 | resp := &csi.GetPluginInfoResponse{ 37 | Name: glusterfsCSIDriverName, 38 | VendorVersion: glusterfsCSIDriverVersion, 39 | } 40 | klog.V(1).Infof("plugininfo response: %+v", resp) 41 | return resp, nil 42 | } 43 | 44 | // GetPluginCapabilities returns available capabilities of the plugin 45 | func (is *IdentityServer) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { 46 | resp := &csi.GetPluginCapabilitiesResponse{ 47 | Capabilities: []*csi.PluginCapability{ 48 | { 49 | Type: &csi.PluginCapability_Service_{ 50 | Service: &csi.PluginCapability_Service{ 51 | Type: csi.PluginCapability_Service_CONTROLLER_SERVICE, 52 | }, 53 | }, 54 | }, 55 | }, 56 | } 57 | klog.V(1).Infof("plugin capability response: %+v", resp) 58 | return resp, nil 59 | } 60 | 61 | // Probe returns the health and readiness of the plugin 62 | func (is *IdentityServer) Probe(ctx context.Context, req *csi.ProbeRequest) (*csi.ProbeResponse, error) { 63 | return &csi.ProbeResponse{Ready: &wrappers.BoolValue{Value: true}}, nil 64 | } 65 | -------------------------------------------------------------------------------- /pkg/glusterfs/identityserver_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package glusterfs 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "testing" 23 | 24 | "github.com/container-storage-interface/spec/lib/go/csi" 25 | "github.com/stretchr/testify/assert" 26 | "google.golang.org/grpc/codes" 27 | "google.golang.org/grpc/status" 28 | ) 29 | 30 | func TestGetPluginInfo(t *testing.T) { 31 | req := csi.GetPluginInfoRequest{} 32 | emptyNameDriver := NewEmptyDriver("name") 33 | emptyVersionDriver := NewEmptyDriver("version") 34 | tests := []struct { 35 | desc string 36 | driver *Driver 37 | expectedErr error 38 | }{ 39 | { 40 | desc: "Successful Request", 41 | driver: NewEmptyDriver(""), 42 | expectedErr: nil, 43 | }, 44 | { 45 | desc: "Driver name missing", 46 | driver: emptyNameDriver, 47 | expectedErr: status.Error(codes.Unavailable, "Driver name not configured"), 48 | }, 49 | { 50 | desc: "Driver version missing", 51 | driver: emptyVersionDriver, 52 | expectedErr: status.Error(codes.Unavailable, "Driver is missing version"), 53 | }, 54 | } 55 | 56 | for _, test := range tests { 57 | fakeIdentityServer := IdentityServer{ 58 | Driver: test.driver, 59 | } 60 | _, err := fakeIdentityServer.GetPluginInfo(context.Background(), &req) 61 | if !errors.Is(err, test.expectedErr) { 62 | t.Errorf("Unexpected error: %v\nExpected: %v", err, test.expectedErr) 63 | } 64 | } 65 | } 66 | 67 | func TestProbe(t *testing.T) { 68 | d := NewEmptyDriver("") 69 | req := csi.ProbeRequest{} 70 | fakeIdentityServer := IdentityServer{ 71 | Driver: d, 72 | } 73 | resp, err := fakeIdentityServer.Probe(context.Background(), &req) 74 | if result := assert.NoError(t, err); result == false { 75 | t.Fatal() 76 | } 77 | if result := assert.NotNil(t, resp); result == false { 78 | t.Fatal() 79 | } 80 | if result := assert.NotNil(t, resp.Ready); result == false { 81 | t.Fatal() 82 | } 83 | if result := assert.NotNil(t, resp.Ready.Value); result == false { 84 | t.Fatal() 85 | } 86 | assert.Equal(t, resp.XXX_sizecache, int32(0)) 87 | assert.Equal(t, resp.Ready.Value, true) 88 | } 89 | 90 | func TestGetPluginCapabilities(t *testing.T) { 91 | expectedCap := []*csi.PluginCapability{ 92 | { 93 | Type: &csi.PluginCapability_Service_{ 94 | Service: &csi.PluginCapability_Service{ 95 | Type: csi.PluginCapability_Service_CONTROLLER_SERVICE, 96 | }, 97 | }, 98 | }, 99 | } 100 | 101 | d := NewEmptyDriver("") 102 | fakeIdentityServer := IdentityServer{ 103 | Driver: d, 104 | } 105 | req := csi.GetPluginCapabilitiesRequest{} 106 | resp, err := fakeIdentityServer.GetPluginCapabilities(context.Background(), &req) 107 | assert.NoError(t, err) 108 | assert.NotNil(t, resp) 109 | assert.Equal(t, int32(0), resp.XXX_sizecache) 110 | assert.Equal(t, expectedCap, resp.Capabilities) 111 | 112 | } 113 | -------------------------------------------------------------------------------- /pkg/glusterfs/nodeserver.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/container-storage-interface/spec/lib/go/csi" 9 | "github.com/kubernetes-csi/csi-lib-utils/protosanitizer" 10 | "golang.org/x/net/context" 11 | "google.golang.org/grpc/codes" 12 | "google.golang.org/grpc/status" 13 | "k8s.io/klog/v2" 14 | mount "k8s.io/mount-utils" 15 | ) 16 | 17 | // NodeServer struct of Glusterfs CSI driver with supported methods of CSI node 18 | // server spec. 19 | type NodeServer struct { 20 | *Driver 21 | mounter mount.Interface 22 | } 23 | 24 | var glusterMounter = mount.New("") 25 | 26 | // NodePublishVolume mounts the volume mounted to the staging path to the target 27 | // path 28 | func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { 29 | klog.V(2).Infof("received node publish volume request %+v", protosanitizer.StripSecrets(req)) 30 | 31 | if err := ns.validateNodePublishVolumeReq(req); err != nil { 32 | return nil, err 33 | } 34 | 35 | targetPath := req.GetTargetPath() 36 | 37 | notMnt, err := glusterMounter.IsLikelyNotMountPoint(targetPath) 38 | 39 | if err != nil { 40 | if os.IsNotExist(err) { 41 | // #nosec 42 | if err = os.MkdirAll(targetPath, 0777); err != nil { 43 | return nil, status.Error(codes.Internal, err.Error()) 44 | } 45 | notMnt = true 46 | } else { 47 | return nil, status.Error(codes.Internal, err.Error()) 48 | } 49 | } 50 | 51 | if !notMnt { 52 | return &csi.NodePublishVolumeResponse{}, nil 53 | } 54 | 55 | mo := req.GetVolumeCapability().GetMount().GetMountFlags() 56 | 57 | if req.GetReadonly() { 58 | mo = append(mo, "ro") 59 | } 60 | gs := req.GetVolumeContext()["glusterserver"] 61 | 62 | ep := req.GetVolumeContext()["glustervol"] 63 | source := fmt.Sprintf("%s:%s", gs, ep) 64 | err = doMount(source, targetPath, mo) 65 | if err != nil { 66 | return nil, status.Error(codes.InvalidArgument, err.Error()) 67 | } 68 | 69 | return &csi.NodePublishVolumeResponse{}, nil 70 | } 71 | 72 | func doMount(source, targetPath string, mo []string) error { 73 | err := glusterMounter.Mount(source, targetPath, "glusterfs", mo) 74 | if err != nil { 75 | if os.IsPermission(err) { 76 | return status.Error(codes.PermissionDenied, err.Error()) 77 | } 78 | if strings.Contains(err.Error(), "invalid argument") { 79 | return status.Error(codes.InvalidArgument, err.Error()) 80 | } 81 | return status.Error(codes.Internal, err.Error()) 82 | } 83 | // #nosec 84 | err = os.Chmod(targetPath, 0777) 85 | if err != nil { 86 | return status.Error(codes.Internal, err.Error()) 87 | } 88 | return nil 89 | } 90 | 91 | func (ns *NodeServer) validateNodePublishVolumeReq(req *csi.NodePublishVolumeRequest) error { 92 | if req == nil { 93 | return status.Error(codes.InvalidArgument, "request cannot be empty") 94 | } 95 | 96 | if req.GetVolumeId() == "" { 97 | return status.Error(codes.InvalidArgument, "NodePublishVolume Volume ID must be provided") 98 | } 99 | 100 | if req.GetTargetPath() == "" { 101 | return status.Error(codes.InvalidArgument, "NodePublishVolume Target Path cannot be empty") 102 | } 103 | 104 | if req.GetVolumeCapability() == nil { 105 | return status.Error(codes.InvalidArgument, "NodePublishVolume Volume Capability must be provided") 106 | } 107 | return nil 108 | } 109 | 110 | // NodeUnpublishVolume unmounts the volume from the target path 111 | func (ns *NodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { 112 | if req == nil { 113 | return nil, status.Errorf(codes.InvalidArgument, "request cannot be empty") 114 | } 115 | 116 | if req.VolumeId == "" { 117 | return nil, status.Error(codes.InvalidArgument, "NodeUnpublishVolume Volume ID must be provided") 118 | } 119 | 120 | if req.TargetPath == "" { 121 | return nil, status.Error(codes.InvalidArgument, "NodeUnpublishVolume Target Path must be provided") 122 | } 123 | 124 | targetPath := req.GetTargetPath() 125 | notMnt, err := glusterMounter.IsLikelyNotMountPoint(targetPath) 126 | if err != nil { 127 | if os.IsNotExist(err) { 128 | return nil, status.Error(codes.NotFound, "targetpath not found") 129 | } 130 | return nil, status.Error(codes.Internal, err.Error()) 131 | 132 | } 133 | 134 | if notMnt { 135 | return nil, status.Error(codes.NotFound, "volume not mounted") 136 | } 137 | 138 | err = mount.CleanupMountPoint(req.GetTargetPath(), glusterMounter, false) 139 | if err != nil { 140 | return nil, status.Error(codes.Internal, err.Error()) 141 | } 142 | 143 | return &csi.NodeUnpublishVolumeResponse{}, nil 144 | } 145 | 146 | // NodeGetInfo returns NodeGetInfoResponse for CO. 147 | func (ns *NodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) { 148 | return &csi.NodeGetInfoResponse{ 149 | NodeId: ns.Driver.nodeID, 150 | }, nil 151 | } 152 | 153 | // NodeGetCapabilities returns the supported capabilities of the node server 154 | func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { 155 | return &csi.NodeGetCapabilitiesResponse{}, nil 156 | } 157 | 158 | // NodeStageVolume mounts the volume to a staging path on the node. 159 | func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { 160 | return nil, status.Error(codes.Unimplemented, "") 161 | } 162 | 163 | // NodeUnstageVolume unstages the volume from the staging path 164 | func (ns *NodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) { 165 | return nil, status.Error(codes.Unimplemented, "") 166 | } 167 | 168 | // NodeGetVolumeStats returns volume capacity statistics available for the 169 | // volume 170 | func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) { 171 | 172 | // TODO need to implement volume status call 173 | return nil, status.Error(codes.Unimplemented, "") 174 | 175 | } 176 | 177 | // NodeExpandVolume returns volume capacity statistics available for the 178 | // volume 179 | func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) { 180 | 181 | // TODO need to implement volume status call 182 | return nil, status.Error(codes.Unimplemented, "") 183 | 184 | } 185 | -------------------------------------------------------------------------------- /pkg/glusterfs/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package glusterfs 18 | 19 | import ( 20 | "net" 21 | "os" 22 | "sync" 23 | "time" 24 | 25 | "github.com/container-storage-interface/spec/lib/go/csi" 26 | "google.golang.org/grpc" 27 | "k8s.io/klog/v2" 28 | ) 29 | 30 | // Defines Non blocking GRPC server interfaces 31 | type NonBlockingGRPCServer interface { 32 | // Start services at the endpoint 33 | Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, testMode bool) 34 | // Waits for the service to stop 35 | Wait() 36 | // Stops the service gracefully 37 | Stop() 38 | // Stops the service forcefully 39 | ForceStop() 40 | } 41 | 42 | func NewNonBlockingGRPCServer() NonBlockingGRPCServer { 43 | return &nonBlockingGRPCServer{} 44 | } 45 | 46 | // NonBlocking server 47 | type nonBlockingGRPCServer struct { 48 | wg sync.WaitGroup 49 | server *grpc.Server 50 | } 51 | 52 | func (s *nonBlockingGRPCServer) Start(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, testMode bool) { 53 | 54 | s.wg.Add(1) 55 | 56 | go s.serve(endpoint, ids, cs, ns, testMode) 57 | } 58 | 59 | func (s *nonBlockingGRPCServer) Wait() { 60 | s.wg.Wait() 61 | } 62 | 63 | func (s *nonBlockingGRPCServer) Stop() { 64 | s.server.GracefulStop() 65 | } 66 | 67 | func (s *nonBlockingGRPCServer) ForceStop() { 68 | s.server.Stop() 69 | } 70 | 71 | func (s *nonBlockingGRPCServer) serve(endpoint string, ids csi.IdentityServer, cs csi.ControllerServer, ns csi.NodeServer, testMode bool) { 72 | 73 | proto, addr, err := ParseEndpoint(endpoint) 74 | if err != nil { 75 | klog.Fatal(err.Error()) 76 | } 77 | 78 | if proto == "unix" { 79 | addr = "/" + addr 80 | if err := os.Remove(addr); err != nil && !os.IsNotExist(err) { 81 | klog.Fatalf("Failed to remove %s, error: %s", addr, err.Error()) 82 | } 83 | } 84 | 85 | listener, err := net.Listen(proto, addr) 86 | if err != nil { 87 | klog.Fatalf("Failed to listen: %v", err) 88 | } 89 | 90 | opts := []grpc.ServerOption{ 91 | grpc.UnaryInterceptor(logGRPC), 92 | } 93 | server := grpc.NewServer(opts...) 94 | s.server = server 95 | 96 | if ids != nil { 97 | csi.RegisterIdentityServer(server, ids) 98 | } 99 | if cs != nil { 100 | csi.RegisterControllerServer(server, cs) 101 | } 102 | if ns != nil { 103 | csi.RegisterNodeServer(server, ns) 104 | } 105 | 106 | // Used to stop the server while running tests 107 | if testMode { 108 | s.wg.Done() 109 | go func() { 110 | // make sure Serve() is called 111 | s.wg.Wait() 112 | time.Sleep(time.Millisecond * 1000) 113 | s.server.GracefulStop() 114 | }() 115 | } 116 | 117 | klog.Infof("Listening for connections on address: %#v", listener.Addr()) 118 | 119 | err = server.Serve(listener) 120 | if err != nil { 121 | klog.Fatalf("Failed to serve grpc server: %v", err) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /pkg/glusterfs/utils.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/container-storage-interface/spec/lib/go/csi" 9 | "github.com/kubernetes-csi/csi-lib-utils/protosanitizer" 10 | "google.golang.org/grpc" 11 | "k8s.io/klog/v2" 12 | ) 13 | 14 | func NewNodeServiceCapability(cap csi.NodeServiceCapability_RPC_Type) *csi.NodeServiceCapability { 15 | return &csi.NodeServiceCapability{ 16 | Type: &csi.NodeServiceCapability_Rpc{ 17 | Rpc: &csi.NodeServiceCapability_RPC{ 18 | Type: cap, 19 | }, 20 | }, 21 | } 22 | } 23 | 24 | func ParseEndpoint(ep string) (string, string, error) { 25 | if strings.HasPrefix(strings.ToLower(ep), "unix://") || strings.HasPrefix(strings.ToLower(ep), "tcp://") { 26 | s := strings.SplitN(ep, "://", 2) 27 | if s[1] != "" { 28 | return s[0], s[1], nil 29 | } 30 | } 31 | return "", "", fmt.Errorf("invalid endpoint: %v", ep) 32 | } 33 | 34 | func getLogLevel(method string) int32 { 35 | if method == "/csi.v1.Identity/Probe" || 36 | method == "/csi.v1.Node/NodeGetCapabilities" || 37 | method == "/csi.v1.Node/NodeGetVolumeStats" { 38 | return 8 39 | } 40 | return 2 41 | } 42 | 43 | func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 44 | level := klog.Level(getLogLevel(info.FullMethod)) 45 | klog.V(level).Infof("GRPC call: %s", info.FullMethod) 46 | klog.V(level).Infof("GRPC request: %s", protosanitizer.StripSecrets(req)) 47 | 48 | resp, err := handler(ctx, req) 49 | if err != nil { 50 | klog.Errorf("GRPC error: %v", err) 51 | } else { 52 | klog.V(level).Infof("GRPC response: %s", protosanitizer.StripSecrets(resp)) 53 | } 54 | return resp, err 55 | } 56 | -------------------------------------------------------------------------------- /pkg/glusterfs/version.go: -------------------------------------------------------------------------------- 1 | package glusterfs 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | 8 | "sigs.k8s.io/yaml" 9 | ) 10 | 11 | // These are set during build time via -ldflags 12 | var ( 13 | driverVersion = "N/A" 14 | gitCommit = "N/A" 15 | buildDate = "N/A" 16 | ) 17 | 18 | // VersionInfo holds the version information of the driver 19 | type VersionInfo struct { 20 | DriverName string `json:"Driver Name"` 21 | DriverVersion string `json:"Driver Version"` 22 | GitCommit string `json:"Git Commit"` 23 | BuildDate string `json:"Build Date"` 24 | GoVersion string `json:"Go Version"` 25 | Compiler string `json:"Compiler"` 26 | Platform string `json:"Platform"` 27 | } 28 | 29 | // GetVersion returns the version information of the driver 30 | func GetVersion(driverName string) VersionInfo { 31 | return VersionInfo{ 32 | DriverName: driverName, 33 | DriverVersion: driverVersion, 34 | GitCommit: gitCommit, 35 | BuildDate: buildDate, 36 | GoVersion: runtime.Version(), 37 | Compiler: runtime.Compiler, 38 | Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), 39 | } 40 | } 41 | 42 | // GetVersionYAML returns the version information of the driver 43 | // in YAML format 44 | func GetVersionYAML(driverName string) (string, error) { 45 | info := GetVersion(driverName) 46 | marshalled, err := yaml.Marshal(&info) 47 | if err != nil { 48 | return "", err 49 | } 50 | return strings.TrimSpace(string(marshalled)), nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/utils/common-utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/container-storage-interface/spec/lib/go/csi" 11 | "github.com/gluster/glusterd2/pkg/restclient" 12 | "google.golang.org/grpc/codes" 13 | "google.golang.org/grpc/status" 14 | "k8s.io/klog/v2" 15 | mount "k8s.io/mount-utils" 16 | ) 17 | 18 | // Common allocation units 19 | const ( 20 | KB int64 = 1000 21 | MB int64 = 1000 * KB 22 | GB int64 = 1000 * MB 23 | TB int64 = 1000 * GB 24 | 25 | minReplicaCount = 1 26 | maxReplicaCount = 10 27 | ) 28 | 29 | var mounter = mount.New("") 30 | 31 | // RoundUpSize calculates how many allocation units are needed to accommodate 32 | // a volume of given size. 33 | // RoundUpSize(1500 * 1000*1000, 1000*1000*1000) returns '2' 34 | // (2 GB is the smallest allocatable volume that can hold 1500MiB) 35 | func RoundUpSize(volumeSizeBytes int64, allocationUnitBytes int64) int64 { 36 | return (volumeSizeBytes + allocationUnitBytes - 1) / allocationUnitBytes 37 | } 38 | 39 | // RoundUpToGB rounds up given quantity upto chunks of GB 40 | func RoundUpToGB(sizeBytes int64) int64 { 41 | return RoundUpSize(sizeBytes, GB) 42 | } 43 | 44 | // MountVolume mounts the given source at the target 45 | func MountVolume(srcPath string, targetPath string, fstype string, mo []string) error { 46 | notMnt, err := mounter.IsLikelyNotMountPoint(targetPath) 47 | if err != nil { 48 | if os.IsNotExist(err) { 49 | if err = os.MkdirAll(targetPath, 0750); err != nil { 50 | return status.Error(codes.Internal, err.Error()) 51 | } 52 | notMnt = true 53 | } else { 54 | return status.Error(codes.Internal, err.Error()) 55 | } 56 | } 57 | 58 | if !notMnt { 59 | return nil 60 | } 61 | 62 | err = mounter.Mount(srcPath, targetPath, fstype, mo) 63 | if err != nil { 64 | if os.IsPermission(err) { 65 | return status.Error(codes.PermissionDenied, err.Error()) 66 | } 67 | if strings.Contains(err.Error(), "invalid argument") { 68 | return status.Error(codes.InvalidArgument, err.Error()) 69 | } 70 | return status.Error(codes.Internal, err.Error()) 71 | } 72 | return nil 73 | } 74 | 75 | // GetClusterNodes returns the gluster cluster peer addresses 76 | func GetClusterNodes(client *restclient.Client) (string, []string, error) { 77 | peers, err := client.Peers() 78 | if err != nil { 79 | return "", nil, err 80 | } 81 | glusterServer := "" 82 | bkpservers := []string{} 83 | 84 | for i, p := range peers { 85 | if i == 0 { 86 | for _, a := range p.PeerAddresses { 87 | ip := strings.Split(a, ":") 88 | glusterServer = ip[0] 89 | } 90 | 91 | continue 92 | } 93 | for _, a := range p.PeerAddresses { 94 | ip := strings.Split(a, ":") 95 | bkpservers = append(bkpservers, ip[0]) 96 | } 97 | 98 | } 99 | klog.V(2).Infof("primary and backup gluster servers [%+v,%+v]", glusterServer, bkpservers) 100 | 101 | return glusterServer, bkpservers, err 102 | } 103 | 104 | // ValidateThinArbiter validates the thin arbiter volume parameters 105 | func ValidateThinArbiter(req *csi.CreateVolumeRequest) error { 106 | if _, ok := req.Parameters["arbiterPath"]; ok { 107 | rc, ok := req.Parameters["replicas"] 108 | if ok { 109 | count, err := strconv.ParseInt(rc, 10, 32) 110 | if err != nil { 111 | return err 112 | } 113 | if count != 2 { 114 | return errors.New("thin arbiter can only be enabled for replica count 2") 115 | } 116 | } 117 | } else { 118 | return errors.New("thin arbiterPath not specified") 119 | } 120 | return nil 121 | } 122 | 123 | // ParseVolumeParamInt validates replicaCount 124 | func ParseVolumeParamInt(key, valueString string) (int, error) { 125 | errPrefix := fmt.Sprintf("invalid value for parameter '%s'", key) 126 | count, err := strconv.Atoi(valueString) 127 | if err != nil { 128 | return 0, fmt.Errorf("%s, value '%s' must be an integer between %d and %d", errPrefix, valueString, minReplicaCount, maxReplicaCount) 129 | } 130 | 131 | if count < minReplicaCount { 132 | return 0, fmt.Errorf("%s, value '%s' must be >= %v", errPrefix, valueString, minReplicaCount) 133 | } 134 | if count > maxReplicaCount { 135 | return 0, fmt.Errorf("%s, value '%s' must be <= %v", errPrefix, valueString, maxReplicaCount) 136 | } 137 | 138 | return count, nil 139 | } 140 | -------------------------------------------------------------------------------- /scripts/install-go-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | GOPATH=$(go env GOPATH) 6 | GOBINDIR="${GOPATH}/bin" 7 | 8 | install_golangci-lint() { 9 | VER="${GOLANGCILINT_VERSION}" 10 | if type golangci-lint >/dev/null 2>&1; then 11 | echo "golangci-lint already installed" 12 | return 13 | fi 14 | 15 | echo "Installing golangci-lint. Version: ${VER}" 16 | curl -L "https://github.com/golangci/golangci-lint/releases/download/v${VER}/golangci-lint-${VER}-linux-amd64.tar.gz" | tar xzv -C "${GOBINDIR}" "golangci-lint-${VER}-linux-amd64/golangci-lint" --strip 1 17 | } 18 | 19 | install_golangci-lint 20 | -------------------------------------------------------------------------------- /scripts/lint-go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o pipefail 4 | 5 | if [[ -x "$(command -v golangci-lint)" ]]; then 6 | golangci-lint run 7 | else 8 | echo "WARNING: gometalinter not found, skipping lint tests" >&2 9 | fi 10 | -------------------------------------------------------------------------------- /scripts/lint-text.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # vim: set ts=4 sw=4 et : 3 | 4 | # Usage: pre-commit.sh [--require-all] 5 | # --require-all Fail instead of warn if a checker is not found 6 | 7 | set -e 8 | 9 | # Run checks from root of the repo 10 | scriptdir="$(dirname "$(realpath "$0")")" 11 | cd "$scriptdir/.." 12 | 13 | # run_check [optional args to checker...] 14 | function run_check() { 15 | regex="$1" 16 | shift 17 | exe="$1" 18 | shift 19 | 20 | if [ -x "$(command -v "$exe")" ]; then 21 | find . -path ./vendor -prune -o -regextype egrep -iregex "$regex" -print0 | \ 22 | xargs -0rt -n1 "$exe" "$@" 23 | elif [ "$all_required" -eq 0 ]; then 24 | echo "Warning: $exe not found... skipping some tests." 25 | else 26 | echo "FAILED: All checks required, but $exe not found!" 27 | exit 1 28 | fi 29 | } 30 | 31 | all_required=0 32 | if [ "x$1" == "x--require-all" ]; then 33 | all_required=1 34 | fi 35 | 36 | # Install via: gem install asciidoctor 37 | run_check '.*\.adoc' asciidoctor -o /dev/null -v --failure-level WARN 38 | 39 | # markdownlint: https://github.com/markdownlint/markdownlint 40 | # https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md 41 | # Install via: gem install mdl 42 | run_check '.*\.md' mdl --style scripts/mdl-style.rb 43 | 44 | # Install via: dnf install shellcheck 45 | run_check '.*\.(ba)?sh' shellcheck 46 | run_check '.*\.(ba)?sh' bash -n 47 | 48 | # Install via: pip install yamllint 49 | run_check '.*\.ya?ml' yamllint -s -d "{extends: default, rules: {line-length: {allow-non-breakable-inline-mappings: true}}}" 50 | 51 | echo "ALL OK." 52 | -------------------------------------------------------------------------------- /scripts/mdl-style.rb: -------------------------------------------------------------------------------- 1 | all 2 | 3 | #Refer below url for more information about the markdown rules. 4 | #https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md 5 | 6 | rule 'MD013', :code_blocks => false, :tables => false 7 | 8 | exclude_rule 'MD040' # Fenced code blocks should have a language specified 9 | exclude_rule 'MD041' # First line in file should be a top level header 10 | -------------------------------------------------------------------------------- /scripts/test-go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | GOPACKAGES="$(go list ./... | grep -v vendor | grep -v e2e)" 4 | COVERFILE="${GO_COVER_DIR}profile.cov" 5 | 6 | # no special options, exec to go test w/ all pkgs 7 | if [[ ${TEST_EXITFIRST} != "yes" && -z ${TEST_COVERAGE} ]]; then 8 | # shellcheck disable=SC2086 9 | exec go test ${GOPACKAGES} 10 | fi 11 | 12 | # our options are set so we need to handle each go package one 13 | # at at time 14 | if [[ ${TEST_COVERAGE} ]]; then 15 | GOTESTOPTS="-covermode=count -coverprofile=cover.out" 16 | echo "mode: count" > "${COVERFILE}" 17 | fi 18 | 19 | failed=0 20 | for gopackage in ${GOPACKAGES}; do 21 | echo "--- testing: ${gopackage} ---" 22 | # shellcheck disable=SC2086 23 | go test ${GOTESTOPTS} "${gopackage}" || ((failed+=1)) 24 | if [[ -f cover.out ]]; then 25 | # Append to coverfile 26 | grep -v "^mode: count" cover.out >> "${COVERFILE}" 27 | fi 28 | if [[ ${TEST_COVERAGE} = "stdout" && -f cover.out ]]; then 29 | go tool cover -func=cover.out 30 | fi 31 | if [[ ${TEST_COVERAGE} = "html" && -f cover.out ]]; then 32 | mkdir -p coverage 33 | fn="coverage/${gopackage////-}.html" 34 | echo " * generating coverage html: ${fn}" 35 | go tool cover -html=cover.out -o "${fn}" 36 | fi 37 | rm -f cover.out 38 | if [[ ${failed} -ne 0 && ${TEST_EXITFIRST} = "yes" ]]; then 39 | exit ${failed} 40 | fi 41 | done 42 | exit ${failed} 43 | --------------------------------------------------------------------------------