├── .github ├── CODEOWNERS ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── common-workflows.yaml │ ├── go-version.yaml │ ├── image-version-update.yaml │ ├── release.yaml │ ├── update-libraries-to-commits.yaml │ └── update-libraries.yaml ├── .gitignore ├── .mockery.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd └── csi-powerstore │ ├── main.go │ └── main_test.go ├── core ├── .gitignore ├── core.go ├── core_test.go ├── semver.tpl └── semver │ ├── semver.go │ └── semver_test.go ├── dell-csi-helm-installer ├── .gitignore ├── README.md ├── common.sh ├── csi-install.sh ├── csi-offline-bundle.md ├── csi-offline-bundle.sh ├── csi-uninstall.sh ├── verify-csi-powerstore.sh └── verify.sh ├── docker.mk ├── env.sh ├── go.mod ├── go.sum ├── licenses └── Apache.txt ├── mocks ├── Consumer.go ├── ControllerInterface.go ├── FcConnector.go ├── FileInfo.go ├── FsInterface.go ├── ISCSIConnector.go ├── NFSv4ACLsInterface.go ├── NVMEConnector.go ├── NodeInterface.go ├── NodeLabelsModifier.go ├── NodeLabelsRetriever.go ├── NodeVolumePublisher.go ├── NodeVolumeStager.go ├── TracerConfigurator.go ├── UtilInterface.go ├── VolumeCreator.go └── VolumePublisher.go ├── pkg ├── array │ ├── array.go │ ├── array_test.go │ └── testdata │ │ ├── incorrect-endpoint.yaml │ │ ├── invalid-endpoint.yaml │ │ ├── no-arr.yaml │ │ ├── no-globalID.yaml │ │ ├── one-arr.yaml │ │ └── two-arr.yaml ├── common │ ├── common.go │ ├── common_test.go │ ├── envvars.go │ ├── fs │ │ ├── fs.go │ │ └── fs_test.go │ ├── k8sutils │ │ ├── k8sutils.go │ │ └── k8sutils_test.go │ └── logger.go ├── controller │ ├── base.go │ ├── base_test.go │ ├── controller.go │ ├── controller_node_to_array_connectivity.go │ ├── controller_test.go │ ├── creator.go │ ├── creator_test.go │ ├── csi_extension_server.go │ ├── csi_extension_server_test.go │ ├── publisher.go │ ├── publisher_test.go │ ├── replication.go │ ├── replication_test.go │ └── snapshotter.go ├── identity │ ├── identity.go │ └── identity_test.go ├── interceptors │ ├── interceptors.go │ └── interceptors_test.go ├── node │ ├── acl.go │ ├── acl_test.go │ ├── base.go │ ├── ephemeral.go │ ├── node.go │ ├── node_connectivity_checker.go │ ├── node_connectivity_checker_test.go │ ├── node_test.go │ ├── publisher.go │ ├── stager.go │ └── stager_test.go ├── provider │ ├── provider.go │ └── provider_test.go ├── service │ ├── controller.go │ ├── controller_test.go │ ├── identity.go │ ├── identity_test.go │ ├── node.go │ ├── node_test.go │ ├── service.go │ └── service_test.go └── tracer │ ├── tracer.go │ └── tracer_test.go ├── samples ├── secret │ └── secret.yaml ├── storageclass │ ├── README.md │ ├── powerstore-ext4.yaml │ ├── powerstore-metro.yaml │ ├── powerstore-nfs.yaml │ ├── powerstore-replication.yaml │ ├── powerstore-topology.yaml │ └── powerstore-xfs.yaml └── volumesnapshotclass │ └── snapclass.yaml └── tests ├── e2e └── k8s │ ├── README.md │ ├── e2e-values.yaml │ ├── externalAccess.go │ ├── go.mod │ ├── go.sum │ ├── run.sh │ ├── suite_test.go │ ├── testing-manifests │ └── statefulset │ │ └── statefulset.yaml │ └── utils.go ├── sanity ├── .gitignore ├── Dockerfile ├── README.md ├── helm │ ├── sanity-csi-powerstore │ │ ├── Chart.yaml │ │ ├── templates │ │ │ ├── _helpers.tpl │ │ │ ├── controller.yaml │ │ │ ├── driver-config-params.yaml │ │ │ └── node.yaml │ │ └── values.yaml │ └── secret.yaml ├── install-sanity.sh ├── params.yaml └── test.sh ├── scale ├── .gitignore ├── longevity.sh └── volumes │ ├── Chart.yaml │ ├── templates │ └── test.yaml │ └── values.yaml └── simple └── simple.yaml /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS 2 | # 3 | # Documentation for this file can be found at: 4 | # https://help.github.com/en/articles/about-code-owners 5 | 6 | # These are the default owners for the code and will 7 | # be requested for review when someone opens a pull request. 8 | # Order is alphabetical for easier maintenance. 9 | # 10 | # Abhilash Muralidhara (abhi16394) 11 | # Adarsh Kumar Yadav (adarsh-dell) 12 | # Akshay Saini (AkshaySainiDell) 13 | # Don Khan (donatwork) 14 | # Santhosh Lakshmanan (santhoshatdell) 15 | 16 | # for all files 17 | * @abhi16394 @adarsh-dell @AkshaySainiDell @donatwork @santhoshatdell 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # To get started with Dependabot version updates, you'll need to specify which 3 | # package ecosystems to update and where the package manifests are located. 4 | # Please see the documentation for all configuration options: 5 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 6 | 7 | version: 2 8 | updates: 9 | # Schedule for go module updates 10 | - package-ecosystem: "gomod" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | day: "sunday" 15 | time: "18:00" 16 | allow: 17 | # Allow direct updates for packages 18 | - dependency-type: direct 19 | ignore: 20 | - dependency-name: "*" 21 | update-types: 22 | - version-update:semver-patch 23 | # a group of dependencies will be updated together in one pull request 24 | groups: 25 | golang: 26 | # group all semantic versioning levels together in one pull request 27 | update-types: 28 | - major 29 | - minor 30 | patterns: 31 | - "*" 32 | 33 | # github actions 34 | - package-ecosystem: "github-actions" 35 | directory: "/" 36 | schedule: 37 | # Check for updates to GitHub Actions every week 38 | interval: "weekly" 39 | day: "saturday" 40 | groups: 41 | github-actions: 42 | patterns: 43 | - "*" 44 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | A few sentences describing the overall goals of the pull request's commits. 3 | 4 | # GitHub Issues 5 | List the GitHub issues impacted by this PR: 6 | 7 | | GitHub Issue # | 8 | | -------------- | 9 | | | 10 | 11 | # Checklist: 12 | 13 | - [ ] I have performed a self-review of my own code to ensure there are no formatting, vetting, linting, or security issues 14 | - [ ] I have verified that new and existing unit tests pass locally with my changes 15 | - [ ] I have not allowed coverage numbers to degenerate 16 | - [ ] I have maintained at least 90% code coverage 17 | - [ ] I have commented my code, particularly in hard-to-understand areas 18 | - [ ] I have made corresponding changes to the documentation 19 | - [ ] I have added tests that prove my fix is effective or that my feature works 20 | - [ ] Backward compatibility is not broken 21 | 22 | # How Has This Been Tested? 23 | Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration 24 | 25 | - [ ] Test A 26 | - [ ] Test B 27 | -------------------------------------------------------------------------------- /.github/workflows/common-workflows.yaml: -------------------------------------------------------------------------------- 1 | name: Common Workflows 2 | on: # yamllint disable-line rule:truthy 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: ["**"] 7 | 8 | jobs: 9 | 10 | # golang static analysis checks 11 | go-static-analysis: 12 | uses: dell/common-github-actions/.github/workflows/go-static-analysis.yaml@main 13 | name: Golang Validation 14 | 15 | common: 16 | name: Quality Checks 17 | uses: dell/common-github-actions/.github/workflows/go-common.yml@main 18 | -------------------------------------------------------------------------------- /.github/workflows/go-version.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved. 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 | # Reusable workflow to perform go version update on Golang based projects 10 | name: Go Version Update 11 | 12 | on: # yamllint disable-line rule:truthy 13 | workflow_dispatch: 14 | repository_dispatch: 15 | types: [go-update-workflow] 16 | 17 | jobs: 18 | # go version update 19 | go-version-update: 20 | uses: dell/common-github-actions/.github/workflows/go-version-workflow.yaml@main 21 | name: Go Version Update 22 | secrets: inherit 23 | -------------------------------------------------------------------------------- /.github/workflows/image-version-update.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Dell Inc., or its subsidiaries. All Rights Reserved. 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 | # Reusable workflow to perform image version update on Golang based projects 10 | name: Image Version Update 11 | 12 | on: # yamllint disable-line rule:truthy 13 | workflow_dispatch: 14 | inputs: 15 | version: 16 | description: "Version to release (major, minor, patch) Ex: minor" 17 | required: true 18 | repository_dispatch: 19 | types: [image-update-workflow] 20 | 21 | jobs: 22 | # image version update 23 | image-version-update: 24 | uses: dell/common-github-actions/.github/workflows/image-version-workflow.yaml@main 25 | with: 26 | version: "${{ github.event.inputs.version || 'minor' }}" 27 | secrets: inherit 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release CSI-Powerstore 2 | # Invocable as a reusable workflow 3 | # Can be manually triggered 4 | on: # yamllint disable-line rule:truthy 5 | workflow_call: 6 | workflow_dispatch: 7 | inputs: 8 | option: 9 | description: 'Select version to release' 10 | required: true 11 | type: choice 12 | default: 'minor' 13 | options: 14 | - major 15 | - minor 16 | - patch 17 | - n-1/n-2 patch (Provide input in the below box) 18 | version: 19 | description: "Patch version to release. example: 2.1.x (Use this only if n-1/n-2 patch is selected)" 20 | required: false 21 | type: string 22 | repository_dispatch: 23 | types: [auto-release-workflow] 24 | jobs: 25 | process-inputs: 26 | name: Process Inputs 27 | runs-on: ubuntu-latest 28 | outputs: 29 | processedVersion: ${{ steps.set-version.outputs.versionEnv }} 30 | steps: 31 | - name: Process input 32 | id: set-version 33 | shell: bash 34 | run: | 35 | echo "Triggered by: ${{ github.event_name }}" 36 | if [[ "${{ github.event_name }}" == "repository_dispatch" ]]; then 37 | echo "versionEnv=minor" >> $GITHUB_OUTPUT 38 | exit 0 39 | fi 40 | if [[ "${{ github.event.inputs.version }}" != "" && "${{ github.event.inputs.option }}" == "n-1/n-2 patch (Provide input in the below box)" ]]; then 41 | # if both version and option are provided, then version takes precedence i.e. patch release for n-1/n-2 42 | echo "versionEnv=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT 43 | exit 0 44 | fi 45 | if [[ "${{ github.event.inputs.option }}" != "n-1/n-2 patch (Provide input in the below box)" ]]; then 46 | # if only option is provided, then option takes precedence i.e. minor, major or patch release 47 | echo "versionEnv=${{ github.event.inputs.option }}" >> $GITHUB_OUTPUT 48 | exit 0 49 | fi 50 | # if neither option nor version is provided, then minor release is taken by default (Auto-release) 51 | echo "versionEnv=minor" >> $GITHUB_OUTPUT 52 | csm-release: 53 | needs: [process-inputs] 54 | uses: dell/common-github-actions/.github/workflows/csm-release-driver-module.yaml@main 55 | name: Release CSM Drivers and Modules 56 | with: 57 | version: ${{ needs.process-inputs.outputs.processedVersion }} 58 | images: 'csi-powerstore' 59 | secrets: inherit 60 | -------------------------------------------------------------------------------- /.github/workflows/update-libraries-to-commits.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Dell Inc., or its subsidiaries. All Rights Reserved. 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 | # Reusable workflow to perform updates of Dell client libraries to latest commits 10 | name: Dell Libraries Commit Update 11 | on: # yamllint disable-line rule:truthy 12 | workflow_dispatch: 13 | repository_dispatch: 14 | types: [latest-commits-libraries] 15 | 16 | jobs: 17 | package-update: 18 | uses: dell/common-github-actions/.github/workflows/update-libraries-to-commits.yml@main 19 | name: Dell Libraries Update 20 | secrets: inherit 21 | -------------------------------------------------------------------------------- /.github/workflows/update-libraries.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Dell Inc., or its subsidiaries. All Rights Reserved. 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 | # Reusable workflow to perform updates of Dell client libraries 10 | name: Dell Libraries Release Update 11 | on: # yamllint disable-line rule:truthy 12 | workflow_dispatch: 13 | repository_dispatch: 14 | types: [latest-released-libraries] 15 | 16 | jobs: 17 | package-update: 18 | uses: dell/common-github-actions/.github/workflows/update-libraries.yml@main 19 | name: Dell Libraries Update 20 | secrets: inherit 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # binaries and temporary files 2 | /csi-powerstore 3 | semver.mk 4 | vars.mk 5 | Dockerfile-debug 6 | 7 | # test directories and artifacts 8 | service/c.out 9 | service/test/ 10 | 11 | # IDE 12 | .idea 13 | .vscode 14 | 15 | # gosec 16 | gosec.log 17 | gosecresults.csv 18 | 19 | # reports 20 | *.csv 21 | *.xml 22 | 23 | # coverage 24 | *.out 25 | *.html 26 | 27 | # logs 28 | *.log 29 | 30 | -------------------------------------------------------------------------------- /.mockery.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | quiet: false 18 | all: true 19 | inpackage: false 20 | dir: ./pkg 21 | disable-version-string: true 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright © 2023-2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # some arguments that must be supplied 14 | ARG GOIMAGE 15 | ARG BASEIMAGE 16 | 17 | # Stage to build the driver 18 | FROM $GOIMAGE as builder 19 | 20 | WORKDIR /workspace 21 | COPY . . 22 | 23 | RUN go generate ./cmd/csi-powerstore 24 | RUN GOOS=linux CGO_ENABLED=0 go build -o csi-powerstore ./cmd/csi-powerstore 25 | 26 | # Stage to build the driver image 27 | FROM $BASEIMAGE 28 | WORKDIR / 29 | LABEL vendor="Dell Technologies" \ 30 | maintainer="Dell Technologies" \ 31 | name="csi-powerstore" \ 32 | summary="CSI Driver for Dell EMC PowerStore" \ 33 | description="CSI Driver for provisioning persistent storage from Dell EMC PowerStore" \ 34 | release="1.14.0" \ 35 | version="2.14.0" \ 36 | license="Apache-2.0" 37 | COPY licenses /licenses 38 | 39 | # validate some cli utilities are found 40 | RUN which mkfs.ext4 41 | RUN which mkfs.xfs 42 | RUN echo "export PATH=$PATH:/sbin:/bin" > /etc/profile.d/ubuntu_path.sh 43 | 44 | COPY --from=builder /workspace/csi-powerstore / 45 | ENTRYPOINT ["/csi-powerstore"] 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2025 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # for variables override 18 | -include vars.mk 19 | 20 | all: clean build 21 | 22 | # Tag parameters 23 | ifndef TAGMSG 24 | TAGMSG="CSI Spec 1.6" 25 | endif 26 | 27 | clean: 28 | rm -f core/core_generated.go 29 | rm -f semver.mk 30 | go clean -cache 31 | 32 | generate: 33 | go generate ./cmd/csi-powerstore 34 | 35 | build: generate 36 | GOOS=linux CGO_ENABLED=0 go build ./cmd/csi-powerstore 37 | 38 | install: generate 39 | GOOS=linux CGO_ENABLED=0 go install ./cmd/csi-powerstore 40 | 41 | # Tags the release with the Tag parameters set above 42 | tag: 43 | go run core/semver/semver.go -f mk >semver.mk 44 | make -f docker.mk tag TAGMSG='$(TAGMSG)' 45 | 46 | # Generates the docker container (but does not push) 47 | docker: 48 | go run core/semver/semver.go -f mk >semver.mk 49 | make -f docker.mk docker 50 | 51 | # Same as `docker` but without cached layers and will pull latest version of base image 52 | docker-no-cache: 53 | go run core/semver/semver.go -f mk >semver.mk 54 | make -f docker.mk docker-no-cache 55 | 56 | # Pushes container to the repository 57 | push: docker 58 | make -f docker.mk push 59 | 60 | check: gosec 61 | gofmt -w ./. 62 | ifeq (, $(shell which golint)) 63 | go install golang.org/x/lint/golint@latest 64 | endif 65 | golint -set_exit_status ./. 66 | go vet ./... 67 | 68 | mocks: 69 | mockery 70 | 71 | test: 72 | cd ./pkg; go test -race -cover -coverprofile=coverage.out ./... 73 | 74 | coverage: 75 | cd ./pkg; go tool cover -html=coverage.out -o coverage.html 76 | 77 | gosec: 78 | ifeq (, $(shell which gosec)) 79 | go install github.com/securego/gosec/v2/cmd/gosec@latest 80 | $(shell $(GOBIN)/gosec -quiet -log gosec.log -out=gosecresults.csv -fmt=csv ./...) 81 | else 82 | $(shell gosec -quiet -log gosec.log -out=gosecresults.csv -fmt=csv ./...) 83 | endif 84 | @echo "Logs are stored at gosec.log, Outputfile at gosecresults.csv" 85 | 86 | 87 | .PHONY: actions action-help 88 | actions: ## Run all GitHub Action checks that run on a pull request creation 89 | @echo "Running all GitHub Action checks for pull request events..." 90 | @act -l | grep -v ^Stage | grep pull_request | grep -v image_security_scan | awk '{print $$2}' | while read WF; do \ 91 | echo "Running workflow: $${WF}"; \ 92 | act pull_request --no-cache-server --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --job "$${WF}"; \ 93 | done 94 | 95 | action-help: ## Echo instructions to run one specific workflow locally 96 | @echo "GitHub Workflows can be run locally with the following command:" 97 | @echo "act pull_request --no-cache-server --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --job " 98 | @echo "" 99 | @echo "Where '' is a Job ID returned by the command:" 100 | @echo "act -l" 101 | @echo "" 102 | @echo "NOTE: if act is not installed, it can be downloaded from https://github.com/nektos/act" 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSI Driver for Dell PowerStore 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/dell/csi-powerstore?style=flat-square)](https://goreportcard.com/report/github.com/dell/csi-powerstore) 4 | [![License](https://img.shields.io/github/license/dell/csi-powerstore?style=flat-square&color=blue&label=License)](https://github.com/dell/csi-powerstore/blob/master/LICENSE) 5 | [![Docker](https://img.shields.io/docker/pulls/dellemc/csi-powerstore.svg?logo=docker&style=flat-square&label=Pulls)](https://hub.docker.com/r/dellemc/csi-powerstore) 6 | [![Last Release](https://img.shields.io/github/v/release/dell/csi-powerstore?label=Latest&style=flat-square&logo=go)](https://github.com/dell/csi-powerstore/releases) 7 | 8 | **Repository for CSI Driver for Dell PowerStore** 9 | 10 | ## Description 11 | CSI Driver for PowerStore is part of the [CSM (Container Storage Modules)](https://github.com/dell/csm) open-source suite of Kubernetes storage enablers for Dell products. CSI Driver for PowerStore is a Container Storage Interface (CSI) driver that provides support for provisioning persistent storage using Dell PowerStore storage array. 12 | 13 | This project may be compiled as a stand-alone binary using Golang that, when run, provides a valid CSI endpoint. It also can be used as a precompiled container image. 14 | 15 | ## Table of Contents 16 | 17 | * [Code of Conduct](https://github.com/dell/csm/blob/main/docs/CODE_OF_CONDUCT.md) 18 | * [Maintainer Guide](https://github.com/dell/csm/blob/main/docs/MAINTAINER_GUIDE.md) 19 | * [Committer Guide](https://github.com/dell/csm/blob/main/docs/COMMITTER_GUIDE.md) 20 | * [Contributing Guide](https://github.com/dell/csm/blob/main/docs/CONTRIBUTING.md) 21 | * [List of Adopters](https://github.com/dell/csm/blob/main/docs/ADOPTERS.md) 22 | * [Support](#support) 23 | * [Security](https://github.com/dell/csm/blob/main/docs/SECURITY.md) 24 | * [Building](#building) 25 | * [Runtime Dependecies](#runtime-dependencies) 26 | * [Documentation](#documentation) 27 | 28 | ## Support 29 | For any issues, questions or feedback, please contact [Dell support](https://www.dell.com/support/incidents-online/en-us/contactus/product/container-storage-modules). 30 | 31 | ## Building 32 | This project is a Go module (see golang.org Module information for explanation). 33 | The dependencies for this project are listed in the go.mod file. 34 | 35 | To build the source, execute `make clean build`. 36 | 37 | To run unit tests, execute `make test`. 38 | 39 | To build an image, execute `make docker`. 40 | 41 | ## Runtime Dependencies 42 | 43 | Both the Controller and the Node portions of the driver can only be run on nodes with network connectivity to a Dell PowerStore server (which is used by the driver). 44 | 45 | If you want to use iSCSI as a transport protocol be sure that `iscsi-initiator-utils` package is installed on your node. 46 | 47 | If you want to use FC be sure that zoning of Host Bus Adapters to the FC port directors was done. 48 | 49 | If you want to use NFS be sure to enable it in `myvalues.yaml` or in your storage classes, and configure corresponding NAS servers on PowerStore. 50 | 51 | If you want to use NVMe/TCP be sure that the `nvme-cli` package is installed on your node. 52 | 53 | If you want to use NVMe/FC be sure that the NVMeFC zoning of the Host Bus Adapters to the Fibre Channel port is done. 54 | 55 | ## Documentation 56 | For more detailed information on the driver, please refer to [Container Storage Modules documentation](https://dell.github.io/csm-docs/). 57 | -------------------------------------------------------------------------------- /cmd/csi-powerstore/main_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2025 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | package main 18 | 19 | import ( 20 | "io" 21 | "os" 22 | "path/filepath" 23 | "strings" 24 | "testing" 25 | "time" 26 | 27 | "github.com/dell/csi-powerstore/v2/pkg/common" 28 | "github.com/dell/gocsi" 29 | "github.com/fsnotify/fsnotify" 30 | log "github.com/sirupsen/logrus" 31 | "github.com/spf13/viper" 32 | "github.com/stretchr/testify/assert" 33 | "github.com/stretchr/testify/require" 34 | ) 35 | 36 | func TestUpdateDriverName(t *testing.T) { 37 | tests := []struct { 38 | name string 39 | envVar string 40 | expected string 41 | }{ 42 | { 43 | name: "Environment variable is present", 44 | envVar: "test-driver", 45 | expected: "test-driver", 46 | }, 47 | { 48 | name: "Environment variable is not present", 49 | envVar: "", 50 | expected: "", 51 | }, 52 | } 53 | 54 | for _, tc := range tests { 55 | t.Run(tc.name, func(t *testing.T) { 56 | err := os.Setenv(common.EnvDriverName, tc.envVar) 57 | if err != nil { 58 | t.Fatalf("Failed to set environment variable: %v", err) 59 | } 60 | 61 | updateDriverName() 62 | 63 | assert.Equal(t, tc.expected, common.Name) 64 | }) 65 | } 66 | } 67 | 68 | func TestInitilizeDriverConfigParams(t *testing.T) { 69 | tmpDir := t.TempDir() 70 | content := `CSI_LOG_FORMAT: "JSON"` 71 | driverConfigParams := filepath.Join(tmpDir, "driver-config-params.yaml") 72 | writeToFile(t, driverConfigParams, content) 73 | os.Setenv(common.EnvConfigParamsFilePath, driverConfigParams) 74 | initilizeDriverConfigParams() 75 | assert.Equal(t, log.DebugLevel, log.GetLevel()) 76 | writeToFile(t, driverConfigParams, "CSI_LOG_LEVEL: \"info\"") 77 | time.Sleep(time.Second) 78 | assert.Equal(t, log.InfoLevel, log.GetLevel()) 79 | } 80 | 81 | func TestMainControllerMode(t *testing.T) { 82 | tmpDir := t.TempDir() 83 | config := copyConfigFileToTmpDir(t, "../../pkg/array/testdata/one-arr.yaml", tmpDir) 84 | 85 | // Set required environment variables 86 | os.Setenv(common.EnvArrayConfigFilePath, config) 87 | os.Setenv("CSI_ENDPOINT", "mock_endpoint") 88 | os.Setenv(common.EnvDriverName, "test") 89 | os.Setenv(common.EnvDebugEnableTracing, "true") 90 | os.Setenv("JAEGER_SERVICE_NAME", "controller-test") 91 | os.Setenv(string(gocsi.EnvVarMode), "controller") 92 | 93 | array2 := ` - endpoint: "https://127.0.0.2/api/rest" 94 | username: "admin" 95 | globalID: "gid2" 96 | password: "password" 97 | skipCertificateValidation: true 98 | blockProtocol: "auto" 99 | isDefault: false` 100 | 101 | runCSIPlugin = func(test *gocsi.StoragePlugin) { 102 | // Assertions 103 | require.NotNil(t, test.Controller) 104 | require.NotNil(t, test.Identity) 105 | require.NotNil(t, test.Node) 106 | 107 | // Update the config file 108 | writeToFile(t, config, array2) 109 | time.Sleep(time.Second) 110 | 111 | // Assertions 112 | require.NotNil(t, test.Controller) 113 | } 114 | 115 | defer func() { 116 | if r := recover(); r != nil { 117 | t.Fatalf("the code panicked with error: %v", r) 118 | } 119 | }() 120 | 121 | main() 122 | } 123 | 124 | func TestMainNodeMode(t *testing.T) { 125 | tmpDir := t.TempDir() 126 | config := copyConfigFileToTmpDir(t, "../../pkg/array/testdata/one-arr.yaml", tmpDir) 127 | 128 | // Set required environment variables 129 | os.Setenv(common.EnvArrayConfigFilePath, config) 130 | os.Setenv(gocsi.EnvVarMode, "node") 131 | os.Setenv(common.EnvDebugEnableTracing, "") 132 | tempNodeIDFile, err := os.CreateTemp("", "node-id") 133 | require.NoError(t, err) 134 | defer os.Remove(tempNodeIDFile.Name()) 135 | os.Setenv("X_CSI_POWERSTORE_NODE_ID_PATH", tempNodeIDFile.Name()) 136 | 137 | array2 := ` - endpoint: "https://127.0.0.2/api/rest" 138 | username: "admin" 139 | globalID: "gid2" 140 | password: "password" 141 | skipCertificateValidation: true 142 | blockProtocol: "auto" 143 | isDefault: false` 144 | 145 | runCSIPlugin = func(test *gocsi.StoragePlugin) { 146 | // Assertions 147 | require.NotNil(t, test.Controller) 148 | require.NotNil(t, test.Identity) 149 | require.NotNil(t, test.Node) 150 | 151 | // Update the config file 152 | writeToFile(t, config, array2) 153 | time.Sleep(time.Second) 154 | 155 | // Assertions 156 | require.NotNil(t, test.Node) 157 | } 158 | 159 | defer func() { 160 | if r := recover(); r != nil { 161 | t.Fatalf("the code panicked with error: %v", r) 162 | } 163 | }() 164 | 165 | main() 166 | } 167 | 168 | func copyConfigFileToTmpDir(t *testing.T, src string, tmpDir string) string { 169 | t.Helper() 170 | 171 | srcF, err := os.Open(src) 172 | require.NoError(t, err) 173 | defer srcF.Close() 174 | 175 | dstF, err := os.CreateTemp(tmpDir, "config_*.yaml") 176 | require.NoError(t, err) 177 | defer dstF.Close() 178 | 179 | _, err = io.Copy(dstF, srcF) 180 | require.NoError(t, err) 181 | 182 | return dstF.Name() 183 | } 184 | 185 | func writeToFile(t *testing.T, controllerConfigFile string, array2 string) { 186 | f, err := os.OpenFile(controllerConfigFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) 187 | if err != nil { 188 | t.Errorf("failed to open confg file %s, err %v", controllerConfigFile, err) 189 | } else { 190 | defer f.Close() 191 | _, err = f.WriteString(array2 + "\n") 192 | if err != nil { 193 | t.Errorf("failed to update confg file %s, err %v", controllerConfigFile, err) 194 | } 195 | } 196 | } 197 | 198 | func TestUpdateDriverConfigParams(t *testing.T) { 199 | v := viper.New() 200 | v.SetConfigType("yaml") 201 | v.SetDefault("CSI_LOG_FORMAT", "text") 202 | v.SetDefault("CSI_LOG_LEVEL", "debug") 203 | 204 | viperChan := make(chan bool) 205 | v.WatchConfig() 206 | v.OnConfigChange(func(_ fsnotify.Event) { 207 | updateDriverConfigParams(v) 208 | viperChan <- true 209 | }) 210 | 211 | logFormat := strings.ToLower(v.GetString("CSI_LOG_FORMAT")) 212 | assert.Equal(t, "text", logFormat) 213 | 214 | updateDriverConfigParams(v) 215 | level := log.GetLevel() 216 | 217 | assert.Equal(t, log.DebugLevel, level) 218 | 219 | v.Set("CSI_LOG_FORMAT", "json") 220 | v.Set("CSI_LOG_LEVEL", "info") 221 | updateDriverConfigParams(v) 222 | level = log.GetLevel() 223 | 224 | assert.Equal(t, log.InfoLevel, level) 225 | logFormatter, ok := log.StandardLogger().Formatter.(*log.JSONFormatter) 226 | assert.True(t, ok) 227 | assert.Equal(t, time.RFC3339Nano, logFormatter.TimestampFormat) 228 | 229 | v.Set("CSI_LOG_LEVEL", "notalevel") 230 | updateDriverConfigParams(v) 231 | level = log.GetLevel() 232 | assert.Equal(t, log.DebugLevel, level) 233 | } 234 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | core_generated.go 2 | -------------------------------------------------------------------------------- /core/core.go: -------------------------------------------------------------------------------- 1 | //go:generate go run semver/semver.go -f semver.tpl -o core_generated.go 2 | 3 | /* 4 | * 5 | * Copyright © 2020 Dell Inc. or its subsidiaries. All Rights Reserved. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | package core 22 | 23 | import "time" 24 | 25 | var ( 26 | // SemVer is the semantic version. 27 | SemVer = "unknown" 28 | 29 | // CommitSha7 is the short version of the commit hash from which 30 | // this program was built. 31 | CommitSha7 string 32 | 33 | // CommitSha32 is the long version of the commit hash from which 34 | // this program was built. 35 | CommitSha32 string 36 | 37 | // CommitTime is the commit timestamp of the commit from which 38 | // this program was built. 39 | CommitTime time.Time 40 | ) 41 | -------------------------------------------------------------------------------- /core/core_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCoreVariables(t *testing.T) { 11 | // Check if SemVer is set to a non-default value 12 | if SemVer != "unknown" { 13 | assert.NotEqual(t, "unknown", SemVer, "SemVer should not be 'unknown' if set during build") 14 | } else { 15 | assert.Equal(t, "unknown", SemVer, "SemVer should be 'unknown' by default") 16 | } 17 | 18 | // Check if CommitSha7 is set to a non-default value 19 | if CommitSha7 != "" { 20 | assert.NotEmpty(t, CommitSha7, "CommitSha7 should not be empty if set during build") 21 | } else { 22 | assert.Empty(t, CommitSha7, "CommitSha7 should be empty by default") 23 | } 24 | 25 | // Check if CommitSha32 is set to a non-default value 26 | if CommitSha32 != "" { 27 | assert.NotEmpty(t, CommitSha32, "CommitSha32 should not be empty if set during build") 28 | } else { 29 | assert.Empty(t, CommitSha32, "CommitSha32 should be empty by default") 30 | } 31 | 32 | // Check if CommitTime is set to a non-default value 33 | if !CommitTime.IsZero() { 34 | assert.False(t, CommitTime.IsZero(), "CommitTime should not be zero if set during build") 35 | } else { 36 | assert.True(t, CommitTime.IsZero(), "CommitTime should be zero by default") 37 | } 38 | 39 | // Test setting values 40 | SemVer = "1.0.0" 41 | CommitSha7 = "abcdefg" 42 | CommitSha32 = "abcdefg1234567890abcdefg1234567890" 43 | CommitTime = time.Now() 44 | 45 | assert.Equal(t, "1.0.0", SemVer, "SemVer should be '1.0.0'") 46 | assert.Equal(t, "abcdefg", CommitSha7, "CommitSha7 should be 'abcdefg'") 47 | assert.Equal(t, "abcdefg1234567890abcdefg1234567890", CommitSha32, "CommitSha32 should be 'abcdefg1234567890abcdefg1234567890'") 48 | assert.False(t, CommitTime.IsZero(), "CommitTime should not be zero") 49 | } 50 | -------------------------------------------------------------------------------- /core/semver.tpl: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "time" 4 | 5 | func init() { 6 | SemVer = "{{.SemVer}}" 7 | CommitSha7 = "{{.Sha7}}" 8 | CommitSha32 = "{{.Sha32}}" 9 | CommitTime = time.Unix({{.Epoch}}, 0) 10 | } 11 | -------------------------------------------------------------------------------- /dell-csi-helm-installer/.gitignore: -------------------------------------------------------------------------------- 1 | images.manifest 2 | images.tar 3 | -------------------------------------------------------------------------------- /dell-csi-helm-installer/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright © 2020-2022 Dell Inc., or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | 12 | 13 | RED='\033[0;31m' 14 | GREEN='\033[0;32m' 15 | YELLOW='\033[1;33m' 16 | DARK_GRAY='\033[1;30m' 17 | NC='\033[0m' # No Color 18 | 19 | function decho() { 20 | if [ -n "${DEBUGLOG}" ]; then 21 | echo "$@" | tee -a "${DEBUGLOG}" 22 | fi 23 | } 24 | 25 | function debuglog_only() { 26 | if [ -n "${DEBUGLOG}" ]; then 27 | echo "$@" >> "${DEBUGLOG}" 28 | fi 29 | } 30 | 31 | function log() { 32 | case $1 in 33 | separator) 34 | decho "------------------------------------------------------" 35 | ;; 36 | error) 37 | decho 38 | log separator 39 | printf "${RED}Error: $2\n" 40 | printf "${RED}Installation cannot continue${NC}\n" 41 | debuglog_only "Error: $2" 42 | debuglog_only "Installation cannot continue" 43 | exit 1 44 | ;; 45 | uninstall_error) 46 | log separator 47 | printf "${RED}Error: $2\n" 48 | printf "${RED}Uninstallation cannot continue${NC}\n" 49 | debuglog_only "Error: $2" 50 | debuglog_only "Uninstallation cannot continue" 51 | exit 1 52 | ;; 53 | step) 54 | printf "|\n|- %-65s" "$2" 55 | debuglog_only "${2}" 56 | ;; 57 | small_step) 58 | printf "%-61s" "$2" 59 | debuglog_only "${2}" 60 | ;; 61 | section) 62 | log separator 63 | printf "> %s\n" "$2" 64 | debuglog_only "${2}" 65 | log separator 66 | ;; 67 | smart_step) 68 | if [[ $3 == "small" ]]; then 69 | log small_step "$2" 70 | else 71 | log step "$2" 72 | fi 73 | ;; 74 | arrow) 75 | printf " %s\n %s" "|" "|--> " 76 | ;; 77 | step_success) 78 | printf "${GREEN}Success${NC}\n" 79 | ;; 80 | step_failure) 81 | printf "${RED}Failed${NC}\n" 82 | ;; 83 | step_warning) 84 | printf "${YELLOW}Warning${NC}\n" 85 | ;; 86 | info) 87 | printf "${DARK_GRAY}%s${NC}\n" "$2" 88 | ;; 89 | passed) 90 | printf "${GREEN}Success${NC}\n" 91 | ;; 92 | warnings) 93 | printf "${YELLOW}Warnings:${NC}\n" 94 | ;; 95 | errors) 96 | printf "${RED}Errors:${NC}\n" 97 | ;; 98 | *) 99 | echo -n "Unknown" 100 | ;; 101 | esac 102 | } 103 | 104 | function check_error() { 105 | if [[ $1 -ne 0 ]]; then 106 | log step_failure 107 | else 108 | log step_success 109 | fi 110 | } 111 | 112 | # get_release will determine the helm release name to use 113 | # If ${RELEASE} is set, use that 114 | # Otherwise, use the driver name minus any "csi-" prefix 115 | # argument 1: Driver name 116 | function get_release_name() { 117 | local D="${1}" 118 | if [ ! -z "${RELEASE}" ]; then 119 | decho "${RELEASE}" 120 | return 121 | fi 122 | 123 | local PREFIX="csi-" 124 | R=${D#"$PREFIX"} 125 | decho "${R}" 126 | } 127 | 128 | function run_command() { 129 | local RC=0 130 | if [ -n "${DEBUGLOG}" ]; then 131 | local ME=$(basename "${0}") 132 | echo "---------------" >> "${DEBUGLOG}" 133 | echo "${ME}:${BASH_LINENO[0]} - Running command: $@" >> "${DEBUGLOG}" 134 | debuglog_only "Results:" 135 | eval "$@" | tee -a "${DEBUGLOG}" 136 | RC=${PIPESTATUS[0]} 137 | echo "---------------" >> "${DEBUGLOG}" 138 | else 139 | eval "$@" 140 | RC=$? 141 | fi 142 | return $RC 143 | } 144 | 145 | # dump out information about a helm chart to the debug file 146 | # takes a few arguments 147 | # $1 the namespace 148 | # $2 the release 149 | function debuglog_helm_status() { 150 | local NS="${1}" 151 | local RLS="${2}" 152 | 153 | debuglog_only "Getting information about Helm release: ${RLS}" 154 | debuglog_only "****************" 155 | debuglog_only "Helm Status:" 156 | helm status "${RLS}" -n "${NS}" >> "${DEBUGLOG}" 157 | debuglog_only "****************" 158 | debuglog_only "Manifest" 159 | helm get manifest "${RLS}" -n "${NS}" >> "${DEBUGLOG}" 160 | debuglog_only "****************" 161 | debuglog_only "Status of resources" 162 | helm get manifest "${RLS}" -n "${NS}" | kubectl get -f - >> "${DEBUGLOG}" 163 | 164 | } 165 | 166 | # determines if the current KUBECONFIG is pointing to an OpenShift cluster 167 | # echos "true" or "false" 168 | function isOpenShift() { 169 | # check if the securitycontextconstraints.security.openshift.io crd exists 170 | run_command kubectl get crd | grep securitycontextconstraints.security.openshift.io --quiet >/dev/null 2>&1 171 | local O=$? 172 | if [[ ${O} == 0 ]]; then 173 | # this is openshift 174 | echo "true" 175 | else 176 | echo "false" 177 | fi 178 | } 179 | 180 | # determines the version of OpenShift 181 | # echos version, or empty string if not OpenShift 182 | function OpenShiftVersion() { 183 | # check if this is OpenShift 184 | local O=$(isOpenShift) 185 | if [ "${O}" == "false" ]; then 186 | # this is not openshift 187 | echo "" 188 | else 189 | local V=$(run_command kubectl get clusterversions -o jsonpath="{.items[*].status.desired.version}") 190 | local MAJOR=$(echo "${V}" | awk -F '.' '{print $1}') 191 | local MINOR=$(echo "${V}" | awk -F '.' '{print $2}') 192 | echo "${MAJOR}.${MINOR}" 193 | fi 194 | } 195 | 196 | -------------------------------------------------------------------------------- /dell-csi-helm-installer/csi-uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright © 2020-2022 Dell Inc., or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 12 | PROG="${0}" 13 | DRIVER="csi-powerstore" 14 | 15 | # export the name of the debug log, so child processes will see it 16 | export DEBUGLOG="${SCRIPTDIR}/uninstall-debug.log" 17 | 18 | declare -a VALIDDRIVERS 19 | 20 | source "$SCRIPTDIR"/common.sh 21 | 22 | if [ -f "${DEBUGLOG}" ]; then 23 | rm -f "${DEBUGLOG}" 24 | fi 25 | 26 | # 27 | # usage will print command execution help and then exit 28 | function usage() { 29 | decho "Help for $PROG" 30 | decho 31 | decho "Usage: $PROG options..." 32 | decho "Options:" 33 | decho " Required" 34 | decho " --namespace[=] Kubernetes namespace to uninstall the CSI driver from" 35 | 36 | decho " Optional" 37 | decho " --release[=] Name to register with helm, default value will match the driver name" 38 | decho " -h Help" 39 | decho 40 | 41 | exit 0 42 | } 43 | 44 | 45 | 46 | # 47 | # validate_params will validate the parameters passed in 48 | function validate_params() { 49 | # make sure the driver was specified 50 | if [ -z "${DRIVER}" ]; then 51 | decho "No driver specified" 52 | exit 1 53 | fi 54 | # make sure the driver name is valid 55 | # the namespace is required 56 | if [ -z "${NAMESPACE}" ]; then 57 | decho "No namespace specified" 58 | usage 59 | exit 1 60 | fi 61 | } 62 | 63 | 64 | # check_for_driver will see if the driver is installed within the namespace provided 65 | function check_for_driver() { 66 | NUM=$(run_command helm list --namespace "${NAMESPACE}" | grep "^${RELEASE}\b" | wc -l) 67 | if [ "${NUM}" == "0" ]; then 68 | log uninstall_error "The CSI Driver is not installed." 69 | exit 1 70 | fi 71 | } 72 | 73 | # get the list of valid CSI Drivers, this will be the list of directories in drivers/ that contain helm charts 74 | 75 | DRIVERDIR="${SCRIPTDIR}/../helm-charts/charts" 76 | 77 | while getopts ":h-:" optchar; do 78 | case "${optchar}" in 79 | -) 80 | case "${OPTARG}" in 81 | # NAMESPACE 82 | namespace) 83 | NAMESPACE="${!OPTIND}" 84 | OPTIND=$((OPTIND + 1)) 85 | ;; 86 | namespace=*) 87 | NAMESPACE=${OPTARG#*=} 88 | ;; 89 | # RELEASE 90 | release) 91 | RELEASE="${!OPTIND}" 92 | OPTIND=$((OPTIND + 1)) 93 | ;; 94 | release=*) 95 | RELEASE=${OPTARG#*=} 96 | ;; 97 | *) 98 | decho "Unknown option --${OPTARG}" 99 | decho "For help, run $PROG -h" 100 | exit 1 101 | ;; 102 | esac 103 | ;; 104 | h) 105 | usage 106 | ;; 107 | *) 108 | decho "Unknown option -${OPTARG}" 109 | decho "For help, run $PROG -h" 110 | exit 1 111 | ;; 112 | esac 113 | done 114 | 115 | # by default the NAME of the helm release of the driver is the same as the driver name 116 | RELEASE=$(get_release_name "${DRIVER}") 117 | 118 | # validate the parameters passed in 119 | validate_params 120 | 121 | check_for_driver 122 | run_command helm delete -n "${NAMESPACE}" "${RELEASE}" 123 | if [ $? -ne 0 ]; then 124 | decho "Removal of the CSI Driver was unsuccessful" 125 | exit 1 126 | fi 127 | 128 | decho "Removal of the CSI Driver is in progress." 129 | decho "It may take a few minutes for all pods to terminate." 130 | 131 | -------------------------------------------------------------------------------- /dell-csi-helm-installer/verify-csi-powerstore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright © 2021-2025 Dell Inc., or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | # verify-csi-powerstore method 12 | function verify-csi-powerstore() { 13 | verify_k8s_versions "1.31" "1.33" 14 | verify_openshift_versions "4.18" "4.19" 15 | verify_namespace "${NS}" 16 | verify_required_secrets "${RELEASE}-config" 17 | verify_alpha_snap_resources 18 | verify_optional_replication_requirements 19 | verify_iscsi_installation 20 | verify_nvmetcp_installation 21 | verify_nvmefc_installation 22 | verify_helm_3 23 | } 24 | 25 | function verify_optional_replication_requirements() { 26 | log step "Verifying Replication requirements" 27 | decho 28 | log arrow 29 | log smart_step "Verifying that Dell CSI Replication CRDs are available" "small" 30 | 31 | error=0 32 | # check for the CRDs. These are required for installation 33 | CRDS=("DellCSIReplicationGroups") 34 | for C in "${CRDS[@]}"; do 35 | # Verify if the CRD is present on the system 36 | run_command kubectl explain ${C} 2> /dev/null | grep "dell" --quiet 37 | if [ $? -ne 0 ]; then 38 | error=1 39 | found_warning "The CRD for ${C} is not installed. This needs to be installed if you are going to enable replication support" 40 | fi 41 | done 42 | check_error error 43 | } 44 | -------------------------------------------------------------------------------- /docker.mk: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # for variables override 18 | -include vars.mk 19 | 20 | # Includes the following generated file to get semantic version information 21 | include semver.mk 22 | ifdef NOTES 23 | RELNOTE="$(NOTES)" 24 | else 25 | RELNOTE= 26 | endif 27 | 28 | ifeq ($(IMAGETAG),) 29 | IMAGETAG=v$(MAJOR).$(MINOR).$(PATCH)$(RELNOTE) 30 | endif 31 | 32 | ifndef DOCKER_REGISTRY 33 | DOCKER_REGISTRY=dellemc 34 | endif 35 | 36 | ifndef DOCKER_IMAGE_NAME 37 | DOCKER_IMAGE_NAME=csi-powerstore 38 | endif 39 | 40 | # set the GOVERSION 41 | export GOVERSION="1.21" 42 | 43 | # figure out if podman or docker should be used (use podman if found) 44 | ifneq (, $(shell which podman 2>/dev/null)) 45 | BUILDER=podman 46 | else 47 | BUILDER=docker 48 | endif 49 | 50 | docker: download-csm-common 51 | $(eval include csm-common.mk) 52 | $(BUILDER) build --pull -t "$(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(IMAGETAG)" --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) --build-arg BASEIMAGE=$(CSM_BASEIMAGE) . 53 | 54 | docker-no-cache: download-csm-common 55 | $(eval include csm-common.mk) 56 | $(BUILDER) build --pull --no-cache -t "$(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(IMAGETAG)" --build-arg GOIMAGE=$(DEFAULT_GOIMAGE) --build-arg BASEIMAGE=$(CSM_BASEIMAGE) . 57 | 58 | push: 59 | $(BUILDER) push "$(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(IMAGETAG)" 60 | 61 | download-csm-common: 62 | curl -O -L https://raw.githubusercontent.com/dell/csm/main/config/csm-common.mk 63 | 64 | tag: 65 | -git tag -d $(IMAGETAG) 66 | git tag -a -m $(TAGMSG) $(IMAGETAG) 67 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # 4 | # Copyright © 2020-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | 19 | # HTTP endpoint of PowerStore 20 | export X_CSI_POWERSTORE_ENDPOINT="" 21 | 22 | # EnvUser is the name of the enviroment variable used to set the 23 | # username when authenticating to PowerStore 24 | export X_CSI_POWERSTORE_USER="smc" 25 | 26 | # EnvPassword is the name of the enviroment variable used to set the 27 | # user's password when authenticating to PowerStore 28 | export X_CSI_POWERSTORE_PASSWORD="smc" 29 | 30 | # EnvInsecure is the name of the enviroment variable used to specify 31 | # that PowerStore's certificate chain and host name should not 32 | # be verified 33 | export X_CSI_POWERSTORE_INSECURE="true" 34 | 35 | # EnvAutoProbe is the name of the environment variable used to specify 36 | # that the controller service should automatically probe itself if it 37 | # receives incoming requests before having been probed, in direct 38 | # violation of the CSI spec 39 | export X_CSI_POWERSTORE_AUTOPROBE="true" 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dell/csi-powerstore/v2 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | github.com/akutz/gosync v0.1.0 9 | github.com/apparentlymart/go-cidr v1.1.0 10 | github.com/container-storage-interface/spec v1.6.0 11 | github.com/dell/csi-metadata-retriever v1.11.1-0.20250529172548-aa16d68dc33f 12 | github.com/dell/csm-sharednfs v1.0.0 13 | github.com/dell/dell-csi-extensions/common v1.8.1-0.20250514175456-5ddd200c5e5c 14 | github.com/dell/dell-csi-extensions/podmon v1.8.0 15 | github.com/dell/dell-csi-extensions/replication v1.11.0 16 | github.com/dell/dell-csi-extensions/volumeGroupSnapshot v1.8.1 17 | github.com/dell/gobrick v1.14.0 18 | github.com/dell/gocsi v1.14.0 19 | github.com/dell/gofsutil v1.19.0 20 | github.com/dell/goiscsi v1.12.0 21 | github.com/dell/gonvme v1.11.0 22 | github.com/dell/gopowerstore v1.19.0 23 | github.com/fsnotify/fsnotify v1.9.0 24 | github.com/go-openapi/strfmt v0.23.0 25 | github.com/golang/mock v1.6.0 26 | github.com/google/uuid v1.6.0 27 | github.com/gorilla/mux v1.8.1 28 | github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 29 | github.com/kubernetes-csi/csi-lib-utils v0.11.0 30 | github.com/onsi/ginkgo v1.16.5 31 | github.com/onsi/gomega v1.37.0 32 | github.com/opentracing/opentracing-go v1.2.0 33 | github.com/sirupsen/logrus v1.9.3 34 | github.com/spf13/viper v1.20.0 35 | github.com/stretchr/testify v1.10.0 36 | github.com/uber/jaeger-client-go v2.30.0+incompatible 37 | github.com/uber/jaeger-lib v2.4.1+incompatible 38 | go.uber.org/mock v0.5.0 39 | golang.org/x/net v0.40.0 40 | google.golang.org/grpc v1.72.0 41 | google.golang.org/protobuf v1.36.5 42 | gopkg.in/yaml.v3 v3.0.1 43 | k8s.io/api v0.33.0 44 | k8s.io/apimachinery v0.33.0 45 | k8s.io/client-go v0.33.0 46 | ) 47 | 48 | require ( 49 | github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect 50 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 51 | github.com/beorn7/perks v1.0.1 // indirect 52 | github.com/blang/semver/v4 v4.0.0 // indirect 53 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 54 | github.com/coreos/go-semver v0.3.1 // indirect 55 | github.com/coreos/go-systemd/v22 v22.5.0 // indirect 56 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 57 | github.com/emicklei/go-restful/v3 v3.12.1 // indirect 58 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 59 | github.com/go-logr/logr v1.4.2 // indirect 60 | github.com/go-openapi/errors v0.22.0 // indirect 61 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 62 | github.com/go-openapi/jsonreference v0.21.0 // indirect 63 | github.com/go-openapi/swag v0.23.0 // indirect 64 | github.com/go-viper/mapstructure/v2 v2.2.1 // indirect 65 | github.com/gogo/protobuf v1.3.2 // indirect 66 | github.com/golang/protobuf v1.5.4 // indirect 67 | github.com/google/gnostic-models v0.6.9 // indirect 68 | github.com/google/go-cmp v0.7.0 // indirect 69 | github.com/josharian/intern v1.0.0 // indirect 70 | github.com/json-iterator/go v1.1.12 // indirect 71 | github.com/klauspost/compress v1.17.11 // indirect 72 | github.com/mailru/easyjson v0.9.0 // indirect 73 | github.com/mitchellh/mapstructure v1.5.0 // indirect 74 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 75 | github.com/modern-go/reflect2 v1.0.2 // indirect 76 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 77 | github.com/nxadm/tail v1.4.11 // indirect 78 | github.com/oklog/ulid v1.3.1 // indirect 79 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 80 | github.com/pkg/errors v0.9.1 // indirect 81 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 82 | github.com/prometheus/client_golang v1.20.5 // indirect 83 | github.com/prometheus/client_model v0.6.1 // indirect 84 | github.com/prometheus/common v0.62.0 // indirect 85 | github.com/prometheus/procfs v0.15.1 // indirect 86 | github.com/sagikazarmark/locafero v0.7.0 // indirect 87 | github.com/sourcegraph/conc v0.3.0 // indirect 88 | github.com/spf13/afero v1.12.0 // indirect 89 | github.com/spf13/cast v1.7.1 // indirect 90 | github.com/spf13/pflag v1.0.6 // indirect 91 | github.com/stretchr/objx v0.5.2 // indirect 92 | github.com/subosito/gotenv v1.6.0 // indirect 93 | github.com/x448/float16 v0.8.4 // indirect 94 | go.etcd.io/etcd/api/v3 v3.5.18 // indirect 95 | go.etcd.io/etcd/client/pkg/v3 v3.5.18 // indirect 96 | go.etcd.io/etcd/client/v3 v3.5.18 // indirect 97 | go.mongodb.org/mongo-driver v1.17.2 // indirect 98 | go.opentelemetry.io/otel v1.34.0 // indirect 99 | go.opentelemetry.io/otel/trace v1.34.0 // indirect 100 | go.uber.org/atomic v1.11.0 // indirect 101 | go.uber.org/multierr v1.11.0 // indirect 102 | go.uber.org/zap v1.27.0 // indirect 103 | golang.org/x/oauth2 v0.27.0 // indirect 104 | golang.org/x/sync v0.14.0 // indirect 105 | golang.org/x/sys v0.33.0 // indirect 106 | golang.org/x/term v0.32.0 // indirect 107 | golang.org/x/text v0.25.0 // indirect 108 | golang.org/x/time v0.9.0 // indirect 109 | google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect 110 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 111 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 112 | gopkg.in/inf.v0 v0.9.1 // indirect 113 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 114 | k8s.io/component-base v0.32.1 // indirect 115 | k8s.io/klog/v2 v2.130.1 // indirect 116 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 117 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 118 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 119 | sigs.k8s.io/randfill v1.0.0 // indirect 120 | sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect 121 | sigs.k8s.io/yaml v1.4.0 // indirect 122 | ) 123 | -------------------------------------------------------------------------------- /mocks/Consumer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery v1.0.0. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import array "github.com/dell/csi-powerstore/v2/pkg/array" 22 | import fs "github.com/dell/csi-powerstore/v2/pkg/common/fs" 23 | import mock "github.com/stretchr/testify/mock" 24 | 25 | // Consumer is an autogenerated mock type for the Consumer type 26 | type Consumer struct { 27 | mock.Mock 28 | } 29 | 30 | // Arrays provides a mock function with given fields: 31 | func (_m *Consumer) Arrays() map[string]*array.PowerStoreArray { 32 | ret := _m.Called() 33 | 34 | var r0 map[string]*array.PowerStoreArray 35 | if rf, ok := ret.Get(0).(func() map[string]*array.PowerStoreArray); ok { 36 | r0 = rf() 37 | } else { 38 | if ret.Get(0) != nil { 39 | r0 = ret.Get(0).(map[string]*array.PowerStoreArray) 40 | } 41 | } 42 | 43 | return r0 44 | } 45 | 46 | // DefaultArray provides a mock function with given fields: 47 | func (_m *Consumer) DefaultArray() *array.PowerStoreArray { 48 | ret := _m.Called() 49 | 50 | var r0 *array.PowerStoreArray 51 | if rf, ok := ret.Get(0).(func() *array.PowerStoreArray); ok { 52 | r0 = rf() 53 | } else { 54 | if ret.Get(0) != nil { 55 | r0 = ret.Get(0).(*array.PowerStoreArray) 56 | } 57 | } 58 | 59 | return r0 60 | } 61 | 62 | // SetArrays provides a mock function with given fields: _a0 63 | func (_m *Consumer) SetArrays(_a0 map[string]*array.PowerStoreArray) { 64 | _m.Called(_a0) 65 | } 66 | 67 | // SetDefaultArray provides a mock function with given fields: _a0 68 | func (_m *Consumer) SetDefaultArray(_a0 *array.PowerStoreArray) { 69 | _m.Called(_a0) 70 | } 71 | 72 | // UpdateArrays provides a mock function with given fields: _a0, _a1 73 | func (_m *Consumer) UpdateArrays(_a0 string, _a1 fs.Interface) error { 74 | ret := _m.Called(_a0, _a1) 75 | 76 | var r0 error 77 | if rf, ok := ret.Get(0).(func(string, fs.Interface) error); ok { 78 | r0 = rf(_a0, _a1) 79 | } else { 80 | r0 = ret.Error(0) 81 | } 82 | 83 | return r0 84 | } 85 | -------------------------------------------------------------------------------- /mocks/FcConnector.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | gobrick "github.com/dell/gobrick" 25 | mock "github.com/stretchr/testify/mock" 26 | ) 27 | 28 | // FcConnector is an autogenerated mock type for the FcConnector type 29 | type FcConnector struct { 30 | mock.Mock 31 | } 32 | 33 | // ConnectVolume provides a mock function with given fields: ctx, info 34 | func (_m *FcConnector) ConnectVolume(ctx context.Context, info gobrick.FCVolumeInfo) (gobrick.Device, error) { 35 | ret := _m.Called(ctx, info) 36 | 37 | var r0 gobrick.Device 38 | if rf, ok := ret.Get(0).(func(context.Context, gobrick.FCVolumeInfo) gobrick.Device); ok { 39 | r0 = rf(ctx, info) 40 | } else { 41 | r0 = ret.Get(0).(gobrick.Device) 42 | } 43 | 44 | var r1 error 45 | if rf, ok := ret.Get(1).(func(context.Context, gobrick.FCVolumeInfo) error); ok { 46 | r1 = rf(ctx, info) 47 | } else { 48 | r1 = ret.Error(1) 49 | } 50 | 51 | return r0, r1 52 | } 53 | 54 | // DisconnectVolumeByDeviceName provides a mock function with given fields: ctx, name 55 | func (_m *FcConnector) DisconnectVolumeByDeviceName(ctx context.Context, name string) error { 56 | ret := _m.Called(ctx, name) 57 | 58 | var r0 error 59 | if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { 60 | r0 = rf(ctx, name) 61 | } else { 62 | r0 = ret.Error(0) 63 | } 64 | 65 | return r0 66 | } 67 | 68 | // GetInitiatorPorts provides a mock function with given fields: ctx 69 | func (_m *FcConnector) GetInitiatorPorts(ctx context.Context) ([]string, error) { 70 | ret := _m.Called(ctx) 71 | 72 | var r0 []string 73 | if rf, ok := ret.Get(0).(func(context.Context) []string); ok { 74 | r0 = rf(ctx) 75 | } else { 76 | if ret.Get(0) != nil { 77 | r0 = ret.Get(0).([]string) 78 | } 79 | } 80 | 81 | var r1 error 82 | if rf, ok := ret.Get(1).(func(context.Context) error); ok { 83 | r1 = rf(ctx) 84 | } else { 85 | r1 = ret.Error(1) 86 | } 87 | 88 | return r0, r1 89 | } 90 | -------------------------------------------------------------------------------- /mocks/FileInfo.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | iofs "io/fs" 23 | time "time" 24 | 25 | mock "github.com/stretchr/testify/mock" 26 | ) 27 | 28 | // FileInfo is an autogenerated mock type for the FileInfo type 29 | type FileInfo struct { 30 | mock.Mock 31 | } 32 | 33 | // IsDir provides a mock function with given fields: 34 | func (_m *FileInfo) IsDir() bool { 35 | ret := _m.Called() 36 | 37 | var r0 bool 38 | if rf, ok := ret.Get(0).(func() bool); ok { 39 | r0 = rf() 40 | } else { 41 | r0 = ret.Get(0).(bool) 42 | } 43 | 44 | return r0 45 | } 46 | 47 | // ModTime provides a mock function with given fields: 48 | func (_m *FileInfo) ModTime() time.Time { 49 | ret := _m.Called() 50 | 51 | var r0 time.Time 52 | if rf, ok := ret.Get(0).(func() time.Time); ok { 53 | r0 = rf() 54 | } else { 55 | r0 = ret.Get(0).(time.Time) 56 | } 57 | 58 | return r0 59 | } 60 | 61 | // Mode provides a mock function with given fields: 62 | func (_m *FileInfo) Mode() iofs.FileMode { 63 | ret := _m.Called() 64 | 65 | var r0 iofs.FileMode 66 | if rf, ok := ret.Get(0).(func() iofs.FileMode); ok { 67 | r0 = rf() 68 | } else { 69 | r0 = ret.Get(0).(iofs.FileMode) 70 | } 71 | 72 | return r0 73 | } 74 | 75 | // Name provides a mock function with given fields: 76 | func (_m *FileInfo) Name() string { 77 | ret := _m.Called() 78 | 79 | var r0 string 80 | if rf, ok := ret.Get(0).(func() string); ok { 81 | r0 = rf() 82 | } else { 83 | r0 = ret.Get(0).(string) 84 | } 85 | 86 | return r0 87 | } 88 | 89 | // Size provides a mock function with given fields: 90 | func (_m *FileInfo) Size() int64 { 91 | ret := _m.Called() 92 | 93 | var r0 int64 94 | if rf, ok := ret.Get(0).(func() int64); ok { 95 | r0 = rf() 96 | } else { 97 | r0 = ret.Get(0).(int64) 98 | } 99 | 100 | return r0 101 | } 102 | 103 | // Sys provides a mock function with given fields: 104 | func (_m *FileInfo) Sys() interface{} { 105 | ret := _m.Called() 106 | 107 | var r0 interface{} 108 | if rf, ok := ret.Get(0).(func() interface{}); ok { 109 | r0 = rf() 110 | } else { 111 | if ret.Get(0) != nil { 112 | r0 = ret.Get(0).(interface{}) 113 | } 114 | } 115 | 116 | return r0 117 | } 118 | -------------------------------------------------------------------------------- /mocks/ISCSIConnector.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | gobrick "github.com/dell/gobrick" 25 | mock "github.com/stretchr/testify/mock" 26 | ) 27 | 28 | // ISCSIConnector is an autogenerated mock type for the ISCSIConnector type 29 | type ISCSIConnector struct { 30 | mock.Mock 31 | } 32 | 33 | // ConnectVolume provides a mock function with given fields: ctx, info 34 | func (_m *ISCSIConnector) ConnectVolume(ctx context.Context, info gobrick.ISCSIVolumeInfo) (gobrick.Device, error) { 35 | ret := _m.Called(ctx, info) 36 | 37 | var r0 gobrick.Device 38 | if rf, ok := ret.Get(0).(func(context.Context, gobrick.ISCSIVolumeInfo) gobrick.Device); ok { 39 | r0 = rf(ctx, info) 40 | } else { 41 | r0 = ret.Get(0).(gobrick.Device) 42 | } 43 | 44 | var r1 error 45 | if rf, ok := ret.Get(1).(func(context.Context, gobrick.ISCSIVolumeInfo) error); ok { 46 | r1 = rf(ctx, info) 47 | } else { 48 | r1 = ret.Error(1) 49 | } 50 | 51 | return r0, r1 52 | } 53 | 54 | // DisconnectVolumeByDeviceName provides a mock function with given fields: ctx, name 55 | func (_m *ISCSIConnector) DisconnectVolumeByDeviceName(ctx context.Context, name string) error { 56 | ret := _m.Called(ctx, name) 57 | 58 | var r0 error 59 | if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { 60 | r0 = rf(ctx, name) 61 | } else { 62 | r0 = ret.Error(0) 63 | } 64 | 65 | return r0 66 | } 67 | 68 | // GetInitiatorName provides a mock function with given fields: ctx 69 | func (_m *ISCSIConnector) GetInitiatorName(ctx context.Context) ([]string, error) { 70 | ret := _m.Called(ctx) 71 | 72 | var r0 []string 73 | if rf, ok := ret.Get(0).(func(context.Context) []string); ok { 74 | r0 = rf(ctx) 75 | } else { 76 | if ret.Get(0) != nil { 77 | r0 = ret.Get(0).([]string) 78 | } 79 | } 80 | 81 | var r1 error 82 | if rf, ok := ret.Get(1).(func(context.Context) error); ok { 83 | r1 = rf(ctx) 84 | } else { 85 | r1 = ret.Error(1) 86 | } 87 | 88 | return r0, r1 89 | } 90 | -------------------------------------------------------------------------------- /mocks/NFSv4ACLsInterface.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery v1.0.0. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import mock "github.com/stretchr/testify/mock" 22 | 23 | // NFSv4ACLsInterface is an autogenerated mock type for the NFSv4ACLsInterface type 24 | type NFSv4ACLsInterface struct { 25 | mock.Mock 26 | } 27 | 28 | // SetNfsv4Acls provides a mock function with given fields: acls, dir 29 | func (_m *NFSv4ACLsInterface) SetNfsv4Acls(acls string, dir string) error { 30 | ret := _m.Called(acls, dir) 31 | 32 | var r0 error 33 | if rf, ok := ret.Get(0).(func(string, string) error); ok { 34 | r0 = rf(acls, dir) 35 | } else { 36 | r0 = ret.Error(0) 37 | } 38 | 39 | return r0 40 | } 41 | -------------------------------------------------------------------------------- /mocks/NVMEConnector.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery v2.12.2. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | gobrick "github.com/dell/gobrick" 25 | mock "github.com/stretchr/testify/mock" 26 | 27 | testing "testing" 28 | ) 29 | 30 | // NVMEConnector is an autogenerated mock type for the NVMEConnector type 31 | type NVMEConnector struct { 32 | mock.Mock 33 | } 34 | 35 | // ConnectVolume provides a mock function with given fields: ctx, info, useFC 36 | func (_m *NVMEConnector) ConnectVolume(ctx context.Context, info gobrick.NVMeVolumeInfo, useFC bool) (gobrick.Device, error) { 37 | ret := _m.Called(ctx, info, useFC) 38 | 39 | var r0 gobrick.Device 40 | if rf, ok := ret.Get(0).(func(context.Context, gobrick.NVMeVolumeInfo, bool) gobrick.Device); ok { 41 | r0 = rf(ctx, info, useFC) 42 | } else { 43 | r0 = ret.Get(0).(gobrick.Device) 44 | } 45 | 46 | var r1 error 47 | if rf, ok := ret.Get(1).(func(context.Context, gobrick.NVMeVolumeInfo, bool) error); ok { 48 | r1 = rf(ctx, info, useFC) 49 | } else { 50 | r1 = ret.Error(1) 51 | } 52 | 53 | return r0, r1 54 | } 55 | 56 | // DisconnectVolumeByDeviceName provides a mock function with given fields: ctx, name 57 | func (_m *NVMEConnector) DisconnectVolumeByDeviceName(ctx context.Context, name string) error { 58 | ret := _m.Called(ctx, name) 59 | 60 | var r0 error 61 | if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { 62 | r0 = rf(ctx, name) 63 | } else { 64 | r0 = ret.Error(0) 65 | } 66 | 67 | return r0 68 | } 69 | 70 | // GetInitiatorName provides a mock function with given fields: ctx 71 | func (_m *NVMEConnector) GetInitiatorName(ctx context.Context) ([]string, error) { 72 | ret := _m.Called(ctx) 73 | 74 | var r0 []string 75 | if rf, ok := ret.Get(0).(func(context.Context) []string); ok { 76 | r0 = rf(ctx) 77 | } else { 78 | if ret.Get(0) != nil { 79 | r0 = ret.Get(0).([]string) 80 | } 81 | } 82 | 83 | var r1 error 84 | if rf, ok := ret.Get(1).(func(context.Context) error); ok { 85 | r1 = rf(ctx) 86 | } else { 87 | r1 = ret.Error(1) 88 | } 89 | 90 | return r0, r1 91 | } 92 | 93 | // NewNVMEConnector creates a new instance of NVMEConnector. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. 94 | func NewNVMEConnector(t testing.TB) *NVMEConnector { 95 | mock := &NVMEConnector{} 96 | mock.Mock.Test(t) 97 | 98 | t.Cleanup(func() { mock.AssertExpectations(t) }) 99 | 100 | return mock 101 | } 102 | -------------------------------------------------------------------------------- /mocks/NodeLabelsModifier.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2025 Dell Inc, or its subsidiaries. 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | mock "github.com/stretchr/testify/mock" 25 | ) 26 | 27 | // NodeLabelsModifierInterface is an autogenerated mock type for the NodeLabelsModifierInterface type 28 | type NodeLabelsModifierInterface struct { 29 | mock.Mock 30 | } 31 | 32 | // AddNVMeLabels provides a mock function with given fields: ctx, kubeNodeName 33 | func (_m *NodeLabelsModifierInterface) AddNVMeLabels(ctx context.Context, kubeNodeName string, labelKey string, labelValue []string) error { 34 | ret := _m.Called(ctx, kubeNodeName, labelKey, labelValue) 35 | 36 | var r0 error 37 | if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) error); ok { 38 | return rf(ctx, kubeNodeName, labelKey, labelValue) 39 | } 40 | 41 | if rf, ok := ret.Get(0).(func(context.Context, string, string, []string)); ok { 42 | rf(ctx, kubeNodeName, labelKey, labelValue) 43 | } 44 | 45 | if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) error); ok { 46 | r0 = rf(ctx, kubeNodeName, labelKey, labelValue) 47 | } else { 48 | r0 = ret.Error(0) 49 | } 50 | 51 | return r0 52 | } 53 | 54 | // NewNodeLabelsModifierInterface creates a new instance of NodeLabelsModifierInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 55 | // The first argument is typically a *testing.T value. 56 | func NewNodeLabelsModifierInterface(t interface { 57 | mock.TestingT 58 | Cleanup(func()) 59 | }) *NodeLabelsModifierInterface { 60 | mock := &NodeLabelsModifierInterface{} 61 | mock.Mock.Test(t) 62 | 63 | t.Cleanup(func() { mock.AssertExpectations(t) }) 64 | 65 | return mock 66 | } 67 | -------------------------------------------------------------------------------- /mocks/NodeLabelsRetriever.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2023-2025 Dell Inc, or its subsidiaries. 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | kubernetes "k8s.io/client-go/kubernetes" 25 | 26 | mock "github.com/stretchr/testify/mock" 27 | 28 | rest "k8s.io/client-go/rest" 29 | ) 30 | 31 | // NodeLabelsRetrieverInterface is an autogenerated mock type for the NodeLabelsRetrieverInterface type 32 | type NodeLabelsRetrieverInterface struct { 33 | mock.Mock 34 | } 35 | 36 | // BuildConfigFromFlags provides a mock function with given fields: masterURL, kubeconfig 37 | func (_m *NodeLabelsRetrieverInterface) BuildConfigFromFlags(masterURL string, kubeconfig string) (*rest.Config, error) { 38 | ret := _m.Called(masterURL, kubeconfig) 39 | 40 | var r0 *rest.Config 41 | var r1 error 42 | if rf, ok := ret.Get(0).(func(string, string) (*rest.Config, error)); ok { 43 | return rf(masterURL, kubeconfig) 44 | } 45 | if rf, ok := ret.Get(0).(func(string, string) *rest.Config); ok { 46 | r0 = rf(masterURL, kubeconfig) 47 | } else { 48 | if ret.Get(0) != nil { 49 | r0 = ret.Get(0).(*rest.Config) 50 | } 51 | } 52 | 53 | if rf, ok := ret.Get(1).(func(string, string) error); ok { 54 | r1 = rf(masterURL, kubeconfig) 55 | } else { 56 | r1 = ret.Error(1) 57 | } 58 | 59 | return r0, r1 60 | } 61 | 62 | // GetNVMeUUIDs provides a mock function with given fields: ctx 63 | func (_m *NodeLabelsRetrieverInterface) GetNVMeUUIDs(ctx context.Context) (map[string]string, error) { 64 | ret := _m.Called(ctx) 65 | 66 | var r0 map[string]string 67 | var r1 error 68 | if rf, ok := ret.Get(0).(func(context.Context) (map[string]string, error)); ok { 69 | return rf(ctx) 70 | } 71 | if rf, ok := ret.Get(0).(func(context.Context) map[string]string); ok { 72 | r0 = rf(ctx) 73 | } else { 74 | if ret.Get(0) != nil { 75 | r0 = ret.Get(0).(map[string]string) 76 | } 77 | } 78 | 79 | if rf, ok := ret.Get(1).(func(context.Context) error); ok { 80 | r1 = rf(ctx) 81 | } else { 82 | r1 = ret.Error(1) 83 | } 84 | 85 | return r0, r1 86 | } 87 | 88 | // GetNodeLabels provides a mock function with given fields: ctx, kubeNodeName 89 | func (_m *NodeLabelsRetrieverInterface) GetNodeLabels(ctx context.Context, kubeNodeName string) (map[string]string, error) { 90 | ret := _m.Called(ctx, kubeNodeName) 91 | 92 | var r0 map[string]string 93 | var r1 error 94 | if rf, ok := ret.Get(0).(func(context.Context, string) (map[string]string, error)); ok { 95 | return rf(ctx, kubeNodeName) 96 | } 97 | if rf, ok := ret.Get(0).(func(context.Context, string) map[string]string); ok { 98 | r0 = rf(ctx, kubeNodeName) 99 | } else { 100 | if ret.Get(0) != nil { 101 | r0 = ret.Get(0).(map[string]string) 102 | } 103 | } 104 | 105 | if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { 106 | r1 = rf(ctx, kubeNodeName) 107 | } else { 108 | r1 = ret.Error(1) 109 | } 110 | 111 | return r0, r1 112 | } 113 | 114 | // InClusterConfig provides a mock function with given fields: 115 | func (_m *NodeLabelsRetrieverInterface) InClusterConfig() (*rest.Config, error) { 116 | ret := _m.Called() 117 | 118 | var r0 *rest.Config 119 | var r1 error 120 | if rf, ok := ret.Get(0).(func() (*rest.Config, error)); ok { 121 | return rf() 122 | } 123 | if rf, ok := ret.Get(0).(func() *rest.Config); ok { 124 | r0 = rf() 125 | } else { 126 | if ret.Get(0) != nil { 127 | r0 = ret.Get(0).(*rest.Config) 128 | } 129 | } 130 | 131 | if rf, ok := ret.Get(1).(func() error); ok { 132 | r1 = rf() 133 | } else { 134 | r1 = ret.Error(1) 135 | } 136 | 137 | return r0, r1 138 | } 139 | 140 | // NewForConfig provides a mock function with given fields: config 141 | func (_m *NodeLabelsRetrieverInterface) NewForConfig(config *rest.Config) (*kubernetes.Clientset, error) { 142 | ret := _m.Called(config) 143 | 144 | var r0 *kubernetes.Clientset 145 | var r1 error 146 | if rf, ok := ret.Get(0).(func(*rest.Config) (*kubernetes.Clientset, error)); ok { 147 | return rf(config) 148 | } 149 | if rf, ok := ret.Get(0).(func(*rest.Config) *kubernetes.Clientset); ok { 150 | r0 = rf(config) 151 | } else { 152 | if ret.Get(0) != nil { 153 | r0 = ret.Get(0).(*kubernetes.Clientset) 154 | } 155 | } 156 | 157 | if rf, ok := ret.Get(1).(func(*rest.Config) error); ok { 158 | r1 = rf(config) 159 | } else { 160 | r1 = ret.Error(1) 161 | } 162 | 163 | return r0, r1 164 | } 165 | 166 | // NewNodeLabelsRetrieverInterface creates a new instance of NodeLabelsRetrieverInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 167 | // The first argument is typically a *testing.T value. 168 | func NewNodeLabelsRetrieverInterface(t interface { 169 | mock.TestingT 170 | Cleanup(func()) 171 | }) *NodeLabelsRetrieverInterface { 172 | mock := &NodeLabelsRetrieverInterface{} 173 | mock.Mock.Test(t) 174 | 175 | t.Cleanup(func() { mock.AssertExpectations(t) }) 176 | 177 | return mock 178 | } 179 | -------------------------------------------------------------------------------- /mocks/NodeVolumePublisher.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | csi "github.com/container-storage-interface/spec/lib/go/csi" 25 | fs "github.com/dell/csi-powerstore/v2/pkg/common/fs" 26 | 27 | logrus "github.com/sirupsen/logrus" 28 | 29 | mock "github.com/stretchr/testify/mock" 30 | ) 31 | 32 | // NodeVolumePublisher is an autogenerated mock type for the NodeVolumePublisher type 33 | type NodeVolumePublisher struct { 34 | mock.Mock 35 | } 36 | 37 | // Publish provides a mock function with given fields: ctx, logFields, _a2, cap, isRO, targetPath, stagingPath 38 | func (_m *NodeVolumePublisher) Publish(ctx context.Context, logFields logrus.Fields, _a2 fs.Interface, cap *csi.VolumeCapability, isRO bool, targetPath string, stagingPath string) (*csi.NodePublishVolumeResponse, error) { 39 | ret := _m.Called(ctx, logFields, _a2, cap, isRO, targetPath, stagingPath) 40 | 41 | var r0 *csi.NodePublishVolumeResponse 42 | if rf, ok := ret.Get(0).(func(context.Context, logrus.Fields, fs.Interface, *csi.VolumeCapability, bool, string, string) *csi.NodePublishVolumeResponse); ok { 43 | r0 = rf(ctx, logFields, _a2, cap, isRO, targetPath, stagingPath) 44 | } else { 45 | if ret.Get(0) != nil { 46 | r0 = ret.Get(0).(*csi.NodePublishVolumeResponse) 47 | } 48 | } 49 | 50 | var r1 error 51 | if rf, ok := ret.Get(1).(func(context.Context, logrus.Fields, fs.Interface, *csi.VolumeCapability, bool, string, string) error); ok { 52 | r1 = rf(ctx, logFields, _a2, cap, isRO, targetPath, stagingPath) 53 | } else { 54 | r1 = ret.Error(1) 55 | } 56 | 57 | return r0, r1 58 | } 59 | -------------------------------------------------------------------------------- /mocks/NodeVolumeStager.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | csi "github.com/container-storage-interface/spec/lib/go/csi" 25 | fs "github.com/dell/csi-powerstore/v2/pkg/common/fs" 26 | 27 | logrus "github.com/sirupsen/logrus" 28 | 29 | mock "github.com/stretchr/testify/mock" 30 | ) 31 | 32 | // NodeVolumeStager is an autogenerated mock type for the NodeVolumeStager type 33 | type NodeVolumeStager struct { 34 | mock.Mock 35 | } 36 | 37 | // Stage provides a mock function with given fields: ctx, req, logFields, _a3, id 38 | func (_m *NodeVolumeStager) Stage(ctx context.Context, req *csi.NodeStageVolumeRequest, logFields logrus.Fields, _a3 fs.Interface, id string) (*csi.NodeStageVolumeResponse, error) { 39 | ret := _m.Called(ctx, req, logFields, _a3, id) 40 | 41 | var r0 *csi.NodeStageVolumeResponse 42 | if rf, ok := ret.Get(0).(func(context.Context, *csi.NodeStageVolumeRequest, logrus.Fields, fs.Interface, string) *csi.NodeStageVolumeResponse); ok { 43 | r0 = rf(ctx, req, logFields, _a3, id) 44 | } else { 45 | if ret.Get(0) != nil { 46 | r0 = ret.Get(0).(*csi.NodeStageVolumeResponse) 47 | } 48 | } 49 | 50 | var r1 error 51 | if rf, ok := ret.Get(1).(func(context.Context, *csi.NodeStageVolumeRequest, logrus.Fields, fs.Interface, string) error); ok { 52 | r1 = rf(ctx, req, logFields, _a3, id) 53 | } else { 54 | r1 = ret.Error(1) 55 | } 56 | 57 | return r0, r1 58 | } 59 | -------------------------------------------------------------------------------- /mocks/TracerConfigurator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | mock "github.com/stretchr/testify/mock" 23 | config "github.com/uber/jaeger-client-go/config" 24 | ) 25 | 26 | // TracerConfigurator is an autogenerated mock type for the TracerConfigurator type 27 | type TracerConfigurator struct { 28 | mock.Mock 29 | } 30 | 31 | // FromEnv provides a mock function with given fields: 32 | func (_m *TracerConfigurator) FromEnv() (*config.Configuration, error) { 33 | ret := _m.Called() 34 | 35 | var r0 *config.Configuration 36 | if rf, ok := ret.Get(0).(func() *config.Configuration); ok { 37 | r0 = rf() 38 | } else { 39 | if ret.Get(0) != nil { 40 | r0 = ret.Get(0).(*config.Configuration) 41 | } 42 | } 43 | 44 | var r1 error 45 | if rf, ok := ret.Get(1).(func() error); ok { 46 | r1 = rf() 47 | } else { 48 | r1 = ret.Error(1) 49 | } 50 | 51 | return r0, r1 52 | } 53 | -------------------------------------------------------------------------------- /mocks/VolumeCreator.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | csi "github.com/container-storage-interface/spec/lib/go/csi" 25 | 26 | gopowerstore "github.com/dell/gopowerstore" 27 | 28 | mock "github.com/stretchr/testify/mock" 29 | ) 30 | 31 | // VolumeCreator is an autogenerated mock type for the VolumeCreator type 32 | type VolumeCreator struct { 33 | mock.Mock 34 | } 35 | 36 | // CheckIfAlreadyExists provides a mock function with given fields: ctx, name, sizeInBytes, client 37 | func (_m *VolumeCreator) CheckIfAlreadyExists(ctx context.Context, name string, sizeInBytes int64, client gopowerstore.Client) (*csi.Volume, error) { 38 | ret := _m.Called(ctx, name, sizeInBytes, client) 39 | 40 | var r0 *csi.Volume 41 | if rf, ok := ret.Get(0).(func(context.Context, string, int64, gopowerstore.Client) *csi.Volume); ok { 42 | r0 = rf(ctx, name, sizeInBytes, client) 43 | } else { 44 | if ret.Get(0) != nil { 45 | r0 = ret.Get(0).(*csi.Volume) 46 | } 47 | } 48 | 49 | var r1 error 50 | if rf, ok := ret.Get(1).(func(context.Context, string, int64, gopowerstore.Client) error); ok { 51 | r1 = rf(ctx, name, sizeInBytes, client) 52 | } else { 53 | r1 = ret.Error(1) 54 | } 55 | 56 | return r0, r1 57 | } 58 | 59 | // CheckName provides a mock function with given fields: ctx, name 60 | func (_m *VolumeCreator) CheckName(ctx context.Context, name string) error { 61 | ret := _m.Called(ctx, name) 62 | 63 | var r0 error 64 | if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { 65 | r0 = rf(ctx, name) 66 | } else { 67 | r0 = ret.Error(0) 68 | } 69 | 70 | return r0 71 | } 72 | 73 | // CheckSize provides a mock function with given fields: ctx, cr 74 | func (_m *VolumeCreator) CheckSize(ctx context.Context, cr *csi.CapacityRange) (int64, error) { 75 | ret := _m.Called(ctx, cr) 76 | 77 | var r0 int64 78 | if rf, ok := ret.Get(0).(func(context.Context, *csi.CapacityRange) int64); ok { 79 | r0 = rf(ctx, cr) 80 | } else { 81 | r0 = ret.Get(0).(int64) 82 | } 83 | 84 | var r1 error 85 | if rf, ok := ret.Get(1).(func(context.Context, *csi.CapacityRange) error); ok { 86 | r1 = rf(ctx, cr) 87 | } else { 88 | r1 = ret.Error(1) 89 | } 90 | 91 | return r0, r1 92 | } 93 | 94 | // Clone provides a mock function with given fields: ctx, volumeSource, volumeName, sizeInBytes, parameters, client 95 | func (_m *VolumeCreator) Clone(ctx context.Context, volumeSource *csi.VolumeContentSource_VolumeSource, volumeName string, sizeInBytes int64, parameters map[string]string, client gopowerstore.Client) (*csi.Volume, error) { 96 | ret := _m.Called(ctx, volumeSource, volumeName, sizeInBytes, parameters, client) 97 | 98 | var r0 *csi.Volume 99 | if rf, ok := ret.Get(0).(func(context.Context, *csi.VolumeContentSource_VolumeSource, string, int64, map[string]string, gopowerstore.Client) *csi.Volume); ok { 100 | r0 = rf(ctx, volumeSource, volumeName, sizeInBytes, parameters, client) 101 | } else { 102 | if ret.Get(0) != nil { 103 | r0 = ret.Get(0).(*csi.Volume) 104 | } 105 | } 106 | 107 | var r1 error 108 | if rf, ok := ret.Get(1).(func(context.Context, *csi.VolumeContentSource_VolumeSource, string, int64, map[string]string, gopowerstore.Client) error); ok { 109 | r1 = rf(ctx, volumeSource, volumeName, sizeInBytes, parameters, client) 110 | } else { 111 | r1 = ret.Error(1) 112 | } 113 | 114 | return r0, r1 115 | } 116 | 117 | // Create provides a mock function with given fields: ctx, req, sizeInBytes, client 118 | func (_m *VolumeCreator) Create(ctx context.Context, req *csi.CreateVolumeRequest, sizeInBytes int64, client gopowerstore.Client) (gopowerstore.CreateResponse, error) { 119 | ret := _m.Called(ctx, req, sizeInBytes, client) 120 | 121 | var r0 gopowerstore.CreateResponse 122 | if rf, ok := ret.Get(0).(func(context.Context, *csi.CreateVolumeRequest, int64, gopowerstore.Client) gopowerstore.CreateResponse); ok { 123 | r0 = rf(ctx, req, sizeInBytes, client) 124 | } else { 125 | r0 = ret.Get(0).(gopowerstore.CreateResponse) 126 | } 127 | 128 | var r1 error 129 | if rf, ok := ret.Get(1).(func(context.Context, *csi.CreateVolumeRequest, int64, gopowerstore.Client) error); ok { 130 | r1 = rf(ctx, req, sizeInBytes, client) 131 | } else { 132 | r1 = ret.Error(1) 133 | } 134 | 135 | return r0, r1 136 | } 137 | 138 | // CreateVolumeFromSnapshot provides a mock function with given fields: ctx, snapshotSource, volumeName, sizeInBytes, parameters, client 139 | func (_m *VolumeCreator) CreateVolumeFromSnapshot(ctx context.Context, snapshotSource *csi.VolumeContentSource_SnapshotSource, volumeName string, sizeInBytes int64, parameters map[string]string, client gopowerstore.Client) (*csi.Volume, error) { 140 | ret := _m.Called(ctx, snapshotSource, volumeName, sizeInBytes, parameters, client) 141 | 142 | var r0 *csi.Volume 143 | if rf, ok := ret.Get(0).(func(context.Context, *csi.VolumeContentSource_SnapshotSource, string, int64, map[string]string, gopowerstore.Client) *csi.Volume); ok { 144 | r0 = rf(ctx, snapshotSource, volumeName, sizeInBytes, parameters, client) 145 | } else { 146 | if ret.Get(0) != nil { 147 | r0 = ret.Get(0).(*csi.Volume) 148 | } 149 | } 150 | 151 | var r1 error 152 | if rf, ok := ret.Get(1).(func(context.Context, *csi.VolumeContentSource_SnapshotSource, string, int64, map[string]string, gopowerstore.Client) error); ok { 153 | r1 = rf(ctx, snapshotSource, volumeName, sizeInBytes, parameters, client) 154 | } else { 155 | r1 = ret.Error(1) 156 | } 157 | 158 | return r0, r1 159 | } 160 | -------------------------------------------------------------------------------- /mocks/VolumePublisher.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | // Code generated by mockery. DO NOT EDIT. 18 | 19 | package mocks 20 | 21 | import ( 22 | context "context" 23 | 24 | csi "github.com/container-storage-interface/spec/lib/go/csi" 25 | 26 | gopowerstore "github.com/dell/gopowerstore" 27 | 28 | mock "github.com/stretchr/testify/mock" 29 | ) 30 | 31 | // VolumePublisher is an autogenerated mock type for the VolumePublisher type 32 | type VolumePublisher struct { 33 | mock.Mock 34 | } 35 | 36 | // CheckIfVolumeExists provides a mock function with given fields: ctx, client, volID 37 | func (_m *VolumePublisher) CheckIfVolumeExists(ctx context.Context, client gopowerstore.Client, volID string) error { 38 | ret := _m.Called(ctx, client, volID) 39 | 40 | var r0 error 41 | if rf, ok := ret.Get(0).(func(context.Context, gopowerstore.Client, string) error); ok { 42 | r0 = rf(ctx, client, volID) 43 | } else { 44 | r0 = ret.Error(0) 45 | } 46 | 47 | return r0 48 | } 49 | 50 | // Publish provides a mock function with given fields: ctx, req, client, kubeNodeID, volumeID 51 | func (_m *VolumePublisher) Publish(ctx context.Context, req *csi.ControllerPublishVolumeRequest, client gopowerstore.Client, kubeNodeID string, volumeID string) (*csi.ControllerPublishVolumeResponse, error) { 52 | ret := _m.Called(ctx, req, client, kubeNodeID, volumeID) 53 | 54 | var r0 *csi.ControllerPublishVolumeResponse 55 | if rf, ok := ret.Get(0).(func(context.Context, *csi.ControllerPublishVolumeRequest, gopowerstore.Client, string, string) *csi.ControllerPublishVolumeResponse); ok { 56 | r0 = rf(ctx, req, client, kubeNodeID, volumeID) 57 | } else { 58 | if ret.Get(0) != nil { 59 | r0 = ret.Get(0).(*csi.ControllerPublishVolumeResponse) 60 | } 61 | } 62 | 63 | var r1 error 64 | if rf, ok := ret.Get(1).(func(context.Context, *csi.ControllerPublishVolumeRequest, gopowerstore.Client, string, string) error); ok { 65 | r1 = rf(ctx, req, client, kubeNodeID, volumeID) 66 | } else { 67 | r1 = ret.Error(1) 68 | } 69 | 70 | return r0, r1 71 | } 72 | -------------------------------------------------------------------------------- /pkg/array/testdata/incorrect-endpoint.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - endpoint: "https://512.542.143.0/api/rest" 19 | username: "admin" 20 | globalID: "gid1" 21 | password: "password" 22 | skipCertificateValidation: true 23 | blockProtocol: "auto" 24 | isDefault: true 25 | -------------------------------------------------------------------------------- /pkg/array/testdata/invalid-endpoint.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - endpoint: "aaaaaaaaaaaaaa" 19 | username: "admin" 20 | globalID: "gid1" 21 | password: "password" 22 | skipCertificateValidation: true 23 | blockProtocol: "auto" 24 | isDefault: true 25 | -------------------------------------------------------------------------------- /pkg/array/testdata/no-arr.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - 19 | -------------------------------------------------------------------------------- /pkg/array/testdata/no-globalID.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - endpoint: "https://10.10.10.10/api/rest" 19 | username: "admin" 20 | password: "password" 21 | skipCertificateValidation: true 22 | blockProtocol: "auto" 23 | isDefault: true 24 | -------------------------------------------------------------------------------- /pkg/array/testdata/one-arr.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - endpoint: "https://127.0.0.1/api/rest" 19 | username: "admin" 20 | globalID: "gid1" 21 | password: "password" 22 | skipCertificateValidation: true 23 | blockProtocol: "auto" 24 | isDefault: true 25 | -------------------------------------------------------------------------------- /pkg/array/testdata/two-arr.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | arrays: 18 | - endpoint: "https://127.0.0.1/api/rest" 19 | globalID: "gid1" 20 | username: "admin" 21 | password: "password" 22 | skipCertificateValidation: true 23 | blockProtocol: "iSCSI" 24 | nasName: "test-nas" 25 | isDefault: true 26 | 27 | - endpoint: "https://127.0.0.2/api/rest" 28 | globalID: "gid2" 29 | username: "user" 30 | password: "password" 31 | skipCertificateValidation: true 32 | blockProtocol: "" 33 | -------------------------------------------------------------------------------- /pkg/common/envvars.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package common 20 | 21 | const ( 22 | // EnvDriverName is the name of the csi driver (provisioner) 23 | EnvDriverName = "X_CSI_DRIVER_NAME" 24 | 25 | // EnvNodeIDFilePath is the name of the environment variable used to 26 | // specify the file with the node ID 27 | EnvNodeIDFilePath = "X_CSI_POWERSTORE_NODE_ID_PATH" 28 | 29 | // EnvKubeNodeName is the name of the environment variable which stores current kubernetes 30 | // node name 31 | EnvKubeNodeName = "X_CSI_POWERSTORE_KUBE_NODE_NAME" 32 | 33 | // EnvKubeConfigPath indicates kubernetes configuration path that has to be used by CSI Driver 34 | EnvKubeConfigPath = "KUBECONFIG" 35 | 36 | // EnvNodeNamePrefix is the name of the environment variable which stores prefix which will be 37 | // used when registering node on PowerStore array 38 | EnvNodeNamePrefix = "X_CSI_POWERSTORE_NODE_NAME_PREFIX" 39 | 40 | // EnvMaxVolumesPerNode specifies maximum number of volumes that controller can publish to the node 41 | EnvMaxVolumesPerNode = "X_CSI_POWERSTORE_MAX_VOLUMES_PER_NODE" 42 | 43 | // EnvNodeChrootPath is the name of the environment variable which store path to chroot where 44 | // to execute iSCSI commands 45 | EnvNodeChrootPath = "X_CSI_POWERSTORE_NODE_CHROOT_PATH" 46 | 47 | // EnvTmpDir is the name of the environment variable which store path to the folder which will be used 48 | // for csi-powerstore temporary files 49 | EnvTmpDir = "X_CSI_POWERSTORE_TMP_DIR" // #nosec G101 50 | 51 | // EnvFCPortsFilterFilePath is the name of the environment variable which store path to the file which 52 | // provide list of WWPN which should be used by the driver for FC connection on this node 53 | // example: 54 | // content of the file: 55 | // 21:00:00:29:ff:48:9f:6e,21:00:00:29:ff:48:9f:6e 56 | // If file not exist or empty or in invalid format, then the driver will use all available FC ports 57 | EnvFCPortsFilterFilePath = "X_CSI_FC_PORTS_FILTER_FILE_PATH" 58 | 59 | // EnvThrottlingRateLimit sets a number of concurrent requests to APi 60 | EnvThrottlingRateLimit = "X_CSI_POWERSTORE_THROTTLING_RATE_LIMIT" 61 | 62 | // EnvEnableCHAP is the flag which determines if the driver is going 63 | // to set the CHAP credentials in the ISCSI node database at the time 64 | // of node plugin boot 65 | EnvEnableCHAP = "X_CSI_POWERSTORE_ENABLE_CHAP" 66 | 67 | // EnvExternalAccess is the IP of an additional router you wish to add for nfs export 68 | // Used to provide NFS volumes behind NAT 69 | EnvExternalAccess = "X_CSI_POWERSTORE_EXTERNAL_ACCESS" // #nosec G101 70 | 71 | // EnvArrayConfigFilePath is filepath to powerstore arrays config file 72 | EnvArrayConfigFilePath = "X_CSI_POWERSTORE_CONFIG_PATH" 73 | 74 | // EnvConfigParamsFilePath is filepath to powerstore driver params config file 75 | EnvConfigParamsFilePath = "X_CSI_POWERSTORE_CONFIG_PARAMS_PATH" 76 | 77 | // EnvDebugEnableTracing allow to enable tracing in driver 78 | EnvDebugEnableTracing = "ENABLE_TRACING" 79 | 80 | // EnvReplicationContextPrefix enables sidecars to read required information from volume context 81 | EnvReplicationContextPrefix = "X_CSI_REPLICATION_CONTEXT_PREFIX" 82 | 83 | // EnvReplicationPrefix is used as a prefix to find out if replication is enabled 84 | EnvReplicationPrefix = "X_CSI_REPLICATION_PREFIX" // #nosec G101 85 | 86 | // EnvGOCSIDebug indicates whether to print REQUESTs and RESPONSEs of all CSI method calls(from gocsi) 87 | EnvGOCSIDebug = "X_CSI_DEBUG" 88 | 89 | // EnvIsHealthMonitorEnabled specifies if health monitor is enabled. 90 | EnvIsHealthMonitorEnabled = "X_CSI_HEALTH_MONITOR_ENABLED" 91 | 92 | // EnvNfsAcls specifies acls to be set on NFS mount directory 93 | EnvNfsAcls = "X_CSI_NFS_ACLS" 94 | 95 | // EnvMetadataRetrieverEndpoint specifies the endpoint address for csi-metadata-retriever sidecar 96 | EnvMetadataRetrieverEndpoint = "CSI_RETRIEVER_ENDPOINT" 97 | 98 | // EnvAllowAutoRoundOffFilesystemSize specifies if auto round off minimum filesystem size is enabled 99 | EnvAllowAutoRoundOffFilesystemSize = "CSI_AUTO_ROUND_OFF_FILESYSTEM_SIZE" 100 | 101 | // EnvPodmonEnabled indicates that podmon is enabled 102 | EnvPodmonEnabled = "X_CSI_PODMON_ENABLED" 103 | 104 | // EnvPodmonAPIPORT indicates the port to be used for exposing podmon API health, ToDo: Rename to var EnvPodmonArrayConnectivityAPIPORT 105 | EnvPodmonAPIPORT = "X_CSI_PODMON_API_PORT" 106 | 107 | // EnvPodmonArrayConnectivityPollRate indicates the polling frequency to check array connectivity 108 | EnvPodmonArrayConnectivityPollRate = "X_CSI_PODMON_ARRAY_CONNECTIVITY_POLL_RATE" 109 | 110 | // EnvMultiNASThreshold specifies the failure threshold used to put NAS in cooldown. 111 | EnvMultiNASFailureThreshold = "X_CSI_MULTI_NAS_FAILURE_THRESHOLD" 112 | 113 | // EnvMultiNASCooldownPeriod specifies the cooldown period for multiple NAS devices. 114 | EnvMultiNASCooldownPeriod = "X_CSI_MULTI_NAS_COOLDOWN_PERIOD" 115 | 116 | // EnvNFSExportDirectory is the path to the folder where the nfs volumes are mounted 117 | EnvNFSExportDirectory = "X_CSI_NFS_EXPORT_DIRECTORY" 118 | 119 | // EnvDriverNamespace is the namespace where the powerstore driver is deployed 120 | EnvDriverNamespace = "X_CSI_DRIVER_NAMESPACE" 121 | ) 122 | -------------------------------------------------------------------------------- /pkg/common/fs/fs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package fs 20 | 21 | import ( 22 | "context" 23 | "os" 24 | "strings" 25 | "testing" 26 | 27 | "github.com/dell/gofsutil" 28 | "github.com/stretchr/testify/suite" 29 | ) 30 | 31 | type FsTestSuite struct { 32 | suite.Suite 33 | fs Interface 34 | tmp string 35 | } 36 | 37 | func (suite *FsTestSuite) SetupSuite() { 38 | suite.fs = &Fs{Util: &gofsutil.FS{}} 39 | suite.tmp = "./tmp" 40 | err := os.Mkdir(suite.tmp, 0o750) 41 | if err != nil { 42 | suite.T().Error("couldn't create the tmp folder") 43 | } 44 | } 45 | 46 | func (suite *FsTestSuite) TearDownSuite() { 47 | err := os.RemoveAll(suite.tmp) 48 | if err != nil { 49 | suite.T().Error("couldn't remove the tmp folder") 50 | } 51 | } 52 | 53 | func (suite *FsTestSuite) TestCreate() { 54 | file, err := suite.fs.Create(suite.tmp + "/create") 55 | suite.Assert().NoError(err) 56 | 57 | _, err = suite.fs.Stat(suite.tmp + "/create") 58 | suite.Assert().NoError(err) 59 | 60 | _, err = suite.fs.WriteString(file, "random string") 61 | suite.Assert().NoError(err) 62 | 63 | bytes, err := suite.fs.ReadFile(suite.tmp + "/create") 64 | suite.Assert().NoError(err) 65 | suite.Assert().Equal(string(bytes), "random string") 66 | 67 | err = suite.fs.Remove(suite.tmp + "/create") 68 | suite.Assert().NoError(err) 69 | suite.Assert().NoFileExists(suite.tmp + "/create") 70 | 71 | _, err = suite.fs.ReadFile(suite.tmp + "/create") 72 | suite.Assert().Error(err) 73 | suite.fs.IsNotExist(err) 74 | } 75 | 76 | func (suite *FsTestSuite) TestWriteFile() { 77 | str := "random string \n hello" 78 | data := []byte(str) 79 | err := suite.fs.WriteFile(suite.tmp+"/create", data, 0o640) 80 | suite.Assert().NoError(err) 81 | 82 | bytes, err := suite.fs.ReadFile(suite.tmp + "/create") 83 | suite.Assert().NoError(err) 84 | suite.Assert().Equal(bytes, data) 85 | } 86 | 87 | func (suite *FsTestSuite) TestOpenFile() { 88 | file, err := suite.fs.OpenFile(suite.tmp+"/file", os.O_CREATE, 0o600) 89 | suite.Assert().NoError(err) 90 | 91 | err = suite.fs.Chmod(suite.tmp+"/file", os.ModeSticky) 92 | suite.Assert().NoError(err) 93 | 94 | err = file.Close() 95 | suite.Assert().NoError(err) 96 | } 97 | 98 | func (suite *FsTestSuite) TestMkDir() { 99 | err := suite.fs.Mkdir(suite.tmp+"/dir", 0o750) 100 | suite.Assert().NoError(err) 101 | suite.Assert().DirExists(suite.tmp + "/dir") 102 | 103 | err = suite.fs.MkdirAll(suite.tmp+"/1/2/3", 0o750) 104 | suite.Assert().NoError(err) 105 | suite.Assert().DirExists(suite.tmp + "/1/2") 106 | 107 | err = suite.fs.RemoveAll(suite.tmp + "/1") 108 | suite.Assert().NoError(err) 109 | suite.Assert().NoDirExists(suite.tmp + "/1") 110 | } 111 | 112 | func (suite *FsTestSuite) TestMkFileIdempotent() { 113 | created, err := suite.fs.MkFileIdempotent(suite.tmp + "/myfile") 114 | suite.Assert().NoError(err) 115 | suite.Assert().Equal(true, created) 116 | 117 | err = suite.fs.Mkdir(suite.tmp+"/mydir", 0o750) 118 | suite.Assert().NoError(err) 119 | _, err = suite.fs.MkFileIdempotent(suite.tmp + "/mydir") 120 | suite.Assert().EqualError(err, "existing path is a directory") 121 | 122 | created, err = suite.fs.MkFileIdempotent(suite.tmp + "/myfile") 123 | suite.Assert().NoError(err) 124 | suite.Assert().Equal(false, created) 125 | } 126 | 127 | func (suite *FsTestSuite) TestExecCommand() { 128 | out, err := suite.fs.ExecCommand("find") 129 | suite.Assert().NoError(err) 130 | suite.Assert().NotEmpty(out) 131 | 132 | out, err = suite.fs.ExecCommandOutput("find") 133 | suite.Assert().NoError(err) 134 | suite.Assert().NotEmpty(out) 135 | } 136 | 137 | func (suite *FsTestSuite) TestIsDeviceOrResourceBusy() { 138 | err := suite.fs.Remove(suite.tmp + "/busy") 139 | res := suite.fs.IsDeviceOrResourceBusy(err) 140 | suite.Assert().False(res) 141 | } 142 | 143 | func (suite *FsTestSuite) TestParseProcMounts() { 144 | input := `17 60 0:16 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel 145 | 18 60 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw 146 | 19 60 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=1930460k,nr_inodes=482615,mode=755 147 | 20 17 0:15 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw 148 | 21 19 0:17 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel 149 | 22 19 0:11 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000 150 | 23 60 0:18 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755 151 | 24 17 0:19 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:8 - tmpfs tmpfs ro,seclabel,mode=755` 152 | mounts, err := suite.fs.ParseProcMounts(context.Background(), strings.NewReader(input)) 153 | suite.Assert().NoError(err) 154 | suite.Assert().NotEmpty(mounts) 155 | } 156 | 157 | func (suite *FsTestSuite) TestNetDial() { 158 | conn, err := suite.fs.NetDial("localhost") 159 | suite.Assert().NoError(err) 160 | conn.Close() 161 | } 162 | 163 | func (suite *FsTestSuite) TestGetUtil() { 164 | util := suite.fs.GetUtil() 165 | suite.Assert().NotNil(util) 166 | } 167 | 168 | func TestFsTestSuite(t *testing.T) { 169 | suite.Run(t, new(FsTestSuite)) 170 | } 171 | -------------------------------------------------------------------------------- /pkg/common/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package common 20 | 21 | import ( 22 | "context" 23 | 24 | log "github.com/sirupsen/logrus" 25 | ) 26 | 27 | // CustomLogger is logger wrapper that can be passed to gopowerstore, gobrick allowing to logging context fields with each call 28 | type CustomLogger struct{} 29 | 30 | // Info is a wrapper of logrus Info method 31 | func (lg *CustomLogger) Info(ctx context.Context, format string, args ...interface{}) { 32 | log.WithFields(GetLogFields(ctx)).Infof(format, args...) 33 | } 34 | 35 | // Debug is a wrapper of logrus Debug method 36 | func (lg *CustomLogger) Debug(ctx context.Context, format string, args ...interface{}) { 37 | log.WithFields(GetLogFields(ctx)).Debugf(format, args...) 38 | } 39 | 40 | // Error is a wrapper of logrus Error method 41 | func (lg *CustomLogger) Error(ctx context.Context, format string, args ...interface{}) { 42 | log.WithFields(GetLogFields(ctx)).Errorf(format, args...) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/controller/base.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package controller 20 | 21 | import ( 22 | "context" 23 | "strings" 24 | "unicode/utf8" 25 | 26 | "github.com/container-storage-interface/spec/lib/go/csi" 27 | "github.com/dell/csi-powerstore/v2/pkg/common" 28 | "github.com/dell/gopowerstore" 29 | "google.golang.org/grpc/codes" 30 | "google.golang.org/grpc/status" 31 | "google.golang.org/protobuf/types/known/timestamppb" 32 | ) 33 | 34 | const ( 35 | // MinVolumeSizeBytes is minimal size for volume creation on PowerStore 36 | MinVolumeSizeBytes = 1048576 37 | // MinFilesystemSizeBytes is minimal size for Filesystem creation on PowerStore - 1.5Gi 38 | MinFilesystemSizeBytes = 1610612736 39 | // MaxVolumeSizeBytes is maximum size for volume creation on PowerStore 40 | MaxVolumeSizeBytes = 1099511627776 * 256 // 256 TB 41 | // VolumeSizeMultiple multiplier for volumes 42 | VolumeSizeMultiple = 8192 43 | // MaxVolumeNameLength max length for the volume name 44 | MaxVolumeNameLength = 128 45 | // ReplicationPrefix represents replication prefix 46 | ReplicationPrefix = "replication.storage.dell.com" 47 | // ErrUnknownAccessType represents error message for unknown access type 48 | ErrUnknownAccessType = "unknown access type is not Block or Mount" 49 | // ErrUnknownAccessMode represents error message for unknown access mode 50 | ErrUnknownAccessMode = "access mode cannot be UNKNOWN" 51 | // ErrNoMultiNodeWriter represents error message for multi node access 52 | ErrNoMultiNodeWriter = "multi-node with writer(s) only supported for block access type" 53 | // KeyFsType represents key for Fs Type 54 | KeyFsType = "csi.storage.k8s.io/fstype" 55 | // KeyFsTypeOld represents old key for Fs Type 56 | KeyFsTypeOld = "FsType" 57 | // KeyReplicationEnabled represents key for replication enabled 58 | KeyReplicationEnabled = "isReplicationEnabled" 59 | // KeyReplicationMode represents key for replication mode 60 | KeyReplicationMode = "mode" 61 | // KeyReplicationRPO represents key for replication RPO 62 | KeyReplicationRPO = "rpo" 63 | // KeyReplicationRemoteSystem represents key for replication remote system 64 | KeyReplicationRemoteSystem = "remoteSystem" 65 | // KeyReplicationIgnoreNamespaces represents key for replication ignore namespaces 66 | KeyReplicationIgnoreNamespaces = "ignoreNamespaces" 67 | // KeyReplicationVGPrefix represents key for replication vg prefix 68 | KeyReplicationVGPrefix = "volumeGroupPrefix" 69 | // KeyNasName represents key for nas name 70 | KeyNasName = "nasName" 71 | // KeyCSIPVCNamespace represents key for csi pvc namespace 72 | KeyCSIPVCNamespace = "csi.storage.k8s.io/pvc/namespace" 73 | // KeyCSIPVCName represents key for csi pvc name 74 | KeyCSIPVCName = "csi.storage.k8s.io/pvc/name" 75 | ) 76 | 77 | func volumeNameValidation(volumeName string) error { 78 | if volumeName == "" { 79 | return status.Errorf(codes.InvalidArgument, "name cannot be empty") 80 | } 81 | 82 | if utf8.RuneCountInString(volumeName) > MaxVolumeNameLength { 83 | return status.Errorf(codes.InvalidArgument, "name must contain %d or fewer printable Unicode characters", MaxVolumeNameLength) 84 | } 85 | 86 | return nil 87 | } 88 | 89 | func volumeSizeValidation(minSize, maxSize int64) error { 90 | if minSize < 0 || maxSize < 0 { 91 | return status.Errorf( 92 | codes.OutOfRange, 93 | "bad capacity: volume size bytes %d and limit size bytes: %d must not be negative", minSize, maxSize) 94 | } 95 | 96 | if maxSize < minSize { 97 | return status.Errorf( 98 | codes.OutOfRange, 99 | "bad capacity: max size bytes %d can't be less than minimum size bytes %d", maxSize, minSize) 100 | } 101 | 102 | if maxSize > MaxVolumeSizeBytes { 103 | return status.Errorf( 104 | codes.OutOfRange, 105 | "bad capacity: max size bytes %d can't be more than maximum size bytes %d", maxSize, MaxVolumeSizeBytes) 106 | } 107 | 108 | return nil 109 | } 110 | 111 | func getCSIVolume(volumeID string, size int64) *csi.Volume { 112 | volume := &csi.Volume{ 113 | VolumeId: volumeID, 114 | CapacityBytes: size, 115 | } 116 | return volume 117 | } 118 | 119 | func getCSISnapshot(snapshotID string, sourceVolumeID string, sizeInBytes int64) *csi.Snapshot { 120 | snap := &csi.Snapshot{ 121 | SizeBytes: sizeInBytes, 122 | SnapshotId: snapshotID, 123 | SourceVolumeId: sourceVolumeID, 124 | CreationTime: timestamppb.Now(), 125 | ReadyToUse: true, 126 | } 127 | return snap 128 | } 129 | 130 | func detachVolumeFromHost(ctx context.Context, hostID string, volumeID string, client gopowerstore.Client) error { 131 | dp := &gopowerstore.HostVolumeDetach{VolumeID: &volumeID} 132 | _, err := client.DetachVolumeFromHost(ctx, hostID, dp) 133 | if err != nil { 134 | apiError, ok := err.(gopowerstore.APIError) 135 | if !ok { 136 | return status.Errorf(codes.Unknown, "failed to detach volume '%s' from host: %s", volumeID, err.Error()) 137 | } 138 | // In case of resiliency we can have multiple calls simultaneously (from podmon and k8) so to keep it idempotent 139 | if strings.Contains(apiError.Message, "Host is not attached to volume") { 140 | return nil 141 | } 142 | if apiError.HostIsNotExist() { 143 | return status.Errorf(codes.NotFound, "host with ID '%s' not found", hostID) 144 | } 145 | if !apiError.VolumeIsNotAttachedToHost() && !apiError.HostIsNotAttachedToVolume() && !apiError.NotFound() && !apiError.VolumeDetachedFromHost() { 146 | return status.Errorf(codes.Unknown, "unexpected api error when detaching volume from host:%s", err.Error()) 147 | } 148 | 149 | } 150 | return nil 151 | } 152 | 153 | func accTypeIsBlock(vcs []*csi.VolumeCapability) bool { 154 | for _, vc := range vcs { 155 | if at := vc.GetBlock(); at != nil { 156 | return true 157 | } 158 | } 159 | return false 160 | } 161 | 162 | func checkValidAccessTypes(vcs []*csi.VolumeCapability) bool { 163 | for _, vc := range vcs { 164 | if vc == nil { 165 | continue 166 | } 167 | atblock := vc.GetBlock() 168 | if atblock != nil { 169 | continue 170 | } 171 | atmount := vc.GetMount() 172 | if atmount != nil { 173 | continue 174 | } 175 | // Unknown access type, we should reject it. 176 | return false 177 | } 178 | return true 179 | } 180 | 181 | func getDescription(params map[string]string) string { 182 | if description, ok := params[common.KeyVolumeDescription]; ok { 183 | return description 184 | } 185 | return params[KeyCSIPVCName] + "-" + params[KeyCSIPVCNamespace] 186 | } 187 | -------------------------------------------------------------------------------- /pkg/controller/base_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package controller 20 | 21 | import ( 22 | "context" 23 | "errors" 24 | "net/http" 25 | "strings" 26 | "testing" 27 | 28 | "github.com/dell/gopowerstore" 29 | "github.com/dell/gopowerstore/api" 30 | "github.com/dell/gopowerstore/mocks" 31 | "github.com/stretchr/testify/assert" 32 | "github.com/stretchr/testify/mock" 33 | "google.golang.org/grpc/codes" 34 | "google.golang.org/grpc/status" 35 | ) 36 | 37 | func TestVolumeName(t *testing.T) { 38 | tests := []struct { 39 | name string 40 | err error 41 | }{ 42 | {"IsOkName", nil}, 43 | {"", status.Errorf(codes.InvalidArgument, "name cannot be empty")}, 44 | { 45 | strings.Repeat("N", MaxVolumeNameLength+1), 46 | status.Errorf(codes.InvalidArgument, "name must contain %d or fewer printable Unicode characters", MaxVolumeNameLength), 47 | }, 48 | } 49 | 50 | for _, test := range tests { 51 | test := test 52 | t.Run("", func(st *testing.T) { 53 | st.Parallel() 54 | result := volumeNameValidation(test.name) 55 | 56 | assert.Equal(t, result, test.err) 57 | }) 58 | } 59 | } 60 | 61 | func TestVolumeSize(t *testing.T) { 62 | tests := []struct { 63 | min int64 64 | max int64 65 | err error 66 | }{ 67 | { 68 | -1, 69 | -1, 70 | status.Errorf( 71 | codes.OutOfRange, 72 | "bad capacity: volume size bytes -1 and limit size bytes: -1 must not be negative"), 73 | }, 74 | { 75 | 236364574767, 76 | 235345345, 77 | status.Errorf( 78 | codes.OutOfRange, 79 | "bad capacity: max size bytes 235345345 can't be less than minimum size bytes 236364574767"), 80 | }, 81 | { 82 | 8192, 83 | MaxVolumeSizeBytes + 1, 84 | status.Errorf( 85 | codes.OutOfRange, 86 | "bad capacity: max size bytes %d can't be more than maximum size bytes %d", 87 | MaxVolumeSizeBytes+1, MaxVolumeSizeBytes), 88 | }, 89 | { 90 | 8192, 91 | MaxVolumeSizeBytes - 1, 92 | nil, 93 | }, 94 | } 95 | 96 | for _, test := range tests { 97 | test := test 98 | t.Run("", func(st *testing.T) { 99 | st.Parallel() 100 | result := volumeSizeValidation(test.min, test.max) 101 | 102 | assert.Equal(t, result, test.err) 103 | }) 104 | } 105 | } 106 | 107 | func TestDetachVolumeFromHost(t *testing.T) { 108 | t.Run("unknown error", func(t *testing.T) { 109 | ctx := context.Background() 110 | hostID := "host-id" 111 | volumeID := "vol-id" 112 | 113 | clientMock := new(mocks.Client) 114 | clientMock.On("DetachVolumeFromHost", ctx, hostID, mock.AnythingOfType("*gopowerstore.HostVolumeDetach")). 115 | Return(gopowerstore.EmptyResponse(""), errors.New("unknown")) 116 | 117 | err := detachVolumeFromHost(ctx, hostID, volumeID, clientMock) 118 | assert.Error(t, err) 119 | assert.Contains(t, err.Error(), "failed to detach volume") 120 | }) 121 | 122 | t.Run("host does not exist", func(t *testing.T) { 123 | ctx := context.Background() 124 | hostID := "host-id" 125 | volumeID := "vol-id" 126 | 127 | clientMock := new(mocks.Client) 128 | clientMock.On("DetachVolumeFromHost", ctx, hostID, mock.AnythingOfType("*gopowerstore.HostVolumeDetach")). 129 | Return(gopowerstore.EmptyResponse(""), gopowerstore.APIError{ 130 | ErrorMsg: &api.ErrorMsg{ 131 | StatusCode: http.StatusNotFound, 132 | }, 133 | }) 134 | 135 | err := detachVolumeFromHost(ctx, hostID, volumeID, clientMock) 136 | assert.Error(t, err) 137 | assert.Contains(t, err.Error(), "host with ID '"+hostID+"' not found") 138 | }) 139 | 140 | t.Run("unknown api error", func(t *testing.T) { 141 | ctx := context.Background() 142 | hostID := "host-id" 143 | volumeID := "vol-id" 144 | 145 | clientMock := new(mocks.Client) 146 | clientMock.On("DetachVolumeFromHost", ctx, hostID, mock.AnythingOfType("*gopowerstore.HostVolumeDetach")). 147 | Return(gopowerstore.EmptyResponse(""), gopowerstore.APIError{ 148 | ErrorMsg: &api.ErrorMsg{ 149 | StatusCode: 0, 150 | Severity: "", 151 | Message: "", 152 | Arguments: nil, 153 | }, 154 | }) 155 | 156 | err := detachVolumeFromHost(ctx, hostID, volumeID, clientMock) 157 | assert.Error(t, err) 158 | assert.Contains(t, err.Error(), "unexpected api error when detaching volume from host") 159 | }) 160 | } 161 | -------------------------------------------------------------------------------- /pkg/controller/controller_node_to_array_connectivity.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2022-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | // Package controller provides CSI specification compatible controller service. 20 | package controller 21 | 22 | import ( 23 | "context" 24 | "encoding/json" 25 | "fmt" 26 | "io" 27 | "net/http" 28 | "time" 29 | 30 | log "github.com/sirupsen/logrus" 31 | 32 | "github.com/dell/csi-powerstore/v2/pkg/common" 33 | ) 34 | 35 | // QueryArrayStatus make API call to the specified url to retrieve connection status 36 | func (s *Service) QueryArrayStatus(ctx context.Context, url string) (bool, error) { 37 | defer func() { 38 | if err := recover(); err != nil { 39 | log.Println("panic occurred in queryStatus:", err) 40 | } 41 | }() 42 | client := http.Client{ 43 | Timeout: common.Timeout, 44 | } 45 | resp, err := client.Get(url) 46 | 47 | log.Debugf("Received response %+v for url %s", resp, url) 48 | if err != nil { 49 | log.Errorf("failed to call API %s due to %s ", url, err.Error()) 50 | return false, err 51 | } 52 | defer resp.Body.Close() // #nosec G307 53 | bodyBytes, err := io.ReadAll(resp.Body) 54 | if err != nil { 55 | log.Errorf("failed to read API response due to %s ", err.Error()) 56 | return false, err 57 | } 58 | if resp.StatusCode != 200 { 59 | log.Errorf("Found unexpected response from the server while fetching array status %d ", resp.StatusCode) 60 | return false, fmt.Errorf("unexpected response from the server") 61 | } 62 | var statusResponse common.ArrayConnectivityStatus 63 | err = json.Unmarshal(bodyBytes, &statusResponse) 64 | if err != nil { 65 | log.Errorf("unable to unmarshal and determine connectivity due to %s ", err) 66 | return false, err 67 | } 68 | log.Infof("API Response received is %+v\n", statusResponse) 69 | // responseObject has last success and last attempt timestamp in Unix format 70 | timeDiff := statusResponse.LastAttempt - statusResponse.LastSuccess 71 | tolerance := common.SetPollingFrequency(ctx) 72 | currTime := time.Now().Unix() 73 | // checking if the status response is stale and connectivity test is still running 74 | // since nodeProbe is run at frequency tolerance/2, ideally below check should never be true 75 | if (currTime - statusResponse.LastAttempt) > tolerance*2 { 76 | log.Errorf("seems like connectivity test is not being run, current time is %d and last run was at %d", currTime, statusResponse.LastAttempt) 77 | // considering connectivity is broken 78 | return false, nil 79 | } 80 | log.Debugf("last connectivity was %d sec back, tolerance is %d sec", timeDiff, tolerance) 81 | // give 2s leeway for tolerance check 82 | if timeDiff <= tolerance+2 { 83 | return true, nil 84 | } 85 | return false, nil 86 | } 87 | -------------------------------------------------------------------------------- /pkg/controller/snapshotter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package controller 20 | 21 | import ( 22 | "context" 23 | 24 | "github.com/dell/gopowerstore" 25 | "google.golang.org/grpc/codes" 26 | "google.golang.org/grpc/status" 27 | ) 28 | 29 | const ( 30 | // FilesystemSnapshotType represents filesystem snapshot type 31 | FilesystemSnapshotType SnapshotType = "filesystem" 32 | // BlockSnapshotType represents block snapshot type 33 | BlockSnapshotType SnapshotType = "block" 34 | ) 35 | 36 | // SnapshotType represents type of snapshot 37 | type SnapshotType string 38 | 39 | // VolumeSnapshot represents snapshot of the block Volume 40 | type VolumeSnapshot gopowerstore.Volume 41 | 42 | // FilesystemSnapshot represents snapshot of the FileSystem 43 | type FilesystemSnapshot gopowerstore.FileSystem 44 | 45 | // GeneralSnapshot is an interface for combining both Volume and FileSystem 46 | type GeneralSnapshot interface { 47 | // GetID returns ID of the snapshot 48 | GetID() string 49 | // GetSourceID returns ID of the volume/fs that snapshot was created from 50 | GetSourceID() string 51 | // GetSize returns current size of the snapshot 52 | GetSize() int64 53 | // GetType returns type of general snapshot (either filesystem or block) 54 | GetType() SnapshotType 55 | } 56 | 57 | // GetID returns ID of the snapshot 58 | func (v VolumeSnapshot) GetID() string { 59 | return v.ID 60 | } 61 | 62 | // GetSourceID returns ID of the volume/fs that snapshot was created from 63 | func (v VolumeSnapshot) GetSourceID() string { 64 | return v.ProtectionData.SourceID 65 | } 66 | 67 | // GetSize returns current size of the snapshot 68 | func (v VolumeSnapshot) GetSize() int64 { 69 | return v.Size 70 | } 71 | 72 | // GetType returns type of general snapshot (either filesystem or block) 73 | func (v VolumeSnapshot) GetType() SnapshotType { 74 | return BlockSnapshotType 75 | } 76 | 77 | // GetID returns ID of the snapshot 78 | func (f FilesystemSnapshot) GetID() string { 79 | return f.ID 80 | } 81 | 82 | // GetSourceID returns ID of the volume/fs that snapshot was created from 83 | func (f FilesystemSnapshot) GetSourceID() string { 84 | return f.ParentID 85 | } 86 | 87 | // GetSize returns current size of the snapshot 88 | func (f FilesystemSnapshot) GetSize() int64 { 89 | return f.SizeTotal 90 | } 91 | 92 | // GetType returns type of general snapshot (either filesystem or block) 93 | func (f FilesystemSnapshot) GetType() SnapshotType { 94 | return FilesystemSnapshotType 95 | } 96 | 97 | // VolumeSnapshotter allow to create snapshot of the volume/fs 98 | type VolumeSnapshotter interface { 99 | // GetExistingSnapshot queries storage array if given snapshot already exists 100 | GetExistingSnapshot(context.Context, string, gopowerstore.Client) (GeneralSnapshot, error) 101 | // Create creates new snapshot of a given volume 102 | Create(context.Context, string, string, gopowerstore.Client) (gopowerstore.CreateResponse, error) 103 | } 104 | 105 | // SCSISnapshotter is a implementation of VolumeSnapshotter for SCSI based (FC, iSCSI) volumes 106 | type SCSISnapshotter struct{} 107 | 108 | // GetExistingSnapshot queries storage array if given snapshot of the Volume already exists 109 | func (*SCSISnapshotter) GetExistingSnapshot(ctx context.Context, snapName string, client gopowerstore.Client) (GeneralSnapshot, error) { 110 | snap, err := client.GetVolumeByName(ctx, snapName) 111 | if err != nil { 112 | return VolumeSnapshot{}, status.Errorf(codes.Internal, 113 | "can't find volume snapshot '%s'", snapName) 114 | } 115 | return VolumeSnapshot(snap), nil 116 | } 117 | 118 | // Create creates new snapshot of a given Volume 119 | func (*SCSISnapshotter) Create(ctx context.Context, snapName string, sourceID string, client gopowerstore.Client) (gopowerstore.CreateResponse, error) { 120 | reqParams := &gopowerstore.SnapshotCreate{ 121 | Name: &snapName, 122 | Description: nil, 123 | } 124 | return client.CreateSnapshot(ctx, reqParams, sourceID) 125 | } 126 | 127 | // NfsSnapshotter is a implementation of VolumeSnapshotter for NFS volumes 128 | type NfsSnapshotter struct{} 129 | 130 | // GetExistingSnapshot queries storage array if given snapshot of the FileSystem already exists 131 | func (*NfsSnapshotter) GetExistingSnapshot(ctx context.Context, snapName string, client gopowerstore.Client) (GeneralSnapshot, error) { 132 | snap, err := client.GetFSByName(ctx, snapName) 133 | if err != nil { 134 | return FilesystemSnapshot{}, status.Errorf(codes.Internal, 135 | "can't find filesystem snapshot '%s': %s", snapName, err.Error()) 136 | } 137 | return FilesystemSnapshot(snap), nil 138 | } 139 | 140 | // Create creates new snapshot of a given FileSystem 141 | func (*NfsSnapshotter) Create(ctx context.Context, snapName string, sourceID string, client gopowerstore.Client) (gopowerstore.CreateResponse, error) { 142 | reqParams := &gopowerstore.SnapshotFSCreate{ 143 | Name: snapName, 144 | Description: "", 145 | } 146 | return client.CreateFsSnapshot(ctx, reqParams, sourceID) 147 | } 148 | -------------------------------------------------------------------------------- /pkg/identity/identity.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | // Package identity provides CSI specification compatible identity service. 20 | package identity 21 | 22 | import ( 23 | "context" 24 | 25 | "github.com/container-storage-interface/spec/lib/go/csi" 26 | "google.golang.org/protobuf/types/known/wrapperspb" 27 | ) 28 | 29 | // NewIdentityService creates new identity service 30 | func NewIdentityService(name string, version string, manifest map[string]string) *Service { 31 | return &Service{ 32 | name: name, 33 | version: version, 34 | ready: true, 35 | manifest: manifest, 36 | } 37 | } 38 | 39 | // Service is a identity service allows driver to return capabilities, health, and other metadata 40 | type Service struct { 41 | name string 42 | version string 43 | manifest map[string]string 44 | ready bool 45 | } 46 | 47 | // GetPluginInfo returns general information about plugin (driver) such as name, version and manifest 48 | func (s Service) GetPluginInfo(_ context.Context, _ *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { 49 | return &csi.GetPluginInfoResponse{ 50 | Name: s.name, 51 | VendorVersion: s.version, 52 | Manifest: s.manifest, 53 | }, nil 54 | } 55 | 56 | // GetPluginCapabilities returns capabilities that are supported by the driver 57 | func (s Service) GetPluginCapabilities(_ context.Context, _ *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { 58 | var rep csi.GetPluginCapabilitiesResponse 59 | rep.Capabilities = []*csi.PluginCapability{ 60 | { 61 | Type: &csi.PluginCapability_Service_{ 62 | Service: &csi.PluginCapability_Service{ 63 | Type: csi.PluginCapability_Service_CONTROLLER_SERVICE, 64 | }, 65 | }, 66 | }, 67 | { 68 | Type: &csi.PluginCapability_VolumeExpansion_{ 69 | VolumeExpansion: &csi.PluginCapability_VolumeExpansion{ 70 | Type: csi.PluginCapability_VolumeExpansion_ONLINE, 71 | }, 72 | }, 73 | }, 74 | { 75 | Type: &csi.PluginCapability_VolumeExpansion_{ 76 | VolumeExpansion: &csi.PluginCapability_VolumeExpansion{ 77 | Type: csi.PluginCapability_VolumeExpansion_OFFLINE, 78 | }, 79 | }, 80 | }, 81 | { 82 | Type: &csi.PluginCapability_Service_{ 83 | Service: &csi.PluginCapability_Service{ 84 | Type: csi.PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS, 85 | }, 86 | }, 87 | }, 88 | } 89 | 90 | return &rep, nil 91 | } 92 | 93 | // Probe returns current state of the driver and if it is ready to receive requests 94 | func (s Service) Probe(_ context.Context, _ *csi.ProbeRequest) (*csi.ProbeResponse, error) { 95 | return &csi.ProbeResponse{Ready: &wrapperspb.BoolValue{Value: s.ready}}, nil 96 | } 97 | -------------------------------------------------------------------------------- /pkg/identity/identity_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package identity 20 | 21 | import ( 22 | "context" 23 | "testing" 24 | 25 | "github.com/container-storage-interface/spec/lib/go/csi" 26 | "github.com/dell/csi-powerstore/v2/pkg/common" 27 | ginkgo "github.com/onsi/ginkgo" 28 | "github.com/onsi/ginkgo/reporters" 29 | gomega "github.com/onsi/gomega" 30 | "google.golang.org/protobuf/types/known/wrapperspb" 31 | ) 32 | 33 | var idntySvc *Service 34 | 35 | func TestCSIIdentityService(t *testing.T) { 36 | gomega.RegisterFailHandler(ginkgo.Fail) 37 | junitReporter := reporters.NewJUnitReporter("idnty-svc.xml") 38 | ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "CSIIdentityService testing suite", []ginkgo.Reporter{junitReporter}) 39 | } 40 | 41 | func setVariables() { 42 | idntySvc = NewIdentityService(common.Name, "v1.3.0", common.Manifest) 43 | } 44 | 45 | var _ = ginkgo.Describe("CSIIdentityService", func() { 46 | ginkgo.BeforeEach(func() { 47 | setVariables() 48 | }) 49 | 50 | ginkgo.Describe("calling GetPluginInfo()", func() { 51 | ginkgo.It("should return correct info", func() { 52 | res, err := idntySvc.GetPluginInfo(context.Background(), &csi.GetPluginInfoRequest{}) 53 | gomega.Expect(err).To(gomega.BeNil()) 54 | gomega.Expect(res).To(gomega.Equal(&csi.GetPluginInfoResponse{ 55 | Name: idntySvc.name, 56 | VendorVersion: idntySvc.version, 57 | Manifest: idntySvc.manifest, 58 | })) 59 | }) 60 | }) 61 | 62 | ginkgo.Describe("calling GetPluginCapabilities()", func() { 63 | ginkgo.It("should return correct capabilities", func() { 64 | res, err := idntySvc.GetPluginCapabilities(context.Background(), &csi.GetPluginCapabilitiesRequest{}) 65 | gomega.Expect(err).To(gomega.BeNil()) 66 | gomega.Expect(res).To(gomega.Equal(&csi.GetPluginCapabilitiesResponse{ 67 | Capabilities: []*csi.PluginCapability{ 68 | { 69 | Type: &csi.PluginCapability_Service_{ 70 | Service: &csi.PluginCapability_Service{ 71 | Type: csi.PluginCapability_Service_CONTROLLER_SERVICE, 72 | }, 73 | }, 74 | }, 75 | { 76 | Type: &csi.PluginCapability_VolumeExpansion_{ 77 | VolumeExpansion: &csi.PluginCapability_VolumeExpansion{ 78 | Type: csi.PluginCapability_VolumeExpansion_ONLINE, 79 | }, 80 | }, 81 | }, 82 | { 83 | Type: &csi.PluginCapability_VolumeExpansion_{ 84 | VolumeExpansion: &csi.PluginCapability_VolumeExpansion{ 85 | Type: csi.PluginCapability_VolumeExpansion_OFFLINE, 86 | }, 87 | }, 88 | }, 89 | { 90 | Type: &csi.PluginCapability_Service_{ 91 | Service: &csi.PluginCapability_Service{ 92 | Type: csi.PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS, 93 | }, 94 | }, 95 | }, 96 | }, 97 | }, 98 | )) 99 | }) 100 | }) 101 | 102 | ginkgo.Describe("calling Probe()", func() { 103 | ginkgo.It("should return current status'", func() { 104 | res, err := idntySvc.Probe(context.Background(), &csi.ProbeRequest{}) 105 | gomega.Expect(err).To(gomega.BeNil()) 106 | gomega.Expect(res).To(gomega.Equal(&csi.ProbeResponse{Ready: &wrapperspb.BoolValue{Value: idntySvc.ready}})) 107 | }) 108 | }) 109 | }) 110 | -------------------------------------------------------------------------------- /pkg/node/acl.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package node 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | "os/exec" 25 | "regexp" 26 | "strings" 27 | 28 | "github.com/dell/gopowerstore" 29 | log "github.com/sirupsen/logrus" 30 | "google.golang.org/grpc/codes" 31 | "google.golang.org/grpc/status" 32 | ) 33 | 34 | // NFSv4ACLsInterface contains method definition to set NFSv4 ACLs 35 | type NFSv4ACLsInterface interface { 36 | // SetNfsv4Acls sets NFSv4 ACLs 37 | SetNfsv4Acls(acls string, dir string) error 38 | } 39 | 40 | // NFSv4ACLs implements setting NFSv4 ACLs 41 | type NFSv4ACLs struct{} 42 | 43 | func validateAndSetACLs(ctx context.Context, s NFSv4ACLsInterface, nasName string, client gopowerstore.Client, acls string, dir string) (bool, error) { 44 | aclsConfigured := false 45 | if nfsv4ACLs(acls) { 46 | if isNfsv4Enabled(ctx, client, nasName) { 47 | if err := s.SetNfsv4Acls(acls, dir); err != nil { 48 | log.Error(fmt.Sprintf("can't assign NFSv4 ACLs to folder %s: %s", dir, err.Error())) 49 | return false, err 50 | } 51 | aclsConfigured = true 52 | } else { 53 | return false, status.Errorf(codes.Internal, "can't assign NFSv4 ACLs to folder %s: NAS server is not NFSv4 enabled", dir) 54 | } 55 | } else { 56 | return false, status.Errorf(codes.Internal, "can't assign ACLs to folder %s: invalid NFSv4 ACL format %s", dir, acls) 57 | } 58 | 59 | return aclsConfigured, nil 60 | } 61 | 62 | func posixMode(acls string) bool { 63 | if matched, _ := regexp.Match(`\d{3,4}`, []byte(acls)); matched { 64 | return true 65 | } 66 | return false 67 | } 68 | 69 | func nfsv4ACLs(acls string) bool { 70 | aclsList := strings.Split(acls, ",") 71 | for _, acl := range aclsList { 72 | matched, err := regexp.Match(`([ADUL]:\w*:[\w.]*[@]*[\w.]*:\w*)`, []byte(acl)) 73 | if !matched || err != nil { 74 | return false 75 | } 76 | } 77 | return true 78 | } 79 | 80 | // SetNfsv4Acls sets NFSv4 ACLS 81 | func (n *NFSv4ACLs) SetNfsv4Acls(acls string, dir string) error { 82 | command := []string{"nfs4_setfacl", "-s", acls, dir} 83 | log.Infof("NFSv4 ACL command: %s \n", strings.Join(command, " ")) 84 | // arguments for exec.Command() are validated in caller 85 | cmd := exec.Command(command[0], command[1:]...) // #nosec G204 86 | outStr, err := cmd.Output() 87 | log.Infof("NFSv4 ACL output: %s \n", string(outStr)) 88 | return err 89 | } 90 | 91 | func isNfsv4Enabled(ctx context.Context, client gopowerstore.Client, nasName string) bool { 92 | nfsv4Enabled := false 93 | nas, err := gopowerstore.Client.GetNASByName(client, ctx, nasName) 94 | if err == nil { 95 | nfsServer, err := gopowerstore.Client.GetNfsServer(client, ctx, nas.NfsServers[0].ID) 96 | if err == nil { 97 | if nfsServer.IsNFSv4Enabled { 98 | nfsv4Enabled = true 99 | } else { 100 | log.Error(fmt.Sprintf("NFS v4 not enabled on NAS server: %s\n", nasName)) 101 | } 102 | } else { 103 | log.Error(fmt.Sprintf("can't fetch nfs server with id %s: %s", nas.NfsServers[0].ID, err.Error())) 104 | } 105 | } else { 106 | log.Error(fmt.Sprintf("can't determine nfsv4 enabled: %s", err.Error())) 107 | } 108 | return nfsv4Enabled 109 | } 110 | -------------------------------------------------------------------------------- /pkg/provider/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | // http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | package provider 15 | 16 | import ( 17 | "os" 18 | "strings" 19 | 20 | "github.com/dell/csi-powerstore/v2/pkg/common" 21 | "github.com/dell/csi-powerstore/v2/pkg/controller" 22 | "github.com/dell/csi-powerstore/v2/pkg/identity" 23 | "github.com/dell/csi-powerstore/v2/pkg/node" 24 | "github.com/dell/csi-powerstore/v2/pkg/service" 25 | "github.com/dell/csm-sharednfs/nfs" 26 | "github.com/dell/gocsi" 27 | logrus "github.com/sirupsen/logrus" 28 | "google.golang.org/grpc" 29 | ) 30 | 31 | // Log init 32 | var Log = logrus.New() 33 | 34 | const namespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" 35 | 36 | // New returns a new gocsi Storage Plug-in Provider. 37 | func New(controllerSvc controller.Interface, identitySvc *identity.Service, nodeSvc node.Interface, interList []grpc.UnaryServerInterceptor) *gocsi.StoragePlugin { 38 | svc := service.New() 39 | service.PutControllerService(controllerSvc) 40 | service.PutNodeService(nodeSvc) 41 | nfssvc := nfs.New(common.Name) 42 | service.PutNfsService(nfssvc) 43 | nfs.PutVcsiService(svc) 44 | nfs.DriverName = common.Name 45 | 46 | driverNamespace := os.Getenv(common.EnvDriverNamespace) 47 | if driverNamespace != "" { 48 | Log.Infof("Reading driver namespace from env variable %s", common.EnvDriverNamespace) 49 | nfs.DriverNamespace = driverNamespace 50 | } else { 51 | // Read the namespace associated with the service account 52 | namespaceData, err := os.ReadFile(namespaceFile) 53 | if err == nil { 54 | if driverNamespace = strings.TrimSpace(string(namespaceData)); len(driverNamespace) > 0 { 55 | Log.Infof("Driver Namespace not set, reading from the associated service account") 56 | nfs.DriverNamespace = driverNamespace 57 | } 58 | } 59 | } 60 | 61 | nfs.NfsExportDirectory = os.Getenv(common.EnvNFSExportDirectory) 62 | if nfs.NfsExportDirectory == "" { 63 | Log.Infof("NFS export directory not set. using default directory") 64 | nfs.NfsExportDirectory = "/var/lib/dell/nfs" 65 | } 66 | Log.Infof("Setting nfsExportDirectory to %s", nfs.NfsExportDirectory) 67 | return &gocsi.StoragePlugin{ 68 | Controller: svc, 69 | Identity: identitySvc, 70 | Node: svc, 71 | BeforeServe: svc.BeforeServe, 72 | RegisterAdditionalServers: svc.RegisterAdditionalServers, 73 | Interceptors: interList, 74 | 75 | EnvVars: []string{ 76 | // Enable request validation 77 | gocsi.EnvVarSpecReqValidation + "=true", 78 | 79 | // Enable serial volume access 80 | gocsi.EnvVarSerialVolAccess + "=true", 81 | }, 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/provider/provider_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | // http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | package provider 15 | 16 | import ( 17 | "os" 18 | "testing" 19 | 20 | "github.com/dell/csi-powerstore/v2/pkg/controller" 21 | "github.com/dell/csi-powerstore/v2/pkg/identity" 22 | "github.com/dell/csi-powerstore/v2/pkg/node" 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestNew(t *testing.T) { 27 | tests := []struct { 28 | name string 29 | setEnv func() 30 | unsetEnv func() 31 | createNamespaceFile func() 32 | deleteNamespaceFile func() 33 | }{ 34 | { 35 | name: "X_CSI_DRIVER_NAMESPACE environment variable is not set", 36 | setEnv: func() {}, 37 | unsetEnv: func() {}, 38 | createNamespaceFile: func() { 39 | err := os.MkdirAll("/var/run/secrets/kubernetes.io/serviceaccount", 0o755) 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | file, err := os.Create(namespaceFile) 44 | if err != nil { 45 | t.Errorf("error creating file: %v", err) 46 | t.Error(err) 47 | } 48 | defer func(file *os.File) { 49 | err := file.Close() 50 | if err != nil { 51 | t.Error(err) 52 | } 53 | }(file) 54 | 55 | _, err = file.Write([]byte("powerstore")) 56 | if err != nil { 57 | t.Error(err) 58 | } 59 | }, 60 | deleteNamespaceFile: func() { 61 | err := os.Remove(namespaceFile) 62 | if err != nil { 63 | t.Error(err) 64 | } 65 | }, 66 | }, 67 | { 68 | name: "X_CSI_DRIVER_NAMESPACE environment variable is set", 69 | setEnv: func() { 70 | err := os.Setenv("X_CSI_DRIVER_NAMESPACE", "powerstore") 71 | if err != nil { 72 | t.Error(err) 73 | } 74 | }, 75 | unsetEnv: func() { 76 | err := os.Unsetenv("X_CSI_DRIVER_NAMESPACE") 77 | if err != nil { 78 | t.Error(err) 79 | } 80 | }, 81 | createNamespaceFile: func() {}, 82 | deleteNamespaceFile: func() {}, 83 | }, 84 | } 85 | 86 | for _, tt := range tests { 87 | t.Run(tt.name, func(t *testing.T) { 88 | controllerSvc := controller.Service{} 89 | identitySvc := identity.Service{} 90 | nodeSvc := node.Service{} 91 | tt.setEnv() 92 | tt.createNamespaceFile() 93 | p := New(&controllerSvc, &identitySvc, &nodeSvc, nil) 94 | tt.deleteNamespaceFile() 95 | tt.unsetEnv() 96 | assert.NotNil(t, p) 97 | }) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /pkg/service/identity.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | // Package identity provides CSI specification compatible identity service. 20 | package service 21 | 22 | import ( 23 | "context" 24 | "fmt" 25 | 26 | "github.com/container-storage-interface/spec/lib/go/csi" 27 | ) 28 | 29 | // GetPluginInfo returns general information about plugin (driver) such as name, version and manifest 30 | func (s *service) GetPluginInfo(_ context.Context, _ *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { 31 | return &csi.GetPluginInfoResponse{}, fmt.Errorf("not implemented") 32 | } 33 | 34 | // GetPluginCapabilities returns capabilities that are supported by the driver 35 | func (s *service) GetPluginCapabilities(_ context.Context, _ *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { 36 | return &csi.GetPluginCapabilitiesResponse{}, fmt.Errorf("not implemented") 37 | } 38 | 39 | // Probe returns current state of the driver and if it is ready to receive requests 40 | func (s *service) Probe(_ context.Context, _ *csi.ProbeRequest) (*csi.ProbeResponse, error) { 41 | return &csi.ProbeResponse{}, fmt.Errorf("not implemented") 42 | } 43 | -------------------------------------------------------------------------------- /pkg/service/identity_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | // http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | package service 15 | 16 | import ( 17 | "context" 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestGetPluginInfo(t *testing.T) { 24 | svc := service{} 25 | resp, err := svc.GetPluginInfo(context.Background(), nil) 26 | assert.Empty(t, resp) 27 | assert.Equal(t, err.Error(), "not implemented") 28 | } 29 | 30 | func TestGetPluginCapabilities(t *testing.T) { 31 | svc := service{} 32 | resp, err := svc.GetPluginCapabilities(context.Background(), nil) 33 | assert.Empty(t, resp) 34 | assert.Equal(t, err.Error(), "not implemented") 35 | } 36 | 37 | func TestProbe(t *testing.T) { 38 | svc := service{} 39 | resp, err := svc.Probe(context.Background(), nil) 40 | assert.Empty(t, resp) 41 | assert.Equal(t, err.Error(), "not implemented") 42 | } 43 | -------------------------------------------------------------------------------- /pkg/service/service.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | // http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | package service 15 | 16 | import ( 17 | "context" 18 | "fmt" 19 | "net" 20 | "os" 21 | "strings" 22 | "sync" 23 | "time" 24 | 25 | "github.com/dell/csi-powerstore/v2/pkg/common" 26 | "github.com/dell/csi-powerstore/v2/pkg/controller" 27 | "github.com/dell/csi-powerstore/v2/pkg/node" 28 | "github.com/dell/csm-sharednfs/nfs" 29 | "github.com/dell/gocsi" 30 | csictx "github.com/dell/gocsi/context" 31 | "github.com/fsnotify/fsnotify" 32 | "github.com/sirupsen/logrus" 33 | "github.com/spf13/viper" 34 | "google.golang.org/grpc" 35 | ) 36 | 37 | // Log controlls the logger 38 | // give default value, will be overwritten by configmap 39 | var log = logrus.New() 40 | 41 | var ( 42 | // DriverConfig driver config 43 | DriverConfig string 44 | // DriverSecret driver secret 45 | DriverSecret string 46 | // Name of the driver 47 | controllerSvc controller.Interface 48 | nodeSvc node.Interface 49 | nfssvc nfs.Service 50 | 51 | mx = sync.Mutex{} 52 | ) 53 | 54 | type service struct { 55 | mode string 56 | } 57 | 58 | func New() nfs.Service { 59 | return &service{} 60 | } 61 | 62 | func (s *service) BeforeServe(ctx context.Context, sp *gocsi.StoragePlugin, lis net.Listener) error { 63 | log.Info("-----Inside Before Serve-----") 64 | // Get the SP's operating mode. 65 | s.mode = csictx.Getenv(ctx, gocsi.EnvVarMode) 66 | log.Info("Driver Mode:", s.mode) 67 | // TODO: add nfs code here 68 | nodeName := os.Getenv(common.EnvKubeNodeName) 69 | if nodeName == "" { 70 | nodeName = os.Getenv("KUBE_NODE_NAME") 71 | } 72 | 73 | if nodeName == "" { 74 | nodeName = os.Getenv("X_CSI_NODE_NAME") 75 | } 76 | 77 | if s.mode == "node" { 78 | nodeRoot := os.Getenv(common.EnvNodeChrootPath) 79 | if nodeRoot == "" { 80 | return fmt.Errorf("X_CSI_POWERSTORE_NODE_CHROOT_PATH environment variable not set") 81 | } 82 | nfs.NodeRoot = nodeRoot 83 | } 84 | 85 | err := os.Setenv("X_CSI_NODE_NAME", nodeName) 86 | if err != nil { 87 | log.Errorf("failed to set env X_CSI_NODE_NAME. err: %s", err.Error()) 88 | return err 89 | } 90 | 91 | log.Infof("Setting node name env to %s for NFS", nodeName) 92 | 93 | err = nfssvc.BeforeServe(ctx, sp, lis) 94 | if err != nil { 95 | log.Errorf("unable to start up nfsserver: %s", err.Error()) 96 | } 97 | 98 | return nil 99 | } 100 | 101 | func (s *service) RegisterAdditionalServers(server *grpc.Server) { 102 | controllerSvc.RegisterAdditionalServers(server) 103 | } 104 | 105 | func (s *service) ProcessMapSecretChange() error { 106 | // Update dynamic config params 107 | vc := viper.New() 108 | vc.AutomaticEnv() 109 | paramsPath, ok := csictx.LookupEnv(context.Background(), common.EnvConfigParamsFilePath) 110 | if !ok { 111 | log.Warnf("config path X_CSI_POWERSTORE_CONFIG_PARAMS_PATH is not specified") 112 | } 113 | log.WithField("file", paramsPath).Info("driver configuration file ") 114 | vc.SetConfigFile(paramsPath) 115 | vc.SetConfigType("yaml") 116 | if err := vc.ReadInConfig(); err != nil { 117 | log.WithError(err).Error("unable to read config file, using default values") 118 | } 119 | 120 | vc.WatchConfig() 121 | vc.OnConfigChange(func(_ fsnotify.Event) { 122 | // Putting in mutex to allow tests to pass with race flag 123 | mx.Lock() 124 | defer mx.Unlock() 125 | log.WithField("file", paramsPath).Info("log configuration file changed") 126 | updateDriverConfigParams(vc) 127 | }) 128 | 129 | updateDriverConfigParams(vc) 130 | 131 | // If we don't set this env gocsi will overwrite log level with default Info level 132 | err := os.Setenv(gocsi.EnvVarLogLevel, log.GetLevel().String()) 133 | if err != nil { 134 | log.WithError(err).Errorf("unable to set env variable %s", gocsi.EnvVarDebug) 135 | } 136 | 137 | return err 138 | } 139 | 140 | func updateDriverConfigParams(v *viper.Viper) { 141 | logLevelParam := "CSI_LOG_LEVEL" 142 | logFormatParam := "CSI_LOG_FORMAT" 143 | logFormat := strings.ToLower(v.GetString(logFormatParam)) 144 | fmt.Printf("Read CSI_LOG_FORMAT from log configuration file, format: %s\n", logFormat) 145 | 146 | // Use JSON logger as default 147 | if !strings.EqualFold(logFormat, "text") { 148 | log.SetFormatter(&logrus.JSONFormatter{ 149 | TimestampFormat: time.RFC3339Nano, 150 | }) 151 | } else { 152 | log.SetFormatter(&logrus.TextFormatter{}) 153 | } 154 | 155 | level := logrus.DebugLevel 156 | if v.IsSet(logLevelParam) { 157 | logLevel := v.GetString(logLevelParam) 158 | if logLevel != "" { 159 | logLevel = strings.ToLower(logLevel) 160 | fmt.Printf("Read CSI_LOG_LEVEL from log configuration file, level: %s\n", logLevel) 161 | var err error 162 | 163 | l, err := logrus.ParseLevel(logLevel) 164 | if err != nil { 165 | log.WithError(err).Errorf("LOG_LEVEL %s value not recognized, setting to default error: %s ", logLevel, err.Error()) 166 | } else { 167 | level = l 168 | } 169 | } 170 | } 171 | log.SetLevel(level) 172 | } 173 | 174 | // VolumeIDToArrayID returns the array ID for a given volume. 175 | // Example: abc-123 returns abc 176 | func (s *service) VolumeIDToArrayID(volumeID string) string { 177 | if volumeID == "" { 178 | return "" 179 | } 180 | fields := strings.Split(volumeID, "-") 181 | return fields[0] 182 | } 183 | 184 | func PutNfsService(nfs nfs.Service) { 185 | nfssvc = nfs 186 | } 187 | 188 | func PutControllerService(ctlSvc controller.Interface) { 189 | controllerSvc = ctlSvc 190 | } 191 | 192 | func PutNodeService(nsSvc node.Interface) { 193 | nodeSvc = nsSvc 194 | } 195 | -------------------------------------------------------------------------------- /pkg/service/service_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | // http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | package service 15 | 16 | import ( 17 | "context" 18 | "fmt" 19 | "os" 20 | "strings" 21 | "testing" 22 | 23 | "github.com/dell/csi-powerstore/v2/mocks" 24 | "github.com/dell/csi-powerstore/v2/pkg/common" 25 | nfsmock "github.com/dell/csm-sharednfs/nfs/mocks" 26 | "github.com/dell/gocsi" 27 | csictx "github.com/dell/gocsi/context" 28 | "github.com/fsnotify/fsnotify" 29 | "github.com/sirupsen/logrus" 30 | "github.com/spf13/viper" 31 | "github.com/stretchr/testify/assert" 32 | "github.com/stretchr/testify/mock" 33 | "go.uber.org/mock/gomock" 34 | "google.golang.org/grpc" 35 | ) 36 | 37 | func TestNew(t *testing.T) { 38 | assert.NotNil(t, New()) 39 | } 40 | 41 | func TestVolumeIDToArrayID(t *testing.T) { 42 | t.Run("empty volume id", func(t *testing.T) { 43 | resp := New().VolumeIDToArrayID("") 44 | assert.Empty(t, resp) 45 | }) 46 | 47 | t.Run("normal volume id", func(t *testing.T) { 48 | resp := New().VolumeIDToArrayID("123-456") 49 | assert.Equal(t, "123", resp) 50 | }) 51 | } 52 | 53 | func TestRegisterAdditionalServers(t *testing.T) { 54 | ctrl := gomock.NewController(t) 55 | defer ctrl.Finish() 56 | mockController := new(mocks.ControllerInterface) 57 | mockController.On("RegisterAdditionalServers", mock.Anything).Return() 58 | svc := New() 59 | PutControllerService(mockController) 60 | server := grpc.Server{} 61 | svc.RegisterAdditionalServers(&server) 62 | } 63 | 64 | func TestBeforeServe(t *testing.T) { 65 | ctrl := gomock.NewController(t) 66 | defer ctrl.Finish() 67 | 68 | t.Run("no node name", func(t *testing.T) { 69 | mockNfs := nfsmock.NewMockService(ctrl) 70 | mockNfs.EXPECT().BeforeServe(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(fmt.Errorf("no node name")) 71 | PutNfsService(mockNfs) 72 | 73 | err := New().BeforeServe(context.Background(), &gocsi.StoragePlugin{}, nil) 74 | assert.Nil(t, err) 75 | }) 76 | 77 | t.Run("X_CSI_POWERSTORE_NODE_CHROOT_PATH", func(t *testing.T) { 78 | os.Setenv("X_CSI_NODE_NAME", "test") 79 | ctx := context.Background() 80 | csictx.Setenv(ctx, gocsi.EnvVarMode, "node") 81 | 82 | err := New().BeforeServe(context.Background(), &gocsi.StoragePlugin{}, nil) 83 | assert.Error(t, err, fmt.Errorf("X_CSI_POWERSTORE_NODE_CHROOT_PATH environment variable not set")) 84 | }) 85 | 86 | t.Run("success", func(t *testing.T) { 87 | os.Setenv("X_CSI_NODE_NAME", "test") 88 | ctx := context.Background() 89 | csictx.Setenv(ctx, gocsi.EnvVarMode, "node") 90 | os.Setenv(common.EnvNodeChrootPath, "test-path") 91 | mockNfs := nfsmock.NewMockService(ctrl) 92 | mockNfs.EXPECT().BeforeServe(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) 93 | PutNfsService(mockNfs) 94 | assert.Nil(t, New().BeforeServe(ctx, &gocsi.StoragePlugin{}, nil)) 95 | }) 96 | } 97 | 98 | func TestProcessMapSecretChange(t *testing.T) { 99 | err := New().ProcessMapSecretChange() 100 | assert.Nil(t, err) 101 | } 102 | 103 | func TestUpdateDriverConfigParams(t *testing.T) { 104 | v := viper.New() 105 | v.SetConfigType("yaml") 106 | v.SetDefault("CSI_LOG_FORMAT", "text") 107 | v.SetDefault("CSI_LOG_LEVEL", "debug") 108 | 109 | viperChan := make(chan bool) 110 | v.WatchConfig() 111 | v.OnConfigChange(func(_ fsnotify.Event) { 112 | updateDriverConfigParams(v) 113 | viperChan <- true 114 | }) 115 | 116 | logFormat := strings.ToLower(v.GetString("CSI_LOG_FORMAT")) 117 | assert.Equal(t, "text", logFormat) 118 | 119 | updateDriverConfigParams(v) 120 | level := log.GetLevel() 121 | 122 | assert.Equal(t, logrus.DebugLevel, level) 123 | 124 | v.Set("CSI_LOG_FORMAT", "json") 125 | v.Set("CSI_LOG_LEVEL", "info") 126 | updateDriverConfigParams(v) 127 | level = log.GetLevel() 128 | assert.Equal(t, logrus.InfoLevel, level) 129 | 130 | v.Set("CSI_LOG_LEVEL", "notalevel") 131 | updateDriverConfigParams(v) 132 | level = log.GetLevel() 133 | assert.Equal(t, logrus.DebugLevel, level) 134 | } 135 | -------------------------------------------------------------------------------- /pkg/tracer/tracer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | // Package tracer provides OpenTracing tracer implementation 20 | package tracer 21 | 22 | import ( 23 | "io" 24 | 25 | "github.com/opentracing/opentracing-go" 26 | 27 | "github.com/uber/jaeger-client-go/config" 28 | jprom "github.com/uber/jaeger-lib/metrics/prometheus" 29 | ) 30 | 31 | // Configurator represents tracer configurator 32 | type Configurator interface { 33 | FromEnv() (*config.Configuration, error) 34 | } 35 | 36 | // NewTracer returns a new tracer object 37 | func NewTracer(configurator Configurator) (opentracing.Tracer, io.Closer, error) { 38 | // load config from environment variables 39 | cfg, err := configurator.FromEnv() 40 | if err != nil { 41 | return nil, nil, err 42 | } 43 | 44 | // create tracer from config 45 | return cfg.NewTracer( 46 | config.Metrics(jprom.New()), 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/tracer/tracer_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package tracer 20 | 21 | import ( 22 | "errors" 23 | "testing" 24 | 25 | "github.com/dell/csi-powerstore/v2/mocks" 26 | "github.com/stretchr/testify/assert" 27 | "github.com/uber/jaeger-client-go/config" 28 | ) 29 | 30 | func TestNewTracer(t *testing.T) { 31 | t.Run("success test", func(t *testing.T) { 32 | tracerMock := new(mocks.TracerConfigurator) 33 | tracerMock.On("FromEnv").Return(&config.Configuration{ 34 | ServiceName: "SomeServiceName", 35 | }, nil) 36 | tracer, _, err := NewTracer(tracerMock) 37 | assert.Nil(t, err) 38 | assert.NotNil(t, tracer) 39 | }) 40 | t.Run("failed scenario", func(t *testing.T) { 41 | tracerMock := new(mocks.TracerConfigurator) 42 | tracerMock.On("FromEnv").Return(nil, errors.New("error")) 43 | tracer, _, err := NewTracer(tracerMock) 44 | assert.Nil(t, tracer) 45 | assert.NotNil(t, err) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /samples/secret/secret.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # You can apply current config to Kubernetes cluster by running following command: 18 | # 19 | # kubectl create secret generic powerstore-config -n csi-powerstore --from-file=config=secret.yaml 20 | # 21 | arrays: 22 | # endpoint: full URL path to the PowerStore API 23 | # Allowed Values: https://*.*.*.*/api/rest or https://abc.com/api/rest 24 | # Default Value: None 25 | - endpoint: "https://10.0.0.1/api/rest" 26 | 27 | # globalID: unique id of the PowerStore array 28 | # Allowed Values: string 29 | # Default Value: None 30 | globalID: "unique" 31 | 32 | # username: username for connecting to API 33 | # Allowed Values: string 34 | # Default Value: None 35 | username: "user" 36 | 37 | # password: password for connecting to API 38 | # Allowed Values: string 39 | # Default Value: None 40 | password: "password" 41 | 42 | # skipCertificateValidation: indicates if client side validation of (management)server's certificate can be skipped 43 | # Allowed Values: 44 | # true: client side validation of (management)server's certificate will be skipped 45 | # false: client side validation of (management)server's certificate will not be skipped 46 | # Default Value: None 47 | skipCertificateValidation: true 48 | 49 | # isDefault: treat current array as a default 50 | # Allowed Values: 51 | # true: would be used by storage classes without arrayID parameter 52 | # false: would not be used by default 53 | # Default Value: false 54 | isDefault: true 55 | 56 | # blockProtocol: what SCSI transport protocol use on node side (FC, ISCSI, NVMeTCP, NVMeFC, None, or auto) 57 | # Allowed Values: 58 | # FC: FC protocol will be used 59 | # ISCSI: iSCSI protocol will be used 60 | # NVMeTCP: NVMe/TCP protocol will be used 61 | # NVMeFC: NVMe/FC protocol will be used 62 | # None: No block protocol can be used 63 | # auto: NVMeFC, NVMe/TCP, FC or iSCSI protocol will be used 64 | # Default Value: None 65 | blockProtocol: "auto" 66 | 67 | # nasName: what NAS should be used for NFS volumes 68 | # Allowed Values: string - (name of NAS server) 69 | # Default Value: None 70 | nasName: "nas-server" 71 | 72 | # nfsAcls: enables setting permissions on NFS mount directory 73 | # This value will be used if a storage class does not have the NFS ACL (nfsAcls) parameter specified 74 | # Permissions can be specified in two formats: 75 | # 1) Unix mode (NFSv3) 76 | # 2) NFSv4 ACLs (NFSv4) 77 | # NFSv4 ACLs are supported on NFSv4 share only. 78 | # Allowed values: 79 | # 1) Unix mode: valid octal mode number 80 | # Examples: "0777", "777", "0755" 81 | # 2) NFSv4 acls: valid NFSv4 acls, seperated by comma 82 | # Examples: "A::OWNER@:RWX,A::GROUP@:RWX", "A::OWNER@:rxtncy" 83 | # Optional: true 84 | # Default value: "0777" 85 | # nfsAcls: "0777" 86 | 87 | # Host based registration for powerstore metro 88 | # To enable host based registration for powerstore metro, uncomment the following line 89 | # metroTopology: This parameter will be used for host based registration 90 | # Allowed Values: Uniform 91 | # Default Value: Uniform 92 | # metroTopology: Uniform 93 | 94 | # This label will be used to match the node label to decide the type of host registration, only one label should be specified 95 | # Allowed Values: : 96 | # Default Value: None 97 | # labels: 98 | # : 99 | 100 | # To add more PowerStore arrays, uncomment the following lines and provide the required values 101 | # - endpoint: "https://11.0.0.1/api/rest" 102 | # globalID: "unique" 103 | # username: "user" 104 | # password: "password" 105 | # skipCertificateValidation: true 106 | # blockProtocol: "FC" 107 | -------------------------------------------------------------------------------- /samples/storageclass/README.md: -------------------------------------------------------------------------------- 1 | ### Examples for storage classes 2 | 3 | For the driver to work you need storage classes created in the Kubernetes cluster. 4 | You can take the ones located here as example and modify them as you see fit. 5 | 6 | You can change following parameters: 7 | 8 | - *arrayIP*: specifies what array driver should use to provision volumes, 9 | if not specified driver will use array specified as `default` in `helm/config.yaml` 10 | - *FsType*: specifies what filesystem type driver should use, possible variants `ext4`, `xfs`, `nfs`, 11 | if not specified driver will use `ext4` by default 12 | 13 | If you want you can also add topology constraints by adding `allowedTopologies` parameter 14 | ```yaml 15 | allowedTopologies: 16 | - matchLabelExpressions: 17 | - key: csi-powerstore.dellemc.com/12.34.56.78-iscsi 18 | # replace "-iscsi" with "-fc" or "-nfs" at the end to use FC or NFS enabled hosts 19 | # replace "12.34.56.78" with PowerStore endpoint IP 20 | values: 21 | - "true" 22 | ``` 23 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-ext4.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # default ext4 FS, no topology 18 | apiVersion: storage.k8s.io/v1 19 | kind: StorageClass 20 | metadata: 21 | name: "powerstore-ext4" 22 | provisioner: "csi-powerstore.dellemc.com" 23 | parameters: 24 | # arrayID: id of array to be used for volumes 25 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 26 | # Optional: false 27 | # Default value: None 28 | arrayID: "Unique" 29 | # FsType: file system type for mounted volumes 30 | # Allowed values: 31 | # ext3: ext3 filesystem type 32 | # ext4: ext4 filesystem type 33 | # xfs: XFS filesystem type 34 | # nfs: NFS filesystem 35 | # Optional: true 36 | # Default value: None if defaultFsType is not mentioned in values.yaml 37 | # Else defaultFsType value mentioned in values.yaml 38 | # will be used as default value 39 | csi.storage.k8s.io/fstype: "ext4" 40 | 41 | # reclaimPolicy: PVs that are dynamically created by a StorageClass will have the reclaim policy specified here 42 | # reclaimPolicy: PVs that are dynamically created by a StorageClass will have the reclaim policy specified here 43 | # Allowed values: 44 | # Reclaim: retain the PV after PVC deletion 45 | # Delete: delete the PV after PVC deletion 46 | # Optional: true 47 | # Default value: Delete 48 | reclaimPolicy: Delete 49 | 50 | # allowVolumeExpansion: allows the users to resize the volume by editing the corresponding PVC object 51 | # Allowed values: 52 | # true: allow users to resize the PVC 53 | # false: does not allow users to resize the PVC 54 | # Optional: true 55 | # Default value: false 56 | allowVolumeExpansion: true 57 | 58 | # volumeBindingMode controls when volume binding and dynamic provisioning should occur. 59 | # Allowed values: 60 | # Immediate: indicates that volume binding and dynamic provisioning occurs once the 61 | # PersistentVolumeClaim is created 62 | # WaitForFirstConsumer: will delay the binding and provisioning of a PersistentVolume 63 | # until a Pod using the PersistentVolumeClaim is created 64 | # Optional: true 65 | # Default value: Immediate 66 | volumeBindingMode: Immediate 67 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-metro.yaml: -------------------------------------------------------------------------------- 1 | # Copyright © 2024 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | apiVersion: storage.k8s.io/v1 15 | kind: StorageClass 16 | metadata: 17 | name: "powerstore-metro" 18 | provisioner: "csi-powerstore.dellemc.com" 19 | reclaimPolicy: Delete 20 | volumeBindingMode: Immediate 21 | allowVolumeExpansion: true 22 | parameters: 23 | # Indicates whether replication is enabled 24 | # Allowed values: 25 | # true: replication is enabled 26 | # false: replication is disabled 27 | # Default value: false 28 | replication.storage.dell.com/isReplicationEnabled: "true" 29 | 30 | # Indicates the replication mode 31 | # Allowed values: 32 | # "ASYNC" - Asynchronous mode 33 | # "SYNC" - Synchronous mode 34 | # "METRO" - Metro mode 35 | # Default value: "ASYNC" 36 | replication.storage.dell.com/mode: "METRO" 37 | 38 | # Indicates the remote PowerStore system to be used to configure Metro replication 39 | # Allowed values: string 40 | # Default value: None 41 | replication.storage.dell.com/remoteSystem: "RT-0000" 42 | 43 | # Indicates the array ID to be used for provisioning the volume 44 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 45 | # Default value: None 46 | arrayID: "Unique" 47 | 48 | # Indicates the file system type for mounted volumes 49 | # Allowed values: 50 | # ext3: ext3 filesystem type 51 | # ext4: ext4 filesystem type 52 | # xfs: XFS filesystem type 53 | # nfs: NFS filesystem type 54 | # Optional: true 55 | # Default value: None if defaultFsType is not mentioned in values.yaml 56 | # Else defaultFsType value mentioned in values.yaml will be used 57 | csi.storage.k8s.io/fstype: "ext4" 58 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-nfs.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: storage.k8s.io/v1 18 | kind: StorageClass 19 | metadata: 20 | name: "powerstore-nfs" 21 | provisioner: "csi-powerstore.dellemc.com" 22 | parameters: 23 | # arrayID: id of array to be used for volumes 24 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 25 | # Optional: false 26 | # Default value: None 27 | arrayID: "Unique" 28 | 29 | # FsType: file system type for mounted volumes 30 | # Allowed values: 31 | # ext3: ext3 filesystem type 32 | # ext4: ext4 filesystem type 33 | # xfs: XFS filesystem type 34 | # nfs: NFS filesystem 35 | # Optional: true 36 | # Default value: None if defaultFsType is not mentioned in values.yaml 37 | # Else defaultFsType value mentioned in values.yaml 38 | # will be used as default value 39 | csi.storage.k8s.io/fstype: "nfs" 40 | # nasName: NAS server's name. If not specified, value from secret.yaml will be used 41 | # User can specify one or multiple NAS servers, separated by commas. 42 | # Allowed values: string 43 | # Optional: true 44 | # Default value: None 45 | nasName: "nas-server1,nas-server2,nas-server3" 46 | 47 | # allowRoot: enables or disables root squashing (valid only for NFS) 48 | # Allowed values: 49 | # true: will allow root users to use their privileges 50 | # false: will prevent root users on NFS clients from exercising root privileges on the NFS server 51 | # Optional: true 52 | # Default value: false 53 | allowRoot: "false" 54 | 55 | # nfsAcls: enables setting permissions on NFS mount directory 56 | # This value overrides the NFS ACL (nfsAcls) attribute of corresponding array config in secret, if present 57 | # Permissions can be specified in two formats: 58 | # 1) Unix mode (NFSv3) 59 | # 2) NFSv4 ACLs (NFSv4) 60 | # NFSv4 ACLs are supported on NFSv4 share only. 61 | # Allowed values: 62 | # 1) Unix mode: valid octal mode number 63 | # Examples: "0777", "777", "0755" 64 | # 2) NFSv4 acls: valid NFSv4 acls, seperated by comma 65 | # Examples: "A::OWNER@:RWX,A::GROUP@:RWX", "A::OWNER@:rxtncy" 66 | # Optional: true 67 | # Default value: "0777" 68 | # nfsAcls: "0777" 69 | 70 | # reclaimPolicy: PVs that are dynamically created by a StorageClass will have the reclaim policy specified here 71 | # Allowed values: 72 | # Reclaim: retain the PV after PVC deletion 73 | # Delete: delete the PV after PVC deletion 74 | # Optional: true 75 | # Default value: Delete 76 | reclaimPolicy: Delete 77 | 78 | # allowVolumeExpansion: allows the users to resize the volume by editing the corresponding PVC object 79 | # Allowed values: 80 | # true: allow users to resize the PVC 81 | # false: does not allow users to resize the PVC 82 | # Optional: true 83 | # Default value: false 84 | allowVolumeExpansion: true 85 | 86 | # volumeBindingMode controls when volume binding and dynamic provisioning should occur. 87 | # Allowed values: 88 | # Immediate: indicates that volume binding and dynamic provisioning occurs once the 89 | # PersistentVolumeClaim is created 90 | # WaitForFirstConsumer: will delay the binding and provisioning of a PersistentVolume 91 | # until a Pod using the PersistentVolumeClaim is created 92 | # Optional: true 93 | # Default value: Immediate 94 | volumeBindingMode: Immediate 95 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-replication.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: storage.k8s.io/v1 18 | kind: StorageClass 19 | metadata: 20 | name: "powerstore-replication" 21 | provisioner: "csi-powerstore.dellemc.com" 22 | reclaimPolicy: Delete 23 | volumeBindingMode: Immediate 24 | parameters: 25 | # replicationPrefix paramater in values.yaml must be used as prefix for all replication parameters in storage class 26 | # for e.g., all replication parameters have prefix: replication.storage.dell.com here 27 | 28 | # replication.storage.dell.com/isReplicationEnabled: 29 | # Allowed values: 30 | # true: replication is enabled 31 | # false: replication is disabled 32 | # Optional: true 33 | # Default value: false 34 | replication.storage.dell.com/isReplicationEnabled: "true" 35 | 36 | # replication.storage.dell.com/mode: replication mode 37 | # Allowed values: 38 | # "ASYNC" - Asynchronous mode 39 | # "SYNC" - Synchronous mode 40 | # "METRO" - Metro mode 41 | # Optional: true 42 | # Default value: "ASYNC" 43 | replication.storage.dell.com/mode: "ASYNC" 44 | 45 | # replication.storage.dell.com/remoteStorageClassName: 46 | # Allowed values: string 47 | # Optional: true 48 | # Default value: None 49 | replication.storage.dell.com/remoteStorageClassName: "powerstore-replication" 50 | 51 | # replication.storage.dell.com/remoteClusterID: point to correct remote cluster id 52 | # Allowed values: string 53 | # Optional: true 54 | # Default value: None 55 | replication.storage.dell.com/remoteClusterID: "tgt-cluster-id" 56 | 57 | # replication.storage.dell.com/remoteSystem: point to correct remote PowerStore system 58 | # Allowed values: string 59 | # Optional: true 60 | # Default value: None 61 | replication.storage.dell.com/remoteSystem: "RT-0000" 62 | 63 | # replication.storage.dell.com/rpo: change to any other RPOs supported by PowerStore 64 | # Allowed values: "Five_Minutes", "Fifteen_Minutes", "Thirty_Minutes", "One_Hour", "Six_Hours", "Twelve_Hours", "One_Day","Zero" 65 | # Optional: true 66 | # Default value: None 67 | # For SYNC replication, this value must be set to Zero 68 | replication.storage.dell.com/rpo: Five_Minutes 69 | 70 | # replication.storage.dell.com/ignoreNamespaces: set to 'true' if you want to ignore namespaces and use one volume group 71 | # Allowed values: 72 | # true: ignore namespaces and use one volume group 73 | # false: create separate volume group per namespace 74 | # Optional: true 75 | # Default value: None 76 | replication.storage.dell.com/ignoreNamespaces: "false" 77 | 78 | # replication.storage.dell.com/volumeGroupPrefix: volume group prefix 79 | # Allowed values: string 80 | # Optional: true 81 | # Default value: None 82 | replication.storage.dell.com/volumeGroupPrefix: "csi" 83 | 84 | # arrayID: id of array to be used for volumes 85 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 86 | # Optional: false 87 | # Default value: None 88 | arrayID: "Unique" 89 | 90 | # FsType: file system type for mounted volumes 91 | # Allowed values: 92 | # ext3: ext3 filesystem type 93 | # ext4: ext4 filesystem type 94 | # xfs: XFS filesystem type 95 | # nfs: NFS filesystem 96 | # Optional: true 97 | # Default value: None if defaultFsType is not mentioned in values.yaml 98 | # Else defaultFsType value mentioned in values.yaml 99 | # will be used as default value 100 | csi.storage.k8s.io/fstype: "ext4" 101 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-topology.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # topology can be used with any other SC as well 18 | apiVersion: storage.k8s.io/v1 19 | kind: StorageClass 20 | metadata: 21 | name: "powerstore-topology" 22 | provisioner: "csi-powerstore.dellemc.com" 23 | parameters: 24 | # arrayID: id of array to be used for volumes 25 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 26 | # Optional: false 27 | # Default value: None 28 | arrayID: "Unique" 29 | # FsType: file system type for mounted volumes 30 | # Allowed values: 31 | # ext3: ext3 filesystem type 32 | # ext4: ext4 filesystem type 33 | # xfs: XFS filesystem type 34 | # nfs: NFS filesystem 35 | # Optional: true 36 | # Default value: None if defaultFsType is not mentioned in values.yaml 37 | # Else defaultFsType value mentioned in values.yaml 38 | # will be used as default value 39 | csi.storage.k8s.io/fstype: "ext4" 40 | 41 | # reclaimPolicy: PVs that are dynamically created by a StorageClass will have the reclaim policy specified here 42 | # Allowed values: 43 | # Reclaim: retain the PV after PVC deletion 44 | # Delete: delete the PV after PVC deletion 45 | # Optional: true 46 | # Default value: Delete 47 | reclaimPolicy: Delete 48 | 49 | # allowVolumeExpansion: allows the users to resize the volume by editing the corresponding PVC object 50 | # Allowed values: 51 | # true: allow users to resize the PVC 52 | # false: does not allow users to resize the PVC 53 | # Optional: true 54 | # Default value: false 55 | allowVolumeExpansion: true 56 | 57 | # volumeBindingMode controls when volume binding and dynamic provisioning should occur. 58 | # Allowed values: 59 | # Immediate: indicates that volume binding and dynamic provisioning occurs once the 60 | # PersistentVolumeClaim is created 61 | # WaitForFirstConsumer: will delay the binding and provisioning of a PersistentVolume (must use this with topology) 62 | # until a Pod using the PersistentVolumeClaim is created 63 | # Optional: true 64 | # Default value: Immediate 65 | volumeBindingMode: WaitForFirstConsumer 66 | 67 | # allowedTopologies: helps scheduling pods on worker nodes which match all of below expressions. 68 | # replace "-iscsi" with "-fc", "-nvmetcp", "-nvmefc" or "-nfs" at the end to use FC, NVMeTCP, NVMeFC, or NFS enabled hosts 69 | # replace "12.34.56.78" with PowerStore endpoint IP or abc.com 70 | # Optional: true 71 | allowedTopologies: 72 | - matchLabelExpressions: 73 | - key: "csi-powerstore.dellemc.com/12.34.56.78-iscsi" 74 | values: 75 | - "true" 76 | -------------------------------------------------------------------------------- /samples/storageclass/powerstore-xfs.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: storage.k8s.io/v1 18 | kind: StorageClass 19 | metadata: 20 | name: "powerstore-xfs" 21 | provisioner: "csi-powerstore.dellemc.com" 22 | parameters: 23 | # arrayID: id of array to be used for volumes 24 | # Allowed values: arrayID corresponding to array's globalID specified in secret.yaml 25 | # Optional: false 26 | # Default value: None 27 | arrayID: "Unique" 28 | 29 | # FsType: file system type for mounted volumes 30 | # Allowed values: 31 | # ext3: ext3 filesystem type 32 | # ext4: ext4 filesystem type 33 | # xfs: XFS filesystem type 34 | # nfs: NFS filesystem 35 | # Optional: true 36 | # Default value: None if defaultFsType is not mentioned in values.yaml 37 | # Else defaultFsType value mentioned in values.yaml 38 | # will be used as default value 39 | csi.storage.k8s.io/fstype: "xfs" 40 | # reclaimPolicy: PVs that are dynamically created by a StorageClass will have the reclaim policy specified here 41 | # Allowed values: 42 | # Reclaim: retain the PV after PVC deletion 43 | # Delete: delete the PV after PVC deletion 44 | # Optional: true 45 | # Default value: Delete 46 | reclaimPolicy: Delete 47 | 48 | # allowVolumeExpansion: allows the users to resize the volume by editing the corresponding PVC object 49 | # Allowed values: 50 | # true: allow users to resize the PVC 51 | # false: does not allow users to resize the PVC 52 | # Optional: true 53 | # Default value: false 54 | allowVolumeExpansion: true 55 | 56 | # volumeBindingMode controls when volume binding and dynamic provisioning should occur. 57 | # Allowed values: 58 | # Immediate: indicates that volume binding and dynamic provisioning occurs once the 59 | # PersistentVolumeClaim is created 60 | # WaitForFirstConsumer: will delay the binding and provisioning of a PersistentVolume 61 | # until a Pod using the PersistentVolumeClaim is created 62 | # Optional: true 63 | # Default value: Immediate 64 | volumeBindingMode: Immediate 65 | -------------------------------------------------------------------------------- /samples/volumesnapshotclass/snapclass.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: snapshot.storage.k8s.io/v1 18 | kind: VolumeSnapshotClass 19 | metadata: 20 | name: powerstore-snapshot 21 | driver: "csi-powerstore.dellemc.com" # driver name from values.yaml 22 | deletionPolicy: Delete 23 | -------------------------------------------------------------------------------- /tests/e2e/k8s/README.md: -------------------------------------------------------------------------------- 1 | # CSI PowerStore K8s E2E Tests 2 | 3 | 4 | ## Prerequisites 5 | * A working Kubernetes cluster with csi-powerstore driver installed. 6 | * A PowerStore storage system. 7 | 8 | ## Test Setup 9 | * Set the KUBECONFIG environment variable using `export KUBECONFIG=/path/to/.kube/config`, replacing the path with the path to your kubeconfig. 10 | If $KUBECONFIG is unset, the test suite will attempt to locate the config under `$HOME/.kube/config`. 11 | * Update `./e2e-values.yaml` with the necessary test values. 12 | 13 | ## Running tests 14 | Execute the run script to run the tests. 15 | ``` 16 | ./run.sh 17 | ``` 18 | 19 | ## Updating Modules 20 | The several modules imported from k8s.io appear to intentionally leave module versions set to v0.0.0. Without overwriting this version and specifying the module version, this test package will be unable to build its go.mod file. To fix this and specify the desired version, you must give the version using the 'replace' keyword in the go.mod file. 21 | 22 | When updating modules for this test package, make sure to update the list of replaced versions at the bottom of the go.mod file. 23 | -------------------------------------------------------------------------------- /tests/e2e/k8s/e2e-values.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | kubeconfigEnvVar: "KUBECONFIG" 18 | busyBoxImageOnGcr: "gcr.io/google_containers/busybox:1.27" 19 | 20 | driverNamespace: "powerstore" 21 | e2eCSIDriverName: "csi-powerstore.dellemc.com" 22 | arrayID: "unique" 23 | 24 | diskSize: "3Gi" 25 | execCommand: "while true ; do sleep 2 ; done" 26 | 27 | # config for externalAccess e2e test suite 28 | externalAccess: 29 | endPoint: "https://10.0.0.1/api/rest" # array's endpoint 30 | userName: "user" 31 | password: "password" 32 | externalAccessIP: "10.0.0.1/25" # IP configured in values.yaml file while installing the driver 33 | NASServer: "nas-server" 34 | testStatefulset: true # if wanted to test ExternalAccess with Deployment as well as with Statefulset resource 35 | -------------------------------------------------------------------------------- /tests/e2e/k8s/run.sh: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # 4 | # Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | # supress ginkgo 2.0 upgrade hints 19 | export ACK_GINKGO_DEPRECATIONS=1.16.5 20 | 21 | # run all tests 22 | go test -timeout=100m -v ./ -ginkgo.v=1 23 | 24 | -------------------------------------------------------------------------------- /tests/e2e/k8s/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright © 2022-2023 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 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 | package e2etest 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | "testing" 25 | 26 | ginkgo "github.com/onsi/ginkgo/v2" 27 | "github.com/onsi/ginkgo/v2/reporters" 28 | gomega "github.com/onsi/gomega" 29 | "k8s.io/kubernetes/test/e2e/framework" 30 | config "k8s.io/kubernetes/test/e2e/framework/config" 31 | ) 32 | 33 | func init() { 34 | var yamlError error 35 | 36 | testParameters, yamlError = readYaml("e2e-values.yaml") 37 | if yamlError != nil { 38 | framework.Failf("Unable to read yaml e2e-values.yaml: %s", yamlError.Error()) 39 | } 40 | 41 | // k8s.io/kubernetes/tests/e2e/framework requires env KUBECONFIG to be set 42 | // it does not fall back to defaults 43 | if os.Getenv(fmt.Sprintf("%v", testParameters["kubeconfigEnvVar"])) == "" { 44 | kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config") 45 | os.Setenv(fmt.Sprintf("%v", testParameters["kubeconfigEnvVar"]), kubeconfig) 46 | } 47 | 48 | framework.TestContext.Provider = "local" 49 | 50 | t := framework.TestContextType{} 51 | 52 | framework.AfterReadingAllFlags(&t) 53 | } 54 | 55 | func TestE2E(t *testing.T) { 56 | handleFlags() 57 | gomega.RegisterFailHandler(ginkgo.Fail) 58 | 59 | // pass/fail/skip results summarized to this file 60 | junitReporter := reporters.NewJUnitReporter("junit.xml") 61 | 62 | // dont dump huge logs of node / pods on error 63 | framework.TestContext.DumpLogsOnFailure = false 64 | 65 | // framework.TestContext.DeleteNamespace = false 66 | 67 | // runs all ginkgo tests in go files 68 | ginkgo.RunSpecsWithDefaultAndCustomReporters(t, "CSI Driver End-to-End Tests", []ginkgo.Reporter{junitReporter}) 69 | } 70 | 71 | func handleFlags() { 72 | config.CopyFlags(config.Flags, flag.CommandLine) 73 | framework.RegisterCommonFlags(flag.CommandLine) 74 | framework.RegisterClusterFlags(flag.CommandLine) 75 | flag.Parse() 76 | } 77 | -------------------------------------------------------------------------------- /tests/e2e/k8s/testing-manifests/statefulset/statefulset.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: apps/v1 18 | kind: StatefulSet 19 | metadata: 20 | name: external-access 21 | spec: 22 | replicas: 10 23 | selector: 24 | matchLabels: 25 | app: external-access 26 | template: 27 | metadata: 28 | labels: 29 | app: external-access 30 | spec: 31 | containers: 32 | - name: busybox 33 | image: gcr.io/google_containers/busybox:1.27 34 | command: ["/bin/sh", "-c", "sleep 3600"] 35 | volumeMounts: 36 | - name: www 37 | mountPath: /data 38 | volumeClaimTemplates: 39 | - metadata: 40 | name: www 41 | annotations: 42 | volume.beta.kubernetes.io/storage-class: external-access-sc 43 | spec: 44 | accessModes: ["ReadWriteMany"] 45 | resources: 46 | requests: 47 | storage: 3Gi 48 | -------------------------------------------------------------------------------- /tests/sanity/.gitignore: -------------------------------------------------------------------------------- 1 | myvalues.yaml 2 | -------------------------------------------------------------------------------- /tests/sanity/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | FROM golang:1.24 18 | 19 | RUN git clone https://github.com/kubernetes-csi/csi-test.git && \ 20 | cd csi-test/cmd/csi-sanity && \ 21 | make clean install && \ 22 | cp ./csi-sanity /csi-sanity 23 | 24 | FROM frolvlad/alpine-glibc 25 | WORKDIR /app/csi-sanity/ 26 | COPY --from=build-env /csi-sanity . 27 | COPY params.yaml /params.yaml 28 | 29 | ENTRYPOINT [ "./csi-sanity" ] -------------------------------------------------------------------------------- /tests/sanity/README.md: -------------------------------------------------------------------------------- 1 | ## Sanity Tests For CSI PowerStore 2 | 3 | Testing done by standard test suite from [Sanity Test Command Line Program](https://github.com/kubernetes-csi/csi-test/tree/master/cmd/csi-sanity) 4 | 5 | ### Building Image 6 | 7 | To run these tests you need to build an image by yourself and upload it to any available repository. 8 | 9 | ### Running 10 | 11 | #### Prerequisites 12 | Copy the `values.yaml` from `sanity-csi-powerstore` folder to folder with `install-sanity.sh` script and rename it to myvalues. 13 | In `myvalues.yaml` ,`/helm/secret.yaml`, `params.yaml` point to your PowerStore array. 14 | Install to kubernetes cluster by running install-sanity.sh. 15 | > It will install bare version of driver without any sidecar containers 16 | 17 | To run the tests run the `install-sanity.sh` script with full path to your csi-sanity image as first argument 18 | 19 | Example: 20 | ``` 21 | ./install-sanity.sh csi-sanity:latest 22 | ``` 23 | 24 | Wait until testing is finished 25 | -------------------------------------------------------------------------------- /tests/sanity/helm/sanity-csi-powerstore/Chart.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | apiVersion: v2 17 | appVersion: "2.9.0" 18 | name: sanity-csi-powerstore 19 | version: 2.9.0 20 | description: | 21 | PowerStore CSI (Container Storage Interface) driver Kubernetes 22 | integration. This chart includes everything required to provision via CSI as 23 | well as a PowerStore StorageClass. 24 | type: application 25 | kubeVersion: ">= 1.24.0 < 1.29.0" 26 | # If you are using a complex K8s version like "v1.24.3-mirantis-1", use this kubeVersion check instead 27 | # WARNING: this version of the check will allow the use of alpha and beta versions, which is NOT SUPPORTED 28 | # kubeVersion: ">= 1.24.0-0 < 1.29.0-0" 29 | keywords: 30 | - csi 31 | - storage 32 | home: https://github.com/dell/csi-powerstore 33 | sources: 34 | - https://github.com/dell/csi-powerstore 35 | maintainers: 36 | - name: DellEMC 37 | -------------------------------------------------------------------------------- /tests/sanity/helm/sanity-csi-powerstore/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Return true if storage capacity tracking is enabled and is supported based on k8s version 3 | */}} 4 | {{- define "csi-powerstore.isStorageCapacitySupported" -}} 5 | {{- if eq .Values.storageCapacity.enabled true -}} 6 | {{- if and (eq .Capabilities.KubeVersion.Major "1") (ge (trimSuffix "+" .Capabilities.KubeVersion.Minor) "24") -}} 7 | {{- true -}} 8 | {{- end -}} 9 | {{- end -}} 10 | {{- end -}} -------------------------------------------------------------------------------- /tests/sanity/helm/sanity-csi-powerstore/templates/driver-config-params.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable-file 2 | # This file is not valid YAML because it is a Helm template 3 | # 4 | # Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: {{ .Release.Name }}-config-params 22 | namespace: {{ .Release.Namespace }} 23 | data: 24 | driver-config-params.yaml: | 25 | CSI_LOG_LEVEL: "{{ .Values.logLevel }}" 26 | CSI_LOG_FORMAT: "{{ .Values.logFormat }}" 27 | {{ if .Values.podmon.enabled }} 28 | PODMON_CONTROLLER_LOG_LEVEL: "{{ .Values.logLevel }}" 29 | PODMON_CONTROLLER_LOG_FORMAT: "{{ .Values.logFormat }}" 30 | PODMON_NODE_LOG_LEVEL: "{{ .Values.logLevel }}" 31 | PODMON_NODE_LOG_FORMAT: "{{ .Values.logFormat }}" 32 | {{ end }} 33 | -------------------------------------------------------------------------------- /tests/sanity/helm/secret.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2021-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | # You can apply current config to Kubernetes cluster by running following command: 18 | # 19 | # kubectl create secret generic powerstore-config -n csi-powerstore --from-file=config=secret.yaml 20 | # 21 | arrays: 22 | # endpoint: full URL path to the PowerStore API 23 | # Allowed Values: https://*.*.*.*/api/rest or https://abc.com/api/rest 24 | # Default Value: None 25 | - endpoint: "https://10.0.0.1/api/rest" 26 | 27 | # globalID: unique id of the PowerStore array 28 | # Allowed Values: string 29 | # Default Value: None 30 | globalID: "unique" 31 | 32 | # username: username for connecting to API 33 | # Allowed Values: string 34 | # Default Value: None 35 | username: "user" 36 | 37 | # password: password for connecting to API 38 | # Allowed Values: string 39 | # Default Value: None 40 | password: "password" 41 | 42 | # skipCertificateValidation: indicates if client side validation of (management)server's certificate can be skipped 43 | # Allowed Values: 44 | # true: client side validation of (management)server's certificate will be skipped 45 | # false: client side validation of (management)server's certificate will not be skipped 46 | # Default Value: None 47 | skipCertificateValidation: true 48 | 49 | # isDefault: treat current array as a default 50 | # Allowed Values: 51 | # true: would be used by storage classes without arrayID parameter 52 | # false: would not be used by default 53 | # Default Value: false 54 | isDefault: true 55 | 56 | # blockProtocol: what SCSI transport protocol use on node side (FC, ISCSI, NVMeTCP, NVMeFC, None, or auto) 57 | # Allowed Values: 58 | # FC: FC protocol will be used 59 | # ISCSI: iSCSI protocol will be used 60 | # NVMeTCP: NVMe/TCP protocol will be used 61 | # NVMeFC: NVMe/FC protocol will be used 62 | # None: No block protocol can be used 63 | # auto: NVMeFC, NVMe/TCP, FC or iSCSI protocol will be used 64 | # Default Value: None 65 | blockProtocol: "auto" 66 | 67 | # nasName: what NAS should be used for NFS volumes 68 | # Allowed Values: string - (name of NAS server) 69 | # Default Value: None 70 | nasName: "nas-server" 71 | 72 | # nfsAcls: enables setting permissions on NFS mount directory 73 | # This value will be used if a storage class does not have the NFS ACL (nfsAcls) parameter specified 74 | # Permissions can be specified in two formats: 75 | # 1) Unix mode (NFSv3) 76 | # 2) NFSv4 ACLs (NFSv4) 77 | # NFSv4 ACLs are supported on NFSv4 share only. 78 | # Allowed values: 79 | # 1) Unix mode: valid octal mode number 80 | # Examples: "0777", "777", "0755" 81 | # 2) NFSv4 acls: valid NFSv4 acls, seperated by comma 82 | # Examples: "A::OWNER@:RWX,A::GROUP@:RWX", "A::OWNER@:rxtncy" 83 | # Optional: true 84 | # Default value: "0777" 85 | # nfsAcls: "0777" 86 | 87 | # To add more PowerStore arrays, uncomment the following lines and provide the required values 88 | # - endpoint: "https://11.0.0.1/api/rest" 89 | # globalID: "unique" 90 | # username: "user" 91 | # password: "password" 92 | # skipCertificateValidation: true 93 | # blockProtocol: "FC" 94 | -------------------------------------------------------------------------------- /tests/sanity/install-sanity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # 4 | # Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | IMAGE=$1 19 | 20 | kubectl create ns sanity 21 | kubectl create secret generic csi-sanity-pstore-config -n sanity --from-file=config=helm/secret.yaml 22 | # Create controller and noce driver instances 23 | helm_command="helm install --values ./myvalues.yaml --name-template csi-sanity-pstore --namespace sanity ./helm/sanity-csi-powerstore --wait --timeout 180s" 24 | echo "Helm install command:" 25 | echo " ${helm_command}" 26 | ${helm_command} 27 | 28 | # Run tests from using csi-sanity container 29 | ./test.sh $1 30 | 31 | # Delete sanity test chart 32 | helm delete --namespace sanity csi-sanity-pstore 33 | kubectl delete ns sanity 34 | -------------------------------------------------------------------------------- /tests/sanity/params.yaml: -------------------------------------------------------------------------------- 1 | arrayID: "unique" 2 | csi.storage.k8s.io/fstype: "nfs" 3 | nasName: "nas-server" 4 | allowRoot: "false" 5 | # nfsAcls: "0777" 6 | -------------------------------------------------------------------------------- /tests/sanity/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # 4 | # Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | IMAGE=$1 19 | kubectl run csi-sanity --image=$IMAGE --overrides=' 20 | { 21 | "apiVersion": "v1", 22 | "spec": { 23 | "containers": [ 24 | { 25 | "name": "csi-sanity", 26 | "image": "'$IMAGE'", 27 | "stdin": true, 28 | "stdinOnce": true, 29 | "tty": true, 30 | "command": ["/app/csi-sanity/csi-sanity"], 31 | "args": ["--ginkgo.v", "--csi.controllerendpoint=/controller.sock", "--csi.endpoint=/node.sock", "--csi.testvolumeparameters=/params.yaml", "--ginkgo.junit-report=/report.xml"], 32 | "volumeMounts": [{ 33 | "name": "controller-socket", 34 | "mountPath": "/controller.sock" 35 | }, 36 | { 37 | "name": "node-socket", 38 | "mountPath": "/node.sock" 39 | }] 40 | } 41 | ], 42 | "volumes": [{ 43 | "name":"controller-socket", 44 | "hostPath":{ 45 | "path": "/var/run/csi/csi.sock" 46 | } 47 | }, 48 | { 49 | "name":"node-socket", 50 | "hostPath":{ 51 | "path": "/var/lib/kubelet/plugins/csi-powerstore.dellemc.com/csi_sock" 52 | } 53 | }] 54 | } 55 | } 56 | ' --rm -ti --attach --restart=Never -------------------------------------------------------------------------------- /tests/scale/.gitignore: -------------------------------------------------------------------------------- 1 | log.output 2 | stop 3 | -------------------------------------------------------------------------------- /tests/scale/volumes/Chart.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | name: volumes 18 | version: 1.0.0 19 | appVersion: 1.0.0 20 | description: | 21 | Tests PowerStore CSI deployments. 22 | keywords: 23 | - csi-powerstore 24 | - storage 25 | engine: gotpl 26 | -------------------------------------------------------------------------------- /tests/scale/volumes/templates/test.yaml: -------------------------------------------------------------------------------- 1 | # yamllint disable-file 2 | # This file is not valid YAML because it is a Helm template 3 | # 4 | # Copyright © 2020-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 18 | apiVersion: apps/v1 19 | kind: StatefulSet 20 | metadata: 21 | name: {{ required "name required " .Values.name | quote }} 22 | spec: 23 | selector: 24 | matchLabels: 25 | app: powerstoretest 26 | serviceName: powerstoretest 27 | replicas: {{ required "replicas required" .Values.replicas }} 28 | podManagementPolicy: "Parallel" 29 | template: 30 | metadata: 31 | labels: 32 | app: powerstoretest 33 | spec: 34 | containers: 35 | - name: test 36 | image: quay.io/centos/centos:latest 37 | imagePullPolicy: IfNotPresent 38 | volumeMounts: 39 | {{ range $i, $e := until (int .Values.volumeCount) }} 40 | - name: pvol-{{ $i }} 41 | mountPath: /data{{ $i }} 42 | {{ end }} 43 | command: ["/bin/bash"] 44 | args: ["-c", "trap 'exit 0' SIGTERM;while true; do sleep 1; done"] 45 | volumeClaimTemplates: 46 | {{ $storageClass := .Values.storageClass | quote }} 47 | {{ range $i, $e := until (int .Values.volumeCount) }} 48 | - metadata: 49 | name: pvol-{{ $i }} 50 | spec: 51 | accessModes: [ "ReadWriteOnce" ] 52 | storageClassName: {{ required "storageClass required" $storageClass }} 53 | resources: 54 | requests: 55 | storage: 10Gi 56 | {{ end }} 57 | -------------------------------------------------------------------------------- /tests/scale/volumes/values.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright © 2020-2022 Dell Inc. or its subsidiaries. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # http://www.apache.org/licenses/LICENSE-2.0 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 | name: scaleStatefulSet 18 | replicas: 1 19 | storageClass: default 20 | volumeCount: 5 21 | -------------------------------------------------------------------------------- /tests/simple/simple.yaml: -------------------------------------------------------------------------------- 1 | # Copyright © 2020-2024 Dell Inc. or its subsidiaries. All Rights Reserved. 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 | # http://www.apache.org/licenses/LICENSE-2.0 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 | 14 | # This test creates three different PersistentVolumeClaims using default 15 | # ext4, xfs, and nfs storage classes and automatically mounts them to the pod. 16 | # 17 | # It assumes that you’ve created the same basic three storage classes from 18 | # samples/storageclass folder without changing their names. 19 | # 20 | # To test the driver just run from root directory of repository 21 | # > kubectl create -f ./tests/simple/ 22 | # 23 | # You can find all resources in testpowerstore namespace. 24 | # Check if pod is created and Ready and Running by running 25 | # > kubectl get all -n testpowerstore 26 | # (if it's in CrashLoopback state then driver installation wasn't successful, check logs of node and controller) 27 | # After that can go into created container and check if everything is mounted correctly. 28 | # 29 | # After that you can uninstall the testing PVCs and StatefulSet 30 | # > kubectl delete -f ./tests/simple/ 31 | # 32 | apiVersion: v1 33 | kind: Namespace 34 | metadata: 35 | name: testpowerstore 36 | --- 37 | kind: PersistentVolumeClaim 38 | apiVersion: v1 39 | metadata: 40 | name: pvol0 41 | namespace: testpowerstore 42 | spec: 43 | accessModes: 44 | - ReadWriteOnce 45 | volumeMode: Filesystem 46 | resources: 47 | requests: 48 | storage: 8Gi 49 | storageClassName: powerstore-ext4 50 | --- 51 | kind: PersistentVolumeClaim 52 | apiVersion: v1 53 | metadata: 54 | name: pvol1 55 | namespace: testpowerstore 56 | spec: 57 | accessModes: 58 | - ReadWriteOnce 59 | volumeMode: Filesystem 60 | resources: 61 | requests: 62 | storage: 12Gi 63 | storageClassName: powerstore-xfs 64 | --- 65 | kind: PersistentVolumeClaim 66 | apiVersion: v1 67 | metadata: 68 | name: pvol2 69 | namespace: testpowerstore 70 | spec: 71 | accessModes: 72 | - ReadWriteMany 73 | volumeMode: Filesystem 74 | resources: 75 | requests: 76 | storage: 10Gi 77 | storageClassName: powerstore-nfs 78 | --- 79 | apiVersion: v1 80 | kind: ServiceAccount 81 | metadata: 82 | name: powerstoretest 83 | namespace: testpowerstore 84 | --- 85 | kind: StatefulSet 86 | apiVersion: apps/v1 87 | metadata: 88 | name: powerstoretest 89 | namespace: testpowerstore 90 | spec: 91 | serviceName: powerstoretest 92 | selector: 93 | matchLabels: 94 | app: powerstoretest 95 | template: 96 | metadata: 97 | labels: 98 | app: powerstoretest 99 | spec: 100 | serviceAccount: powerstoretest 101 | hostNetwork: true 102 | containers: 103 | - name: test 104 | image: quay.io/centos/centos:latest 105 | command: ["/bin/sleep", "3600"] 106 | volumeMounts: 107 | - mountPath: "/data0" 108 | name: pvol0 109 | - mountPath: "/data1" 110 | name: pvol1 111 | - mountPath: "/data2" 112 | name: pvol2 113 | volumes: 114 | - name: pvol0 115 | persistentVolumeClaim: 116 | claimName: pvol0 117 | - name: pvol1 118 | persistentVolumeClaim: 119 | claimName: pvol1 120 | - name: pvol2 121 | persistentVolumeClaim: 122 | claimName: pvol2 123 | --------------------------------------------------------------------------------