diff --git a/Makefile b/Makefile index 125fc711d..cd3c2f382 100644 --- a/Makefile +++ b/Makefile @@ -47,8 +47,8 @@ endif GO_PROJECT=github.com/ceph/ceph-csi CEPH_VERSION ?= $(shell . $(CURDIR)/build.env ; echo $${CEPH_VERSION}) -# TODO: ceph_preview tag may be removed with go-ceph 0.16.0 -# TODO: ceph_ci_untested is added for NFS-export management (go-ceph#655) +# TODO: ceph_preview tag may be removed with go-ceph 0.17.0 +# TODO: ceph_ci_untested is added for subvolume metadata (go-ceph#691) and snapshot metadata management (go-ceph#698) GO_TAGS_LIST ?= $(CEPH_VERSION) ceph_preview ceph_ci_untested # go build flags diff --git a/actions/retest/main.go b/actions/retest/main.go index cc02cd4c2..f019f333f 100644 --- a/actions/retest/main.go +++ b/actions/retest/main.go @@ -59,7 +59,6 @@ func getConfig() *retestConfig { if len(strings.Split(os.Getenv("GITHUB_REPOSITORY"), "/")) == 2 { return strings.Split(os.Getenv("GITHUB_REPOSITORY"), "/")[0], strings.Split(os.Getenv("GITHUB_REPOSITORY"), "/")[1] } - } return "", "" }() @@ -168,7 +167,7 @@ func main() { log.Printf("failed to create comment %v\n", err) continue } - //Post comment with target URL for retesting + // Post comment with target URL for retesting msg = fmt.Sprintf("@%s %q test failed. Logs are available at [location](%s) for debugging", re.GetUser().GetLogin(), r.GetContext(), r.GetTargetURL()) comment.Body = github.String(msg) _, _, err = c.client.Issues.CreateComment(context.TODO(), c.owner, c.repo, prNumber, comment) @@ -199,7 +198,8 @@ func main() { // checkPRRequiredApproval check PullRequest has required approvals. func (c *retestConfig) checkPRRequiredApproval(prNumber int) bool { - rev, _, err := c.client.PullRequests.ListReviews(context.TODO(), c.owner, c.repo, prNumber, &github.ListOptions{}) + opts := github.ListOptions{PerPage: 100} // defaults to 30 reviews, too few sometimes + rev, _, err := c.client.PullRequests.ListReviews(context.TODO(), c.owner, c.repo, prNumber, &opts) if err != nil { log.Printf("failed to list reviews %v\n", err) return false diff --git a/api/deploy/ocp/scc_test.go b/api/deploy/ocp/scc_test.go index 4971218d4..9933d6d82 100644 --- a/api/deploy/ocp/scc_test.go +++ b/api/deploy/ocp/scc_test.go @@ -55,7 +55,7 @@ func TestNewSecurityContextConstraints(t *testing.T) { rookValues := SecurityContextConstraintsValues{ Namespace: "rook-ceph", - Deployer: "rook", + Deployer: "rook", } getSCC := func() { @@ -77,7 +77,7 @@ func TestNewSecurityContextConstraintsYAML(t *testing.T) { t.Parallel() var ( - err error + err error yaml string ) diff --git a/build.env b/build.env index d2af625d6..142d5693a 100644 --- a/build.env +++ b/build.env @@ -16,18 +16,18 @@ BASE_IMAGE=quay.io/ceph/ceph:v17 CEPH_VERSION=quincy # standard Golang options -GOLANG_VERSION=1.17.5 +GOLANG_VERSION=1.17.10 GO111MODULE=on # commitlint version COMMITLINT_VERSION=latest # static checks and linters -GOLANGCI_VERSION=v1.43.0 +GOLANGCI_VERSION=v1.46.2 # external snapshotter version # Refer: https://github.com/kubernetes-csi/external-snapshotter/releases -SNAPSHOT_VERSION=v5.0.1 +SNAPSHOT_VERSION=v6.0.1 # "go test" configuration # set to stdout or html to enable coverage reporting, disabled by default @@ -35,21 +35,21 @@ SNAPSHOT_VERSION=v5.0.1 #GO_COVER_DIR=_output/ # helm chart generation, testing and publishing -HELM_VERSION=v3.8.2 +HELM_VERSION=v3.9.0 # minikube settings -MINIKUBE_VERSION=v1.25.2 +MINIKUBE_VERSION=v1.26.0-beta.1 VM_DRIVER=none CHANGE_MINIKUBE_NONE_USER=true # Rook options -ROOK_VERSION=v1.8.2 +ROOK_VERSION=v1.9.4 # Provide ceph image path -ROOK_CEPH_CLUSTER_IMAGE=quay.io/ceph/ceph:v16 +ROOK_CEPH_CLUSTER_IMAGE=quay.io/ceph/ceph:v17 # CSI sidecar version CSI_ATTACHER_VERSION=v3.4.0 -CSI_SNAPSHOTTER_VERSION=v5.0.1 +CSI_SNAPSHOTTER_VERSION=v6.0.1 CSI_PROVISIONER_VERSION=v3.1.0 CSI_RESIZER_VERSION=v1.4.0 CSI_NODE_DRIVER_REGISTRAR_VERSION=v2.5.1 diff --git a/charts/ceph-csi-cephfs/Chart.yaml b/charts/ceph-csi-cephfs/Chart.yaml index 9238c26e9..46d38df97 100644 --- a/charts/ceph-csi-cephfs/Chart.yaml +++ b/charts/ceph-csi-cephfs/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v1 appVersion: canary description: "Container Storage Interface (CSI) driver, -provisioner, snapshotter and attacher for Ceph cephfs" +provisioner, snapshotter and resizer for Ceph cephfs" name: ceph-csi-cephfs version: 3-canary keywords: diff --git a/charts/ceph-csi-cephfs/README.md b/charts/ceph-csi-cephfs/README.md index 6aa1ec732..60aa16be6 100644 --- a/charts/ceph-csi-cephfs/README.md +++ b/charts/ceph-csi-cephfs/README.md @@ -91,7 +91,7 @@ charts and their default values. | `nodeplugin.priorityClassName` | Set user created priorityclassName for csi plugin pods. default is system-node-critical which is highest priority | `system-node-critical` | | `nodeplugin.profiling.enabled` | Specifies whether profiling should be enabled | `false` | | `nodeplugin.registrar.image.repository` | Node-Registrar image repository URL | `registry.k8s.io/sig-storage/csi-node-driver-registrar` | -| `nodeplugin.registrar.image.tag` | Image tag | `v2.2.0` | +| `nodeplugin.registrar.image.tag` | Image tag | `v2.5.1` | | `nodeplugin.registrar.image.pullPolicy` | Image pull policy | `IfNotPresent` | | `nodeplugin.plugin.image.repository` | Nodeplugin image repository URL | `quay.io/cephcsi/cephcsi` | | `nodeplugin.plugin.image.tag` | Image tag | `canary` | @@ -105,21 +105,16 @@ charts and their default values. | `provisioner.timeout` | GRPC timeout for waiting for creation or deletion of a volume | `60s` | | `provisioner.priorityClassName` | Set user created priorityclassName for csi provisioner pods. Default is `system-cluster-critical` which is less priority than `system-node-critical` | `system-cluster-critical` | | `provisioner.profiling.enabled` | Specifies whether profiling should be enabled | `false` | -| `provisioner.provisioner.image.repository` | Specifies the csi-provisioner image repository URL | `registry.k8s.io/sig-storage/csi-provisioner` | -| `provisioner.provisioner.image.tag` | Specifies image tag | `v2.2.2` | +| `provisioner.provisioner.image.repository` | Specifies the csi-provisioner image repository URL | `registry.k8s.io/sig-storage/csi-provisioner` | +| `provisioner.provisioner.image.tag` | Specifies image tag | `v3.1.0` | | `provisioner.provisioner.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | -| `provisioner.attacher.image.repository` | Specifies the csi-attacher image repository URL | `registry.k8s.io/sig-storage/csi-attacher` | -| `provisioner.attacher.image.tag` | Specifies image tag | `v3.2.1` | -| `provisioner.attacher.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | -| `provisioner.attacher.name` | Specifies the name of csi-attacher sidecar | `attacher` | -| `provisioner.attacher.enabled` | Specifies whether attacher sidecar is enabled | `true` | -| `provisioner.resizer.image.repository` | Specifies the csi-resizer image repository URL | `registry.k8s.io/sig-storage/csi-resizer` | -| `provisioner.resizer.image.tag` | Specifies image tag | `v1.2.0` | +| `provisioner.resizer.image.repository` | Specifies the csi-resizer image repository URL | `registry.k8s.io/sig-storage/csi-resizer` | +| `provisioner.resizer.image.tag` | Specifies image tag | `v1.4.0` | | `provisioner.resizer.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.resizer.name` | Specifies the name of csi-resizer sidecar | `resizer` | | `provisioner.resizer.enabled` | Specifies whether resizer sidecar is enabled | `true` | -| `provisioner.snapshotter.image.repository` | Specifies the csi-snapshotter image repository URL | `registry.k8s.io/sig-storage/csi-snapshotter` | -| `provisioner.snapshotter.image.tag` | Specifies image tag | `v4.1.1` | +| `provisioner.snapshotter.image.repository` | Specifies the csi-snapshotter image repository URL | `registry.k8s.io/sig-storage/csi-snapshotter` | +| `provisioner.snapshotter.image.tag` | Specifies image tag | `v6.0.1` | | `provisioner.snapshotter.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.nodeSelector` | Specifies the node selector for provisioner deployment | `{}` | | `provisioner.tolerations` | Specifies the tolerations for provisioner deployment | `{}` | diff --git a/charts/ceph-csi-cephfs/templates/csidriver-crd.yaml b/charts/ceph-csi-cephfs/templates/csidriver-crd.yaml index 92c3d916a..c3ed01849 100644 --- a/charts/ceph-csi-cephfs/templates/csidriver-crd.yaml +++ b/charts/ceph-csi-cephfs/templates/csidriver-crd.yaml @@ -7,5 +7,5 @@ kind: CSIDriver metadata: name: {{ .Values.driverName }} spec: - attachRequired: true + attachRequired: false podInfoOnMount: false diff --git a/charts/ceph-csi-cephfs/templates/provisioner-clusterrole.yaml b/charts/ceph-csi-cephfs/templates/provisioner-clusterrole.yaml index a78f35168..94319c298 100644 --- a/charts/ceph-csi-cephfs/templates/provisioner-clusterrole.yaml +++ b/charts/ceph-csi-cephfs/templates/provisioner-clusterrole.yaml @@ -40,14 +40,6 @@ rules: - apiGroups: ["snapshot.storage.k8s.io"] resources: ["volumesnapshotcontents/status"] verbs: ["update", "patch"] -{{- if .Values.provisioner.attacher.enabled }} - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments/status"] - verbs: ["patch"] -{{- end -}} {{- if .Values.provisioner.resizer.enabled }} - apiGroups: [""] resources: ["persistentvolumeclaims/status"] diff --git a/charts/ceph-csi-cephfs/templates/provisioner-deployment.yaml b/charts/ceph-csi-cephfs/templates/provisioner-deployment.yaml index 311c8919b..3c20609fe 100644 --- a/charts/ceph-csi-cephfs/templates/provisioner-deployment.yaml +++ b/charts/ceph-csi-cephfs/templates/provisioner-deployment.yaml @@ -86,24 +86,6 @@ spec: mountPath: /csi resources: {{ toYaml .Values.provisioner.snapshotter.resources | indent 12 }} -{{- if .Values.provisioner.attacher.enabled }} - - name: csi-attacher - image: "{{ .Values.provisioner.attacher.image.repository }}:{{ .Values.provisioner.attacher.image.tag }}" - imagePullPolicy: {{ .Values.provisioner.attacher.image.pullPolicy }} - args: - - "--v={{ .Values.logLevel }}" - - "--csi-address=$(ADDRESS)" - - "--leader-election=true" - - "--retry-interval-start=500ms" - env: - - name: ADDRESS - value: "unix:///csi/{{ .Values.provisionerSocketFile }}" - volumeMounts: - - name: socket-dir - mountPath: /csi - resources: -{{ toYaml .Values.nodeplugin.plugin.resources | indent 12 }} -{{- end }} {{- if .Values.provisioner.resizer.enabled }} - name: csi-resizer image: "{{ .Values.provisioner.resizer.image.repository }}:{{ .Values.provisioner.resizer.image.tag }}" diff --git a/charts/ceph-csi-cephfs/values.yaml b/charts/ceph-csi-cephfs/values.yaml index ec37530d4..59902b5e2 100644 --- a/charts/ceph-csi-cephfs/values.yaml +++ b/charts/ceph-csi-cephfs/values.yaml @@ -166,15 +166,6 @@ provisioner: pullPolicy: IfNotPresent resources: {} - attacher: - name: attacher - enabled: true - image: - repository: registry.k8s.io/sig-storage/csi-attacher - tag: v3.4.0 - pullPolicy: IfNotPresent - resources: {} - resizer: name: resizer enabled: true @@ -187,7 +178,7 @@ provisioner: snapshotter: image: repository: registry.k8s.io/sig-storage/csi-snapshotter - tag: v4.2.0 + tag: v6.0.1 pullPolicy: IfNotPresent resources: {} diff --git a/charts/ceph-csi-rbd/Chart.yaml b/charts/ceph-csi-rbd/Chart.yaml index 107647bfc..5ab42a106 100644 --- a/charts/ceph-csi-rbd/Chart.yaml +++ b/charts/ceph-csi-rbd/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v1 appVersion: canary description: "Container Storage Interface (CSI) driver, -provisioner, snapshotter, and attacher for Ceph RBD" +provisioner, snapshotter, resizer and attacher for Ceph RBD" name: ceph-csi-rbd version: 3-canary keywords: diff --git a/charts/ceph-csi-rbd/README.md b/charts/ceph-csi-rbd/README.md index 4c4542bee..bb654e532 100644 --- a/charts/ceph-csi-rbd/README.md +++ b/charts/ceph-csi-rbd/README.md @@ -93,7 +93,7 @@ charts and their default values. | `nodeplugin.priorityClassName` | Set user created priorityclassName for csi plugin pods. default is system-node-critical which is highest priority | `system-node-critical` | | `nodeplugin.profiling.enabled` | Specifies whether profiling should be enabled | `false` | | `nodeplugin.registrar.image.repository` | Node Registrar image repository URL | `registry.k8s.io/sig-storage/csi-node-driver-registrar` | -| `nodeplugin.registrar.image.tag` | Image tag | `v2.2.0` | +| `nodeplugin.registrar.image.tag` | Image tag | `v2.5.1` | | `nodeplugin.registrar.image.pullPolicy` | Image pull policy | `IfNotPresent` | | `nodeplugin.plugin.image.repository` | Nodeplugin image repository URL | `quay.io/cephcsi/cephcsi` | | `nodeplugin.plugin.image.tag` | Image tag | `canary` | @@ -111,23 +111,24 @@ charts and their default values. | `provisioner.minSnapshotsOnImage` | Minimum number of snapshots allowed on rbd image to trigger flattening | `250` | | `provisioner.skipForceFlatten` | Skip image flattening if kernel support mapping of rbd images which has the deep-flatten feature | `false` | | `provisioner.timeout` | GRPC timeout for waiting for creation or deletion of a volume | `60s` | +| `provisioner.clustername` | Cluster name to set on the RBD image | "" | | `provisioner.priorityClassName` | Set user created priorityclassName for csi provisioner pods. Default is `system-cluster-critical` which is less priority than `system-node-critical` | `system-cluster-critical` | | `provisioner.profiling.enabled` | Specifies whether profiling should be enabled | `false` | | `provisioner.provisioner.image.repository` | Specifies the csi-provisioner image repository URL | `registry.k8s.io/sig-storage/csi-provisioner` | -| `provisioner.provisioner.image.tag` | Specifies image tag | `v2.2.2` | +| `provisioner.provisioner.image.tag` | Specifies image tag | `canary` | | `provisioner.provisioner.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.attacher.image.repository` | Specifies the csi-attacher image repository URL | `registry.k8s.io/sig-storage/csi-attacher` | -| `provisioner.attacher.image.tag` | Specifies image tag | `v3.2.1` | +| `provisioner.attacher.image.tag` | Specifies image tag | `v3.4.0` | | `provisioner.attacher.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.attacher.name` | Specifies the name of csi-attacher sidecar | `attacher` | | `provisioner.attacher.enabled` | Specifies whether attacher sidecar is enabled | `true` | | `provisioner.resizer.image.repository` | Specifies the csi-resizer image repository URL | `registry.k8s.io/sig-storage/csi-resizer` | -| `provisioner.resizer.image.tag` | Specifies image tag | `v1.2.0` | +| `provisioner.resizer.image.tag` | Specifies image tag | `v1.4.0` | | `provisioner.resizer.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.resizer.name` | Specifies the name of csi-resizer sidecar | `resizer` | | `provisioner.resizer.enabled` | Specifies whether resizer sidecar is enabled | `true` | -| `provisioner.snapshotter.image.repository` | Specifies the csi-snapshotter image repository URL | `registry.k8s.io/sig-storage/csi-snapshotter` | -| `provisioner.snapshotter.image.tag` | Specifies image tag | `v4.1.1` | +| `provisioner.snapshotter.image.repository` | Specifies the csi-snapshotter image repository URL | `registry.k8s.io/sig-storage/csi-snapshotter` | +| `provisioner.snapshotter.image.tag` | Specifies image tag | `v6.0.1` | | `provisioner.snapshotter.image.pullPolicy` | Specifies pull policy | `IfNotPresent` | | `provisioner.nodeSelector` | Specifies the node selector for provisioner deployment | `{}` | | `provisioner.tolerations` | Specifies the tolerations for provisioner deployment | `{}` | diff --git a/charts/ceph-csi-rbd/templates/nodeplugin-clusterrole.yaml b/charts/ceph-csi-rbd/templates/nodeplugin-clusterrole.yaml index 93ec30ed6..9e24d757e 100644 --- a/charts/ceph-csi-rbd/templates/nodeplugin-clusterrole.yaml +++ b/charts/ceph-csi-rbd/templates/nodeplugin-clusterrole.yaml @@ -31,4 +31,7 @@ rules: - apiGroups: ["storage.k8s.io"] resources: ["volumeattachments"] verbs: ["list", "get"] + - apiGroups: [""] + resources: ["serviceaccounts/token"] + verbs: ["create"] {{- end -}} diff --git a/charts/ceph-csi-rbd/templates/provisioner-clusterrole.yaml b/charts/ceph-csi-rbd/templates/provisioner-clusterrole.yaml index 9a4b1fe83..70c7670b2 100644 --- a/charts/ceph-csi-rbd/templates/provisioner-clusterrole.yaml +++ b/charts/ceph-csi-rbd/templates/provisioner-clusterrole.yaml @@ -70,5 +70,8 @@ rules: resources: ["csinodes"] verbs: ["get", "list", "watch"] {{- end }} + - apiGroups: [""] + resources: ["serviceaccounts/token"] + verbs: ["create"] {{- end -}} diff --git a/charts/ceph-csi-rbd/templates/provisioner-deployment.yaml b/charts/ceph-csi-rbd/templates/provisioner-deployment.yaml index 9cf4bc412..d38203af6 100644 --- a/charts/ceph-csi-rbd/templates/provisioner-deployment.yaml +++ b/charts/ceph-csi-rbd/templates/provisioner-deployment.yaml @@ -152,6 +152,9 @@ spec: {{- if .Values.provisioner.profiling.enabled }} - "--enableprofiling={{ .Values.provisioner.profiling.enabled }}" {{- end }} + {{- if .Values.provisioner.clustername }} + - "--clustername={{ .Values.provisioner.clustername }}" + {{- end }} env: - name: POD_IP valueFrom: @@ -199,6 +202,9 @@ spec: - "--v={{ .Values.logLevel }}" - "--drivername=$(DRIVER_NAME)" - "--drivernamespace=$(DRIVER_NAMESPACE)" + {{- if .Values.provisioner.clustername }} + - "--clustername={{ .Values.provisioner.clustername }}" + {{- end }} env: - name: DRIVER_NAMESPACE valueFrom: diff --git a/charts/ceph-csi-rbd/values.yaml b/charts/ceph-csi-rbd/values.yaml index 01df513b5..b18d3443e 100644 --- a/charts/ceph-csi-rbd/values.yaml +++ b/charts/ceph-csi-rbd/values.yaml @@ -144,6 +144,8 @@ provisioner: deployController: true # Timeout for waiting for creation or deletion of a volume timeout: 60s + # cluster name to set on the RBD image + # clustername: "k8s-cluster-1" # Hard limit for maximum number of nested volume clones that are taken before # a flatten occurs hardMaxCloneDepth: 8 @@ -229,7 +231,7 @@ provisioner: snapshotter: image: repository: registry.k8s.io/sig-storage/csi-snapshotter - tag: v4.2.0 + tag: v6.0.1 pullPolicy: IfNotPresent resources: {} diff --git a/cmd/cephcsi.go b/cmd/cephcsi.go index c36e0cb7c..480215de2 100644 --- a/cmd/cephcsi.go +++ b/cmd/cephcsi.go @@ -68,6 +68,7 @@ func init() { flag.StringVar(&conf.NodeID, "nodeid", "", "node id") flag.StringVar(&conf.PluginPath, "pluginpath", defaultPluginPath, "plugin path") flag.StringVar(&conf.StagingPath, "stagingpath", defaultStagingPath, "staging path") + flag.StringVar(&conf.ClusterName, "clustername", "", "name of the cluster") flag.StringVar(&conf.InstanceID, "instanceid", "", "Unique ID distinguishing this instance of Ceph CSI among other"+ " instances, when sharing Ceph clusters across CSI instances for provisioning") flag.IntVar(&conf.PidLimit, "pidlimit", 0, "the PID limit to configure through cgroups") @@ -247,8 +248,9 @@ func main() { case controllerType: cfg := controller.Config{ - DriverName: dname, - Namespace: conf.DriverNamespace, + DriverName: dname, + Namespace: conf.DriverNamespace, + ClusterName: conf.ClusterName, } // initialize all controllers before starting. initControllers() diff --git a/deploy/cephfs/kubernetes/csi-cephfsplugin-provisioner.yaml b/deploy/cephfs/kubernetes/csi-cephfsplugin-provisioner.yaml index ae49c56db..ed2eeb46f 100644 --- a/deploy/cephfs/kubernetes/csi-cephfsplugin-provisioner.yaml +++ b/deploy/cephfs/kubernetes/csi-cephfsplugin-provisioner.yaml @@ -77,7 +77,7 @@ spec: - name: socket-dir mountPath: /csi - name: csi-snapshotter - image: registry.k8s.io/sig-storage/csi-snapshotter:v5.0.1 + image: registry.k8s.io/sig-storage/csi-snapshotter:v6.0.1 args: - "--csi-address=$(ADDRESS)" - "--v=5" @@ -90,20 +90,6 @@ spec: volumeMounts: - name: socket-dir mountPath: /csi - - name: csi-cephfsplugin-attacher - image: registry.k8s.io/sig-storage/csi-attacher:v3.4.0 - args: - - "--v=5" - - "--csi-address=$(ADDRESS)" - - "--leader-election=true" - - "--retry-interval-start=500ms" - env: - - name: ADDRESS - value: /csi/csi-provisioner.sock - imagePullPolicy: "IfNotPresent" - volumeMounts: - - name: socket-dir - mountPath: /csi - name: csi-cephfsplugin # for stable functionality replace canary with latest release version image: quay.io/cephcsi/cephcsi:canary diff --git a/deploy/cephfs/kubernetes/csi-provisioner-rbac.yaml b/deploy/cephfs/kubernetes/csi-provisioner-rbac.yaml index df949d493..c884daa7f 100644 --- a/deploy/cephfs/kubernetes/csi-provisioner-rbac.yaml +++ b/deploy/cephfs/kubernetes/csi-provisioner-rbac.yaml @@ -37,12 +37,6 @@ rules: - apiGroups: ["snapshot.storage.k8s.io"] resources: ["volumesnapshotclasses"] verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments/status"] - verbs: ["patch"] - apiGroups: [""] resources: ["persistentvolumeclaims/status"] verbs: ["update", "patch"] diff --git a/deploy/cephfs/kubernetes/csidriver.yaml b/deploy/cephfs/kubernetes/csidriver.yaml index 49d0eaaea..d347ff843 100644 --- a/deploy/cephfs/kubernetes/csidriver.yaml +++ b/deploy/cephfs/kubernetes/csidriver.yaml @@ -6,5 +6,5 @@ kind: CSIDriver metadata: name: cephfs.csi.ceph.com spec: - attachRequired: true + attachRequired: false podInfoOnMount: false diff --git a/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml b/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml index 98ffbcaf8..baf5cdedc 100644 --- a/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml +++ b/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml @@ -30,6 +30,9 @@ rules: - apiGroups: ["storage.k8s.io"] resources: ["volumeattachments"] verbs: ["list", "get"] + - apiGroups: [""] + resources: ["serviceaccounts/token"] + verbs: ["create"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml b/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml index 0cc0b8282..0fd06e17d 100644 --- a/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml +++ b/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml @@ -63,6 +63,9 @@ rules: - apiGroups: [""] resources: ["serviceaccounts"] verbs: ["get"] + - apiGroups: [""] + resources: ["serviceaccounts/token"] + verbs: ["create"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml b/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml index 444b564a2..b0fe29235 100644 --- a/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml +++ b/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml @@ -71,7 +71,7 @@ spec: - name: socket-dir mountPath: /csi - name: csi-snapshotter - image: registry.k8s.io/sig-storage/csi-snapshotter:v5.0.1 + image: registry.k8s.io/sig-storage/csi-snapshotter:v6.0.1 args: - "--csi-address=$(ADDRESS)" - "--v=5" diff --git a/docs/cephfs-snapshot-backed-volumes.md b/docs/cephfs-snapshot-backed-volumes.md new file mode 100644 index 000000000..618b2bc8f --- /dev/null +++ b/docs/cephfs-snapshot-backed-volumes.md @@ -0,0 +1,61 @@ +# Provisioning and mounting CephFS snapshot-backed volumes + +Snapshot-backed volumes allow CephFS subvolume snapshots to be exposed as +regular read-only PVCs. No data cloning is performed and provisioning such +volumes is done in constant time. + +For more details please refer to [Snapshots as shallow read-only volumes](./design/proposals/cephfs-snapshot-shallow-ro-vol.md) +design document. + +## Prerequisites + +Prerequisites for this feature are the same as for creating PVCs with snapshot +volume source. See [Create snapshot and Clone Volume](./snap-clone.md) for more +information. + +## Usage + +### Provisioning a snapshot-backed volume from a volume snapshot + +For provisioning new snapshot-backed volumes, following configuration must be +set for storage class(es) and their PVCs respectively: + +* StorageClass: + * Specify `backingSnapshot: "true"` parameter. +* PersistentVolumeClaim: + * Set `storageClassName` to point to your storage class with backing + snapshots enabled. + * Define `spec.dataSource` for your desired source volume snapshot. + * Set `spec.accessModes` to `ReadOnlyMany`. This is the only access mode that + is supported by this feature. + +### Mounting snapshots from pre-provisioned volumes + +Steps for defining a PersistentVolume and PersistentVolumeClaim for +pre-provisioned CephFS subvolumes are identical to those described in +[Static PVC with ceph-csi](./static-pvc.md), except one additional parameter +must be specified: `backingSnapshotID`. CephFS-CSI driver will retrieve the +snapshot identified by the given ID from within the specified subvolume, and +expose it to workloads in read-only mode. Volume access mode must be set to +`ReadOnlyMany`. + +Note that the snapshot retrieval is done by traversing `/.snap` and +searching for a directory that contains `backingSnapshotID` value in its name. +The specified snapshot ID does not necessarily need to be the complete directory +name inside `/.snap`, however it must be complete enough to uniquely +identify that directory. + +Example: + +``` +$ ls .snap +_f279df14-6729-4342-b82f-166f45204233_1099511628283 +_a364870e-6729-4342-b82f-166f45204233_1099635085072 +``` + +`f279df14-6729-4342-b82f-166f45204233` would be considered a valid value for +`backingSnapshotID` volume parameter, whereas `6729-4342-b82f-166f45204233` +would not, as it would be ambiguous. + +If the given snapshot ID is ambiguous, or no such snapshot is found, mounting +the PVC will fail with INVALID_ARGUMENT error code. diff --git a/docs/deploy-cephfs.md b/docs/deploy-cephfs.md index 833d604ca..181e8d1f3 100644 --- a/docs/deploy-cephfs.md +++ b/docs/deploy-cephfs.md @@ -81,6 +81,7 @@ you're running it inside a k8s cluster and find the config itself). | `pool` | no | Ceph pool into which volume data shall be stored | | `volumeNamePrefix` | no | Prefix to use for naming subvolumes (defaults to `csi-vol-`). | | `snapshotNamePrefix` | no | Prefix to use for naming snapshots (defaults to `csi-snap-`) | +| `backingSnapshot` | no | Boolean value. The PVC shall be backed by the CephFS snapshot specified in its data source. `pool` parameter must not be specified. (defaults to `false`) | | `kernelMountOptions` | no | Comma separated string of mount options accepted by cephfs kernel mounter, by default no options are passed. Check man mount.ceph for options. | | `fuseMountOptions` | no | Comma separated string of mount options accepted by ceph-fuse mounter, by default no options are passed. | | `csi.storage.k8s.io/provisioner-secret-name`, `csi.storage.k8s.io/node-stage-secret-name` | for Kubernetes | Name of the Kubernetes Secret object containing Ceph client credentials. Both parameters should have the same value | diff --git a/docs/deploy-rbd.md b/docs/deploy-rbd.md index 0c90bf644..bdbaf101f 100644 --- a/docs/deploy-rbd.md +++ b/docs/deploy-rbd.md @@ -40,6 +40,7 @@ make image-cephcsi | `--enablegrpcmetrics` | `false` | [Deprecated] Enable grpc metrics collection and start prometheus server | | `--polltime` | `"60s"` | Time interval in between each poll | | `--timeout` | `"3s"` | Probe timeout in seconds | +| `--clustername` | _empty_ | Cluster name to set on RBD image | | `--histogramoption` | `0.5,2,6` | [Deprecated] Histogram option for grpc metrics, should be comma separated value (ex:= "0.5,2,6" where start=0.5 factor=2, count=6) | | `--domainlabels` | _empty_ | Kubernetes node labels to use as CSI domain labels for topology aware provisioning, should be a comma separated value (ex:= "failure-domain/region,failure-domain/zone") | | `--rbdhardmaxclonedepth` | `8` | Hard limit for maximum number of nested volume clones that are taken before a flatten occurs | @@ -65,6 +66,9 @@ make image-cephcsi | `mounter` | no | if set to `rbd-nbd`, use `rbd-nbd` on nodes that have `rbd-nbd` and `nbd` kernel modules to map rbd images | | `encrypted` | no | disabled by default, use `"true"` to enable LUKS encryption on PVC and `"false"` to disable it. **Do not change for existing storageclasses** | | `encryptionKMSID` | no | required if encryption is enabled and a kms is used to store passphrases | +| `stripeUnit` | no | stripe unit in bytes | +| `stripeCount` | no | objects to stripe over before looping | +| `objectSize` | no | object size in bytes | **NOTE:** An accompanying CSI configuration file, needs to be provided to the running pods. Refer to [Creating CSI configuration](../examples/README.md#creating-csi-configuration) diff --git a/e2e/README.md b/e2e/README.md index 79a73983a..85c0c352d 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -111,7 +111,7 @@ are available while running tests: After the support for snapshot/clone has been added to ceph-csi, you need to follow these steps before running e2e. -- Install snapshot controller and Beta snapshot CRD +- Install snapshot controller and snapshot CRD ```console ./scripts/install-snapshot.sh install diff --git a/e2e/cephfs.go b/e2e/cephfs.go index 681d6648c..7c8a88466 100644 --- a/e2e/cephfs.go +++ b/e2e/cephfs.go @@ -22,7 +22,7 @@ import ( "strings" "sync" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" . "github.com/onsi/ginkgo" // nolint v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -298,35 +298,32 @@ var _ = Describe(cephfsType, func() { }) } By("verify generic ephemeral volume support", func() { - // generic ephemeral volume support is beta since v1.21. - if k8sVersionGreaterEquals(f.ClientSet, 1, 21) { - err := createCephfsStorageClass(f.ClientSet, f, true, nil) - if err != nil { - e2elog.Failf("failed to create CephFS storageclass: %v", err) - } - // create application - app, err := loadApp(appEphemeralPath) - if err != nil { - e2elog.Failf("failed to load application: %v", err) - } - app.Namespace = f.UniqueName - err = createApp(f.ClientSet, app, deployTimeout) - if err != nil { - e2elog.Failf("failed to create application: %v", err) - } - validateSubvolumeCount(f, 1, fileSystemName, subvolumegroup) - validateOmapCount(f, 1, cephfsType, metadataPool, volumesType) - // delete pod - err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout) - if err != nil { - e2elog.Failf("failed to delete application: %v", err) - } - validateSubvolumeCount(f, 0, fileSystemName, subvolumegroup) - validateOmapCount(f, 0, cephfsType, metadataPool, volumesType) - err = deleteResource(cephFSExamplePath + "storageclass.yaml") - if err != nil { - e2elog.Failf("failed to delete CephFS storageclass: %v", err) - } + err := createCephfsStorageClass(f.ClientSet, f, true, nil) + if err != nil { + e2elog.Failf("failed to create CephFS storageclass: %v", err) + } + // create application + app, err := loadApp(appEphemeralPath) + if err != nil { + e2elog.Failf("failed to load application: %v", err) + } + app.Namespace = f.UniqueName + err = createApp(f.ClientSet, app, deployTimeout) + if err != nil { + e2elog.Failf("failed to create application: %v", err) + } + validateSubvolumeCount(f, 1, fileSystemName, subvolumegroup) + validateOmapCount(f, 1, cephfsType, metadataPool, volumesType) + // delete pod + err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete application: %v", err) + } + validateSubvolumeCount(f, 0, fileSystemName, subvolumegroup) + validateOmapCount(f, 0, cephfsType, metadataPool, volumesType) + err = deleteResource(cephFSExamplePath + "storageclass.yaml") + if err != nil { + e2elog.Failf("failed to delete CephFS storageclass: %v", err) } }) @@ -517,8 +514,8 @@ var _ = Describe(cephfsType, func() { e2elog.Failf("failed to list pods for Deployment: %v", err) } - doStat := func(podName string) (stdErr string, err error) { - _, stdErr, err = execCommandInContainerByPodName( + doStat := func(podName string) (string, error) { + _, stdErr, execErr := execCommandInContainerByPodName( f, fmt.Sprintf("stat %s", depl.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath), depl.Namespace, @@ -526,7 +523,7 @@ var _ = Describe(cephfsType, func() { depl.Spec.Template.Spec.Containers[0].Name, ) - return stdErr, err + return stdErr, execErr } ensureStatSucceeds := func(podName string) error { stdErr, statErr := doStat(podName) @@ -1212,6 +1209,142 @@ var _ = Describe(cephfsType, func() { validateSubvolumeCount(f, 0, fileSystemName, subvolumegroup) validateOmapCount(f, 0, cephfsType, metadataPool, volumesType) validateOmapCount(f, 0, cephfsType, metadataPool, snapsType) + + err = deleteResource(cephFSExamplePath + "snapshotclass.yaml") + if err != nil { + e2elog.Failf("failed to delete CephFS snapshotclass: %v", err) + } + }) + + By("checking snapshot-backed volume", func() { + err := createCephFSSnapshotClass(f) + if err != nil { + e2elog.Failf("failed to delete CephFS storageclass: %v", err) + } + + pvc, err := loadPVC(pvcPath) + if err != nil { + e2elog.Failf("failed to load PVC: %v", err) + } + pvc.Namespace = f.UniqueName + err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout) + if err != nil { + e2elog.Failf("failed to create PVC: %v", err) + } + + app, err := loadApp(appPath) + if err != nil { + e2elog.Failf("failed to load application: %v", err) + } + app.Namespace = f.UniqueName + app.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = pvc.Name + appLabels := map[string]string{ + appKey: appLabel, + } + app.Labels = appLabels + optApp := metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", appKey, appLabels[appKey]), + } + err = writeDataInPod(app, &optApp, f) + if err != nil { + e2elog.Failf("failed to write data: %v", err) + } + + appTestFilePath := app.Spec.Containers[0].VolumeMounts[0].MountPath + "/test" + + snap := getSnapshot(snapshotPath) + snap.Namespace = f.UniqueName + snap.Spec.Source.PersistentVolumeClaimName = &pvc.Name + err = createSnapshot(&snap, deployTimeout) + if err != nil { + e2elog.Failf("failed to create snapshot: %v", err) + } + + err = appendToFileInContainer(f, app, appTestFilePath, "hello", &optApp) + if err != nil { + e2elog.Failf("failed to append data: %v", err) + } + + parentFileSum, err := calculateSHA512sum(f, app, appTestFilePath, &optApp) + if err != nil { + e2elog.Failf("failed to get SHA512 sum for file: %v", err) + } + + err = deleteResource(cephFSExamplePath + "storageclass.yaml") + if err != nil { + e2elog.Failf("failed to delete CephFS storageclass: %v", err) + } + err = createCephfsStorageClass(f.ClientSet, f, false, map[string]string{ + "backingSnapshot": "true", + }) + if err != nil { + e2elog.Failf("failed to create CephFS storageclass: %v", err) + } + + pvcClone, err := loadPVC(pvcClonePath) + if err != nil { + e2elog.Failf("failed to load PVC: %v", err) + } + // Snapshot-backed volumes support read-only access modes only. + pvcClone.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany} + appClone, err := loadApp(appClonePath) + if err != nil { + e2elog.Failf("failed to load application: %v", err) + } + appCloneLabels := map[string]string{ + appKey: appCloneLabel, + } + appClone.Labels = appCloneLabels + optAppClone := metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", appKey, appCloneLabels[appKey]), + } + pvcClone.Namespace = f.UniqueName + appClone.Namespace = f.UniqueName + err = createPVCAndApp("", f, pvcClone, appClone, deployTimeout) + if err != nil { + e2elog.Failf("failed to create PVC and app: %v", err) + } + + // Snapshot-backed volume shouldn't contribute to total subvolume count. + validateSubvolumeCount(f, 1, fileSystemName, subvolumegroup) + + // Deleting snapshot before deleting pvcClone should succeed. It will be + // deleted once all volumes that are backed by this snapshot are gone. + err = deleteSnapshot(&snap, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete snapshot: %v", err) + } + + appCloneTestFilePath := appClone.Spec.Containers[0].VolumeMounts[0].MountPath + "/test" + + snapFileSum, err := calculateSHA512sum(f, appClone, appCloneTestFilePath, &optAppClone) + if err != nil { + e2elog.Failf("failed to get SHA512 sum for file: %v", err) + } + + if parentFileSum == snapFileSum { + e2elog.Failf("SHA512 sums of files in parent subvol and snapshot should differ") + } + + err = deletePVCAndApp("", f, pvcClone, appClone) + if err != nil { + e2elog.Failf("failed to delete PVC or application: %v", err) + } + + err = deletePVCAndApp("", f, pvc, app) + if err != nil { + e2elog.Failf("failed to delete PVC or application: %v", err) + } + + err = deleteResource(cephFSExamplePath + "storageclass.yaml") + if err != nil { + e2elog.Failf("failed to delete CephFS storageclass: %v", err) + } + + err = createCephfsStorageClass(f.ClientSet, f, false, nil) + if err != nil { + e2elog.Failf("failed to create CephFS storageclass: %v", err) + } }) By("create a PVC-PVC clone and bind it to an app", func() { diff --git a/e2e/cephfs_helper.go b/e2e/cephfs_helper.go index fb247ee01..4cdd25d0e 100644 --- a/e2e/cephfs_helper.go +++ b/e2e/cephfs_helper.go @@ -24,7 +24,7 @@ import ( "strings" "time" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" @@ -60,7 +60,8 @@ func createCephfsStorageClass( c kubernetes.Interface, f *framework.Framework, enablePool bool, - params map[string]string) error { + params map[string]string, +) error { scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml") sc, err := getStorageClass(scPath) if err != nil { @@ -253,7 +254,8 @@ func getSnapName(snapNamespace, snapName string) (string, error) { func deleteBackingCephFSSubvolumeSnapshot( f *framework.Framework, pvc *v1.PersistentVolumeClaim, - snap *snapapi.VolumeSnapshot) error { + snap *snapapi.VolumeSnapshot, +) error { snapshotName, err := getSnapName(snap.Namespace, snap.Name) if err != nil { return err diff --git a/e2e/clone.go b/e2e/clone.go index 4f508ecad..fa24dd1e7 100644 --- a/e2e/clone.go +++ b/e2e/clone.go @@ -30,7 +30,8 @@ func validateBiggerCloneFromPVC(f *framework.Framework, pvcPath, appPath, pvcClonePath, - appClonePath string) error { + appClonePath string, +) error { const ( size = "1Gi" newSize = "2Gi" diff --git a/e2e/configmap.go b/e2e/configmap.go index 676968a3a..6eb366065 100644 --- a/e2e/configmap.go +++ b/e2e/configmap.go @@ -95,7 +95,8 @@ func createConfigMap(pluginPath string, c kubernetes.Interface, f *framework.Fra func createCustomConfigMap( c kubernetes.Interface, pluginPath string, - clusterInfo map[string]map[string]string) error { + clusterInfo map[string]map[string]string, +) error { path := pluginPath + configMap cm := v1.ConfigMap{} err := unmarshal(path, &cm) diff --git a/e2e/deployment.go b/e2e/deployment.go index b694fb36c..faea048fe 100644 --- a/e2e/deployment.go +++ b/e2e/deployment.go @@ -24,6 +24,7 @@ import ( "time" appsv1 "k8s.io/api/apps/v1" + autoscalingv1 "k8s.io/api/autoscaling/v1" v1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,7 +41,8 @@ func execCommandInPodWithName( cmdString, podName, containerName, - nameSpace string) (string, string, error) { + nameSpace string, +) (string, string, error) { cmd := []string{"/bin/sh", "-c", cmdString} podOpt := framework.ExecOptions{ Command: cmd, @@ -296,3 +298,180 @@ func (rnr *rookNFSResource) Do(action kubectlAction) error { return nil } + +func waitForDeploymentUpdateScale( + c kubernetes.Interface, + ns, + deploymentName string, + scale *autoscalingv1.Scale, + timeout int, +) error { + t := time.Duration(timeout) * time.Minute + start := time.Now() + err := wait.PollImmediate(poll, t, func() (bool, error) { + scaleResult, upsErr := c.AppsV1().Deployments(ns).UpdateScale(context.TODO(), + deploymentName, scale, metav1.UpdateOptions{}) + if upsErr != nil { + if isRetryableAPIError(upsErr) { + return false, nil + } + e2elog.Logf( + "Deployment UpdateScale %s/%s has not completed yet (%d seconds elapsed)", + ns, deploymentName, int(time.Since(start).Seconds())) + + return false, fmt.Errorf("error update scale deployment %s/%s: %w", ns, deploymentName, upsErr) + } + if scaleResult.Spec.Replicas != scale.Spec.Replicas { + e2elog.Logf("scale result not matching for deployment %s/%s, desired scale %d, got %d", + ns, deploymentName, scale.Spec.Replicas, scaleResult.Spec.Replicas) + + return false, fmt.Errorf("error scale not matching in deployment %s/%s: %w", ns, deploymentName, upsErr) + } + + return true, nil + }) + if err != nil { + return fmt.Errorf("failed update scale deployment %s/%s: %w", ns, deploymentName, err) + } + + return nil +} + +func waitForDeploymentUpdate( + c kubernetes.Interface, + deployment *appsv1.Deployment, + timeout int, +) error { + t := time.Duration(timeout) * time.Minute + start := time.Now() + err := wait.PollImmediate(poll, t, func() (bool, error) { + _, upErr := c.AppsV1().Deployments(deployment.Namespace).Update( + context.TODO(), deployment, metav1.UpdateOptions{}) + if upErr != nil { + if isRetryableAPIError(upErr) { + return false, nil + } + e2elog.Logf( + "Deployment Update %s/%s has not completed yet (%d seconds elapsed)", + deployment.Namespace, deployment.Name, int(time.Since(start).Seconds())) + + return false, fmt.Errorf("error updating deployment %s/%s: %w", + deployment.Namespace, deployment.Name, upErr) + } + + return true, nil + }) + if err != nil { + return fmt.Errorf("failed update deployment %s/%s: %w", deployment.Namespace, deployment.Name, err) + } + + return nil +} + +// contains check if slice contains string. +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + + return false +} + +func waitForContainersArgsUpdate( + c kubernetes.Interface, + ns, + deploymentName, + key, + value string, + containers []string, + timeout int, +) error { + e2elog.Logf("waiting for deployment updates %s/%s", ns, deploymentName) + + // Scale down to 0. + scale, err := c.AppsV1().Deployments(ns).GetScale(context.TODO(), deploymentName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("error get scale deployment %s/%s: %w", ns, deploymentName, err) + } + count := scale.Spec.Replicas + scale.ResourceVersion = "" // indicate the scale update should be unconditional + scale.Spec.Replicas = 0 + err = waitForDeploymentUpdateScale(c, ns, deploymentName, scale, timeout) + if err != nil { + return err + } + + // Update deployment. + deployment, err := c.AppsV1().Deployments(ns).Get(context.TODO(), deploymentName, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("error get deployment %s/%s: %w", ns, deploymentName, err) + } + cid := deployment.Spec.Template.Spec.Containers // cid: read as containers in deployment + for i := range cid { + if contains(containers, cid[i].Name) { + match := false + for j, ak := range cid[i].Args { + if ak == key { + // do replacement of value + match = true + cid[i].Args[j] = fmt.Sprintf("--%s=%s", key, value) + + break + } + } + if !match { + // append a new key value + cid[i].Args = append(cid[i].Args, fmt.Sprintf("--%s=%s", key, value)) + } + deployment.Spec.Template.Spec.Containers[i].Args = cid[i].Args + } + } + // clear creationTimestamp, generation, resourceVersion, and uid + deployment.CreationTimestamp = metav1.Time{} + deployment.Generation = 0 + deployment.ResourceVersion = "0" + deployment.UID = "" + err = waitForDeploymentUpdate(c, deployment, timeout) + if err != nil { + return err + } + + // Scale up to count. + scale.Spec.Replicas = count + err = waitForDeploymentUpdateScale(c, ns, deploymentName, scale, timeout) + if err != nil { + return err + } + + // wait for scale to become count + t := time.Duration(timeout) * time.Minute + start := time.Now() + err = wait.PollImmediate(poll, t, func() (bool, error) { + deploy, getErr := c.AppsV1().Deployments(ns).Get(context.TODO(), deploymentName, metav1.GetOptions{}) + if getErr != nil { + if isRetryableAPIError(getErr) { + return false, nil + } + e2elog.Logf( + "Deployment Get %s/%s has not completed yet (%d seconds elapsed)", + ns, deploymentName, int(time.Since(start).Seconds())) + + return false, fmt.Errorf("error getting deployment %s/%s: %w", ns, deploymentName, getErr) + } + if deploy.Status.Replicas != count { + e2elog.Logf("Expected deployment %s/%s replicas %d, got %d", ns, deploymentName, count, deploy.Status.Replicas) + + return false, fmt.Errorf("error expected deployment %s/%s replicas %d, got %d", + ns, deploymentName, count, deploy.Status.Replicas) + } + + return true, nil + }) + if err != nil { + return fmt.Errorf("failed getting deployment %s/%s: %w", ns, deploymentName, err) + } + + return nil +} diff --git a/e2e/nfs.go b/e2e/nfs.go index 3ef1a8549..89860b96e 100644 --- a/e2e/nfs.go +++ b/e2e/nfs.go @@ -23,7 +23,7 @@ import ( "sync" "time" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" . "github.com/onsi/ginkgo" // nolint v1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" @@ -143,7 +143,8 @@ func createNFSStorageClass( c clientset.Interface, f *framework.Framework, enablePool bool, - params map[string]string) error { + params map[string]string, +) error { scPath := fmt.Sprintf("%s/%s", nfsExamplePath, "storageclass.yaml") sc, err := getStorageClass(scPath) if err != nil { diff --git a/e2e/pod.go b/e2e/pod.go index 4083589a1..cf281caa3 100644 --- a/e2e/pod.go +++ b/e2e/pod.go @@ -136,7 +136,8 @@ func findPodAndContainerName(f *framework.Framework, ns, cn string, opt *metav1. func getCommandInPodOpts( f *framework.Framework, c, ns, cn string, - opt *metav1.ListOptions) (framework.ExecOptions, error) { + opt *metav1.ListOptions, +) (framework.ExecOptions, error) { cmd := []string{"/bin/sh", "-c", c} pName, cName, err := findPodAndContainerName(f, ns, cn, opt) if err != nil { @@ -161,7 +162,8 @@ func getCommandInPodOpts( // stderr is returned as a string, and err will be set on a failure. func execCommandInDaemonsetPod( f *framework.Framework, - c, daemonsetName, nodeName, containerName, ns string) (string, error) { + c, daemonsetName, nodeName, containerName, ns string, +) (string, error) { selector, err := getDaemonSetLabelSelector(f, ns, daemonsetName) if err != nil { return "", err @@ -224,7 +226,8 @@ func execCommandInPod(f *framework.Framework, c, ns string, opt *metav1.ListOpti } func execCommandInContainer( - f *framework.Framework, c, ns, cn string, opt *metav1.ListOptions) (string, string, error) { + f *framework.Framework, c, ns, cn string, opt *metav1.ListOptions, +) (string, string, error) { podOpt, err := getCommandInPodOpts(f, c, ns, cn, opt) if err != nil { return "", "", err @@ -425,6 +428,25 @@ func calculateSHA512sum(f *framework.Framework, app *v1.Pod, filePath string, op return checkSum, nil } +func appendToFileInContainer( + f *framework.Framework, + app *v1.Pod, + filePath, + toAppend string, + opt *metav1.ListOptions, +) error { + cmd := fmt.Sprintf("echo %q >> %s", toAppend, filePath) + _, stdErr, err := execCommandInPod(f, cmd, app.Namespace, opt) + if err != nil { + return fmt.Errorf("could not append to file %s: %w ; stderr: %s", filePath, err, stdErr) + } + if stdErr != "" { + return fmt.Errorf("could not append to file %s: %v", filePath, stdErr) + } + + return nil +} + // getKernelVersionFromDaemonset gets the kernel version from the specified container. func getKernelVersionFromDaemonset(f *framework.Framework, ns, dsn, cn string) (string, error) { selector, err := getDaemonSetLabelSelector(f, ns, dsn) @@ -468,7 +490,8 @@ func validateRWOPPodCreation( f *framework.Framework, pvc *v1.PersistentVolumeClaim, app *v1.Pod, - baseAppName string) error { + baseAppName string, +) error { var err error // create one more app with same PVC name := fmt.Sprintf("%s%d", f.UniqueName, deployTimeout) diff --git a/e2e/pvc.go b/e2e/pvc.go index 9f1854474..f87a153cd 100644 --- a/e2e/pvc.go +++ b/e2e/pvc.go @@ -239,7 +239,8 @@ func getPersistentVolume(c kubernetes.Interface, name string) (*v1.PersistentVol func getPVCAndPV( c kubernetes.Interface, - pvcName, pvcNamespace string) (*v1.PersistentVolumeClaim, *v1.PersistentVolume, error) { + pvcName, pvcNamespace string, +) (*v1.PersistentVolumeClaim, *v1.PersistentVolume, error) { pvc, err := getPersistentVolumeClaim(c, pvcNamespace, pvcName) if err != nil { return nil, nil, fmt.Errorf("failed to get PVC: %w", err) diff --git a/e2e/rbd.go b/e2e/rbd.go index 2b0c14d01..e222bb291 100644 --- a/e2e/rbd.go +++ b/e2e/rbd.go @@ -211,6 +211,37 @@ func checkGetKeyError(err error, stdErr string) bool { return false } +// checkClusternameInMetadata check for cluster name metadata on RBD image. +// nolint:nilerr // intentionally returning nil on error in the retry loop. +func checkClusternameInMetadata(f *framework.Framework, ns, pool, image string) { + t := time.Duration(deployTimeout) * time.Minute + var ( + coName string + stdErr string + execErr error + ) + err := wait.PollImmediate(poll, t, func() (bool, error) { + coName, stdErr, execErr = execCommandInToolBoxPod(f, + fmt.Sprintf("rbd image-meta get %s --image=%s %s", rbdOptions(pool), image, clusterNameKey), + ns) + if execErr != nil || stdErr != "" { + e2elog.Logf("failed to get cluster name %s/%s %s: err=%v stdErr=%q", + rbdOptions(pool), image, clusterNameKey, execErr, stdErr) + + return false, nil + } + + return true, nil + }) + if err != nil { + e2elog.Failf("could not get cluster name %s/%s %s: %v", rbdOptions(pool), image, clusterNameKey, err) + } + coName = strings.TrimSuffix(coName, "\n") + if coName != defaultClusterName { + e2elog.Failf("expected coName %q got %q", defaultClusterName, coName) + } +} + var _ = Describe("RBD", func() { f := framework.NewDefaultFramework(rbdType) var c clientset.Interface @@ -290,6 +321,14 @@ var _ = Describe("RBD", func() { if !util.CheckKernelSupport(kernelRelease, nbdZeroIOtimeoutSupport) { nbdMapOptions = "nbd:debug-rbd=20,io-timeout=330" } + + // wait for cluster name update in deployment + containers := []string{"csi-rbdplugin", "csi-rbdplugin-controller"} + err = waitForContainersArgsUpdate(c, cephCSINamespace, rbdDeploymentName, + "clustername", defaultClusterName, containers, deployTimeout) + if err != nil { + e2elog.Failf("timeout waiting for deployment update %s/%s: %v", cephCSINamespace, rbdDeploymentName, err) + } }) AfterEach(func() { @@ -453,6 +492,8 @@ var _ = Describe("RBD", func() { e2elog.Failf("expected pvName %q got %q", pvcObj.Spec.VolumeName, pvName) } + checkClusternameInMetadata(f, rookNamespace, defaultRBDPool, imageList[0]) + err = deletePVCAndValidatePV(f.ClientSet, pvc, deployTimeout) if err != nil { e2elog.Failf("failed to delete pvc: %v", err) @@ -701,6 +742,7 @@ var _ = Describe("RBD", func() { e2elog.Failf("PV name found on %s/%s %s=%s: err=%v stdErr=%q", rbdOptions(defaultRBDPool), imageList[0], pvNameKey, pvName, err, stdErr) } + checkClusternameInMetadata(f, rookNamespace, defaultRBDPool, imageList[0]) err = deleteSnapshot(&snap, deployTimeout) if err != nil { @@ -715,33 +757,30 @@ var _ = Describe("RBD", func() { }) By("verify generic ephemeral volume support", func() { - // generic ephemeral volume support is supported from 1.21 - if k8sVersionGreaterEquals(f.ClientSet, 1, 21) { - // create application - app, err := loadApp(appEphemeralPath) - if err != nil { - e2elog.Failf("failed to load application: %v", err) - } - app.Namespace = f.UniqueName - err = createApp(f.ClientSet, app, deployTimeout) - if err != nil { - e2elog.Failf("failed to create application: %v", err) - } - // validate created backend rbd images - validateRBDImageCount(f, 1, defaultRBDPool) - validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType) - err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout) - if err != nil { - e2elog.Failf("failed to delete application: %v", err) - } - // validate created backend rbd images - validateRBDImageCount(f, 0, defaultRBDPool) - validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType) - // validate images in trash - err = waitToRemoveImagesFromTrash(f, defaultRBDPool, deployTimeout) - if err != nil { - e2elog.Failf("failed to validate rbd images in pool %s trash: %v", defaultRBDPool, err) - } + // create application + app, err := loadApp(appEphemeralPath) + if err != nil { + e2elog.Failf("failed to load application: %v", err) + } + app.Namespace = f.UniqueName + err = createApp(f.ClientSet, app, deployTimeout) + if err != nil { + e2elog.Failf("failed to create application: %v", err) + } + // validate created backend rbd images + validateRBDImageCount(f, 1, defaultRBDPool) + validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType) + err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete application: %v", err) + } + // validate created backend rbd images + validateRBDImageCount(f, 0, defaultRBDPool) + validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType) + // validate images in trash + err = waitToRemoveImagesFromTrash(f, defaultRBDPool, deployTimeout) + if err != nil { + e2elog.Failf("failed to validate rbd images in pool %s trash: %v", defaultRBDPool, err) } }) @@ -4041,6 +4080,153 @@ var _ = Describe("RBD", func() { }) }) + By("validate rbd image stripe", func() { + stripeUnit := 4096 + stripeCount := 8 + objectSize := 131072 + err := deleteResource(rbdExamplePath + "storageclass.yaml") + if err != nil { + e2elog.Failf("failed to delete storageclass: %v", err) + } + + err = createRBDStorageClass( + f.ClientSet, + f, + defaultSCName, + nil, + map[string]string{ + "stripeUnit": fmt.Sprintf("%d", stripeUnit), + "stripeCount": fmt.Sprintf("%d", stripeCount), + "objectSize": fmt.Sprintf("%d", objectSize), + }, + deletePolicy) + if err != nil { + e2elog.Failf("failed to create storageclass: %v", err) + } + defer func() { + err = deleteResource(rbdExamplePath + "storageclass.yaml") + if err != nil { + e2elog.Failf("failed to delete storageclass: %v", err) + } + err = createRBDStorageClass(f.ClientSet, f, defaultSCName, nil, nil, deletePolicy) + if err != nil { + e2elog.Failf("failed to create storageclass: %v", err) + } + }() + + err = createRBDSnapshotClass(f) + if err != nil { + e2elog.Failf("failed to create storageclass: %v", err) + } + defer func() { + err = deleteRBDSnapshotClass() + if err != nil { + e2elog.Failf("failed to delete VolumeSnapshotClass: %v", err) + } + }() + + // create PVC and bind it to an app + pvc, err := loadPVC(pvcPath) + if err != nil { + e2elog.Failf("failed to load PVC: %v", err) + } + + pvc.Namespace = f.UniqueName + + err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout) + if err != nil { + e2elog.Failf("failed to create PVC and application: %v", err) + } + // validate created backend rbd images + validateRBDImageCount(f, 1, defaultRBDPool) + validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType) + err = validateStripe(f, pvc, stripeUnit, stripeCount, objectSize) + if err != nil { + e2elog.Failf("failed to validate stripe: %v", err) + } + + snap := getSnapshot(snapshotPath) + snap.Namespace = f.UniqueName + snap.Spec.Source.PersistentVolumeClaimName = &pvc.Name + + err = createSnapshot(&snap, deployTimeout) + if err != nil { + e2elog.Failf("failed to create snapshot: %v", err) + } + // validate created backend rbd images + // parent PVC + snapshot + totalImages := 2 + validateRBDImageCount(f, totalImages, defaultRBDPool) + validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType) + validateOmapCount(f, 1, rbdType, defaultRBDPool, snapsType) + pvcClone, err := loadPVC(pvcClonePath) + if err != nil { + e2elog.Failf("failed to load PVC: %v", err) + } + + // create clone PVC as ROX + pvcClone.Namespace = f.UniqueName + pvcClone.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany} + err = createPVCAndvalidatePV(f.ClientSet, pvcClone, deployTimeout) + if err != nil { + e2elog.Failf("failed to create PVC: %v", err) + } + // validate created backend rbd images + // parent pvc + snapshot + clone + totalImages = 3 + validateRBDImageCount(f, totalImages, defaultRBDPool) + validateOmapCount(f, 2, rbdType, defaultRBDPool, volumesType) + validateOmapCount(f, 1, rbdType, defaultRBDPool, snapsType) + err = validateStripe(f, pvcClone, stripeUnit, stripeCount, objectSize) + if err != nil { + e2elog.Failf("failed to validate stripe for clone: %v", err) + } + // delete snapshot + err = deleteSnapshot(&snap, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete snapshot: %v", err) + } + // delete clone pvc + err = deletePVCAndValidatePV(f.ClientSet, pvcClone, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete PVC: %v", err) + } + + pvcSmartClone, err := loadPVC(pvcSmartClonePath) + if err != nil { + e2elog.Failf("failed to load pvcSmartClone: %v", err) + } + pvcSmartClone.Namespace = f.UniqueName + + err = createPVCAndvalidatePV(f.ClientSet, pvcSmartClone, deployTimeout) + if err != nil { + e2elog.Failf("failed to create pvc: %v", err) + } + // validate created backend rbd images + // parent pvc + temp clone + clone + totalImages = 3 + validateRBDImageCount(f, totalImages, defaultRBDPool) + validateOmapCount(f, 2, rbdType, defaultRBDPool, volumesType) + err = validateStripe(f, pvcSmartClone, stripeUnit, stripeCount, objectSize) + if err != nil { + e2elog.Failf("failed to validate stripe for clone: %v", err) + } + // delete parent pvc + err = deletePVCAndValidatePV(f.ClientSet, pvc, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete PVC: %v", err) + } + + // delete clone pvc + err = deletePVCAndValidatePV(f.ClientSet, pvcSmartClone, deployTimeout) + if err != nil { + e2elog.Failf("failed to delete PVC: %v", err) + } + // validate created backend rbd images + validateRBDImageCount(f, 0, defaultRBDPool) + validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType) + }) + // Make sure this should be last testcase in this file, because // it deletes pool By("Create a PVC and delete PVC when backend pool deleted", func() { diff --git a/e2e/rbd_helper.go b/e2e/rbd_helper.go index 8dbcfb5a2..ab1a11fc7 100644 --- a/e2e/rbd_helper.go +++ b/e2e/rbd_helper.go @@ -28,7 +28,7 @@ import ( "github.com/ceph/ceph-csi/internal/util" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" v1 "k8s.io/api/core/v1" scv1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -117,7 +117,8 @@ func createRBDStorageClass( f *framework.Framework, name string, scOptions, parameters map[string]string, - policy v1.PersistentVolumeReclaimPolicy) error { + policy v1.PersistentVolumeReclaimPolicy, +) error { scPath := fmt.Sprintf("%s/%s", rbdExamplePath, "storageclass.yaml") sc, err := getStorageClass(scPath) if err != nil { @@ -281,7 +282,7 @@ func getImageMeta(rbdImageSpec, metaKey string, f *framework.Framework) (string, return "", err } if stdErr != "" { - return strings.TrimSpace(stdOut), fmt.Errorf(stdErr) + return strings.TrimSpace(stdOut), fmt.Errorf("%s", stdErr) } return strings.TrimSpace(stdOut), nil @@ -757,7 +758,8 @@ func checkPVCImageInPool(f *framework.Framework, pvc *v1.PersistentVolumeClaim, func checkPVCDataPoolForImageInPool( f *framework.Framework, pvc *v1.PersistentVolumeClaim, - pool, dataPool string) error { + pool, dataPool string, +) error { stdOut, err := getPVCImageInfoInPool(f, pvc, pool) if err != nil { return err @@ -940,3 +942,69 @@ func waitToRemoveImagesFromTrash(f *framework.Framework, poolName string, t int) return err } + +// imageInfo strongly typed JSON spec for image info. +type imageInfo struct { + Name string `json:"name"` + StripeUnit int `json:"stripe_unit"` + StripeCount int `json:"stripe_count"` + ObjectSize int `json:"object_size"` +} + +// getImageInfo queries rbd about the given image and returns its metadata, and returns +// error if provided image is not found. +func getImageInfo(f *framework.Framework, imageName, poolName string) (imageInfo, error) { + // rbd --format=json info [image-spec | snap-spec] + var imgInfo imageInfo + + stdOut, stdErr, err := execCommandInToolBoxPod( + f, + fmt.Sprintf("rbd info %s %s --format json", rbdOptions(poolName), imageName), + rookNamespace) + if err != nil { + return imgInfo, fmt.Errorf("failed to get rbd info: %w", err) + } + if stdErr != "" { + return imgInfo, fmt.Errorf("failed to get rbd info: %v", stdErr) + } + err = json.Unmarshal([]byte(stdOut), &imgInfo) + if err != nil { + return imgInfo, fmt.Errorf("unmarshal failed: %w. raw buffer response: %s", + err, stdOut) + } + + return imgInfo, nil +} + +// validateStripe validate the stripe count, stripe unit and object size of the +// image. +func validateStripe(f *framework.Framework, + pvc *v1.PersistentVolumeClaim, + stripeUnit, + stripeCount, + objectSize int, +) error { + imageData, err := getImageInfoFromPVC(pvc.Namespace, pvc.Name, f) + if err != nil { + return err + } + + imgInfo, err := getImageInfo(f, imageData.imageName, defaultRBDPool) + if err != nil { + return err + } + + if imgInfo.ObjectSize != objectSize { + return fmt.Errorf("objectSize %d does not match expected %d", imgInfo.ObjectSize, objectSize) + } + + if imgInfo.StripeUnit != stripeUnit { + return fmt.Errorf("stripeUnit %d does not match expected %d", imgInfo.StripeUnit, stripeUnit) + } + + if imgInfo.StripeCount != stripeCount { + return fmt.Errorf("stripeCount %d does not match expected %d", imgInfo.StripeCount, stripeCount) + } + + return nil +} diff --git a/e2e/snapshot.go b/e2e/snapshot.go index 9e48e0f58..935142018 100644 --- a/e2e/snapshot.go +++ b/e2e/snapshot.go @@ -21,8 +21,8 @@ import ( "fmt" "time" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - snapclient "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + snapclient "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1" . "github.com/onsi/gomega" // nolint v1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" @@ -300,7 +300,8 @@ func validateBiggerPVCFromSnapshot(f *framework.Framework, appPath, snapPath, pvcClonePath, - appClonePath string) error { + appClonePath string, +) error { const ( size = "1Gi" newSize = "2Gi" diff --git a/e2e/staticpvc.go b/e2e/staticpvc.go index 391b44fa3..57771c8ec 100644 --- a/e2e/staticpvc.go +++ b/e2e/staticpvc.go @@ -41,7 +41,8 @@ const ( func getStaticPV( name, volName, size, secretName, secretNS, sc, driverName string, blockPV bool, - options, annotations map[string]string, policy v1.PersistentVolumeReclaimPolicy) *v1.PersistentVolume { + options, annotations map[string]string, policy v1.PersistentVolumeReclaimPolicy, +) *v1.PersistentVolume { pv := &v1.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -491,7 +492,8 @@ func validateRBDStaticResize( app *v1.Pod, appOpt *metav1.ListOptions, pvc *v1.PersistentVolumeClaim, - rbdImageName string) error { + rbdImageName string, +) error { // resize rbd image size := staticPVNewSize cmd := fmt.Sprintf( diff --git a/e2e/utils.go b/e2e/utils.go index fa204ad7e..be124960f 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -30,7 +30,7 @@ import ( "sync" "time" - snapapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" appsv1 "k8s.io/api/apps/v1" batch "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" @@ -62,14 +62,19 @@ const ( // deletePolicy is the default policy in E2E. deletePolicy = v1.PersistentVolumeReclaimDelete // Default key and label for Listoptions. - appKey = "app" - appLabel = "write-data-in-pod" + appKey = "app" + appLabel = "write-data-in-pod" + appCloneLabel = "app-clone" noError = "" // labels/selector used to list/delete rbd pods. rbdPodLabels = "app in (ceph-csi-rbd, csi-rbdplugin, csi-rbdplugin-provisioner)" exitOneErr = "command terminated with exit code 1" + + // cluster Name, set by user. + clusterNameKey = "csi.ceph.com/cluster/name" + defaultClusterName = "k8s-cluster-1" ) var ( @@ -341,7 +346,8 @@ func createPVCAndApp( f *framework.Framework, pvc *v1.PersistentVolumeClaim, app *v1.Pod, - pvcTimeout int) error { + pvcTimeout int, +) error { if name != "" { pvc.Name = name app.Name = name @@ -361,7 +367,8 @@ func createPVCAndDeploymentApp( f *framework.Framework, pvc *v1.PersistentVolumeClaim, app *appsv1.Deployment, - pvcTimeout int) error { + pvcTimeout int, +) error { err := createPVCAndvalidatePV(f.ClientSet, pvc, pvcTimeout) if err != nil { return err @@ -414,7 +421,8 @@ func validatePVCAndDeploymentAppBinding( func deletePVCAndDeploymentApp( f *framework.Framework, pvc *v1.PersistentVolumeClaim, - app *appsv1.Deployment) error { + app *appsv1.Deployment, +) error { err := deleteDeploymentApp(f.ClientSet, app.Name, app.Namespace, deployTimeout) if err != nil { return err @@ -445,7 +453,8 @@ func deletePVCAndApp(name string, f *framework.Framework, pvc *v1.PersistentVolu func createPVCAndAppBinding( pvcPath, appPath string, f *framework.Framework, - pvcTimeout int) (*v1.PersistentVolumeClaim, *v1.Pod, error) { + pvcTimeout int, +) (*v1.PersistentVolumeClaim, *v1.Pod, error) { pvc, err := loadPVC(pvcPath) if err != nil { return nil, nil, err @@ -486,7 +495,7 @@ func getMountType(selector, mountPath string, f *framework.Framework) (string, e return "", err } if stdErr != "" { - return strings.TrimSpace(stdOut), fmt.Errorf(stdErr) + return strings.TrimSpace(stdOut), fmt.Errorf("%s", stdErr) } return strings.TrimSpace(stdOut), nil @@ -802,7 +811,8 @@ func validatePVCClone( dataPool string, kms kmsConfig, validatePVC validateFunc, - f *framework.Framework) { + f *framework.Framework, +) { var wg sync.WaitGroup wgErrs := make([]error, totalCount) chErrs := make([]error, totalCount) @@ -1013,7 +1023,8 @@ func validatePVCSnapshot( totalCount int, pvcPath, appPath, snapshotPath, pvcClonePath, appClonePath string, kms, restoreKMS kmsConfig, restoreSCName, - dataPool string, f *framework.Framework) { + dataPool string, f *framework.Framework, +) { var wg sync.WaitGroup wgErrs := make([]error, totalCount) chErrs := make([]error, totalCount) @@ -1358,7 +1369,8 @@ func validatePVCSnapshot( func validateController( f *framework.Framework, pvcPath, appPath, scPath string, - scOptions, scParams map[string]string) error { + scOptions, scParams map[string]string, +) error { size := "1Gi" poolName := defaultRBDPool expandSize := "10Gi" diff --git a/examples/cephfs/storageclass.yaml b/examples/cephfs/storageclass.yaml index 42fa35536..b23fa0213 100644 --- a/examples/cephfs/storageclass.yaml +++ b/examples/cephfs/storageclass.yaml @@ -47,6 +47,11 @@ parameters: # If omitted, defaults to "csi-vol-". # volumeNamePrefix: "foo-bar-" + # (optional) Boolean value. The PVC shall be backed by the CephFS snapshot + # specified in its data source. `pool` parameter must not be specified. + # (defaults to `false`) + # backingSnapshot: "true" + reclaimPolicy: Delete allowVolumeExpansion: true mountOptions: diff --git a/examples/rbd/storageclass.yaml b/examples/rbd/storageclass.yaml index 94c9413e1..a30114667 100644 --- a/examples/rbd/storageclass.yaml +++ b/examples/rbd/storageclass.yaml @@ -134,6 +134,14 @@ parameters: # {"domainLabel":"zone","value":"zone1"}]} # ] + # Image striping, Refer https://docs.ceph.com/en/latest/man/8/rbd/#striping + # For more details + # (optional) stripe unit in bytes. + # stripeUnit: <> + # (optional) objects to stripe over before looping. + # stripeCount: <> + # (optional) The object size in bytes. + # objectSize: <> reclaimPolicy: Delete allowVolumeExpansion: true mountOptions: diff --git a/go.mod b/go.mod index ddf87273f..d3f1d329f 100644 --- a/go.mod +++ b/go.mod @@ -4,40 +4,40 @@ go 1.17 require ( github.com/IBM/keyprotect-go-client v0.7.0 - github.com/aws/aws-sdk-go v1.44.20 - github.com/aws/aws-sdk-go-v2/service/sts v1.16.5 + github.com/aws/aws-sdk-go v1.44.28 + github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000 - // TODO: API for managing NFS-exports requires `ceph_ci_untested` build-tag - github.com/ceph/go-ceph v0.15.0 + // TODO: API for managing subvolume metadata and snapshot metadata requires `ceph_ci_untested` build-tag + github.com/ceph/go-ceph v0.16.0 github.com/container-storage-interface/spec v1.6.0 github.com/csi-addons/replication-lib-utils v0.2.0 github.com/csi-addons/spec v0.1.2-0.20211220115741-32fa508dadbe github.com/golang/protobuf v1.5.2 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/vault/api v1.5.0 + github.com/hashicorp/vault/api v1.6.0 github.com/kubernetes-csi/csi-lib-utils v0.11.0 - github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 + github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0 github.com/libopenstorage/secrets v0.0.0-20210908194121-a1d19aa9713a github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.19.0 github.com/pborman/uuid v1.2.1 github.com/prometheus/client_golang v1.12.2 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.7.2 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 golang.org/x/sys v0.0.0-20220209214540-3681064d5158 - google.golang.org/grpc v1.46.2 + google.golang.org/grpc v1.47.0 google.golang.org/protobuf v1.28.0 - k8s.io/api v0.24.0 - k8s.io/apimachinery v0.24.0 + k8s.io/api v0.24.1 + k8s.io/apimachinery v0.24.1 k8s.io/client-go v12.0.0+incompatible - k8s.io/cloud-provider v0.24.0 + k8s.io/cloud-provider v0.24.1 k8s.io/klog/v2 v2.60.1 // // when updating k8s.io/kubernetes, make sure to update the replace section too // - k8s.io/kubernetes v1.24.0 - k8s.io/mount-utils v0.24.0 + k8s.io/kubernetes v1.24.1 + k8s.io/mount-utils v0.24.1 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 sigs.k8s.io/controller-runtime v0.11.0-beta.0.0.20211208212546-f236f0345ad2 ) @@ -47,11 +47,11 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/armon/go-metrics v0.3.9 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.16.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 // indirect - github.com/aws/smithy-go v1.11.2 // indirect + github.com/aws/aws-sdk-go-v2 v1.16.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.6 // indirect + github.com/aws/smithy-go v1.11.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect @@ -73,7 +73,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -86,15 +86,15 @@ require ( github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-uuid v1.0.2 // indirect github.com/hashicorp/go-version v1.2.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault v1.4.2 // indirect - github.com/hashicorp/vault/sdk v0.4.1 // indirect + github.com/hashicorp/vault/sdk v0.5.0 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -108,7 +108,7 @@ require ( github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -151,11 +151,11 @@ require ( gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect - k8s.io/apiserver v0.24.0 // indirect - k8s.io/component-base v0.24.0 // indirect - k8s.io/component-helpers v0.24.0 // indirect + k8s.io/apiserver v0.24.1 // indirect + k8s.io/component-base v0.24.1 // indirect + k8s.io/component-helpers v0.24.1 // indirect k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect k8s.io/kubectl v0.0.0 // indirect k8s.io/kubelet v0.0.0 // indirect @@ -175,31 +175,31 @@ replace ( // // k8s.io/kubernetes depends on these k8s.io packages, but unversioned // - k8s.io/api => k8s.io/api v0.24.0 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.0 - k8s.io/apimachinery => k8s.io/apimachinery v0.24.0 - k8s.io/apiserver => k8s.io/apiserver v0.24.0 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.0 - k8s.io/client-go => k8s.io/client-go v0.24.0 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.0 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.0 - k8s.io/code-generator => k8s.io/code-generator v0.24.0 - k8s.io/component-base => k8s.io/component-base v0.24.0 - k8s.io/component-helpers => k8s.io/component-helpers v0.24.0 - k8s.io/controller-manager => k8s.io/controller-manager v0.24.0 - k8s.io/cri-api => k8s.io/cri-api v0.24.0 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.0 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.0 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.0 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.0 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.0 - k8s.io/kubectl => k8s.io/kubectl v0.24.0 - k8s.io/kubelet => k8s.io/kubelet v0.24.0 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.0 - k8s.io/metrics => k8s.io/metrics v0.24.0 - k8s.io/mount-utils => k8s.io/mount-utils v0.24.0 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.0 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.0 + k8s.io/api => k8s.io/api v0.24.1 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.1 + k8s.io/apimachinery => k8s.io/apimachinery v0.24.1 + k8s.io/apiserver => k8s.io/apiserver v0.24.1 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.1 + k8s.io/client-go => k8s.io/client-go v0.24.1 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.1 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.1 + k8s.io/code-generator => k8s.io/code-generator v0.24.1 + k8s.io/component-base => k8s.io/component-base v0.24.1 + k8s.io/component-helpers => k8s.io/component-helpers v0.24.1 + k8s.io/controller-manager => k8s.io/controller-manager v0.24.1 + k8s.io/cri-api => k8s.io/cri-api v0.24.1 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.1 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.1 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.1 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.1 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.1 + k8s.io/kubectl => k8s.io/kubectl v0.24.1 + k8s.io/kubelet => k8s.io/kubelet v0.24.1 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.1 + k8s.io/metrics => k8s.io/metrics v0.24.1 + k8s.io/mount-utils => k8s.io/mount-utils v0.24.1 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.1 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.1 // layeh.com seems to be misbehaving layeh.com/radius => github.com/layeh/radius v0.0.0-20190322222518-890bc1058917 ) diff --git a/go.sum b/go.sum index 522e9e063..97bacec2d 100644 --- a/go.sum +++ b/go.sum @@ -141,20 +141,20 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.20 h1:nllTRN24EfhDSeKsNbIc6HoC8Ogd2NCJTRB8l84kDlM= -github.com/aws/aws-sdk-go v1.44.20/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.16.3 h1:0W1TSJ7O6OzwuEvIXAtJGvOeQ0SGAhcpxPN2/NK5EhM= -github.com/aws/aws-sdk-go-v2 v1.16.3/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 h1:uFWgo6mGJI1n17nbcvSc6fxVuR3xLNqvXt12JCnEcT8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 h1:cnsvEKSoHN4oAN7spMMr0zhEW2MHnhAVpmqQg8E6UcM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4/go.mod h1:8glyUqVIM4AmeenIsPo0oVh3+NUwnsQml2OFupfQW+0= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 h1:b16QW0XWl0jWjLABFc1A+uh145Oqv+xDcObNk0iQgUk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4/go.mod h1:uKkN7qmSIsNJVyMtxNQoCEYMvFEXbOg9fwCJPdfp2u8= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.5 h1:aEMP+STBsvCY1U/yL4pAqUrU4oFli46AdbCXvVbTiHI= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.5/go.mod h1:lfSYenAXtavyX2A1LsViglqlG9eEFYxNryTZS5rn3QE= -github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE= -github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/aws-sdk-go v1.44.28 h1:h/OAqEqY18wq//v6h4GNPMmCkxuzSDrWuGyrvSiRqf4= +github.com/aws/aws-sdk-go v1.44.28/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v1.16.5 h1:Ah9h1TZD9E2S1LzHpViBO3Jz9FPL5+rmflmb8hXirtI= +github.com/aws/aws-sdk-go-v2 v1.16.5/go.mod h1:Wh7MEsmEApyL5hrWzpDkba4gwAPc5/piwLVLFnCxp48= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 h1:Zt7DDk5V7SyQULUUwIKzsROtVzp/kVvcz15uQx/Tkow= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12/go.mod h1:Afj/U8svX6sJ77Q+FPWMzabJ9QjbwP32YlopgKALUpg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 h1:eeXdGVtXEe+2Jc49+/vAzna3FAQnUD4AagAw8tzbmfc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.6 h1:0ZxYAZ1cn7Swi/US55VKciCE6RhRHIwCKIWaMLdT6pg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.6/go.mod h1:DxAPjquoEHf3rUHh1b9+47RAaXB8/7cB6jkzCt/GOEI= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 h1:HLzjwQM9975FQWSF3uENDGHT1gFQm/q3QXu2BYIcI08= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.7/go.mod h1:lVxTdiiSHY3jb1aeg+BBFtDzZGSUCv6qaNOyEGCJ1AY= +github.com/aws/smithy-go v1.11.3 h1:DQixirEFM9IaKxX1olZ3ke3nvxRS2xMDteKIDWxozW8= +github.com/aws/smithy-go v1.11.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -182,8 +182,8 @@ github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3 github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/centrify/cloud-golang-sdk v0.0.0-20190214225812-119110094d0f/go.mod h1:C0rtzmGXgN78pYR0tGJFhtHgkbAs0lIbHwkB81VxDQE= -github.com/ceph/go-ceph v0.15.0 h1:ILB3NaLWOtt4u/2d8I8HZTC4Ycm1PsOYVar3IFU1xlo= -github.com/ceph/go-ceph v0.15.0/go.mod h1:mafFpf5Vg8Ai8Bd+FAMvKBHLmtdpTXdRP/TNq8XWegY= +github.com/ceph/go-ceph v0.16.0 h1:hEhVfEFsLoGJF+i3r7Wwh4QlMN+MnWqNxfic9v6GV04= +github.com/ceph/go-ceph v0.16.0/go.mod h1:SzhpLdyU+ixxJ68bbqoEa481P5N5d5lv5jVMxcRMLfU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -404,9 +404,9 @@ github.com/gocql/gocql v0.0.0-20190402132108-0e1d5de854df/go.mod h1:4Fw1eo5iaEhD github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -465,8 +465,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-metrics-stackdriver v0.2.0/go.mod h1:KLcPyp3dWJAFD+yHisGlJSZktIsTjb50eB72U2YZ9K0= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -592,12 +592,14 @@ github.com/hashicorp/go-secure-stdlib/base62 v0.1.1 h1:6KMBnfEv0/kLAz0O76sliN5mX github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 h1:78ki3QBevHwYrVxnyVeaEz+7WtifHhauYF23es/0KlI= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 h1:MBgwAFPUbfuI0+tmDU/aeM1MARvdbqWmiieXIalKqDE= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/password v0.1.1 h1:6JzmBqXprakgFEHwBgdchsjaA9x3GyjdI568bXKxa60= github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 h1:nd0HIW15E6FG1MsnArYaHfuw9C2zgzM8LxkG5Ty/788= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1 h1:Yc026VyMyIpq1UWRnakHRG01U8fJm+nEfEmjoAb00n8= github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -662,8 +664,8 @@ github.com/hashicorp/vault/api v1.0.5-0.20191122173911-80fcc7907c78/go.mod h1:Uf github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o= github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o= github.com/hashicorp/vault/api v1.0.5-0.20200902155336-f9d5ce5a171a/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= -github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28= -github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= +github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4= +github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU= github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU= @@ -673,8 +675,8 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02/go.mod h1:W github.com/hashicorp/vault/sdk v0.1.14-0.20200427170607-03332aaf8d18/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= github.com/hashicorp/vault/sdk v0.1.14-0.20200429182704-29fce8f27ce4/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= -github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= -github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= +github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= +github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -760,8 +762,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/csi-lib-utils v0.11.0 h1:FHWOBtAZBA/hVk7v/qaXgG9Sxv0/n06DebPFuDwumqg= github.com/kubernetes-csi/csi-lib-utils v0.11.0/go.mod h1:BmGZZB16L18+9+Lgg9YWwBKfNEHIDdgGfAyuW6p2NV0= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= -github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= -github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= +github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0 h1:05QzRsnbMQ1Ymg/7iJj1k6RVw+rgelB60Ud5j4GgmGM= +github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0/go.mod h1:tnHiLn3P10N95fjn7O40QH5ovN0EFGAxqdTpUMrX6bU= github.com/layeh/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLgPWsvc0L26IUpVvE/aeIL2JtIQ= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -825,8 +827,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v0.0.0-20190430161007-f252a8fd71c8/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= @@ -1080,8 +1082,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -1110,6 +1112,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1303,6 +1306,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1394,7 +1398,6 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1426,6 +1429,7 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1529,11 +1533,11 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= @@ -1665,8 +1669,8 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= @@ -1723,8 +1727,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= @@ -1737,28 +1742,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg= -k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= -k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY= -k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM= -k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= -k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apiserver v0.24.0 h1:GR7kGsjOMfilRvlG3Stxv/3uz/ryvJ/aZXc5pqdsNV0= -k8s.io/apiserver v0.24.0/go.mod h1:WFx2yiOMawnogNToVvUYT9nn1jaIkMKj41ZYCVycsBA= -k8s.io/cli-runtime v0.24.0/go.mod h1:9XxoZDsEkRFUThnwqNviqzljtT/LdHtNWvcNFrAXl0A= -k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U= -k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= -k8s.io/cloud-provider v0.24.0 h1:kQ6zB2oy0VDl+6vdRAKEbtwDM1MmuhNCyA/v+Fk2g30= -k8s.io/cloud-provider v0.24.0/go.mod h1:cqkEWJWzToaqtS5ti8KQJQcL2IWssWGXHzicxZyaC6s= -k8s.io/cluster-bootstrap v0.24.0/go.mod h1:xw+IfoaUweMCAoi+VYhmqkcjii2G7gNg59dmGn7hi0g= -k8s.io/code-generator v0.24.0/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= -k8s.io/component-base v0.24.0 h1:h5jieHZQoHrY/lHG+HyrSbJeyfuitheBvqvKwKHVC0g= -k8s.io/component-base v0.24.0/go.mod h1:Dgazgon0i7KYUsS8krG8muGiMVtUZxG037l1MKyXgrA= -k8s.io/component-helpers v0.24.0 h1:hZIHGfdd55thhqd9oxjDTw68OAPauDMJ+8hC69aNw1I= -k8s.io/component-helpers v0.24.0/go.mod h1:Q2SlLm4h6g6lPTC9GMMfzdywfLSvJT2f1hOnnjaWD8c= -k8s.io/controller-manager v0.24.0/go.mod h1:ageMNQZc7cNH0FF1oarm7wZs6XyJj/V82nNVmgPaeDU= -k8s.io/cri-api v0.24.0/go.mod h1:t3tImFtGeStN+ES69bQUX9sFg67ek38BM9YIJhMmuig= -k8s.io/csi-translation-lib v0.24.0/go.mod h1:jJaC3a1tI3IShByiAQmOOCl5PKpiZ51Vh70c9Eg2msM= +k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= +k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= +k8s.io/apiextensions-apiserver v0.24.1 h1:5yBh9+ueTq/kfnHQZa0MAo6uNcPrtxPMpNQgorBaKS0= +k8s.io/apiextensions-apiserver v0.24.1/go.mod h1:A6MHfaLDGfjOc/We2nM7uewD5Oa/FnEbZ6cD7g2ca4Q= +k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= +k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apiserver v0.24.1 h1:LAA5UpPOeaREEtFAQRUQOI3eE5So/j5J3zeQJjeLdz4= +k8s.io/apiserver v0.24.1/go.mod h1:dQWNMx15S8NqJMp0gpYfssyvhYnkilc1LpExd/dkLh0= +k8s.io/cli-runtime v0.24.1/go.mod h1:14aVvCTqkA7dNXY51N/6hRY3GUjchyWDOwW84qmR3bs= +k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= +k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8= +k8s.io/cloud-provider v0.24.1 h1:SaQNq2Ax+epdY9wFngwN9GWpOVnM72hUqr2qy20cOvg= +k8s.io/cloud-provider v0.24.1/go.mod h1:h5m/KIiwiQ76hpUBsgrwm/rxteIfJG9kJQ/+/w1as2M= +k8s.io/cluster-bootstrap v0.24.1/go.mod h1:uq2PiYfKh8ZLb6DBU/3/2Z1DkMqXkTOHLemalC4tOgE= +k8s.io/code-generator v0.24.1/go.mod h1:dpVhs00hTuTdTY6jvVxvTFCk6gSMrtfRydbhZwHI15w= +k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts= +k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38= +k8s.io/component-helpers v0.24.1 h1:pk68RSRhkGX75nhtAkilguKbq/0MyXbQqmrZoQu4nbs= +k8s.io/component-helpers v0.24.1/go.mod h1:q5Z1pWV/QfX9ThuNeywxasiwkLw9KsR4Q9TAOdb/Y3s= +k8s.io/controller-manager v0.24.1/go.mod h1:g105ENexD6A2holEq7Bl6ae+69LJHiLnoEEm7wkE6sc= +k8s.io/cri-api v0.24.1/go.mod h1:t3tImFtGeStN+ES69bQUX9sFg67ek38BM9YIJhMmuig= +k8s.io/csi-translation-lib v0.24.1/go.mod h1:16nY6xx3XR4+TASMfTtake2ouK1IPz0t/baNmngzR4I= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1772,28 +1777,28 @@ k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-aggregator v0.24.0/go.mod h1:ftfs6Fi46z3cKzeF2kvNBPLbMlSKuqZbesJGNp/cQnw= -k8s.io/kube-controller-manager v0.24.0/go.mod h1:s0pbwI8UuBEDdXQbTUpQdNIyU4rQ7jOxaXAcRBoWpJQ= +k8s.io/kube-aggregator v0.24.1/go.mod h1:vZvRALCO32hrIuREhkYwLq5Crc0zh6SxzJDAKrQM1+k= +k8s.io/kube-controller-manager v0.24.1/go.mod h1:IlXY8FozezzIBNcfA6TV1//fjz9gNy3LGbigDnX7Q3A= k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= -k8s.io/kube-proxy v0.24.0/go.mod h1:OZ1k9jSwW94Rmj5hepCFea7qlGvvU+bfcosc6+dcFKA= -k8s.io/kube-scheduler v0.24.0/go.mod h1:DUq+fXaC51N1kl2YnT2EZSxOph6JOmIJe/pQe5keZPc= -k8s.io/kubectl v0.24.0 h1:nA+WtMLVdXUs4wLogGd1mPTAesnLdBpCVgCmz3I7dXo= -k8s.io/kubectl v0.24.0/go.mod h1:pdXkmCyHiRTqjYfyUJiXtbVNURhv0/Q1TyRhy2d5ic0= -k8s.io/kubelet v0.24.0 h1:fH+D6mSr4DGIeHp/O2+mCEJhkVq3Gpgv9BVOHI+GrWY= -k8s.io/kubelet v0.24.0/go.mod h1:p3BBacmHTCMpUf+nluhlyzuGHmONKAspqCvpu9oPAyA= -k8s.io/kubernetes v1.24.0 h1:9qRjlCuMjooyFTXLxduMBT+MZSdROWa3idI1AXZirVs= -k8s.io/kubernetes v1.24.0/go.mod h1:8e8maMiZzBR2/8Po5Uulx+MXZUYJuN3vtKwD4Ct1Xi0= -k8s.io/legacy-cloud-providers v0.24.0/go.mod h1:j2gujMUYBEtbYfJaL8JUOgInzERm9fxJwEaOkZcnEUk= -k8s.io/metrics v0.24.0/go.mod h1:jrLlFGdKl3X+szubOXPG0Lf2aVxuV3QJcbsgVRAM6fI= -k8s.io/mount-utils v0.24.0 h1:1SCkAY99QUchRa00HkLcm0HXajy8xlWHvue4wYdvBVU= -k8s.io/mount-utils v0.24.0/go.mod h1:XrSqB3a2e8sq+aU+rlbcBtQ3EgcuDk5RP9ZsGxjoDrI= -k8s.io/pod-security-admission v0.24.0 h1:nTZtZPdJ5ZusFyuxGZxfGxQ5piuhJyxuG5YmVUWG/Gs= -k8s.io/pod-security-admission v0.24.0/go.mod h1:YBS4mAdoba2qMvLPE3S7eMIxGlqUf4amHH26jUUqXX4= -k8s.io/sample-apiserver v0.24.0/go.mod h1:6YGSatoHMHIac/2dTtARwYH8PVWY5qq1L9ZYbxZ9lHY= +k8s.io/kube-proxy v0.24.1/go.mod h1:Q19uL+muS7Q0rxIXlddcanbGcogbDcX5I86GROhrwOM= +k8s.io/kube-scheduler v0.24.1/go.mod h1:mxSsC5sg710qdrN9oY+OSkHRSgYOv6qA2vEEt1t6Ax4= +k8s.io/kubectl v0.24.1 h1:gxcjHrnwntV1c+G/BHWVv4Mtk8CQJ0WTraElLBG+ddk= +k8s.io/kubectl v0.24.1/go.mod h1:NzFqQ50B004fHYWOfhHTrAm4TY6oGF5FAAL13LEaeUI= +k8s.io/kubelet v0.24.1 h1:CLgXZ9kKDQoNQFSwKk6vUE5gXNaX1/s8VM8Oq/P5S+o= +k8s.io/kubelet v0.24.1/go.mod h1:LShXfjNO1or7ktsorODSOu8+Kd5dHzWF3DtVLXeP3JE= +k8s.io/kubernetes v1.24.1 h1:cfRZCNrJN9hR49SBSGLHhn+IdAcfx6OVXadGvWuvYaM= +k8s.io/kubernetes v1.24.1/go.mod h1:8e8maMiZzBR2/8Po5Uulx+MXZUYJuN3vtKwD4Ct1Xi0= +k8s.io/legacy-cloud-providers v0.24.1/go.mod h1:OeDg+OJ5uzmJQyh6vpCkwGY8tVegaiokWErGr7YlSaI= +k8s.io/metrics v0.24.1/go.mod h1:vMs5xpcOyY9D+/XVwlaw8oUHYCo6JTGBCZfyXOOkAhE= +k8s.io/mount-utils v0.24.1 h1:juKCvkiP4sWklb72OIk/qW7UhDns41ldcR/EHu/T1uA= +k8s.io/mount-utils v0.24.1/go.mod h1:XrSqB3a2e8sq+aU+rlbcBtQ3EgcuDk5RP9ZsGxjoDrI= +k8s.io/pod-security-admission v0.24.1 h1:CNcUKc06PgejhdvK1rqBgo5xcpirsl3O574cfKt4hxk= +k8s.io/pod-security-admission v0.24.1/go.mod h1:ZH6e17BuFFdiYHFxn9X6d7iaPj3JyuqBOw/MRytVWp8= +k8s.io/sample-apiserver v0.24.1/go.mod h1:5L12FaHPjpJzr0s/ClAx61Ig5uBjDCvthtmTIORu7F8= k8s.io/system-validators v1.7.0/go.mod h1:gP1Ky+R9wtrSiFbrpEPwWMeYz9yqyy1S/KOh0Vci7WI= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -1823,6 +1828,7 @@ sigs.k8s.io/kustomize/cmd/config v0.10.6/go.mod h1:/S4A4nUANUa4bZJ/Edt7ZQTyKOY9W sigs.k8s.io/kustomize/kustomize/v4 v4.5.4/go.mod h1:Zo/Xc5FKD6sHl0lilbrieeGeZHVYCA4BzxeAaLI05Bg= sigs.k8s.io/kustomize/kyaml v0.13.6/go.mod h1:yHP031rn1QX1lr/Xd934Ri/xdVNG8BE2ECa78Ht/kEg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= diff --git a/internal/cephfs/controllerserver.go b/internal/cephfs/controllerserver.go index d40544d1f..f638f3ef6 100644 --- a/internal/cephfs/controllerserver.go +++ b/internal/cephfs/controllerserver.go @@ -29,6 +29,7 @@ import ( "github.com/ceph/ceph-csi/internal/util" "github.com/ceph/ceph-csi/internal/util/k8s" "github.com/ceph/ceph-csi/internal/util/log" + rterrors "github.com/ceph/ceph-csi/internal/util/reftracker/errors" "github.com/container-storage-interface/spec/lib/go/csi" "github.com/golang/protobuf/ptypes/timestamp" @@ -59,48 +60,19 @@ func (cs *ControllerServer) createBackingVolume( volOptions, parentVolOpt *store.VolumeOptions, pvID *store.VolumeIdentifier, - sID *store.SnapshotIdentifier) error { + sID *store.SnapshotIdentifier, +) error { var err error volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) if sID != nil { - if err = cs.OperationLocks.GetRestoreLock(sID.SnapshotID); err != nil { - log.ErrorLog(ctx, err.Error()) - - return status.Error(codes.Aborted, err.Error()) - } - defer cs.OperationLocks.ReleaseRestoreLock(sID.SnapshotID) - snap := core.Snapshot{ - SnapshotID: sID.FsSnapshotName, - SubVolume: &parentVolOpt.SubVolume, - } - - err = volClient.CreateCloneFromSnapshot(ctx, snap) - if err != nil { - log.ErrorLog(ctx, "failed to create clone from snapshot %s: %v", sID.FsSnapshotName, err) - - return err - } - - return err + return cs.createBackingVolumeFromSnapshotSource(ctx, volOptions, parentVolOpt, volClient, sID) } + if parentVolOpt != nil { - if err = cs.OperationLocks.GetCloneLock(pvID.VolumeID); err != nil { - log.ErrorLog(ctx, err.Error()) - - return status.Error(codes.Aborted, err.Error()) - } - defer cs.OperationLocks.ReleaseCloneLock(pvID.VolumeID) - err = volClient.CreateCloneFromSubvolume( - ctx, &parentVolOpt.SubVolume) - if err != nil { - log.ErrorLog(ctx, "failed to create clone from subvolume %s: %v", fsutil.VolumeID(pvID.FsSubvolName), err) - - return err - } - - return nil + return cs.createBackingVolumeFromVolumeSource(ctx, parentVolOpt, volClient, pvID) } + if err = volClient.CreateVolume(ctx); err != nil { log.ErrorLog(ctx, "failed to create volume %s: %v", volOptions.RequestName, err) @@ -110,10 +82,71 @@ func (cs *ControllerServer) createBackingVolume( return nil } +func (cs *ControllerServer) createBackingVolumeFromSnapshotSource( + ctx context.Context, + volOptions *store.VolumeOptions, + parentVolOpt *store.VolumeOptions, + volClient core.SubVolumeClient, + sID *store.SnapshotIdentifier, +) error { + if err := cs.OperationLocks.GetRestoreLock(sID.SnapshotID); err != nil { + log.ErrorLog(ctx, err.Error()) + + return status.Error(codes.Aborted, err.Error()) + } + defer cs.OperationLocks.ReleaseRestoreLock(sID.SnapshotID) + + if volOptions.BackingSnapshot { + if err := store.AddSnapshotBackedVolumeRef(ctx, volOptions); err != nil { + log.ErrorLog(ctx, "failed to create snapshot-backed volume from snapshot %s: %v", + sID.FsSnapshotName, err) + + return err + } + + return nil + } + + err := volClient.CreateCloneFromSnapshot(ctx, core.Snapshot{ + SnapshotID: sID.FsSnapshotName, + SubVolume: &parentVolOpt.SubVolume, + }) + if err != nil { + log.ErrorLog(ctx, "failed to create clone from snapshot %s: %v", sID.FsSnapshotName, err) + + return err + } + + return nil +} + +func (cs *ControllerServer) createBackingVolumeFromVolumeSource( + ctx context.Context, + parentVolOpt *store.VolumeOptions, + volClient core.SubVolumeClient, + pvID *store.VolumeIdentifier, +) error { + if err := cs.OperationLocks.GetCloneLock(pvID.VolumeID); err != nil { + log.ErrorLog(ctx, err.Error()) + + return status.Error(codes.Aborted, err.Error()) + } + defer cs.OperationLocks.ReleaseCloneLock(pvID.VolumeID) + + if err := volClient.CreateCloneFromSubvolume(ctx, &parentVolOpt.SubVolume); err != nil { + log.ErrorLog(ctx, "failed to create clone from subvolume %s: %v", fsutil.VolumeID(pvID.FsSubvolName), err) + + return err + } + + return nil +} + func checkContentSource( ctx context.Context, req *csi.CreateVolumeRequest, - cr *util.Credentials) (*store.VolumeOptions, *store.VolumeIdentifier, *store.SnapshotIdentifier, error) { + cr *util.Credentials, +) (*store.VolumeOptions, *store.VolumeIdentifier, *store.SnapshotIdentifier, error) { if req.VolumeContentSource == nil { return nil, nil, nil, nil } @@ -155,7 +188,9 @@ func checkValidCreateVolumeRequest( vol, parentVol *store.VolumeOptions, pvID *store.VolumeIdentifier, - sID *store.SnapshotIdentifier) error { + sID *store.SnapshotIdentifier, + req *csi.CreateVolumeRequest, +) error { switch { case pvID != nil: if vol.Size < parentVol.Size { @@ -165,6 +200,10 @@ func checkValidCreateVolumeRequest( parentVol.Size, vol.Size) } + + if vol.BackingSnapshot { + return errors.New("cloning snapshot-backed volumes is currently not supported") + } case sID != nil: if vol.Size < parentVol.Size { return fmt.Errorf( @@ -173,6 +212,25 @@ func checkValidCreateVolumeRequest( parentVol.Size, vol.Size) } + + if vol.BackingSnapshot { + if vol.Size != parentVol.Size { + return fmt.Errorf( + "cannot create snapshot-backed volume of different size: expected %d bytes, got %d bytes", + parentVol.Size, + vol.Size, + ) + } + + volCaps := req.GetVolumeCapabilities() + for _, volCap := range volCaps { + mode := volCap.AccessMode.Mode + if mode != csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY && + mode != csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY { + return errors.New("backingSnapshot may be used only with read-only access modes") + } + } + } } return nil @@ -182,7 +240,8 @@ func checkValidCreateVolumeRequest( // nolint:gocognit,gocyclo,nestif,cyclop // TODO: reduce complexity func (cs *ControllerServer) CreateVolume( ctx context.Context, - req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { + req *csi.CreateVolumeRequest, +) (*csi.CreateVolumeResponse, error) { if err := cs.validateCreateVolumeRequest(req); err != nil { log.ErrorLog(ctx, "CreateVolumeRequest validation failed: %v", err) @@ -229,7 +288,7 @@ func (cs *ControllerServer) CreateVolume( defer parentVol.Destroy() } - err = checkValidCreateVolumeRequest(volOptions, parentVol, pvID, sID) + err = checkValidCreateVolumeRequest(volOptions, parentVol, pvID, sID, req) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -245,7 +304,7 @@ func (cs *ControllerServer) CreateVolume( // TODO return error message if requested vol size greater than found volume return error if vID != nil { - if sID != nil || pvID != nil { + if sID != nil || pvID != nil && !volOptions.BackingSnapshot { volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) err = volClient.ExpandVolume(ctx, volOptions.Size) if err != nil { @@ -279,12 +338,11 @@ func (cs *ControllerServer) CreateVolume( VolumeContext: volumeContext, } if volOptions.Topology != nil { - volume.AccessibleTopology = - []*csi.Topology{ - { - Segments: volOptions.Topology, - }, - } + volume.AccessibleTopology = []*csi.Topology{ + { + Segments: volOptions.Topology, + }, + } } return &csi.CreateVolumeResponse{Volume: volume}, nil @@ -318,26 +376,32 @@ func (cs *ControllerServer) CreateVolume( return nil, err } - volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) - volOptions.RootPath, err = volClient.GetVolumeRootPathCeph(ctx) - if err != nil { - purgeErr := volClient.PurgeVolume(ctx, true) - if purgeErr != nil { - log.ErrorLog(ctx, "failed to delete volume %s: %v", vID.FsSubvolName, purgeErr) - // All errors other than ErrVolumeNotFound should return an error back to the caller - if !errors.Is(purgeErr, cerrors.ErrVolumeNotFound) { - // If the subvolume deletion is failed, we should not cleanup - // the OMAP entry it will stale subvolume in cluster. - // set err=nil so that when we get the request again we can get - // the subvolume info. - err = nil + if !volOptions.BackingSnapshot { + // Get root path for the created subvolume. + // Note that root path for snapshot-backed volumes has been already set when + // building VolumeOptions. - return nil, status.Error(codes.Internal, purgeErr.Error()) + volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) + volOptions.RootPath, err = volClient.GetVolumeRootPathCeph(ctx) + if err != nil { + purgeErr := volClient.PurgeVolume(ctx, true) + if purgeErr != nil { + log.ErrorLog(ctx, "failed to delete volume %s: %v", vID.FsSubvolName, purgeErr) + // All errors other than ErrVolumeNotFound should return an error back to the caller + if !errors.Is(purgeErr, cerrors.ErrVolumeNotFound) { + // If the subvolume deletion is failed, we should not cleanup + // the OMAP entry it will stale subvolume in cluster. + // set err=nil so that when we get the request again we can get + // the subvolume info. + err = nil + + return nil, status.Error(codes.Internal, purgeErr.Error()) + } } - } - log.ErrorLog(ctx, "failed to get subvolume path %s: %v", vID.FsSubvolName, err) + log.ErrorLog(ctx, "failed to get subvolume path %s: %v", vID.FsSubvolName, err) - return nil, status.Error(codes.Internal, err.Error()) + return nil, status.Error(codes.Internal, err.Error()) + } } log.DebugLog(ctx, "cephfs: successfully created backing volume named %s for request name %s", @@ -353,12 +417,11 @@ func (cs *ControllerServer) CreateVolume( VolumeContext: volumeContext, } if volOptions.Topology != nil { - volume.AccessibleTopology = - []*csi.Topology{ - { - Segments: volOptions.Topology, - }, - } + volume.AccessibleTopology = []*csi.Topology{ + { + Segments: volOptions.Topology, + }, + } } return &csi.CreateVolumeResponse{Volume: volume}, nil @@ -367,7 +430,8 @@ func (cs *ControllerServer) CreateVolume( // DeleteVolume deletes the volume in backend and its reservation. func (cs *ControllerServer) DeleteVolume( ctx context.Context, - req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { + req *csi.DeleteVolumeRequest, +) (*csi.DeleteVolumeResponse, error) { if err := cs.validateDeleteVolumeRequest(); err != nil { log.ErrorLog(ctx, "DeleteVolumeRequest validation failed: %v", err) @@ -449,16 +513,8 @@ func (cs *ControllerServer) DeleteVolume( } defer cr.DeleteCredentials() - volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) - if err = volClient.PurgeVolume(ctx, false); err != nil { - log.ErrorLog(ctx, "failed to delete volume %s: %v", volID, err) - if errors.Is(err, cerrors.ErrVolumeHasSnapshots) { - return nil, status.Error(codes.FailedPrecondition, err.Error()) - } - - if !errors.Is(err, cerrors.ErrVolumeNotFound) { - return nil, status.Error(codes.Internal, err.Error()) - } + if err := cleanUpBackingVolume(ctx, volOptions, vID, cr); err != nil { + return nil, err } if err := store.UndoVolReservation(ctx, volOptions, *vID, secrets); err != nil { @@ -470,11 +526,90 @@ func (cs *ControllerServer) DeleteVolume( return &csi.DeleteVolumeResponse{}, nil } +func cleanUpBackingVolume( + ctx context.Context, + volOptions *store.VolumeOptions, + volID *store.VolumeIdentifier, + cr *util.Credentials, +) error { + if !volOptions.BackingSnapshot { + // Regular volumes need to be purged. + + volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) + if err := volClient.PurgeVolume(ctx, false); err != nil { + log.ErrorLog(ctx, "failed to delete volume %s: %v", volID, err) + if errors.Is(err, cerrors.ErrVolumeHasSnapshots) { + return status.Error(codes.FailedPrecondition, err.Error()) + } + + if !errors.Is(err, cerrors.ErrVolumeNotFound) { + return status.Error(codes.Internal, err.Error()) + } + } + + return nil + } + + // Snapshot-backed volumes need to un-reference the backing snapshot, and + // the snapshot itself may need to be deleted if its reftracker doesn't + // hold any references anymore. + + backingSnapNeedsDelete, err := store.UnrefSnapshotBackedVolume(ctx, volOptions) + if err != nil { + if errors.Is(err, rterrors.ErrObjectOutOfDate) { + return status.Error(codes.Aborted, err.Error()) + } + + return status.Error(codes.Internal, err.Error()) + } + + if !backingSnapNeedsDelete { + return nil + } + + snapParentVolOptions, _, snapID, err := store.NewSnapshotOptionsFromID(ctx, volOptions.BackingSnapshotID, cr) + if err != nil { + absorbErrs := []error{ + util.ErrPoolNotFound, + util.ErrKeyNotFound, + cerrors.ErrSnapNotFound, + cerrors.ErrVolumeNotFound, + } + + fatalErr := true + for i := range absorbErrs { + if errors.Is(err, absorbErrs[i]) { + fatalErr = false + + break + } + } + + if fatalErr { + return status.Error(codes.Internal, err.Error()) + } + } else { + snapClient := core.NewSnapshot( + snapParentVolOptions.GetConnection(), + snapID.FsSnapshotName, + &snapParentVolOptions.SubVolume, + ) + + err = deleteSnapshotAndUndoReservation(ctx, snapClient, snapParentVolOptions, snapID, cr) + if err != nil { + return status.Error(codes.Internal, err.Error()) + } + } + + return nil +} + // ValidateVolumeCapabilities checks whether the volume capabilities requested // are supported. func (cs *ControllerServer) ValidateVolumeCapabilities( ctx context.Context, - req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) { + req *csi.ValidateVolumeCapabilitiesRequest, +) (*csi.ValidateVolumeCapabilitiesResponse, error) { // Cephfs doesn't support Block volume for _, capability := range req.VolumeCapabilities { if capability.GetBlock() != nil { @@ -492,7 +627,8 @@ func (cs *ControllerServer) ValidateVolumeCapabilities( // ControllerExpandVolume expands CephFS Volumes on demand based on resizer request. func (cs *ControllerServer) ControllerExpandVolume( ctx context.Context, - req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { + req *csi.ControllerExpandVolumeRequest, +) (*csi.ControllerExpandVolumeResponse, error) { if err := cs.validateExpandVolumeRequest(req); err != nil { log.ErrorLog(ctx, "ControllerExpandVolumeRequest validation failed: %v", err) @@ -532,6 +668,10 @@ func (cs *ControllerServer) ControllerExpandVolume( } defer volOptions.Destroy() + if volOptions.BackingSnapshot { + return nil, status.Error(codes.InvalidArgument, "cannot expand snapshot-backed volume") + } + RoundOffSize := util.RoundOffBytes(req.GetCapacityRange().GetRequiredBytes()) volClient := core.NewSubVolume(volOptions.GetConnection(), &volOptions.SubVolume, volOptions.ClusterID) if err = volClient.ResizeVolume(ctx, RoundOffSize); err != nil { @@ -551,7 +691,8 @@ func (cs *ControllerServer) ControllerExpandVolume( // nolint:gocyclo,cyclop // golangci-lint did not catch this earlier, needs to get fixed late func (cs *ControllerServer) CreateSnapshot( ctx context.Context, - req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { + req *csi.CreateSnapshotRequest, +) (*csi.CreateSnapshotResponse, error) { if err := cs.validateSnapshotReq(ctx, req); err != nil { return nil, err } @@ -609,6 +750,10 @@ func (cs *ControllerServer) CreateSnapshot( parentVolOptions.ClusterID) } + if parentVolOptions.BackingSnapshot { + return nil, status.Error(codes.InvalidArgument, "cannot snapshot a snapshot-backed volume") + } + cephfsSnap, genSnapErr := store.GenSnapFromOptions(ctx, req) if genSnapErr != nil { return nil, status.Error(codes.Internal, genSnapErr.Error()) @@ -714,7 +859,8 @@ func (cs *ControllerServer) CreateSnapshot( func doSnapshot( ctx context.Context, volOpt *store.VolumeOptions, - snapshotName string) (core.SnapshotInfo, error) { + snapshotName string, +) (core.SnapshotInfo, error) { snapID := fsutil.VolumeID(snapshotName) snap := core.SnapshotInfo{} snapClient := core.NewSnapshot(volOpt.GetConnection(), snapshotName, &volOpt.SubVolume) @@ -773,9 +919,11 @@ func (cs *ControllerServer) validateSnapshotReq(ctx context.Context, req *csi.Cr // DeleteSnapshot deletes the snapshot in backend and removes the // snapshot metadata from store. +// nolint:gocyclo,cyclop // TODO: reduce complexity func (cs *ControllerServer) DeleteSnapshot( ctx context.Context, - req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) { + req *csi.DeleteSnapshotRequest, +) (*csi.DeleteSnapshotResponse, error) { if err := cs.Driver.ValidateControllerServiceRequest( csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT); err != nil { log.ErrorLog(ctx, "invalid delete snapshot req: %v", protosanitizer.StripSecrets(req)) @@ -870,17 +1018,51 @@ func (cs *ControllerServer) DeleteSnapshot( return nil, status.Error(codes.Internal, err.Error()) } } - err = snapClient.DeleteSnapshot(ctx) + + needsDelete, err := store.UnrefSelfInSnapshotBackedVolumes(ctx, volOpt, sid.SnapshotID) if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - err = store.UndoSnapReservation(ctx, volOpt, *sid, sid.RequestName, cr) - if err != nil { - log.ErrorLog(ctx, "failed to remove reservation for snapname (%s) with backing snap (%s) (%s)", - sid.RequestName, sid.FsSnapshotName, err) + if errors.Is(err, rterrors.ErrObjectOutOfDate) { + return nil, status.Error(codes.Aborted, err.Error()) + } return nil, status.Error(codes.Internal, err.Error()) } + if needsDelete { + err = deleteSnapshotAndUndoReservation( + ctx, + snapClient, + volOpt, + sid, + cr, + ) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + } + return &csi.DeleteSnapshotResponse{}, nil } + +func deleteSnapshotAndUndoReservation( + ctx context.Context, + snapClient core.SnapshotClient, + parentVolOptions *store.VolumeOptions, + snapID *store.SnapshotIdentifier, + cr *util.Credentials, +) error { + err := snapClient.DeleteSnapshot(ctx) + if err != nil { + return err + } + + err = store.UndoSnapReservation(ctx, parentVolOptions, *snapID, snapID.RequestName, cr) + if err != nil { + log.ErrorLog(ctx, "failed to remove reservation for snapname (%s) with backing snap (%s) (%s)", + snapID.RequestName, snapID.RequestName, err) + + return err + } + + return nil +} diff --git a/internal/cephfs/core/clone.go b/internal/cephfs/core/clone.go index 8dabb74de..beb7d81e6 100644 --- a/internal/cephfs/core/clone.go +++ b/internal/cephfs/core/clone.go @@ -64,7 +64,8 @@ func (cs cephFSCloneState) toError() error { // CreateCloneFromSubvolume creates a clone from a subvolume. func (s *subVolumeClient) CreateCloneFromSubvolume( ctx context.Context, - parentvolOpt *SubVolume) error { + parentvolOpt *SubVolume, +) error { snapshotID := s.VolID snapClient := NewSnapshot(s.conn, snapshotID, parentvolOpt) err := snapClient.CreateSnapshot(ctx) @@ -159,7 +160,8 @@ func (s *subVolumeClient) CreateCloneFromSubvolume( // CleanupSnapshotFromSubvolume removes the snapshot from the subvolume. func (s *subVolumeClient) CleanupSnapshotFromSubvolume( - ctx context.Context, parentVol *SubVolume) error { + ctx context.Context, parentVol *SubVolume, +) error { // snapshot name is same as clone name as we need a name which can be // identified during PVC-PVC cloning. snapShotID := s.VolID @@ -193,7 +195,8 @@ func (s *subVolumeClient) CleanupSnapshotFromSubvolume( // CreateSnapshotFromSubvolume creates a clone from subvolume snapshot. func (s *subVolumeClient) CreateCloneFromSnapshot( - ctx context.Context, snap Snapshot) error { + ctx context.Context, snap Snapshot, +) error { snapID := snap.SnapshotID snapClient := NewSnapshot(s.conn, snapID, snap.SubVolume) err := snapClient.CloneSnapshot(ctx, s.SubVolume) diff --git a/internal/cephfs/fuserecovery.go b/internal/cephfs/fuserecovery.go index 32018a328..e7f25aa6a 100644 --- a/internal/cephfs/fuserecovery.go +++ b/internal/cephfs/fuserecovery.go @@ -192,7 +192,7 @@ func (ns *NodeServer) tryRestoreFuseMountsInNodePublish( // Unmount and mount the volume. if stagingTargetMs != msMounted { - if err := mounter.UnmountVolume(ctx, stagingTargetPath); err != nil { + if err := mounter.UnmountAll(ctx, stagingTargetPath); err != nil { return err } @@ -269,5 +269,5 @@ func (ns *NodeServer) tryRestoreFuseMountInNodeStage( // Restoration here means only unmounting the volume. // NodeStageVolume should take care of the rest. - return mounter.UnmountVolume(ctx, stagingTargetPath) + return mounter.UnmountAll(ctx, stagingTargetPath) } diff --git a/internal/cephfs/identityserver.go b/internal/cephfs/identityserver.go index 58d3c564f..625fc3842 100644 --- a/internal/cephfs/identityserver.go +++ b/internal/cephfs/identityserver.go @@ -33,7 +33,8 @@ type IdentityServer struct { // GetPluginCapabilities returns available capabilities of the ceph driver. func (is *IdentityServer) GetPluginCapabilities( ctx context.Context, - req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { + req *csi.GetPluginCapabilitiesRequest, +) (*csi.GetPluginCapabilitiesResponse, error) { return &csi.GetPluginCapabilitiesResponse{ Capabilities: []*csi.PluginCapability{ { diff --git a/internal/cephfs/mounter/fuse.go b/internal/cephfs/mounter/fuse.go index 2711db5ee..606976ec9 100644 --- a/internal/cephfs/mounter/fuse.go +++ b/internal/cephfs/mounter/fuse.go @@ -107,7 +107,8 @@ func (m *FuseMounter) Mount( ctx context.Context, mountPoint string, cr *util.Credentials, - volOptions *store.VolumeOptions) error { + volOptions *store.VolumeOptions, +) error { if err := util.CreateMountPoint(mountPoint); err != nil { return err } @@ -117,8 +118,8 @@ func (m *FuseMounter) Mount( func (m *FuseMounter) Name() string { return "Ceph FUSE driver" } -func UnmountVolume(ctx context.Context, mountPoint string) error { - if _, stderr, err := util.ExecCommand(ctx, "umount", mountPoint); err != nil { +func UnmountVolume(ctx context.Context, mountPoint string, opts ...string) error { + if _, stderr, err := util.ExecCommand(ctx, "umount", append([]string{mountPoint}, opts...)...); err != nil { err = fmt.Errorf("%w stderr: %s", err, stderr) if strings.Contains(err.Error(), fmt.Sprintf("umount: %s: not mounted", mountPoint)) || strings.Contains(err.Error(), "No such file or directory") { @@ -148,3 +149,7 @@ func UnmountVolume(ctx context.Context, mountPoint string) error { return nil } + +func UnmountAll(ctx context.Context, mountPoint string) error { + return UnmountVolume(ctx, mountPoint, "--all-targets") +} diff --git a/internal/cephfs/mounter/kernel.go b/internal/cephfs/mounter/kernel.go index b7ae67ff8..8d6d833b3 100644 --- a/internal/cephfs/mounter/kernel.go +++ b/internal/cephfs/mounter/kernel.go @@ -72,7 +72,8 @@ func (m *KernelMounter) Mount( ctx context.Context, mountPoint string, cr *util.Credentials, - volOptions *store.VolumeOptions) error { + volOptions *store.VolumeOptions, +) error { if err := util.CreateMountPoint(mountPoint); err != nil { return err } diff --git a/internal/cephfs/nodeserver.go b/internal/cephfs/nodeserver.go index fdadb6497..c6a2eb691 100644 --- a/internal/cephfs/nodeserver.go +++ b/internal/cephfs/nodeserver.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "os" + "path" "strings" cerrors "github.com/ceph/ceph-csi/internal/cephfs/errors" @@ -47,7 +48,8 @@ type NodeServer struct { func getCredentialsForVolume( volOptions *store.VolumeOptions, - secrets map[string]string) (*util.Credentials, error) { + secrets map[string]string, +) (*util.Credentials, error) { var ( err error cr *util.Credentials @@ -100,10 +102,25 @@ func (ns *NodeServer) getVolumeOptions( return volOptions, nil } +func validateSnapshotBackedVolCapability(volCap *csi.VolumeCapability) error { + // Snapshot-backed volumes may be used with read-only volume access modes only. + + mode := volCap.AccessMode.Mode + + if mode != csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY && + mode != csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY { + return status.Error(codes.InvalidArgument, + "snapshot-backed volume supports only read-only access mode") + } + + return nil +} + // NodeStageVolume mounts the volume to a staging path on the node. func (ns *NodeServer) NodeStageVolume( ctx context.Context, - req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { + req *csi.NodeStageVolumeRequest, +) (*csi.NodeStageVolumeResponse, error) { if err := util.ValidateNodeStageVolumeRequest(req); err != nil { return nil, err } @@ -126,12 +143,24 @@ func (ns *NodeServer) NodeStageVolume( } defer volOptions.Destroy() - volOptions.NetNamespaceFilePath, err = util.GetCephFSNetNamespaceFilePath( - util.CsiConfigFile, - volOptions.ClusterID) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) + // Skip extracting NetNamespaceFilePath if the clusterID is empty. + // In case of pre-provisioned volume the clusterID is not set in the + // volume context. + if volOptions.ClusterID != "" { + volOptions.NetNamespaceFilePath, err = util.GetCephFSNetNamespaceFilePath( + util.CsiConfigFile, + volOptions.ClusterID) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } } + + if volOptions.BackingSnapshot { + if err = validateSnapshotBackedVolCapability(req.GetVolumeCapability()); err != nil { + return nil, err + } + } + mnt, err := mounter.New(volOptions) if err != nil { log.ErrorLog(ctx, "failed to create mounter for volume %s: %v", volID, err) @@ -184,7 +213,7 @@ func (ns *NodeServer) NodeStageVolume( log.ErrorLog(ctx, "cephfs: failed to write NodeStageMountinfo for volume %s: %v", volID, err) // Try to clean node stage mount. - if unmountErr := mounter.UnmountVolume(ctx, stagingTargetPath); unmountErr != nil { + if unmountErr := mounter.UnmountAll(ctx, stagingTargetPath); unmountErr != nil { log.ErrorLog(ctx, "cephfs: failed to unmount %s in WriteNodeStageMountinfo clean up: %v", stagingTargetPath, unmountErr) } @@ -215,7 +244,7 @@ func (*NodeServer) mount( log.DebugLog(ctx, "cephfs: mounting volume %s with %s", volID, mnt.Name()) - readOnly := "ro" + const readOnly = "ro" if volCap.AccessMode.Mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY || volCap.AccessMode.Mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY { @@ -240,14 +269,118 @@ func (*NodeServer) mount( return status.Error(codes.Internal, err.Error()) } + defer func() { + if err == nil { + return + } + + unmountErr := mounter.UnmountAll(ctx, stagingTargetPath) + if unmountErr != nil { + log.ErrorLog(ctx, "failed to clean up mounts in rollback procedure: %v", unmountErr) + } + }() + + if volOptions.BackingSnapshot { + snapshotRoot, err := getBackingSnapshotRoot(ctx, volOptions, stagingTargetPath) + if err != nil { + return err + } + + absoluteSnapshotRoot := path.Join(stagingTargetPath, snapshotRoot) + err = mounter.BindMount( + ctx, + absoluteSnapshotRoot, + stagingTargetPath, + true, + []string{"bind", "_netdev"}, + ) + + if err != nil { + log.ErrorLog(ctx, + "failed to bind mount snapshot root %s: %v", absoluteSnapshotRoot, err) + + return status.Error(codes.Internal, err.Error()) + } + } + return nil } +func getBackingSnapshotRoot( + ctx context.Context, + volOptions *store.VolumeOptions, + stagingTargetPath string, +) (string, error) { + if volOptions.ProvisionVolume { + // Provisioned snapshot-backed volumes should have their BackingSnapshotRoot + // already populated. + return volOptions.BackingSnapshotRoot, nil + } + + // Pre-provisioned snapshot-backed volumes are more involved: + // + // Snapshots created with `ceph fs subvolume snapshot create` have following + // snap directory name format inside /.snap: + // + // __ + // + // We don't know what is, and so /.snap + // needs to be traversed in order to determine the full snapshot directory name. + + snapshotsBase := path.Join(stagingTargetPath, ".snap") + + dir, err := os.Open(snapshotsBase) + if err != nil { + log.ErrorLog(ctx, "failed to open %s when searching for snapshot root: %v", snapshotsBase, err) + + return "", status.Errorf(codes.Internal, err.Error()) + } + + // Read the contents of /.snap directory into a string slice. + + contents, err := dir.Readdirnames(0) + if err != nil { + log.ErrorLog(ctx, "failed to read %s when searching for snapshot root: %v", snapshotsBase, err) + + return "", status.Errorf(codes.Internal, err.Error()) + } + + var ( + found bool + snapshotDirName string + ) + + // Look through the directory's contents and try to find the correct snapshot + // dir name. The search must be exhaustive to catch possible ambiguous results. + + for i := range contents { + if !strings.Contains(contents[i], volOptions.BackingSnapshotID) { + continue + } + + if !found { + found = true + snapshotDirName = contents[i] + } else { + return "", status.Errorf(codes.InvalidArgument, "ambiguous backingSnapshotID %s in %s", + volOptions.BackingSnapshotID, snapshotsBase) + } + } + + if !found { + return "", status.Errorf(codes.InvalidArgument, "no snapshot with backingSnapshotID %s found in %s", + volOptions.BackingSnapshotID, snapshotsBase) + } + + return path.Join(".snap", snapshotDirName), nil +} + // NodePublishVolume mounts the volume mounted to the staging path to the target // path. func (ns *NodeServer) NodePublishVolume( ctx context.Context, - req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + req *csi.NodePublishVolumeRequest, +) (*csi.NodePublishVolumeResponse, error) { mountOptions := []string{"bind", "_netdev"} if err := util.ValidateNodePublishVolumeRequest(req); err != nil { return nil, err @@ -330,7 +463,8 @@ func (ns *NodeServer) NodePublishVolume( // NodeUnpublishVolume unmounts the volume from the target path. func (ns *NodeServer) NodeUnpublishVolume( ctx context.Context, - req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { + req *csi.NodeUnpublishVolumeRequest, +) (*csi.NodeUnpublishVolumeResponse, error) { var err error if err = util.ValidateNodeUnpublishVolumeRequest(req); err != nil { return nil, err @@ -385,7 +519,8 @@ func (ns *NodeServer) NodeUnpublishVolume( // NodeUnstageVolume unstages the volume from the staging path. func (ns *NodeServer) NodeUnstageVolume( ctx context.Context, - req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) { + req *csi.NodeUnstageVolumeRequest, +) (*csi.NodeUnstageVolumeResponse, error) { var err error if err = util.ValidateNodeUnstageVolumeRequest(req); err != nil { return nil, err @@ -433,7 +568,7 @@ func (ns *NodeServer) NodeUnstageVolume( return &csi.NodeUnstageVolumeResponse{}, nil } // Unmount the volume - if err = mounter.UnmountVolume(ctx, stagingTargetPath); err != nil { + if err = mounter.UnmountAll(ctx, stagingTargetPath); err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -445,7 +580,8 @@ func (ns *NodeServer) NodeUnstageVolume( // NodeGetCapabilities returns the supported capabilities of the node server. func (ns *NodeServer) NodeGetCapabilities( ctx context.Context, - req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { + req *csi.NodeGetCapabilitiesRequest, +) (*csi.NodeGetCapabilitiesResponse, error) { return &csi.NodeGetCapabilitiesResponse{ Capabilities: []*csi.NodeServiceCapability{ { @@ -476,7 +612,8 @@ func (ns *NodeServer) NodeGetCapabilities( // NodeGetVolumeStats returns volume stats. func (ns *NodeServer) NodeGetVolumeStats( ctx context.Context, - req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) { + req *csi.NodeGetVolumeStatsRequest, +) (*csi.NodeGetVolumeStatsResponse, error) { var err error targetPath := req.GetVolumePath() if targetPath == "" { diff --git a/internal/cephfs/store/backingsnapshot.go b/internal/cephfs/store/backingsnapshot.go new file mode 100644 index 000000000..2c22839d3 --- /dev/null +++ b/internal/cephfs/store/backingsnapshot.go @@ -0,0 +1,168 @@ +/* +Copyright 2022 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package store + +import ( + "context" + "fmt" + + fsutil "github.com/ceph/ceph-csi/internal/cephfs/util" + "github.com/ceph/ceph-csi/internal/util/log" + "github.com/ceph/ceph-csi/internal/util/reftracker" + "github.com/ceph/ceph-csi/internal/util/reftracker/radoswrapper" + "github.com/ceph/ceph-csi/internal/util/reftracker/reftype" +) + +func fmtBackingSnapshotReftrackerName(backingSnapID string) string { + return fmt.Sprintf("rt-backingsnapshot-%s", backingSnapID) +} + +func AddSnapshotBackedVolumeRef( + ctx context.Context, + volOptions *VolumeOptions, +) error { + ioctx, err := volOptions.conn.GetIoctx(volOptions.MetadataPool) + if err != nil { + log.ErrorLog(ctx, "failed to create RADOS ioctx: %s", err) + + return err + } + defer ioctx.Destroy() + + ioctx.SetNamespace(fsutil.RadosNamespace) + + var ( + backingSnapID = volOptions.BackingSnapshotID + ioctxW = radoswrapper.NewIOContext(ioctx) + ) + + created, err := reftracker.Add( + ioctxW, + fmtBackingSnapshotReftrackerName(backingSnapID), + map[string]struct{}{ + backingSnapID: {}, + volOptions.VolID: {}, + }, + ) + if err != nil { + log.ErrorLog(ctx, "failed to add refs for backing snapshot %s: %v", + backingSnapID, err) + + return err + } + + defer func() { + if err == nil { + return + } + + // Clean up after failure. + + var deleted bool + deleted, err = reftracker.Remove( + ioctxW, + fmtBackingSnapshotReftrackerName(backingSnapID), + map[string]reftype.RefType{ + backingSnapID: reftype.Normal, + volOptions.VolID: reftype.Normal, + }, + ) + if err != nil { + log.ErrorLog(ctx, "failed to remove refs in cleanup procedure for backing snapshot %s: %v", + backingSnapID, err) + } + + if created && !deleted { + log.ErrorLog(ctx, "orphaned reftracker object %s (pool %s, namespace %s)", + backingSnapID, volOptions.MetadataPool, fsutil.RadosNamespace) + } + }() + + // There may have been a race between adding a ref to the reftracker and + // deleting the backing snapshot. Make sure the snapshot still exists by + // trying to retrieve it again. + _, _, _, err = NewSnapshotOptionsFromID(ctx, volOptions.BackingSnapshotID, volOptions.conn.Creds) + if err != nil { + log.ErrorLog(ctx, "failed to get backing snapshot %s: %v", volOptions.BackingSnapshotID, err) + } + + return err +} + +func UnrefSnapshotBackedVolume( + ctx context.Context, + volOptions *VolumeOptions, +) (bool, error) { + ioctx, err := volOptions.conn.GetIoctx(volOptions.MetadataPool) + if err != nil { + log.ErrorLog(ctx, "failed to create RADOS ioctx: %s", err) + + return false, err + } + defer ioctx.Destroy() + + ioctx.SetNamespace(fsutil.RadosNamespace) + + var ( + backingSnapID = volOptions.BackingSnapshotID + ioctxW = radoswrapper.NewIOContext(ioctx) + ) + + deleted, err := reftracker.Remove( + ioctxW, + fmtBackingSnapshotReftrackerName(backingSnapID), + map[string]reftype.RefType{ + volOptions.VolID: reftype.Normal, + }, + ) + if err != nil { + log.ErrorLog(ctx, "failed to remove refs for backing snapshot %s: %v", + backingSnapID, err) + + return false, err + } + + return deleted, err +} + +// UnrefSelfInSnapshotBackedVolumes removes (masks) snapshot ID in the +// reftracker for volumes backed by this snapshot. The returned boolean +// value signals whether the snapshot is not referenced by any such volumes +// and needs to be removed. +func UnrefSelfInSnapshotBackedVolumes( + ctx context.Context, + snapParentVolOptions *VolumeOptions, + snapshotID string, +) (bool, error) { + ioctx, err := snapParentVolOptions.conn.GetIoctx(snapParentVolOptions.MetadataPool) + if err != nil { + log.ErrorLog(ctx, "failed to create RADOS ioctx: %s", err) + + return false, err + } + defer ioctx.Destroy() + + ioctx.SetNamespace(fsutil.RadosNamespace) + + return reftracker.Remove( + radoswrapper.NewIOContext(ioctx), + fmtBackingSnapshotReftrackerName(snapshotID), + map[string]reftype.RefType{ + snapshotID: reftype.Mask, + }, + ) +} diff --git a/internal/cephfs/store/fsjournal.go b/internal/cephfs/store/fsjournal.go index 9c28ce84a..f787d6e3b 100644 --- a/internal/cephfs/store/fsjournal.go +++ b/internal/cephfs/store/fsjournal.go @@ -77,7 +77,8 @@ func CheckVolExists(ctx context.Context, pvID *VolumeIdentifier, sID *SnapshotIdentifier, - cr *util.Credentials) (*VolumeIdentifier, error) { + cr *util.Credentials, +) (*VolumeIdentifier, error) { var vid VolumeIdentifier // Connect to cephfs' default radosNamespace (csi) j, err := VolJournal.Connect(volOptions.Monitors, fsutil.RadosNamespace, cr) @@ -99,7 +100,7 @@ func CheckVolExists(ctx context.Context, volOptions.VolID = vid.FsSubvolName vol := core.NewSubVolume(volOptions.conn, &volOptions.SubVolume, volOptions.ClusterID) - if sID != nil || pvID != nil { + if (sID != nil || pvID != nil) && imageData.ImageAttributes.BackingSnapshotID == "" { cloneState, cloneStateErr := vol.GetCloneState(ctx) if cloneStateErr != nil { if errors.Is(cloneStateErr, cerrors.ErrVolumeNotFound) { @@ -152,25 +153,28 @@ func CheckVolExists(ctx context.Context, return nil, fmt.Errorf("clone is not in complete state for %s", vid.FsSubvolName) } } - volOptions.RootPath, err = vol.GetVolumeRootPathCeph(ctx) - if err != nil { - if errors.Is(err, cerrors.ErrVolumeNotFound) { - // If the subvolume is not present, cleanup the stale snapshot - // created for clone. - if parentVolOpt != nil && pvID != nil { - err = vol.CleanupSnapshotFromSubvolume( - ctx, &parentVolOpt.SubVolume) - if err != nil { - return nil, err + + if imageData.ImageAttributes.BackingSnapshotID == "" { + volOptions.RootPath, err = vol.GetVolumeRootPathCeph(ctx) + if err != nil { + if errors.Is(err, cerrors.ErrVolumeNotFound) { + // If the subvolume is not present, cleanup the stale snapshot + // created for clone. + if parentVolOpt != nil && pvID != nil { + err = vol.CleanupSnapshotFromSubvolume( + ctx, &parentVolOpt.SubVolume) + if err != nil { + return nil, err + } } + err = j.UndoReservation(ctx, volOptions.MetadataPool, + volOptions.MetadataPool, vid.FsSubvolName, volOptions.RequestName) + + return nil, err } - err = j.UndoReservation(ctx, volOptions.MetadataPool, - volOptions.MetadataPool, vid.FsSubvolName, volOptions.RequestName) return nil, err } - - return nil, err } // check if topology constraints match what is found @@ -205,7 +209,8 @@ func UndoVolReservation( ctx context.Context, volOptions *VolumeOptions, vid VolumeIdentifier, - secret map[string]string) error { + secret map[string]string, +) error { cr, err := util.NewAdminCredentials(secret) if err != nil { return err @@ -269,7 +274,7 @@ func ReserveVol(ctx context.Context, volOptions *VolumeOptions, secret map[strin imageUUID, vid.FsSubvolName, err = j.ReserveName( ctx, volOptions.MetadataPool, util.InvalidPoolID, volOptions.MetadataPool, util.InvalidPoolID, volOptions.RequestName, - volOptions.NamePrefix, "", "", volOptions.ReservedID, "") + volOptions.NamePrefix, "", "", volOptions.ReservedID, "", volOptions.BackingSnapshotID) if err != nil { return nil, err } @@ -294,7 +299,8 @@ func ReserveSnap( volOptions *VolumeOptions, parentSubVolName string, snap *SnapshotOption, - cr *util.Credentials) (*SnapshotIdentifier, error) { + cr *util.Credentials, +) (*SnapshotIdentifier, error) { var ( vid SnapshotIdentifier imageUUID string @@ -311,7 +317,7 @@ func ReserveSnap( imageUUID, vid.FsSnapshotName, err = j.ReserveName( ctx, volOptions.MetadataPool, util.InvalidPoolID, volOptions.MetadataPool, util.InvalidPoolID, snap.RequestName, - snap.NamePrefix, parentSubVolName, "", snap.ReservedID, "") + snap.NamePrefix, parentSubVolName, "", snap.ReservedID, "", "") if err != nil { return nil, err } @@ -335,7 +341,8 @@ func UndoSnapReservation( volOptions *VolumeOptions, vid SnapshotIdentifier, snapName string, - cr *util.Credentials) error { + cr *util.Credentials, +) error { // Connect to cephfs' default radosNamespace (csi) j, err := SnapJournal.Connect(volOptions.Monitors, fsutil.RadosNamespace, cr) if err != nil { @@ -367,7 +374,8 @@ func CheckSnapExists( ctx context.Context, volOptions *VolumeOptions, snap *SnapshotOption, - cr *util.Credentials) (*SnapshotIdentifier, *core.SnapshotInfo, error) { + cr *util.Credentials, +) (*SnapshotIdentifier, *core.SnapshotInfo, error) { // Connect to cephfs' default radosNamespace (csi) j, err := SnapJournal.Connect(volOptions.Monitors, fsutil.RadosNamespace, cr) if err != nil { diff --git a/internal/cephfs/store/volumeoptions.go b/internal/cephfs/store/volumeoptions.go index 0f1a35e3b..b1765d2f1 100644 --- a/internal/cephfs/store/volumeoptions.go +++ b/internal/cephfs/store/volumeoptions.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "path" "strconv" "strings" @@ -34,27 +35,31 @@ import ( type VolumeOptions struct { core.SubVolume - TopologyPools *[]util.TopologyConstrainedPool - TopologyRequirement *csi.TopologyRequirement - Topology map[string]string - RequestName string - NamePrefix string - ClusterID string - FscID int64 - MetadataPool string + + RequestName string + NamePrefix string + ClusterID string + MetadataPool string // ReservedID represents the ID reserved for a subvolume - ReservedID string - Monitors string `json:"monitors"` - RootPath string `json:"rootPath"` - Mounter string `json:"mounter"` - ProvisionVolume bool `json:"provisionVolume"` - KernelMountOptions string `json:"kernelMountOptions"` - FuseMountOptions string `json:"fuseMountOptions"` - // Network namespace file path to execute nsenter command + ReservedID string + Monitors string `json:"monitors"` + RootPath string `json:"rootPath"` + Mounter string `json:"mounter"` + BackingSnapshotRoot string // Snapshot root relative to RootPath. + BackingSnapshotID string + KernelMountOptions string `json:"kernelMountOptions"` + FuseMountOptions string `json:"fuseMountOptions"` NetNamespaceFilePath string + TopologyPools *[]util.TopologyConstrainedPool + TopologyRequirement *csi.TopologyRequirement + Topology map[string]string + FscID int64 // conn is a connection to the Ceph cluster obtained from a ConnPool conn *util.ClusterConnection + + ProvisionVolume bool `json:"provisionVolume"` + BackingSnapshot bool `json:"backingSnapshot"` } // Connect a CephFS volume to the Ceph cluster. @@ -184,13 +189,20 @@ func (vo *VolumeOptions) GetConnection() *util.ClusterConnection { return vo.conn } +func fmtBackingSnapshotOptionMismatch(optName, expected, actual string) error { + return fmt.Errorf("%s option mismatch with backing snapshot: got %s, expected %s", + optName, actual, expected) +} + // NewVolumeOptions generates a new instance of volumeOptions from the provided // CSI request parameters. func NewVolumeOptions(ctx context.Context, requestName string, req *csi.CreateVolumeRequest, - cr *util.Credentials) (*VolumeOptions, error) { + cr *util.Credentials, +) (*VolumeOptions, error) { var ( - opts VolumeOptions - err error + opts VolumeOptions + backingSnapshotBool string + err error ) volOptions := req.GetParameters() @@ -227,6 +239,16 @@ func NewVolumeOptions(ctx context.Context, requestName string, req *csi.CreateVo return nil, err } + if err = extractOptionalOption(&backingSnapshotBool, "backingSnapshot", volOptions); err != nil { + return nil, err + } + + if backingSnapshotBool != "" { + if opts.BackingSnapshot, err = strconv.ParseBool(backingSnapshotBool); err != nil { + return nil, fmt.Errorf("failed to parse backingSnapshot: %w", err) + } + } + opts.RequestName = requestName err = opts.Connect(cr) @@ -260,6 +282,19 @@ func NewVolumeOptions(ctx context.Context, requestName string, req *csi.CreateVo opts.ProvisionVolume = true + if opts.BackingSnapshot { + if req.GetVolumeContentSource() == nil || req.GetVolumeContentSource().GetSnapshot() == nil { + return nil, errors.New("backingSnapshot option requires snapshot volume source") + } + + opts.BackingSnapshotID = req.GetVolumeContentSource().GetSnapshot().GetSnapshotId() + + err = opts.populateVolumeOptionsFromBackingSnapshot(ctx, cr) + if err != nil { + return nil, err + } + } + return &opts, nil } @@ -268,7 +303,8 @@ func NewVolumeOptions(ctx context.Context, requestName string, req *csi.CreateVo func NewVolumeOptionsFromVolID( ctx context.Context, volID string, - volOpt, secrets map[string]string) (*VolumeOptions, *VolumeIdentifier, error) { + volOpt, secrets map[string]string, +) (*VolumeOptions, *VolumeIdentifier, error) { var ( vi util.CSIIdentifier volOptions VolumeOptions @@ -362,28 +398,115 @@ func NewVolumeOptionsFromVolID( } } - volOptions.ProvisionVolume = true - volOptions.SubVolume.VolID = vid.FsSubvolName - vol := core.NewSubVolume(volOptions.conn, &volOptions.SubVolume, volOptions.ClusterID) - info, err := vol.GetSubVolumeInfo(ctx) - if err == nil { - volOptions.RootPath = info.Path - volOptions.Features = info.Features - volOptions.Size = info.BytesQuota + if imageAttributes.BackingSnapshotID != "" || volOptions.BackingSnapshotID != "" { + volOptions.BackingSnapshot = true + volOptions.BackingSnapshotID = imageAttributes.BackingSnapshotID } - if errors.Is(err, cerrors.ErrInvalidCommand) { - volOptions.RootPath, err = vol.GetVolumeRootPathCeph(ctx) + volOptions.ProvisionVolume = true + volOptions.SubVolume.VolID = vid.FsSubvolName + + if volOptions.BackingSnapshot { + err = volOptions.populateVolumeOptionsFromBackingSnapshot(ctx, cr) + } else { + err = volOptions.populateVolumeOptionsFromSubvolume(ctx) } return &volOptions, &vid, err } +func (vo *VolumeOptions) populateVolumeOptionsFromSubvolume(ctx context.Context) error { + vol := core.NewSubVolume(vo.conn, &vo.SubVolume, vo.ClusterID) + + var info *core.Subvolume + info, err := vol.GetSubVolumeInfo(ctx) + if err == nil { + vo.RootPath = info.Path + vo.Features = info.Features + vo.Size = info.BytesQuota + } + + if errors.Is(err, cerrors.ErrInvalidCommand) { + vo.RootPath, err = vol.GetVolumeRootPathCeph(ctx) + } + + return err +} + +func (vo *VolumeOptions) populateVolumeOptionsFromBackingSnapshot( + ctx context.Context, + cr *util.Credentials, +) error { + // As of CephFS snapshot v2 API, snapshots may be found in two locations: + // + // (a) /volumes///.snap// + // (b) /volumes////.snap/__ + + if !vo.ProvisionVolume { + // Case (b) + // + // If the volume is not provisioned by us, we assume that we have access only + // to snapshot's parent volume root. In this case, o.RootPath is expected to + // be already set in the volume context. + + // BackingSnapshotRoot cannot be determined at this stage, because the + // full directory name is not known (see snapshot path format for case + // (b) above). RootPath/.snap must be traversed in order to find out + // the snapshot directory name. + + return nil + } + + parentBackingSnapVolOpts, _, snapID, err := NewSnapshotOptionsFromID(ctx, vo.BackingSnapshotID, cr) + if err != nil { + return fmt.Errorf("failed to retrieve backing snapshot %s: %w", vo.BackingSnapshotID, err) + } + + // Ensure that backing snapshot parent's volume options match the context. + // Snapshot-backed volume inherits all its parent's (parent of the snapshot) options. + + if vo.ClusterID != parentBackingSnapVolOpts.ClusterID { + return fmtBackingSnapshotOptionMismatch("clusterID", vo.ClusterID, parentBackingSnapVolOpts.ClusterID) + } + + if vo.Pool != "" { + return errors.New("cannot set pool for snapshot-backed volume") + } + + if vo.MetadataPool != parentBackingSnapVolOpts.MetadataPool { + return fmtBackingSnapshotOptionMismatch("MetadataPool", vo.MetadataPool, parentBackingSnapVolOpts.MetadataPool) + } + + if vo.FsName != parentBackingSnapVolOpts.FsName { + return fmtBackingSnapshotOptionMismatch("fsName", vo.FsName, parentBackingSnapVolOpts.FsName) + } + + if vo.SubvolumeGroup != parentBackingSnapVolOpts.SubvolumeGroup { + return fmtBackingSnapshotOptionMismatch("SubvolumeGroup", vo.SubvolumeGroup, parentBackingSnapVolOpts.SubvolumeGroup) + } + + vo.Features = parentBackingSnapVolOpts.Features + vo.Size = parentBackingSnapVolOpts.Size + + // For case (a) (o.ProvisionVolume==true is assumed), snapshot root path + // can be built out of subvolume root path, which is in following format: + // + // /volumes/// + + subvolRoot, subvolUUID := path.Split(parentBackingSnapVolOpts.RootPath) + + vo.RootPath = subvolRoot + vo.BackingSnapshotRoot = path.Join(".snap", snapID.FsSnapshotName, subvolUUID) + + return nil +} + // NewVolumeOptionsFromMonitorList generates a new instance of VolumeOptions and // VolumeIdentifier from the provided CSI volume context. func NewVolumeOptionsFromMonitorList( volID string, - options, secrets map[string]string) (*VolumeOptions, *VolumeIdentifier, error) { + options, secrets map[string]string, +) (*VolumeOptions, *VolumeIdentifier, error) { var ( opts VolumeOptions vid VolumeIdentifier @@ -435,9 +558,17 @@ func NewVolumeOptionsFromMonitorList( return nil, nil, err } + if err = extractOptionalOption(&opts.BackingSnapshotID, "backingSnapshotID", options); err != nil { + return nil, nil, err + } + vid.FsSubvolName = volID vid.VolumeID = volID + if opts.BackingSnapshotID != "" { + opts.BackingSnapshot = true + } + return &opts, &vid, nil } @@ -446,7 +577,8 @@ func NewVolumeOptionsFromMonitorList( // detected to be a statically provisioned volume. func NewVolumeOptionsFromStaticVolume( volID string, - options map[string]string) (*VolumeOptions, *VolumeIdentifier, error) { + options map[string]string, +) (*VolumeOptions, *VolumeIdentifier, error) { var ( opts VolumeOptions vid VolumeIdentifier @@ -507,6 +639,10 @@ func NewVolumeOptionsFromStaticVolume( vid.FsSubvolName = opts.RootPath vid.VolumeID = volID + if opts.BackingSnapshotID != "" { + opts.BackingSnapshot = true + } + return &opts, &vid, nil } @@ -515,7 +651,8 @@ func NewVolumeOptionsFromStaticVolume( func NewSnapshotOptionsFromID( ctx context.Context, snapID string, - cr *util.Credentials) (*VolumeOptions, *core.SnapshotInfo, *SnapshotIdentifier, error) { + cr *util.Credentials, +) (*VolumeOptions, *core.SnapshotInfo, *SnapshotIdentifier, error) { var ( vi util.CSIIdentifier volOptions VolumeOptions @@ -594,6 +731,7 @@ func NewSnapshotOptionsFromID( } volOptions.Features = subvolInfo.Features volOptions.Size = subvolInfo.BytesQuota + volOptions.RootPath = subvolInfo.Path snap := core.NewSnapshot(volOptions.conn, sid.FsSnapshotName, &volOptions.SubVolume) info, err := snap.GetSnapshotInfo(ctx) if err != nil { diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 5f1cc2fe2..bf608ca12 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -35,8 +35,9 @@ type Manager interface { // Config holds the drivername and namespace name. type Config struct { - DriverName string - Namespace string + DriverName string + Namespace string + ClusterName string } // ControllerList holds the list of managers need to be started. diff --git a/internal/controller/persistentvolume/persistentvolume.go b/internal/controller/persistentvolume/persistentvolume.go index 4bf46a7dd..5183fbb73 100644 --- a/internal/controller/persistentvolume/persistentvolume.go +++ b/internal/controller/persistentvolume/persistentvolume.go @@ -93,7 +93,8 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { func (r *ReconcilePersistentVolume) getCredentials( ctx context.Context, name, - namespace string) (*util.Credentials, error) { + namespace string, +) (*util.Credentials, error) { var cr *util.Credentials if name == "" || namespace == "" { @@ -183,6 +184,7 @@ func (r ReconcilePersistentVolume) reconcilePV(ctx context.Context, obj runtime. volumeHandler, requestName, pvcNamespace, + r.config.ClusterName, cr) if err != nil { log.ErrorLogMsg("failed to regenerate journal %s", err) @@ -199,7 +201,8 @@ func (r ReconcilePersistentVolume) reconcilePV(ctx context.Context, obj runtime. // Reconcile reconciles the PersistentVolume object and creates a new omap entries // for the volume. func (r *ReconcilePersistentVolume) Reconcile(ctx context.Context, - request reconcile.Request) (reconcile.Result, error) { + request reconcile.Request, +) (reconcile.Result, error) { pv := &corev1.PersistentVolume{} err := r.client.Get(ctx, request.NamespacedName, pv) if err != nil { diff --git a/internal/csi-addons/networkfence/fencing.go b/internal/csi-addons/networkfence/fencing.go index a7a7cec25..8d40ff6b9 100644 --- a/internal/csi-addons/networkfence/fencing.go +++ b/internal/csi-addons/networkfence/fencing.go @@ -39,7 +39,8 @@ func NewNetworkFence( ctx context.Context, cr *util.Credentials, cidrs []*fence.CIDR, - fenceOptions map[string]string) (*NetworkFence, error) { + fenceOptions map[string]string, +) (*NetworkFence, error) { var err error nwFence := &NetworkFence{} diff --git a/internal/csi-addons/rbd/identity.go b/internal/csi-addons/rbd/identity.go index b3e57e247..ef0486da5 100644 --- a/internal/csi-addons/rbd/identity.go +++ b/internal/csi-addons/rbd/identity.go @@ -49,7 +49,8 @@ func (is *IdentityServer) RegisterService(server grpc.ServiceRegistrar) { // GetIdentity returns available capabilities of the rbd driver. func (is *IdentityServer) GetIdentity( ctx context.Context, - req *identity.GetIdentityRequest) (*identity.GetIdentityResponse, error) { + req *identity.GetIdentityRequest, +) (*identity.GetIdentityResponse, error) { // only include Name and VendorVersion, Manifest is optional res := &identity.GetIdentityResponse{ Name: is.config.DriverName, @@ -62,7 +63,8 @@ func (is *IdentityServer) GetIdentity( // GetCapabilities returns available capabilities of the rbd driver. func (is *IdentityServer) GetCapabilities( ctx context.Context, - req *identity.GetCapabilitiesRequest) (*identity.GetCapabilitiesResponse, error) { + req *identity.GetCapabilitiesRequest, +) (*identity.GetCapabilitiesResponse, error) { // build the list of capabilities, depending on the config caps := make([]*identity.Capability, 0) @@ -121,7 +123,8 @@ func (is *IdentityServer) GetCapabilities( // still healthy. func (is *IdentityServer) Probe( ctx context.Context, - req *identity.ProbeRequest) (*identity.ProbeResponse, error) { + req *identity.ProbeRequest, +) (*identity.ProbeResponse, error) { // there is nothing that would cause a delay in getting ready res := &identity.ProbeResponse{ Ready: &wrapperspb.BoolValue{Value: true}, diff --git a/internal/csi-addons/rbd/network_fence.go b/internal/csi-addons/rbd/network_fence.go index a06347784..d1b5d6eab 100644 --- a/internal/csi-addons/rbd/network_fence.go +++ b/internal/csi-addons/rbd/network_fence.go @@ -60,7 +60,8 @@ func validateNetworkFenceReq(fenceClients []*fence.CIDR, options map[string]stri // to the malicious clients to prevent data corruption. func (fcs *FenceControllerServer) FenceClusterNetwork( ctx context.Context, - req *fence.FenceClusterNetworkRequest) (*fence.FenceClusterNetworkResponse, error) { + req *fence.FenceClusterNetworkRequest, +) (*fence.FenceClusterNetworkResponse, error) { err := validateNetworkFenceReq(req.GetCidrs(), req.Parameters) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) @@ -88,7 +89,8 @@ func (fcs *FenceControllerServer) FenceClusterNetwork( // UnfenceClusterNetwork unblocks the access to a CIDR block by removing the network fence. func (fcs *FenceControllerServer) UnfenceClusterNetwork( ctx context.Context, - req *fence.UnfenceClusterNetworkRequest) (*fence.UnfenceClusterNetworkResponse, error) { + req *fence.UnfenceClusterNetworkRequest, +) (*fence.UnfenceClusterNetworkResponse, error) { err := validateNetworkFenceReq(req.GetCidrs(), req.Parameters) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) diff --git a/internal/csi-addons/rbd/reclaimspace.go b/internal/csi-addons/rbd/reclaimspace.go index ff9f1a8f4..0d7d819da 100644 --- a/internal/csi-addons/rbd/reclaimspace.go +++ b/internal/csi-addons/rbd/reclaimspace.go @@ -49,7 +49,8 @@ func (rscs *ReclaimSpaceControllerServer) RegisterService(server grpc.ServiceReg func (rscs *ReclaimSpaceControllerServer) ControllerReclaimSpace( ctx context.Context, - req *rs.ControllerReclaimSpaceRequest) (*rs.ControllerReclaimSpaceResponse, error) { + req *rs.ControllerReclaimSpaceRequest, +) (*rs.ControllerReclaimSpaceResponse, error) { volumeID := req.GetVolumeId() if volumeID == "" { return nil, status.Error(codes.InvalidArgument, "empty volume ID in request") @@ -97,10 +98,10 @@ func (rsns *ReclaimSpaceNodeServer) RegisterService(server grpc.ServiceRegistrar // an error is returned to prevent potential data corruption. func (rsns *ReclaimSpaceNodeServer) NodeReclaimSpace( ctx context.Context, - req *rs.NodeReclaimSpaceRequest) (*rs.NodeReclaimSpaceResponse, error) { + req *rs.NodeReclaimSpaceRequest, +) (*rs.NodeReclaimSpaceResponse, error) { // volumeID is a required attribute, it is part of the path to run the // space reducing command on - // nolint:ifshort // volumeID is incorrectly assumed to be used only once volumeID := req.GetVolumeId() if volumeID == "" { return nil, status.Error(codes.InvalidArgument, "empty volume ID in request") diff --git a/internal/csi-common/controllerserver-default.go b/internal/csi-common/controllerserver-default.go index 3daa31ca7..a6b016557 100644 --- a/internal/csi-common/controllerserver-default.go +++ b/internal/csi-common/controllerserver-default.go @@ -34,28 +34,32 @@ type DefaultControllerServer struct { // ControllerPublishVolume publish volume on node. func (cs *DefaultControllerServer) ControllerPublishVolume( ctx context.Context, - req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { + req *csi.ControllerPublishVolumeRequest, +) (*csi.ControllerPublishVolumeResponse, error) { return nil, status.Error(codes.Unimplemented, "") } // ControllerUnpublishVolume unpublish on node. func (cs *DefaultControllerServer) ControllerUnpublishVolume( ctx context.Context, - req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) { + req *csi.ControllerUnpublishVolumeRequest, +) (*csi.ControllerUnpublishVolumeResponse, error) { return nil, status.Error(codes.Unimplemented, "") } // ListVolumes lists volumes. func (cs *DefaultControllerServer) ListVolumes( ctx context.Context, - req *csi.ListVolumesRequest) (*csi.ListVolumesResponse, error) { + req *csi.ListVolumesRequest, +) (*csi.ListVolumesResponse, error) { return nil, status.Error(codes.Unimplemented, "") } // GetCapacity get volume capacity. func (cs *DefaultControllerServer) GetCapacity( ctx context.Context, - req *csi.GetCapacityRequest) (*csi.GetCapacityResponse, error) { + req *csi.GetCapacityRequest, +) (*csi.GetCapacityResponse, error) { return nil, status.Error(codes.Unimplemented, "") } @@ -63,7 +67,8 @@ func (cs *DefaultControllerServer) GetCapacity( // Default supports all capabilities. func (cs *DefaultControllerServer) ControllerGetCapabilities( ctx context.Context, - req *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) { + req *csi.ControllerGetCapabilitiesRequest, +) (*csi.ControllerGetCapabilitiesResponse, error) { log.TraceLog(ctx, "Using default ControllerGetCapabilities") if cs.Driver == nil { return nil, status.Error(codes.Unimplemented, "Controller server is not enabled") @@ -77,13 +82,15 @@ func (cs *DefaultControllerServer) ControllerGetCapabilities( // ListSnapshots lists snapshots. func (cs *DefaultControllerServer) ListSnapshots( ctx context.Context, - req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error) { + req *csi.ListSnapshotsRequest, +) (*csi.ListSnapshotsResponse, error) { return nil, status.Error(codes.Unimplemented, "") } // ControllerGetVolume fetch volume information. func (cs *DefaultControllerServer) ControllerGetVolume( ctx context.Context, - req *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) { + req *csi.ControllerGetVolumeRequest, +) (*csi.ControllerGetVolumeResponse, error) { return nil, status.Error(codes.Unimplemented, "") } diff --git a/internal/csi-common/driver.go b/internal/csi-common/driver.go index b7bb3bd0c..ee2b42434 100644 --- a/internal/csi-common/driver.go +++ b/internal/csi-common/driver.go @@ -100,7 +100,8 @@ func (d *CSIDriver) AddControllerServiceCapabilities(cl []csi.ControllerServiceC // AddVolumeCapabilityAccessModes stores volume access modes. func (d *CSIDriver) AddVolumeCapabilityAccessModes( - vc []csi.VolumeCapability_AccessMode_Mode) []*csi.VolumeCapability_AccessMode { + vc []csi.VolumeCapability_AccessMode_Mode, +) []*csi.VolumeCapability_AccessMode { vca := make([]*csi.VolumeCapability_AccessMode, 0, len(vc)) for _, c := range vc { log.DefaultLog("Enabling volume access mode: %v", c.String()) diff --git a/internal/csi-common/identityserver-default.go b/internal/csi-common/identityserver-default.go index ac7cb6fe9..fa891e066 100644 --- a/internal/csi-common/identityserver-default.go +++ b/internal/csi-common/identityserver-default.go @@ -34,7 +34,8 @@ type DefaultIdentityServer struct { // GetPluginInfo returns plugin information. func (ids *DefaultIdentityServer) GetPluginInfo( ctx context.Context, - req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { + req *csi.GetPluginInfoRequest, +) (*csi.GetPluginInfoResponse, error) { log.TraceLog(ctx, "Using default GetPluginInfo") if ids.Driver.name == "" { @@ -59,7 +60,8 @@ func (ids *DefaultIdentityServer) Probe(ctx context.Context, req *csi.ProbeReque // GetPluginCapabilities returns plugin capabilities. func (ids *DefaultIdentityServer) GetPluginCapabilities( ctx context.Context, - req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { + req *csi.GetPluginCapabilitiesRequest, +) (*csi.GetPluginCapabilitiesResponse, error) { log.TraceLog(ctx, "Using default capabilities") return &csi.GetPluginCapabilitiesResponse{ diff --git a/internal/csi-common/nodeserver-default.go b/internal/csi-common/nodeserver-default.go index 6a725995b..ea0967d37 100644 --- a/internal/csi-common/nodeserver-default.go +++ b/internal/csi-common/nodeserver-default.go @@ -35,14 +35,16 @@ type DefaultNodeServer struct { // NodeExpandVolume returns unimplemented response. func (ns *DefaultNodeServer) NodeExpandVolume( ctx context.Context, - req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) { + req *csi.NodeExpandVolumeRequest, +) (*csi.NodeExpandVolumeResponse, error) { return nil, status.Error(codes.Unimplemented, "") } // NodeGetInfo returns node ID. func (ns *DefaultNodeServer) NodeGetInfo( ctx context.Context, - req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) { + req *csi.NodeGetInfoRequest, +) (*csi.NodeGetInfoResponse, error) { log.TraceLog(ctx, "Using default NodeGetInfo") csiTopology := &csi.Topology{ @@ -58,7 +60,8 @@ func (ns *DefaultNodeServer) NodeGetInfo( // NodeGetCapabilities returns RPC unknown capability. func (ns *DefaultNodeServer) NodeGetCapabilities( ctx context.Context, - req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { + req *csi.NodeGetCapabilitiesRequest, +) (*csi.NodeGetCapabilitiesResponse, error) { log.TraceLog(ctx, "Using default NodeGetCapabilities") return &csi.NodeGetCapabilitiesResponse{ diff --git a/internal/csi-common/utils.go b/internal/csi-common/utils.go index f464f1b1c..fbc9ec79d 100644 --- a/internal/csi-common/utils.go +++ b/internal/csi-common/utils.go @@ -173,7 +173,8 @@ func contextIDInjector( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler) (resp interface{}, err error) { + handler grpc.UnaryHandler, +) (interface{}, error) { atomic.AddUint64(&id, 1) ctx = context.WithValue(ctx, log.CtxKey, id) if reqID := getReqID(req); reqID != "" { @@ -187,7 +188,8 @@ func logGRPC( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler) (interface{}, error) { + handler grpc.UnaryHandler, +) (interface{}, error) { log.ExtendedLog(ctx, "GRPC call: %s", info.FullMethod) if isReplicationRequest(req) { log.TraceLog(ctx, "GRPC request: %s", rp.StripReplicationSecrets(req)) @@ -205,11 +207,13 @@ func logGRPC( return resp, err } +//nolint:nonamedreturns // named return used to send recovered panic error. func panicHandler( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler) (resp interface{}, err error) { + handler grpc.UnaryHandler, +) (resp interface{}, err error) { defer func() { if r := recover(); r != nil { klog.Errorf("panic occurred: %v", r) diff --git a/internal/journal/omap.go b/internal/journal/omap.go index 0f0a6fdae..9451cdee2 100644 --- a/internal/journal/omap.go +++ b/internal/journal/omap.go @@ -34,7 +34,8 @@ const chunkSize int64 = 512 func getOMapValues( ctx context.Context, conn *Connection, - poolName, namespace, oid, prefix string, keys []string) (map[string]string, error) { + poolName, namespace, oid, prefix string, keys []string, +) (map[string]string, error) { // fetch and configure the rados ioctx ioctx, err := conn.conn.GetIoctx(poolName) if err != nil { @@ -93,7 +94,8 @@ func getOMapValues( func removeMapKeys( ctx context.Context, conn *Connection, - poolName, namespace, oid string, keys []string) error { + poolName, namespace, oid string, keys []string, +) error { // fetch and configure the rados ioctx ioctx, err := conn.conn.GetIoctx(poolName) if err != nil { @@ -129,7 +131,8 @@ func removeMapKeys( func setOMapKeys( ctx context.Context, conn *Connection, - poolName, namespace, oid string, pairs map[string]string) error { + poolName, namespace, oid string, pairs map[string]string, +) error { // fetch and configure the rados ioctx ioctx, err := conn.conn.GetIoctx(poolName) if err != nil { diff --git a/internal/journal/voljournal.go b/internal/journal/voljournal.go index 23f5267f7..6cbf2f519 100644 --- a/internal/journal/voljournal.go +++ b/internal/journal/voljournal.go @@ -152,6 +152,9 @@ type Config struct { // ownerKey is used to identify the owner of the volume, can be used with some KMS configurations ownerKey string + // backingSnapshotIDKey ID of the snapshot on which the CephFS snapshot-backed volume is based + backingSnapshotIDKey string + // commonPrefix is the prefix common to all omap keys for this Config commonPrefix string } @@ -170,6 +173,7 @@ func NewCSIVolumeJournal(suffix string) *Config { csiImageIDKey: "csi.imageid", encryptKMSKey: "csi.volume.encryptKMS", ownerKey: "csi.volume.owner", + backingSnapshotIDKey: "csi.volume.backingsnapshotid", commonPrefix: "csi.", } } @@ -275,7 +279,8 @@ Return values: - error: non-nil in case of any errors */ func (conn *Connection) CheckReservation(ctx context.Context, - journalPool, reqName, namePrefix, snapParentName, kmsConfig string) (*ImageData, error) { + journalPool, reqName, namePrefix, snapParentName, kmsConfig string, +) (*ImageData, error) { var ( snapSource bool objUUID string @@ -415,7 +420,8 @@ Input arguments: different if image is created in a topology constrained pool) */ func (conn *Connection) UndoReservation(ctx context.Context, - csiJournalPool, volJournalPool, volName, reqName string) error { + csiJournalPool, volJournalPool, volName, reqName string, +) error { // delete volume UUID omap (first, inverse of create order) cj := conn.config @@ -467,7 +473,8 @@ func reserveOMapName( ctx context.Context, monitors string, cr *util.Credentials, - pool, namespace, oMapNamePrefix, volUUID string) (string, error) { + pool, namespace, oMapNamePrefix, volUUID string, +) (string, error) { var iterUUID string maxAttempts := 5 @@ -525,6 +532,7 @@ Input arguments: - kmsConf: Name of the key management service used to encrypt the image (optional) - volUUID: UUID need to be reserved instead of auto-generating one (this is useful for mirroring and metro-DR) - owner: the owner of the volume (optional) + - backingSnapshotID: ID of the snapshot on which the CephFS snapshot-backed volume is based (optional) Return values: - string: Contains the UUID that was reserved for the passed in reqName @@ -534,7 +542,9 @@ Return values: func (conn *Connection) ReserveName(ctx context.Context, journalPool string, journalPoolID int64, imagePool string, imagePoolID int64, - reqName, namePrefix, parentName, kmsConf, volUUID, owner string) (string, string, error) { + reqName, namePrefix, parentName, kmsConf, volUUID, owner, + backingSnapshotID string, +) (string, string, error) { // TODO: Take in-arg as ImageAttributes? var ( snapSource bool @@ -635,6 +645,11 @@ func (conn *Connection) ReserveName(ctx context.Context, omapValues[cj.cephSnapSourceKey] = parentName } + // Update backing snapshot ID for snapshot-backed CephFS volume + if backingSnapshotID != "" { + omapValues[cj.backingSnapshotIDKey] = backingSnapshotID + } + err = setOMapKeys(ctx, conn, journalPool, cj.namespace, oid, omapValues) if err != nil { return "", "", err @@ -645,20 +660,22 @@ func (conn *Connection) ReserveName(ctx context.Context, // ImageAttributes contains all CSI stored image attributes, typically as OMap keys. type ImageAttributes struct { - RequestName string // Contains the request name for the passed in UUID - SourceName string // Contains the parent image name for the passed in UUID, if it is a snapshot - ImageName string // Contains the image or subvolume name for the passed in UUID - KmsID string // Contains encryption KMS, if it is an encrypted image - Owner string // Contains the owner to be used in combination with KmsID (for some KMS) - ImageID string // Contains the image id - JournalPoolID int64 // Pool ID of the CSI journal pool, stored in big endian format (on-disk data) + RequestName string // Contains the request name for the passed in UUID + SourceName string // Contains the parent image name for the passed in UUID, if it is a snapshot + ImageName string // Contains the image or subvolume name for the passed in UUID + KmsID string // Contains encryption KMS, if it is an encrypted image + Owner string // Contains the owner to be used in combination with KmsID (for some KMS) + ImageID string // Contains the image id + JournalPoolID int64 // Pool ID of the CSI journal pool, stored in big endian format (on-disk data) + BackingSnapshotID string // ID of the snapshot on which the CephFS snapshot-backed volume is based } // GetImageAttributes fetches all keys and their values, from a UUID directory, returning ImageAttributes structure. func (conn *Connection) GetImageAttributes( ctx context.Context, pool, objectUUID string, - snapSource bool) (*ImageAttributes, error) { + snapSource bool, +) (*ImageAttributes, error) { var ( err error imageAttributes = &ImageAttributes{} @@ -679,6 +696,7 @@ func (conn *Connection) GetImageAttributes( cj.cephSnapSourceKey, cj.csiImageIDKey, cj.ownerKey, + cj.backingSnapshotIDKey, } values, err := getOMapValues( ctx, conn, pool, cj.namespace, cj.cephUUIDDirectoryPrefix+objectUUID, @@ -695,6 +713,7 @@ func (conn *Connection) GetImageAttributes( imageAttributes.KmsID = values[cj.encryptKMSKey] imageAttributes.Owner = values[cj.ownerKey] imageAttributes.ImageID = values[cj.csiImageIDKey] + imageAttributes.BackingSnapshotID = values[cj.backingSnapshotIDKey] // image key was added at a later point, so not all volumes will have this // key set when ceph-csi was upgraded @@ -782,7 +801,8 @@ func (conn *Connection) Destroy() { // CheckNewUUIDMapping checks is there any UUID mapping between old // volumeHandle and the newly generated volumeHandle. func (conn *Connection) CheckNewUUIDMapping(ctx context.Context, - journalPool, volumeHandle string) (string, error) { + journalPool, volumeHandle string, +) (string, error) { cj := conn.config // check if request name is already part of the directory omap @@ -812,7 +832,8 @@ func (conn *Connection) CheckNewUUIDMapping(ctx context.Context, // secondary cluster cephcsi will generate the new mapping and keep it for // internal reference. func (conn *Connection) ReserveNewUUIDMapping(ctx context.Context, - journalPool, oldVolumeHandle, newVolumeHandle string) error { + journalPool, oldVolumeHandle, newVolumeHandle string, +) error { cj := conn.config setKeys := map[string]string{ diff --git a/internal/kms/aws_metadata.go b/internal/kms/aws_metadata.go index c69786343..f77bce6f3 100644 --- a/internal/kms/aws_metadata.go +++ b/internal/kms/aws_metadata.go @@ -199,8 +199,7 @@ func (kms *awsMetadataKMS) EncryptDEK(volumeID, plainDEK string) (string, error) // base64 encode the encrypted DEK, so that storing it should not have // issues - encryptedDEK := - base64.StdEncoding.EncodeToString(result.CiphertextBlob) + encryptedDEK := base64.StdEncoding.EncodeToString(result.CiphertextBlob) return encryptedDEK, nil } diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 01ac65310..a42e6d0df 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -269,7 +269,8 @@ func RegisterProvider(provider Provider) bool { func (kf *kmsProviderList) buildKMS( tenant string, config map[string]interface{}, - secrets map[string]string) (EncryptionKMS, error) { + secrets map[string]string, +) (EncryptionKMS, error) { providerName, err := getProvider(config) if err != nil { return nil, err diff --git a/internal/kms/secretskms.go b/internal/kms/secretskms.go index 974c2f30e..6fb479310 100644 --- a/internal/kms/secretskms.go +++ b/internal/kms/secretskms.go @@ -138,7 +138,8 @@ func initSecretsMetadataKMS(args ProviderInitArgs) (EncryptionKMS, error) { // fetchEncryptionPassphrase fetches encryptionPassphrase from user provided secret. func (kms secretsMetadataKMS) fetchEncryptionPassphrase( config map[string]interface{}, - defaultNamespace string) (string, error) { + defaultNamespace string, +) (string, error) { var ( secretName string secretNamespace string diff --git a/internal/kms/vault.go b/internal/kms/vault.go index d529c2a88..0633998c6 100644 --- a/internal/kms/vault.go +++ b/internal/kms/vault.go @@ -302,6 +302,7 @@ func (vc *vaultConnection) Destroy() { tmpFile, ok := vc.vaultConfig[api.EnvVaultCACert] if ok { // ignore error on failure to remove tmpfile (gosec complains) + //nolint:forcetypeassert // ignore error on failure to remove tmpfile _ = os.Remove(tmpFile.(string)) } } diff --git a/internal/kms/vault_sa.go b/internal/kms/vault_sa.go index 33c92f915..4a65768dd 100644 --- a/internal/kms/vault_sa.go +++ b/internal/kms/vault_sa.go @@ -23,9 +23,13 @@ import ( "io/ioutil" "os" + "github.com/ceph/ceph-csi/internal/util/k8s" + "github.com/libopenstorage/secrets/vault" + authenticationv1 "k8s.io/api/authentication/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" ) const ( @@ -35,6 +39,9 @@ const ( // should be available in the Tenants namespace. This ServiceAccount // will be used to connect to Hashicorp Vault. vaultTenantSAName = "ceph-csi-vault-sa" + // Kubernetes version which requires ServiceAccount token creation. + // Kubernetes 1.24 => 1 * 1000 + 24. + kubeMinVersionForCreateToken = 1024 ) /* @@ -292,9 +299,9 @@ func (kms *vaultTenantSA) getToken() (string, error) { } for _, secretRef := range sa.Secrets { - secret, err := c.CoreV1().Secrets(kms.Tenant).Get(context.TODO(), secretRef.Name, metav1.GetOptions{}) - if err != nil { - return "", fmt.Errorf("failed to get Secret %s/%s: %w", kms.Tenant, secretRef.Name, err) + secret, sErr := c.CoreV1().Secrets(kms.Tenant).Get(context.TODO(), secretRef.Name, metav1.GetOptions{}) + if sErr != nil { + return "", fmt.Errorf("failed to get Secret %s/%s: %w", kms.Tenant, secretRef.Name, sErr) } token, ok := secret.Data["token"] @@ -303,7 +310,7 @@ func (kms *vaultTenantSA) getToken() (string, error) { } } - return "", fmt.Errorf("failed to find token in ServiceAccount %s/%s", kms.Tenant, kms.tenantSAName) + return kms.createToken(sa, c) } // getTokenPath creates a temporary directory structure that contains the token @@ -327,3 +334,33 @@ func (kms *vaultTenantSA) getTokenPath() (string, error) { return dir + "/token", nil } + +// createToken creates required service account token for kubernetes 1.24+, +// else returns error. +// From kubernetes v1.24+, secret for service account tokens are not +// automatically created. Hence, use the create token api to fetch it. +// refer: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md \ +// #no-really-you-must-read-this-before-you-upgrade-1 . +func (kms *vaultTenantSA) createToken(sa *corev1.ServiceAccount, client *kubernetes.Clientset) (string, error) { + major, minor, err := k8s.GetServerVersion(client) + if err != nil { + return "", fmt.Errorf("failed to get server version: %w", err) + } + + if (major*1000 + minor) >= kubeMinVersionForCreateToken { + tokenRequest := &authenticationv1.TokenRequest{} + token, err := client.CoreV1().ServiceAccounts(kms.Tenant).CreateToken( + context.TODO(), + sa.Name, + tokenRequest, + metav1.CreateOptions{}, + ) + if err != nil { + return "", fmt.Errorf("failed to create token for service account %s/%s: %w", kms.Tenant, sa.Name, err) + } + + return token.Status.Token, nil + } + + return "", fmt.Errorf("failed to find token in ServiceAccount %s/%s", kms.Tenant, kms.tenantSAName) +} diff --git a/internal/kms/vault_tokens_test.go b/internal/kms/vault_tokens_test.go index abf9cc85e..404d3d035 100644 --- a/internal/kms/vault_tokens_test.go +++ b/internal/kms/vault_tokens_test.go @@ -120,6 +120,7 @@ func TestInitVaultTokensKMS(t *testing.T) { // add tenant "bob" bob := make(map[string]interface{}) bob["vaultAddress"] = "https://vault.bob.example.org" + //nolint:forcetypeassert // as its a test we dont need to check assertion here. args.Config["tenants"].(map[string]interface{})["bob"] = bob _, err = initVaultTokensKMS(args) diff --git a/internal/nfs/controller/controllerserver.go b/internal/nfs/controller/controllerserver.go index 3c70305ac..3c0330a15 100644 --- a/internal/nfs/controller/controllerserver.go +++ b/internal/nfs/controller/controllerserver.go @@ -57,7 +57,8 @@ func NewControllerServer(d *csicommon.CSIDriver) *Server { // capabilities that were set in the Driver.Run() function. func (cs *Server) ControllerGetCapabilities( ctx context.Context, - req *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) { + req *csi.ControllerGetCapabilitiesRequest, +) (*csi.ControllerGetCapabilitiesResponse, error) { return cs.backendServer.ControllerGetCapabilities(ctx, req) } @@ -65,7 +66,8 @@ func (cs *Server) ControllerGetCapabilities( // are supported. func (cs *Server) ValidateVolumeCapabilities( ctx context.Context, - req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) { + req *csi.ValidateVolumeCapabilitiesRequest, +) (*csi.ValidateVolumeCapabilitiesResponse, error) { return cs.backendServer.ValidateVolumeCapabilities(ctx, req) } @@ -73,7 +75,8 @@ func (cs *Server) ValidateVolumeCapabilities( // created entities. func (cs *Server) CreateVolume( ctx context.Context, - req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { + req *csi.CreateVolumeRequest, +) (*csi.CreateVolumeResponse, error) { res, err := cs.backendServer.CreateVolume(ctx, req) if err != nil { return nil, err @@ -120,7 +123,8 @@ func (cs *Server) CreateVolume( // DeleteVolume deletes the volume in backend and its reservation. func (cs *Server) DeleteVolume( ctx context.Context, - req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { + req *csi.DeleteVolumeRequest, +) (*csi.DeleteVolumeResponse, error) { secret := req.GetSecrets() cr, err := util.NewAdminCredentials(secret) if err != nil { @@ -157,7 +161,8 @@ func (cs *Server) DeleteVolume( // new size. func (cs *Server) ControllerExpandVolume( ctx context.Context, - req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { + req *csi.ControllerExpandVolumeRequest, +) (*csi.ControllerExpandVolumeResponse, error) { return cs.backendServer.ControllerExpandVolume(ctx, req) } @@ -165,7 +170,8 @@ func (cs *Server) ControllerExpandVolume( // There is no interaction with the NFS-server needed for snapshot creation. func (cs *Server) CreateSnapshot( ctx context.Context, - req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { + req *csi.CreateSnapshotRequest, +) (*csi.CreateSnapshotResponse, error) { return cs.backendServer.CreateSnapshot(ctx, req) } @@ -173,6 +179,7 @@ func (cs *Server) CreateSnapshot( // There is no interaction with the NFS-server needed for snapshot creation. func (cs *Server) DeleteSnapshot( ctx context.Context, - req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) { + req *csi.DeleteSnapshotRequest, +) (*csi.DeleteSnapshotResponse, error) { return cs.backendServer.DeleteSnapshot(ctx, req) } diff --git a/internal/nfs/identity/identityserver.go b/internal/nfs/identity/identityserver.go index 89bb0f508..2e11c6216 100644 --- a/internal/nfs/identity/identityserver.go +++ b/internal/nfs/identity/identityserver.go @@ -40,7 +40,8 @@ func NewIdentityServer(d *csicommon.CSIDriver) *Server { // GetPluginCapabilities returns available capabilities of the ceph driver. func (is *Server) GetPluginCapabilities( ctx context.Context, - req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { + req *csi.GetPluginCapabilitiesRequest, +) (*csi.GetPluginCapabilitiesResponse, error) { return &csi.GetPluginCapabilitiesResponse{ Capabilities: []*csi.PluginCapability{ { diff --git a/internal/rbd/controllerserver.go b/internal/rbd/controllerserver.go index f002896f5..b67760ddb 100644 --- a/internal/rbd/controllerserver.go +++ b/internal/rbd/controllerserver.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "strconv" csicommon "github.com/ceph/ceph-csi/internal/csi-common" "github.com/ceph/ceph-csi/internal/util" @@ -51,6 +52,9 @@ type ControllerServer struct { // A map storing all volumes/snapshots with ongoing operations. OperationLocks *util.OperationLock + + // Cluster name + ClusterName string } func (cs *ControllerServer) validateVolumeReq(ctx context.Context, req *csi.CreateVolumeRequest) error { @@ -91,6 +95,43 @@ func (cs *ControllerServer) validateVolumeReq(ctx context.Context, req *csi.Crea return err } + err = validateStriping(req.Parameters) + if err != nil { + return status.Error(codes.InvalidArgument, err.Error()) + } + + return nil +} + +func validateStriping(parameters map[string]string) error { + stripeUnit := parameters["stripeUnit"] + stripeCount := parameters["stripeCount"] + if stripeUnit != "" && stripeCount == "" { + return errors.New("stripeCount must be specified when stripeUnit is specified") + } + + if stripeUnit == "" && stripeCount != "" { + return errors.New("stripeUnit must be specified when stripeCount is specified") + } + + objectSize := parameters["objectSize"] + if objectSize != "" { + objSize, err := strconv.ParseUint(objectSize, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse objectSize %s: %w", objectSize, err) + } + // check objectSize is power of 2 + /* + Take 2^3=8 for example. + x & (x-1) + 8 & 7 + 1000 & 0111 = 0000 + */ + if objSize == 0 || (objSize&(objSize-1)) != 0 { + return fmt.Errorf("objectSize %s is not power of 2", objectSize) + } + } + return nil } @@ -98,7 +139,8 @@ func (cs *ControllerServer) validateVolumeReq(ctx context.Context, req *csi.Crea // request arguments for subsequent calls. func (cs *ControllerServer) parseVolCreateRequest( ctx context.Context, - req *csi.CreateVolumeRequest) (*rbdVolume, error) { + req *csi.CreateVolumeRequest, +) (*rbdVolume, error) { // TODO (sbezverk) Last check for not exceeding total storage capacity // below capability check indicates that we support both {SINGLE_NODE or MULTI_NODE} WRITERs and the `isMultiWriter` @@ -131,6 +173,8 @@ func (cs *ControllerServer) parseVolCreateRequest( return nil, status.Error(codes.InvalidArgument, err.Error()) } + rbdVol.ClusterName = cs.ClusterName + // if the KMS is of type VaultToken, additional metadata is needed // depending on the tenant, the KMS can be configured with other // options @@ -195,12 +239,11 @@ func buildCreateVolumeResponse(req *csi.CreateVolumeRequest, rbdVol *rbdVolume) ContentSource: req.GetVolumeContentSource(), } if rbdVol.Topology != nil { - volume.AccessibleTopology = - []*csi.Topology{ - { - Segments: rbdVol.Topology, - }, - } + volume.AccessibleTopology = []*csi.Topology{ + { + Segments: rbdVol.Topology, + }, + } } return &csi.CreateVolumeResponse{Volume: volume} @@ -252,7 +295,8 @@ func checkValidCreateVolumeRequest(rbdVol, parentVol *rbdVolume, rbdSnap *rbdSna // CreateVolume creates the volume in backend. func (cs *ControllerServer) CreateVolume( ctx context.Context, - req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { + req *csi.CreateVolumeRequest, +) (*csi.CreateVolumeResponse, error) { err := cs.validateVolumeReq(ctx, req) if err != nil { return nil, err @@ -349,7 +393,8 @@ func flattenParentImage( ctx context.Context, rbdVol *rbdVolume, rbdSnap *rbdSnapshot, - cr *util.Credentials) error { + cr *util.Credentials, +) error { // flatten the image's parent before the reservation to avoid // stale entries in post creation if we return ABORT error and the // DeleteVolume RPC is not called. @@ -417,7 +462,8 @@ func flattenParentImage( // that the state is corrected to what was requested. It is needed to call this // when the process of creating a volume was interrupted. func (cs *ControllerServer) repairExistingVolume(ctx context.Context, req *csi.CreateVolumeRequest, - cr *util.Credentials, rbdVol *rbdVolume, rbdSnap *rbdSnapshot) (*csi.CreateVolumeResponse, error) { + cr *util.Credentials, rbdVol *rbdVolume, rbdSnap *rbdSnapshot, +) (*csi.CreateVolumeResponse, error) { vcs := req.GetVolumeContentSource() switch { @@ -558,7 +604,8 @@ func (cs *ControllerServer) createVolumeFromSnapshot( cr *util.Credentials, secrets map[string]string, rbdVol *rbdVolume, - snapshotID string) error { + snapshotID string, +) error { rbdSnap := &rbdSnapshot{} if acquired := cs.SnapshotLocks.TryAcquire(snapshotID); !acquired { log.ErrorLog(ctx, util.SnapshotOperationAlreadyExistsFmt, snapshotID) @@ -622,7 +669,8 @@ func (cs *ControllerServer) createBackingImage( cr *util.Credentials, secrets map[string]string, rbdVol, parentVol *rbdVolume, - rbdSnap *rbdSnapshot) error { + rbdSnap *rbdSnapshot, +) error { var err error j, err := volJournal.Connect(rbdVol.Monitors, rbdVol.RadosNamespace, cr) @@ -682,7 +730,8 @@ func (cs *ControllerServer) createBackingImage( func checkContentSource( ctx context.Context, req *csi.CreateVolumeRequest, - cr *util.Credentials) (*rbdVolume, *rbdSnapshot, error) { + cr *util.Credentials, +) (*rbdVolume, *rbdSnapshot, error) { if req.VolumeContentSource == nil { return nil, nil, nil } @@ -743,7 +792,8 @@ func (cs *ControllerServer) checkErrAndUndoReserve( ctx context.Context, err error, volumeID string, - rbdVol *rbdVolume, cr *util.Credentials) (*csi.DeleteVolumeResponse, error) { + rbdVol *rbdVolume, cr *util.Credentials, +) (*csi.DeleteVolumeResponse, error) { if errors.Is(err, util.ErrPoolNotFound) { log.WarningLog(ctx, "failed to get backend volume for %s: %v", volumeID, err) @@ -790,7 +840,8 @@ func (cs *ControllerServer) checkErrAndUndoReserve( // from store. func (cs *ControllerServer) DeleteVolume( ctx context.Context, - req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { + req *csi.DeleteVolumeRequest, +) (*csi.DeleteVolumeResponse, error) { var err error if err = cs.Driver.ValidateControllerServiceRequest( csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME); err != nil { @@ -860,7 +911,8 @@ func (cs *ControllerServer) DeleteVolume( // cleanupRBDImage removes the rbd image and OMAP metadata associated with it. func cleanupRBDImage(ctx context.Context, - rbdVol *rbdVolume, cr *util.Credentials) (*csi.DeleteVolumeResponse, error) { + rbdVol *rbdVolume, cr *util.Credentials, +) (*csi.DeleteVolumeResponse, error) { mirroringInfo, err := rbdVol.getImageMirroringInfo() if err != nil { log.ErrorLog(ctx, err.Error()) @@ -954,7 +1006,8 @@ func cleanupRBDImage(ctx context.Context, // are supported. func (cs *ControllerServer) ValidateVolumeCapabilities( ctx context.Context, - req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) { + req *csi.ValidateVolumeCapabilitiesRequest, +) (*csi.ValidateVolumeCapabilitiesResponse, error) { if req.GetVolumeId() == "" { return nil, status.Error(codes.InvalidArgument, "empty volume ID in request") } @@ -980,7 +1033,8 @@ func (cs *ControllerServer) ValidateVolumeCapabilities( // nolint:gocyclo,cyclop // TODO: reduce complexity. func (cs *ControllerServer) CreateSnapshot( ctx context.Context, - req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { + req *csi.CreateSnapshotRequest, +) (*csi.CreateSnapshotResponse, error) { if err := cs.validateSnapshotReq(ctx, req); err != nil { return nil, err } @@ -1079,6 +1133,7 @@ func (cs *ControllerServer) CreateSnapshot( // Update the metadata on snapshot not on the original image rbdVol.RbdImageName = rbdSnap.RbdSnapName + rbdVol.ClusterName = cs.ClusterName err = rbdVol.unsetAllMetadata(k8s.GetVolumeMetadataKeys()) if err != nil { @@ -1110,7 +1165,8 @@ func cloneFromSnapshot( rbdVol *rbdVolume, rbdSnap *rbdSnapshot, cr *util.Credentials, - parameters map[string]string) (*csi.CreateSnapshotResponse, error) { + parameters map[string]string, +) (*csi.CreateSnapshotResponse, error) { vol := generateVolFromSnap(rbdSnap) err := vol.Connect(cr) if err != nil { @@ -1193,7 +1249,8 @@ func (cs *ControllerServer) doSnapshotClone( ctx context.Context, parentVol *rbdVolume, rbdSnap *rbdSnapshot, - cr *util.Credentials) (*rbdVolume, error) { + cr *util.Credentials, +) (*rbdVolume, error) { // generate cloned volume details from snapshot cloneRbd := generateVolFromSnap(rbdSnap) defer cloneRbd.Destroy() @@ -1276,7 +1333,8 @@ func (cs *ControllerServer) doSnapshotClone( // snapshot metadata from store. func (cs *ControllerServer) DeleteSnapshot( ctx context.Context, - req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) { + req *csi.DeleteSnapshotRequest, +) (*csi.DeleteSnapshotResponse, error) { if err := cs.Driver.ValidateControllerServiceRequest( csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT); err != nil { log.ErrorLog(ctx, "invalid delete snapshot req: %v", protosanitizer.StripSecrets(req)) @@ -1417,7 +1475,8 @@ func cleanUpImageAndSnapReservation(ctx context.Context, rbdSnap *rbdSnapshot, c // ControllerExpandVolume expand RBD Volumes on demand based on resizer request. func (cs *ControllerServer) ControllerExpandVolume( ctx context.Context, - req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { + req *csi.ControllerExpandVolumeRequest, +) (*csi.ControllerExpandVolumeResponse, error) { err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_EXPAND_VOLUME) if err != nil { log.ErrorLog(ctx, "invalid expand volume req: %v", protosanitizer.StripSecrets(req)) diff --git a/internal/rbd/controllerserver_test.go b/internal/rbd/controllerserver_test.go new file mode 100644 index 000000000..7570c8ab6 --- /dev/null +++ b/internal/rbd/controllerserver_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2022 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rbd + +import "testing" + +func TestValidateStriping(t *testing.T) { + t.Parallel() + tests := []struct { + name string + parameters map[string]string + wantErr bool + }{ + { + name: "when stripeUnit is not specified", + parameters: map[string]string{ + "stripeUnit": "", + "stripeCount": "10", + "objectSize": "2", + }, + wantErr: true, + }, + { + name: "when stripeCount is not specified", + parameters: map[string]string{ + "stripeUnit": "4096", + "stripeCount": "", + "objectSize": "2", + }, + wantErr: true, + }, + { + name: "when objectSize is not power of 2", + parameters: map[string]string{ + "stripeUnit": "4096", + "stripeCount": "8", + "objectSize": "3", + }, + wantErr: true, + }, + { + name: "when objectSize is 0", + parameters: map[string]string{ + "stripeUnit": "4096", + "stripeCount": "8", + "objectSize": "0", + }, + wantErr: true, + }, + { + name: "when valid stripe parameters are specified", + parameters: map[string]string{ + "stripeUnit": "4096", + "stripeCount": "8", + "objectSize": "131072", + }, + wantErr: false, + }, + { + name: "when no stripe parameters are specified", + parameters: map[string]string{}, + wantErr: false, + }, + } + for _, tt := range tests { + ts := tt + t.Run(ts.name, func(t *testing.T) { + t.Parallel() + if err := validateStriping(ts.parameters); (err != nil) != ts.wantErr { + t.Errorf("validateStriping() error = %v, wantErr %v", err, ts.wantErr) + } + }) + } +} diff --git a/internal/rbd/driver/driver.go b/internal/rbd/driver/driver.go index 06a526819..a74cd2552 100644 --- a/internal/rbd/driver/driver.go +++ b/internal/rbd/driver/driver.go @@ -161,6 +161,7 @@ func (r *Driver) Run(conf *util.Config) { if conf.IsControllerServer { r.cs = NewControllerServer(r.cd) + r.cs.ClusterName = conf.ClusterName r.rs = NewReplicationServer(r.cs) } if !conf.IsControllerServer && !conf.IsNodeServer { diff --git a/internal/rbd/encryption.go b/internal/rbd/encryption.go index 9d22bf060..0b632aa99 100644 --- a/internal/rbd/encryption.go +++ b/internal/rbd/encryption.go @@ -273,7 +273,8 @@ func (ri *rbdImage) initKMS(ctx context.Context, volOptions, credentials map[str // ParseEncryptionOpts returns kmsID and sets Owner attribute. func (ri *rbdImage) ParseEncryptionOpts( ctx context.Context, - volOptions map[string]string) (string, error) { + volOptions map[string]string, +) (string, error) { var ( err error ok bool diff --git a/internal/rbd/identityserver.go b/internal/rbd/identityserver.go index 8582ff6e8..34691c201 100644 --- a/internal/rbd/identityserver.go +++ b/internal/rbd/identityserver.go @@ -33,7 +33,8 @@ type IdentityServer struct { // GetPluginCapabilities returns available capabilities of the rbd driver. func (is *IdentityServer) GetPluginCapabilities( ctx context.Context, - req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { + req *csi.GetPluginCapabilitiesRequest, +) (*csi.GetPluginCapabilitiesResponse, error) { return &csi.GetPluginCapabilitiesResponse{ Capabilities: []*csi.PluginCapability{ { diff --git a/internal/rbd/nodeserver.go b/internal/rbd/nodeserver.go index ddc84cba1..057cf9e44 100644 --- a/internal/rbd/nodeserver.go +++ b/internal/rbd/nodeserver.go @@ -148,7 +148,8 @@ func healerStageTransaction(ctx context.Context, cr *util.Credentials, volOps *r func populateRbdVol( ctx context.Context, req *csi.NodeStageVolumeRequest, - cr *util.Credentials) (*rbdVolume, error) { + cr *util.Credentials, +) (*rbdVolume, error) { var err error var j *journal.Connection volID := req.GetVolumeId() @@ -295,7 +296,8 @@ func populateRbdVol( // - Stage the device (mount the device mapped for image) func (ns *NodeServer) NodeStageVolume( ctx context.Context, - req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { + req *csi.NodeStageVolumeRequest, +) (*csi.NodeStageVolumeResponse, error) { var err error if err = util.ValidateNodeStageVolumeRequest(req); err != nil { return nil, err @@ -384,7 +386,8 @@ func (ns *NodeServer) stageTransaction( req *csi.NodeStageVolumeRequest, cr *util.Credentials, volOptions *rbdVolume, - staticVol bool) (*stageTransaction, error) { + staticVol bool, +) (*stageTransaction, error) { transaction := &stageTransaction{} var err error @@ -466,7 +469,8 @@ func resizeNodeStagePath(ctx context.Context, isBlock bool, transaction *stageTransaction, volID, - stagingTargetPath string) error { + stagingTargetPath string, +) error { var err error devicePath := transaction.devicePath var ok bool @@ -543,7 +547,8 @@ func resizeEncryptedDevice(ctx context.Context, volID, stagingTargetPath, device func flattenImageBeforeMapping( ctx context.Context, - volOptions *rbdVolume) error { + volOptions *rbdVolume, +) error { var err error var feature bool var depth uint @@ -579,7 +584,8 @@ func (ns *NodeServer) undoStagingTransaction( ctx context.Context, req *csi.NodeStageVolumeRequest, transaction *stageTransaction, - volOptions *rbdVolume) { + volOptions *rbdVolume, +) { var err error stagingTargetPath := getStagingTargetPath(req) @@ -661,7 +667,8 @@ func (ns *NodeServer) createStageMountPoint(ctx context.Context, mountPath strin // path. func (ns *NodeServer) NodePublishVolume( ctx context.Context, - req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + req *csi.NodePublishVolumeRequest, +) (*csi.NodePublishVolumeResponse, error) { err := util.ValidateNodePublishVolumeRequest(req) if err != nil { return nil, err @@ -700,7 +707,8 @@ func (ns *NodeServer) mountVolumeToStagePath( ctx context.Context, req *csi.NodeStageVolumeRequest, staticVol bool, - stagingPath, devicePath string) error { + stagingPath, devicePath string, +) error { readOnly := false fsType := req.GetVolumeCapability().GetMount().GetFsType() diskMounter := &mount.SafeFormatAndMount{Interface: ns.Mounter, Exec: utilexec.New()} @@ -841,7 +849,8 @@ func (ns *NodeServer) createTargetMountPath(ctx context.Context, mountPath strin // NodeUnpublishVolume unmounts the volume from the target path. func (ns *NodeServer) NodeUnpublishVolume( ctx context.Context, - req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { + req *csi.NodeUnpublishVolumeRequest, +) (*csi.NodeUnpublishVolumeResponse, error) { err := util.ValidateNodeUnpublishVolumeRequest(req) if err != nil { return nil, err @@ -898,7 +907,8 @@ func getStagingTargetPath(req interface{}) string { // NodeUnstageVolume unstages the volume from the staging path. func (ns *NodeServer) NodeUnstageVolume( ctx context.Context, - req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) { + req *csi.NodeUnstageVolumeRequest, +) (*csi.NodeUnstageVolumeResponse, error) { var err error if err = util.ValidateNodeUnstageVolumeRequest(req); err != nil { return nil, err @@ -1004,7 +1014,8 @@ func (ns *NodeServer) NodeUnstageVolume( // NodeExpandVolume resizes rbd volumes. func (ns *NodeServer) NodeExpandVolume( ctx context.Context, - req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) { + req *csi.NodeExpandVolumeRequest, +) (*csi.NodeExpandVolumeResponse, error) { volumeID := req.GetVolumeId() if volumeID == "" { return nil, status.Error(codes.InvalidArgument, "volume ID must be provided") @@ -1078,7 +1089,8 @@ func (ns *NodeServer) NodeExpandVolume( // NodeGetCapabilities returns the supported capabilities of the node server. func (ns *NodeServer) NodeGetCapabilities( ctx context.Context, - req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { + req *csi.NodeGetCapabilitiesRequest, +) (*csi.NodeGetCapabilitiesResponse, error) { return &csi.NodeGetCapabilitiesResponse{ Capabilities: []*csi.NodeServiceCapability{ { @@ -1116,7 +1128,8 @@ func (ns *NodeServer) NodeGetCapabilities( func (ns *NodeServer) processEncryptedDevice( ctx context.Context, volOptions *rbdVolume, - devicePath string) (string, error) { + devicePath string, +) (string, error) { imageSpec := volOptions.String() encrypted, err := volOptions.checkRbdImageEncrypted(ctx) if err != nil { @@ -1212,7 +1225,8 @@ func (ns *NodeServer) xfsSupportsReflink() bool { // NodeGetVolumeStats returns volume stats. func (ns *NodeServer) NodeGetVolumeStats( ctx context.Context, - req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) { + req *csi.NodeGetVolumeStatsRequest, +) (*csi.NodeGetVolumeStatsResponse, error) { var err error targetPath := req.GetVolumePath() if targetPath == "" { diff --git a/internal/rbd/rbd_attach.go b/internal/rbd/rbd_attach.go index 7afaede51..ad326ac81 100644 --- a/internal/rbd/rbd_attach.go +++ b/internal/rbd/rbd_attach.go @@ -538,7 +538,8 @@ func detachRBDDevice(ctx context.Context, devicePath, volumeID, unmapOptions str // when imageSpec is used to decide if image is already unmapped. func detachRBDImageOrDeviceSpec( ctx context.Context, - dArgs *detachRBDImageArgs) error { + dArgs *detachRBDImageArgs, +) error { if dArgs.encrypted { mapperFile, mapperPath := util.VolumeMapper(dArgs.volumeID) mappedDevice, mapper, err := util.DeviceEncryptionStatus(ctx, mapperPath) diff --git a/internal/rbd/rbd_journal.go b/internal/rbd/rbd_journal.go index 57121c65b..9da5b3ad8 100644 --- a/internal/rbd/rbd_journal.go +++ b/internal/rbd/rbd_journal.go @@ -116,7 +116,8 @@ func checkSnapCloneExists( ctx context.Context, parentVol *rbdVolume, rbdSnap *rbdSnapshot, - cr *util.Credentials) (bool, error) { + cr *util.Credentials, +) (bool, error) { err := validateRbdSnap(rbdSnap) if err != nil { return false, err @@ -392,7 +393,7 @@ func reserveSnap(ctx context.Context, rbdSnap *rbdSnapshot, rbdVol *rbdVolume, c rbdSnap.ReservedID, rbdSnap.RbdSnapName, err = j.ReserveName( ctx, rbdSnap.JournalPool, journalPoolID, rbdSnap.Pool, imagePoolID, - rbdSnap.RequestName, rbdSnap.NamePrefix, rbdVol.RbdImageName, kmsID, rbdSnap.ReservedID, rbdVol.Owner) + rbdSnap.RequestName, rbdSnap.NamePrefix, rbdVol.RbdImageName, kmsID, rbdSnap.ReservedID, rbdVol.Owner, "") if err != nil { return err } @@ -472,7 +473,7 @@ func reserveVol(ctx context.Context, rbdVol *rbdVolume, rbdSnap *rbdSnapshot, cr rbdVol.ReservedID, rbdVol.RbdImageName, err = j.ReserveName( ctx, rbdVol.JournalPool, journalPoolID, rbdVol.Pool, imagePoolID, - rbdVol.RequestName, rbdVol.NamePrefix, "", kmsID, rbdVol.ReservedID, rbdVol.Owner) + rbdVol.RequestName, rbdVol.NamePrefix, "", kmsID, rbdVol.ReservedID, rbdVol.Owner, "") if err != nil { return err } @@ -540,8 +541,10 @@ func RegenerateJournal( claimName, volumeID, requestName, - owner string, - cr *util.Credentials) (string, error) { + owner, + clusterName string, + cr *util.Credentials, +) (string, error) { ctx := context.Background() var ( vi util.CSIIdentifier @@ -553,6 +556,7 @@ func RegenerateJournal( rbdVol = &rbdVolume{} rbdVol.VolID = volumeID + rbdVol.ClusterName = clusterName err = vi.DecomposeCSIID(rbdVol.VolID) if err != nil { @@ -633,7 +637,7 @@ func RegenerateJournal( rbdVol.ReservedID, rbdVol.RbdImageName, err = j.ReserveName( ctx, rbdVol.JournalPool, journalPoolID, rbdVol.Pool, imagePoolID, - rbdVol.RequestName, rbdVol.NamePrefix, "", kmsID, vi.ObjectUUID, rbdVol.Owner) + rbdVol.RequestName, rbdVol.NamePrefix, "", kmsID, vi.ObjectUUID, rbdVol.Owner, "") if err != nil { return "", err } diff --git a/internal/rbd/rbd_util.go b/internal/rbd/rbd_util.go index ce60e3ee3..5f26b2579 100644 --- a/internal/rbd/rbd_util.go +++ b/internal/rbd/rbd_util.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "os" "path/filepath" "strconv" @@ -78,6 +79,9 @@ const ( // krbd attribute file to check supported features. krbdSupportedFeaturesFile = "/sys/bus/rbd/supported_features" + + // clusterNameKey cluster Key, set on RBD image. + clusterNameKey = "csi.ceph.com/cluster/name" ) // rbdImage contains common attributes and methods for the rbdVolume and @@ -96,6 +100,11 @@ type rbdImage struct { // VolSize is the size of the RBD image backing this rbdImage. VolSize int64 + // image striping configurations. + StripeCount uint64 + StripeUnit uint64 + ObjectSize uint64 + Monitors string // JournalPool is the ceph pool in which the CSI Journal/CSI snapshot Journal is // stored @@ -121,6 +130,9 @@ type rbdImage struct { // Primary represent if the image is primary or not. Primary bool + // Cluster name + ClusterName string + // encryption provides access to optional VolumeEncryption functions encryption *util.VolumeEncryption // Owner is the creator (tenant, Kubernetes Namespace) of the volume @@ -402,27 +414,19 @@ func (rs *rbdSnapshot) String() string { // createImage creates a new ceph image with provision and volume options. func createImage(ctx context.Context, pOpts *rbdVolume, cr *util.Credentials) error { volSzMiB := fmt.Sprintf("%dM", util.RoundOffVolSize(pOpts.VolSize)) - options := librbd.NewRbdImageOptions() - logMsg := "rbd: create %s size %s (features: %s) using mon %s" - if pOpts.DataPool != "" { - logMsg += fmt.Sprintf(", data pool %s", pOpts.DataPool) - err := options.SetString(librbd.RbdImageOptionDataPool, pOpts.DataPool) - if err != nil { - return fmt.Errorf("failed to set data pool: %w", err) - } - } - log.DebugLog(ctx, logMsg, + log.DebugLog(ctx, "rbd: create %s size %s (features: %s) using mon %s", pOpts, volSzMiB, pOpts.ImageFeatureSet.Names(), pOpts.Monitors) - if pOpts.ImageFeatureSet != 0 { - err := options.SetUint64(librbd.RbdImageOptionFeatures, uint64(pOpts.ImageFeatureSet)) - if err != nil { - return fmt.Errorf("failed to set image features: %w", err) - } + options := librbd.NewRbdImageOptions() + defer options.Destroy() + + err := pOpts.setImageOptions(ctx, options) + if err != nil { + return err } - err := pOpts.Connect(cr) + err = pOpts.Connect(cr) if err != nil { return err } @@ -722,7 +726,8 @@ func flattenClonedRbdImages( ctx context.Context, snaps []librbd.SnapInfo, pool, monitors, rbdImageName string, - cr *util.Credentials) error { + cr *util.Credentials, +) error { rv := &rbdVolume{} rv.Monitors = monitors rv.Pool = pool @@ -769,7 +774,8 @@ func flattenClonedRbdImages( func (ri *rbdImage) flattenRbdImage( ctx context.Context, forceFlatten bool, - hardlimit, softlimit uint) error { + hardlimit, softlimit uint, +) error { var depth uint var err error @@ -926,7 +932,8 @@ func genSnapFromSnapID( rbdSnap *rbdSnapshot, snapshotID string, cr *util.Credentials, - secrets map[string]string) error { + secrets map[string]string, +) error { var vi util.CSIIdentifier rbdSnap.VolID = snapshotID @@ -1036,7 +1043,8 @@ func generateVolumeFromVolumeID( volumeID string, vi util.CSIIdentifier, cr *util.Credentials, - secrets map[string]string) (*rbdVolume, error) { + secrets map[string]string, +) (*rbdVolume, error) { var ( rbdVol *rbdVolume err error @@ -1123,7 +1131,8 @@ func GenVolFromVolID( ctx context.Context, volumeID string, cr *util.Credentials, - secrets map[string]string) (*rbdVolume, error) { + secrets map[string]string, +) (*rbdVolume, error) { var ( vi util.CSIIdentifier vol *rbdVolume @@ -1165,7 +1174,8 @@ func generateVolumeFromMapping( volumeID string, vi util.CSIIdentifier, cr *util.Credentials, - secrets map[string]string) (*rbdVolume, error) { + secrets map[string]string, +) (*rbdVolume, error) { nvi := vi vol := &rbdVolume{} // extract clusterID mapping @@ -1215,7 +1225,8 @@ func generateVolumeFromMapping( func genVolFromVolumeOptions( ctx context.Context, volOptions map[string]string, - disableInUseChecks, checkClusterIDMapping bool) (*rbdVolume, error) { + disableInUseChecks, checkClusterIDMapping bool, +) (*rbdVolume, error) { var ( ok bool err error @@ -1267,9 +1278,40 @@ func genVolFromVolumeOptions( rbdVol.Mounter) rbdVol.DisableInUseChecks = disableInUseChecks + err = rbdVol.setStripeConfiguration(volOptions) + if err != nil { + return nil, err + } + return rbdVol, nil } +func (ri *rbdImage) setStripeConfiguration(options map[string]string) error { + var err error + if val, ok := options["stripeUnit"]; ok { + ri.StripeUnit, err = strconv.ParseUint(val, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse stripeUnit %s: %w", val, err) + } + } + + if val, ok := options["stripeCount"]; ok { + ri.StripeCount, err = strconv.ParseUint(val, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse stripeCount %s: %w", val, err) + } + } + + if val, ok := options["objectSize"]; ok { + ri.ObjectSize, err = strconv.ParseUint(val, 10, 64) + if err != nil { + return fmt.Errorf("failed to parse objectSize %s: %w", val, err) + } + } + + return nil +} + func (rv *rbdVolume) validateImageFeatures(imageFeatures string) error { // It is possible for image features to be an empty string which // the Go split function would return a single item array with @@ -1368,9 +1410,11 @@ func (ri *rbdImage) deleteSnapshot(ctx context.Context, pOpts *rbdSnapshot) erro func (rv *rbdVolume) cloneRbdImageFromSnapshot( ctx context.Context, pSnapOpts *rbdSnapshot, - parentVol *rbdVolume) error { + parentVol *rbdVolume, +) error { var err error - logMsg := "rbd: clone %s %s (features: %s) using mon %s" + log.DebugLog(ctx, "rbd: clone %s %s (features: %s) using mon %s", + pSnapOpts, rv, rv.ImageFeatureSet.Names(), rv.Monitors) err = parentVol.openIoctx() if err != nil { @@ -1383,30 +1427,15 @@ func (rv *rbdVolume) cloneRbdImageFromSnapshot( options := librbd.NewRbdImageOptions() defer options.Destroy() - - if rv.DataPool != "" { - logMsg += fmt.Sprintf(", data pool %s", rv.DataPool) - err = options.SetString(librbd.RbdImageOptionDataPool, rv.DataPool) - if err != nil { - return fmt.Errorf("failed to set data pool: %w", err) - } - } - - log.DebugLog(ctx, logMsg, - pSnapOpts, rv, rv.ImageFeatureSet.Names(), rv.Monitors) - - if rv.ImageFeatureSet != 0 { - err = options.SetUint64(librbd.RbdImageOptionFeatures, uint64(rv.ImageFeatureSet)) - if err != nil { - return fmt.Errorf("failed to set image features: %w", err) - } + err = rv.setImageOptions(ctx, options) + if err != nil { + return err } err = options.SetUint64(librbd.ImageOptionCloneFormat, 2) if err != nil { - return fmt.Errorf("failed to set image features: %w", err) + return err } - // As the clone is yet to be created, open the Ioctx. err = rv.openIoctx() if err != nil { @@ -1447,6 +1476,52 @@ func (rv *rbdVolume) cloneRbdImageFromSnapshot( return nil } +// setImageOptions sets the image options. +func (rv *rbdVolume) setImageOptions(ctx context.Context, options *librbd.ImageOptions) error { + var err error + + logMsg := fmt.Sprintf("setting image options on %s", rv) + if rv.DataPool != "" { + logMsg += fmt.Sprintf(", data pool %s", rv.DataPool) + err = options.SetString(librbd.RbdImageOptionDataPool, rv.DataPool) + if err != nil { + return fmt.Errorf("failed to set data pool: %w", err) + } + } + + if rv.ImageFeatureSet != 0 { + err = options.SetUint64(librbd.RbdImageOptionFeatures, uint64(rv.ImageFeatureSet)) + if err != nil { + return fmt.Errorf("failed to set image features: %w", err) + } + } + + if rv.StripeCount != 0 { + logMsg += fmt.Sprintf(", stripe count %d, stripe unit %d", rv.StripeCount, rv.StripeUnit) + err = options.SetUint64(librbd.RbdImageOptionStripeCount, rv.StripeCount) + if err != nil { + return fmt.Errorf("failed to set stripe count: %w", err) + } + err = options.SetUint64(librbd.RbdImageOptionStripeUnit, rv.StripeUnit) + if err != nil { + return fmt.Errorf("failed to set stripe unit: %w", err) + } + } + + if rv.ObjectSize != 0 { + order := uint64(math.Log2(float64(rv.ObjectSize))) + logMsg += fmt.Sprintf(", object size %d, order %d", rv.ObjectSize, order) + err = options.SetUint64(librbd.RbdImageOptionOrder, order) + if err != nil { + return fmt.Errorf("failed to set object size: %w", err) + } + } + + log.DebugLog(ctx, logMsg) + + return nil +} + // getImageInfo queries rbd about the given image and returns its metadata, and returns // ErrImageNotFound if provided image is not found. func (ri *rbdImage) getImageInfo() error { @@ -1904,7 +1979,8 @@ func (ri *rbdImage) isCompabitableClone(dst *rbdImage) error { func (ri *rbdImage) addSnapshotScheduling( interval admin.Interval, - startTime admin.StartTime) error { + startTime admin.StartTime, +) error { ls := admin.NewLevelSpec(ri.Pool, ri.RadosNamespace, ri.RbdImageName) ra, err := ri.conn.GetRBDAdmin() if err != nil { @@ -1965,7 +2041,8 @@ func strategicActionOnLogFile(ctx context.Context, logStrategy, logFile string) // genVolFromVolIDWithMigration populate a rbdVol structure based on the volID format. func genVolFromVolIDWithMigration( - ctx context.Context, volID string, cr *util.Credentials, secrets map[string]string) (*rbdVolume, error) { + ctx context.Context, volID string, cr *util.Credentials, secrets map[string]string, +) (*rbdVolume, error) { if isMigrationVolID(volID) { pmVolID, pErr := parseMigrationVolID(volID) if pErr != nil { @@ -1991,6 +2068,14 @@ func (rv *rbdVolume) setAllMetadata(parameters map[string]string) error { } } + if rv.ClusterName != "" { + err := rv.SetMetadata(clusterNameKey, rv.ClusterName) + if err != nil { + return fmt.Errorf("failed to set metadata key %q, value %q on image: %w", + clusterNameKey, rv.ClusterName, err) + } + } + return nil } @@ -2004,5 +2089,11 @@ func (rv *rbdVolume) unsetAllMetadata(keys []string) error { } } + err := rv.RemoveMetadata(clusterNameKey) + // TODO: replace string comparison with errno. + if err != nil && !strings.Contains(err.Error(), "No such file or directory") { + return fmt.Errorf("failed to unset metadata key %q on %q: %w", clusterNameKey, rv, err) + } + return nil } diff --git a/internal/rbd/replicationcontrollerserver.go b/internal/rbd/replicationcontrollerserver.go index 51068daed..f84b0c7a1 100644 --- a/internal/rbd/replicationcontrollerserver.go +++ b/internal/rbd/replicationcontrollerserver.go @@ -457,7 +457,8 @@ func (rs *ReplicationServer) DisableVolumeReplication(ctx context.Context, func disableVolumeReplication(rbdVol *rbdVolume, mirroringInfo *librbd.MirrorImageInfo, - force bool) (*replication.DisableVolumeReplicationResponse, error) { + force bool, +) (*replication.DisableVolumeReplicationResponse, error) { if !mirroringInfo.Primary { // Return success if the below condition is met // Local image is secondary @@ -913,9 +914,8 @@ func resyncRequired(localStatus librbd.SiteMirrorImageStatus) bool { // In some corner cases like `re-player shutdown` the local image will not // be in an error state. It would be also worth considering the `description` // field to make sure about split-brain. - splitBrain := "split-brain" if localStatus.State == librbd.MirrorImageStatusStateError || - strings.Contains(localStatus.Description, splitBrain) { + strings.Contains(localStatus.Description, "split-brain") { return true } diff --git a/internal/rbd/snapshot.go b/internal/rbd/snapshot.go index ddb5387b9..089946757 100644 --- a/internal/rbd/snapshot.go +++ b/internal/rbd/snapshot.go @@ -27,7 +27,8 @@ import ( func createRBDClone( ctx context.Context, parentVol, cloneRbdVol *rbdVolume, - snap *rbdSnapshot) error { + snap *rbdSnapshot, +) error { // create snapshot err := parentVol.createSnapshot(ctx, snap) if err != nil { @@ -72,7 +73,8 @@ func cleanUpSnapshot( ctx context.Context, parentVol *rbdVolume, rbdSnap *rbdSnapshot, - rbdVol *rbdVolume) error { + rbdVol *rbdVolume, +) error { err := parentVol.deleteSnapshot(ctx, rbdSnap) if err != nil { if !errors.Is(err, ErrSnapNotFound) { @@ -119,7 +121,8 @@ func undoSnapshotCloning( parentVol *rbdVolume, rbdSnap *rbdSnapshot, cloneVol *rbdVolume, - cr *util.Credentials) error { + cr *util.Credentials, +) error { err := cleanUpSnapshot(ctx, parentVol, rbdSnap, cloneVol) if err != nil { log.ErrorLog(ctx, "failed to clean up %s or %s: %v", cloneVol, rbdSnap, err) diff --git a/internal/util/cephcmds.go b/internal/util/cephcmds.go index 924024d46..d1b6dd9e8 100644 --- a/internal/util/cephcmds.go +++ b/internal/util/cephcmds.go @@ -119,7 +119,8 @@ func ExecCommandWithTimeout( args ...string) ( string, string, - error) { + error, +) { var ( sanitizedArgs = StripSecretInArgs(args) stdoutBuf bytes.Buffer diff --git a/internal/util/cluster_mapping.go b/internal/util/cluster_mapping.go index e9ec753c0..0f06dfb5a 100644 --- a/internal/util/cluster_mapping.go +++ b/internal/util/cluster_mapping.go @@ -139,7 +139,8 @@ func GetMappedID(key, value, id string) string { // fetchMappedClusterIDAndMons returns monitors and clusterID info after checking cluster mapping. func fetchMappedClusterIDAndMons(ctx context.Context, - clusterID, clusterMappingConfigFile, csiConfigFile string) (string, string, error) { + clusterID, clusterMappingConfigFile, csiConfigFile string, +) (string, string, error) { var mons string clusterMappingInfo, err := getClusterMappingInfo(clusterID, clusterMappingConfigFile) if err != nil { diff --git a/internal/util/crypto.go b/internal/util/crypto.go index 7f30879ea..bec6b18a8 100644 --- a/internal/util/crypto.go +++ b/internal/util/crypto.go @@ -187,9 +187,9 @@ func generateNewEncryptionPassphrase() (string, error) { } // VolumeMapper returns file name and it's path to where encrypted device should be open. -func VolumeMapper(volumeID string) (mapperFile, mapperFilePath string) { - mapperFile = mapperFilePrefix + volumeID - mapperFilePath = path.Join(mapperFilePathPrefix, mapperFile) +func VolumeMapper(volumeID string) (string, string) { + mapperFile := mapperFilePrefix + volumeID + mapperFilePath := path.Join(mapperFilePathPrefix, mapperFile) return mapperFile, mapperFilePath } @@ -248,7 +248,7 @@ func IsDeviceOpen(ctx context.Context, device string) (bool, error) { // DeviceEncryptionStatus looks to identify if the passed device is a LUKS mapping // and if so what the device is and the mapper name as used by LUKS. // If not, just returns the original device and an empty string. -func DeviceEncryptionStatus(ctx context.Context, devicePath string) (mappedDevice, mapper string, err error) { +func DeviceEncryptionStatus(ctx context.Context, devicePath string) (string, string, error) { if !strings.HasPrefix(devicePath, mapperFilePathPrefix) { return devicePath, "", nil } @@ -274,7 +274,7 @@ func DeviceEncryptionStatus(ctx context.Context, devicePath string) (mappedDevic return "", "", fmt.Errorf("device encryption status output for %s is badly formatted: %s", devicePath, lines[i]) } - if strings.Compare(kv[0], "device") == 0 { + if kv[0] == "device" { return strings.TrimSpace(kv[1]), mapPath, nil } } diff --git a/internal/util/k8s/parameters.go b/internal/util/k8s/parameters.go index 2306bcafd..f212e3915 100644 --- a/internal/util/k8s/parameters.go +++ b/internal/util/k8s/parameters.go @@ -26,14 +26,14 @@ const ( // PV and PVC metadata keys used by external provisioner as part of // create requests as parameters, when `extra-create-metadata` is true. - pvcNameKey = "csi.storage.k8s.io/pvc/name" - pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace" - pvNameKey = "csi.storage.k8s.io/pv/name" + pvcNameKey = csiParameterPrefix + "pvc/name" + pvcNamespaceKey = csiParameterPrefix + "pvc/namespace" + pvNameKey = csiParameterPrefix + "pv/name" // snapshot metadata keys. - volSnapNameKey = "csi.storage.k8s.io/volumesnapshot/name" - volSnapNamespaceKey = "csi.storage.k8s.io/volumesnapshot/namespace" - volSnapContentNameKey = "csi.storage.k8s.io/volumesnapshotcontent/name" + volSnapNameKey = csiParameterPrefix + "volumesnapshot/name" + volSnapNamespaceKey = csiParameterPrefix + "volumesnapshot/namespace" + volSnapContentNameKey = csiParameterPrefix + "volumesnapshotcontent/name" ) // RemoveCSIPrefixedParameters removes parameters prefixed with csiParameterPrefix. diff --git a/internal/util/k8s/version.go b/internal/util/k8s/version.go new file mode 100644 index 000000000..9a5e3c98d --- /dev/null +++ b/internal/util/k8s/version.go @@ -0,0 +1,45 @@ +/* +Copyright 2022 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8s + +import ( + "fmt" + "strconv" + + "k8s.io/client-go/kubernetes" +) + +// GetServerVersion returns kubernetes server major and minor version as +// integer. +func GetServerVersion(client *kubernetes.Clientset) (int, int, error) { + version, err := client.ServerVersion() + if err != nil { + return 0, 0, fmt.Errorf("failed to get ServerVersion: %w", err) + } + + major, err := strconv.Atoi(version.Major) + if err != nil { + return 0, 0, fmt.Errorf("failed to convert Kubernetes major version %q to int: %w", version.Major, err) + } + + minor, err := strconv.Atoi(version.Minor) + if err != nil { + return 0, 0, fmt.Errorf("failed to convert Kubernetes minor version %q to int: %w", version.Minor, err) + } + + return major, minor, nil +} diff --git a/internal/util/topology.go b/internal/util/topology.go index 73ae9728b..c6d53e111 100644 --- a/internal/util/topology.go +++ b/internal/util/topology.go @@ -141,7 +141,8 @@ type TopologyConstrainedPool struct { // GetTopologyFromRequest extracts TopologyConstrainedPools and passed in accessibility constraints // from a CSI CreateVolume request. func GetTopologyFromRequest( - req *csi.CreateVolumeRequest) (*[]TopologyConstrainedPool, *csi.TopologyRequirement, error) { + req *csi.CreateVolumeRequest, +) (*[]TopologyConstrainedPool, *csi.TopologyRequirement, error) { var topologyPools []TopologyConstrainedPool // check if parameters have pool configuration pertaining to topology @@ -171,7 +172,8 @@ func GetTopologyFromRequest( // MatchPoolAndTopology returns the topology map, if the passed in pool matches any // passed in accessibility constraints. func MatchPoolAndTopology(topologyPools *[]TopologyConstrainedPool, - accessibilityRequirements *csi.TopologyRequirement, poolName string) (string, string, map[string]string, error) { + accessibilityRequirements *csi.TopologyRequirement, poolName string, +) (string, string, map[string]string, error) { var topologyPool []TopologyConstrainedPool if topologyPools == nil || accessibilityRequirements == nil { @@ -199,7 +201,8 @@ func MatchPoolAndTopology(topologyPools *[]TopologyConstrainedPool, // The return variables are, image poolname, data poolname, and topology map of // matched requirement. func FindPoolAndTopology(topologyPools *[]TopologyConstrainedPool, - accessibilityRequirements *csi.TopologyRequirement) (string, string, map[string]string, error) { + accessibilityRequirements *csi.TopologyRequirement, +) (string, string, map[string]string, error) { if topologyPools == nil || accessibilityRequirements == nil { return "", "", nil, nil } diff --git a/internal/util/util.go b/internal/util/util.go index b2fda13a8..f8d81a3d4 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -121,6 +121,9 @@ type Config struct { // CSI-Addons endpoint CSIAddonsEndpoint string + + // Cluster name + ClusterName string } // ValidateDriverName validates the driver name. @@ -260,7 +263,8 @@ func GenerateVolID( cr *Credentials, locationID int64, pool, clusterID, objUUID string, - volIDVersion uint16) (string, error) { + volIDVersion uint16, +) (string, error) { var err error if locationID == InvalidPoolID { diff --git a/internal/util/volid.go b/internal/util/volid.go index 2c875bb7c..f0b62ce41 100644 --- a/internal/util/volid.go +++ b/internal/util/volid.go @@ -99,7 +99,7 @@ func (ci CSIIdentifier) ComposeCSIID() (string, error) { /* DecomposeCSIID composes a CSIIdentifier from passed in string. */ -func (ci *CSIIdentifier) DecomposeCSIID(composedCSIID string) (err error) { +func (ci *CSIIdentifier) DecomposeCSIID(composedCSIID string) error { bytesToProcess := uint16(len(composedCSIID)) // if length is less that expected constant elements, then bail out! diff --git a/scripts/golangci.yml.in b/scripts/golangci.yml.in index 7af2c05b2..d73ff8917 100644 --- a/scripts/golangci.yml.in +++ b/scripts/golangci.yml.in @@ -190,3 +190,7 @@ linters: - tagliatelle - varnamelen - nilnil + # TODO enable linters added in golangci-lint 1.46 + - maintidx + - exhaustruct + - containedctx diff --git a/scripts/rook.sh b/scripts/rook.sh index e9a644131..4d732c270 100755 --- a/scripts/rook.sh +++ b/scripts/rook.sh @@ -205,6 +205,11 @@ function check_rbd_stat() { RBD_POOL_NAME="device_health_metrics" fi + # Rook v1.9.x creates pool with name .mgr for builtin-mgr CephBlockPool CR + if [[ "${RBD_POOL_NAME}" == "builtin-mgr" ]]; then + RBD_POOL_NAME=".mgr" + fi + echo "Checking RBD ($RBD_POOL_NAME) stats... ${retry}s" && sleep 5 TOOLBOX_POD=$(kubectl_retry -n rook-ceph get pods -l app=rook-ceph-tools -o jsonpath='{.items[0].metadata.name}') diff --git a/troubleshooting/tools/tracevol.py b/troubleshooting/tools/tracevol.py index 84d8fcaa9..45d96099b 100755 --- a/troubleshooting/tools/tracevol.py +++ b/troubleshooting/tools/tracevol.py @@ -185,7 +185,7 @@ def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): """ validate pvc information in rados """ - omapkey = 'csi.volume.%s' % pvc_name + omapkey = f'csi.volume.{pvc_name}' cmd = ['rados', 'getomapval', 'csi.volumes.default', omapkey, "--pool", pool_name] if not arg.userkey: @@ -212,8 +212,8 @@ def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): name += part[-1] if name.decode() != image_id: if arg.debug: - print("expected image Id %s found Id in rados %s" % - (image_id, name.decode())) + decoded_name = name.decode() + print(f"expected image Id {image_id} found Id in rados {decoded_name}") return False return True @@ -246,7 +246,7 @@ def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): """ validate image uuid in rados """ - omapkey = 'csi.volume.%s' % image_id + omapkey = f'csi.volume.{image_id}' cmd = ['rados', 'getomapval', omapkey, "csi.volname", "--pool", pool_name] if not arg.userkey: cmd += ["--id", arg.userid, "--key", arg.userkey] @@ -276,8 +276,8 @@ def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): name += part[-1] if name.decode() != pvc_name: if arg.debug: - print("expected image Id %s found Id in rados %s" % - (pvc_name, name.decode())) + decoded_name = name.decode() + print(f"expected image Id {pvc_name} found Id in rados {decoded_name}") return False return True @@ -565,7 +565,7 @@ def get_fsname_from_pvdata(arg, pvdata): if __name__ == "__main__": ARGS = PARSER.parse_args() if ARGS.command not in ["kubectl", "oc"]: - print("%s command not supported" % ARGS.command) + print(f"{ARGS.command} command not supported") sys.exit(1) if sys.version_info[0] < 3: print("python version less than 3 is not supported.") diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go index 8dd3973bf..4f24b157b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go @@ -3,4 +3,4 @@ package aws // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.16.3" +const goModuleVersion = "1.16.5" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/doc.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/doc.go index 42ced06e2..1d019ca61 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/doc.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/doc.go @@ -2,9 +2,9 @@ // // Retryer Interface and Implementations // -// This packages defines Retryer interface that is used to either implement custom retry behavior -// or to extend the existing retry implementations provided by the SDK. This packages provides a single -// retry implementations: Standard. +// This package defines Retryer interface that is used to either implement custom retry behavior +// or to extend the existing retry implementations provided by the SDK. This package provides a single +// retry implementation: Standard. // // Standard // @@ -33,7 +33,7 @@ // value. // // You can configure the standard retryer implementation to fit your applications by constructing a standard retryer -// using the NewStandard function, and providing one more functional arguments that mutate the StandardOptions +// using the NewStandard function, and providing one more functional argument that mutate the StandardOptions // structure. StandardOptions provides the ability to modify the token bucket rate limiter, retryable error conditions, // and the retry delay policy. // @@ -71,7 +71,7 @@ // standard retryer. // // IsErrorRetryableFunc - Can be used to wrap a function to satisfy the IsErrorRetryable interface. For example, -// this can be used to extend the standard retryer to add additional logic ot determine if a +// this can be used to extend the standard retryer to add additional logic to determine if an // error should be retried. // // IsErrorTimeoutFunc - Can be used to wrap a function to satisfy IsErrorTimeout interface. For example, diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md index 3ab362b42..ded193970 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.1.12 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.11 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.1.10 (2022-04-25) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go index 23ed79f51..edd39d566 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go @@ -3,4 +3,4 @@ package configsources // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.1.10" +const goModuleVersion = "1.1.12" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md index ff627acb0..9ee12e5c6 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md @@ -1,3 +1,11 @@ +# v2.4.6 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.4.5 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + # v2.4.4 (2022-04-25) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go index 79bdc8efd..f50f5d2b4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go @@ -3,4 +3,4 @@ package endpoints // goModuleVersion is the tagged release for this module -const goModuleVersion = "2.4.4" +const goModuleVersion = "2.4.6" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md index ebcc6a461..15abe58b3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.9.6 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.5 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.9.4 (2022-04-25) * **Dependency Update**: Updated to the latest SDK module versions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go index a312a9efa..016b48238 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go @@ -3,4 +3,4 @@ package presignedurl // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.9.4" +const goModuleVersion = "1.9.6" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md index 6b2cd061d..433e39c63 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md @@ -1,3 +1,11 @@ +# v1.16.7 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.6 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.16.5 (2022-05-16) * **Documentation**: Documentation updates for AWS Security Token Service. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go index 8fce8f4fa..f169e953b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go @@ -3,4 +3,4 @@ package sts // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.16.5" +const goModuleVersion = "1.16.7" diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 97b59b122..fa4f51124 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -1937,9 +1937,15 @@ var awsPartition = partition{ }, "app-integrations": service{ Endpoints: serviceEndpoints{ + endpointKey{ + Region: "af-south-1", + }: endpoint{}, endpointKey{ Region: "ap-northeast-1", }: endpoint{}, + endpointKey{ + Region: "ap-northeast-2", + }: endpoint{}, endpointKey{ Region: "ap-southeast-1", }: endpoint{}, @@ -3046,6 +3052,73 @@ var awsPartition = partition{ }: endpoint{}, }, }, + "backup-gateway": service{ + Endpoints: serviceEndpoints{ + endpointKey{ + Region: "af-south-1", + }: endpoint{}, + endpointKey{ + Region: "ap-east-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-2", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-3", + }: endpoint{}, + endpointKey{ + Region: "ap-south-1", + }: endpoint{}, + endpointKey{ + Region: "ap-southeast-1", + }: endpoint{}, + endpointKey{ + Region: "ap-southeast-2", + }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + }: endpoint{}, + endpointKey{ + Region: "eu-central-1", + }: endpoint{}, + endpointKey{ + Region: "eu-north-1", + }: endpoint{}, + endpointKey{ + Region: "eu-south-1", + }: endpoint{}, + endpointKey{ + Region: "eu-west-1", + }: endpoint{}, + endpointKey{ + Region: "eu-west-2", + }: endpoint{}, + endpointKey{ + Region: "eu-west-3", + }: endpoint{}, + endpointKey{ + Region: "me-south-1", + }: endpoint{}, + endpointKey{ + Region: "sa-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-east-2", + }: endpoint{}, + endpointKey{ + Region: "us-west-1", + }: endpoint{}, + endpointKey{ + Region: "us-west-2", + }: endpoint{}, + }, + }, "batch": service{ Defaults: endpointDefaults{ defaultKey{}: endpoint{}, @@ -3080,6 +3153,9 @@ var awsPartition = partition{ endpointKey{ Region: "ap-southeast-2", }: endpoint{}, + endpointKey{ + Region: "ap-southeast-3", + }: endpoint{}, endpointKey{ Region: "ca-central-1", }: endpoint{}, @@ -8314,24 +8390,96 @@ var awsPartition = partition{ }, "email": service{ Endpoints: serviceEndpoints{ + endpointKey{ + Region: "af-south-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-2", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-3", + }: endpoint{}, endpointKey{ Region: "ap-south-1", }: endpoint{}, + endpointKey{ + Region: "ap-southeast-1", + }: endpoint{}, endpointKey{ Region: "ap-southeast-2", }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + }: endpoint{}, endpointKey{ Region: "eu-central-1", }: endpoint{}, + endpointKey{ + Region: "eu-north-1", + }: endpoint{}, + endpointKey{ + Region: "eu-south-1", + }: endpoint{}, endpointKey{ Region: "eu-west-1", }: endpoint{}, + endpointKey{ + Region: "eu-west-2", + }: endpoint{}, + endpointKey{ + Region: "eu-west-3", + }: endpoint{}, + endpointKey{ + Region: "fips-us-east-1", + }: endpoint{ + Hostname: "email-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + Deprecated: boxedTrue, + }, + endpointKey{ + Region: "fips-us-west-2", + }: endpoint{ + Hostname: "email-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + Deprecated: boxedTrue, + }, + endpointKey{ + Region: "me-south-1", + }: endpoint{}, + endpointKey{ + Region: "sa-east-1", + }: endpoint{}, endpointKey{ Region: "us-east-1", }: endpoint{}, + endpointKey{ + Region: "us-east-1", + Variant: fipsVariant, + }: endpoint{ + Hostname: "email-fips.us-east-1.amazonaws.com", + }, + endpointKey{ + Region: "us-east-2", + }: endpoint{}, + endpointKey{ + Region: "us-west-1", + }: endpoint{}, endpointKey{ Region: "us-west-2", }: endpoint{}, + endpointKey{ + Region: "us-west-2", + Variant: fipsVariant, + }: endpoint{ + Hostname: "email-fips.us-west-2.amazonaws.com", + }, }, }, "emr-containers": service{ @@ -8461,6 +8609,52 @@ var awsPartition = partition{ }, }, }, + "emr-serverless": service{ + Endpoints: serviceEndpoints{ + endpointKey{ + Region: "ap-northeast-1", + }: endpoint{}, + endpointKey{ + Region: "eu-west-1", + }: endpoint{}, + endpointKey{ + Region: "fips-us-east-1", + }: endpoint{ + Hostname: "emr-serverless-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + Deprecated: boxedTrue, + }, + endpointKey{ + Region: "fips-us-west-2", + }: endpoint{ + Hostname: "emr-serverless-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + Deprecated: boxedTrue, + }, + endpointKey{ + Region: "us-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-east-1", + Variant: fipsVariant, + }: endpoint{ + Hostname: "emr-serverless-fips.us-east-1.amazonaws.com", + }, + endpointKey{ + Region: "us-west-2", + }: endpoint{}, + endpointKey{ + Region: "us-west-2", + Variant: fipsVariant, + }: endpoint{ + Hostname: "emr-serverless-fips.us-west-2.amazonaws.com", + }, + }, + }, "entitlement.marketplace": service{ Defaults: endpointDefaults{ defaultKey{}: endpoint{ @@ -10475,14 +10669,6 @@ var awsPartition = partition{ }, }, "health": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - Defaults: endpointDefaults{ - defaultKey{}: endpoint{ - SSLCommonName: "health.us-east-1.amazonaws.com", - Protocols: []string{"https"}, - }, - }, Endpoints: serviceEndpoints{ endpointKey{ Region: "fips-us-east-2", @@ -11027,6 +11213,9 @@ var awsPartition = partition{ endpointKey{ Region: "ap-southeast-2", }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + }: endpoint{}, endpointKey{ Region: "eu-central-1", }: endpoint{}, @@ -11089,6 +11278,14 @@ var awsPartition = partition{ Region: "ap-southeast-2", }, }, + endpointKey{ + Region: "ca-central-1", + }: endpoint{ + Hostname: "data.iotevents.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, endpointKey{ Region: "eu-central-1", }: endpoint{ @@ -11297,12 +11494,30 @@ var awsPartition = partition{ endpointKey{ Region: "ap-southeast-2", }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + Variant: fipsVariant, + }: endpoint{ + Hostname: "iotsitewise-fips.ca-central-1.amazonaws.com", + }, endpointKey{ Region: "eu-central-1", }: endpoint{}, endpointKey{ Region: "eu-west-1", }: endpoint{}, + endpointKey{ + Region: "fips-ca-central-1", + }: endpoint{ + Hostname: "iotsitewise-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + Deprecated: boxedTrue, + }, endpointKey{ Region: "fips-us-east-1", }: endpoint{ @@ -11312,6 +11527,15 @@ var awsPartition = partition{ }, Deprecated: boxedTrue, }, + endpointKey{ + Region: "fips-us-east-2", + }: endpoint{ + Hostname: "iotsitewise-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + Deprecated: boxedTrue, + }, endpointKey{ Region: "fips-us-west-2", }: endpoint{ @@ -11330,6 +11554,15 @@ var awsPartition = partition{ }: endpoint{ Hostname: "iotsitewise-fips.us-east-1.amazonaws.com", }, + endpointKey{ + Region: "us-east-2", + }: endpoint{}, + endpointKey{ + Region: "us-east-2", + Variant: fipsVariant, + }: endpoint{ + Hostname: "iotsitewise-fips.us-east-2.amazonaws.com", + }, endpointKey{ Region: "us-west-2", }: endpoint{}, @@ -13770,6 +14003,66 @@ var awsPartition = partition{ }, }, }, + "memory-db": service{ + Endpoints: serviceEndpoints{ + endpointKey{ + Region: "ap-east-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-1", + }: endpoint{}, + endpointKey{ + Region: "ap-northeast-2", + }: endpoint{}, + endpointKey{ + Region: "ap-south-1", + }: endpoint{}, + endpointKey{ + Region: "ap-southeast-1", + }: endpoint{}, + endpointKey{ + Region: "ap-southeast-2", + }: endpoint{}, + endpointKey{ + Region: "ca-central-1", + }: endpoint{}, + endpointKey{ + Region: "eu-central-1", + }: endpoint{}, + endpointKey{ + Region: "eu-north-1", + }: endpoint{}, + endpointKey{ + Region: "eu-west-1", + }: endpoint{}, + endpointKey{ + Region: "eu-west-2", + }: endpoint{}, + endpointKey{ + Region: "fips", + }: endpoint{ + Hostname: "memory-db-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + endpointKey{ + Region: "sa-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-east-2", + }: endpoint{}, + endpointKey{ + Region: "us-west-1", + }: endpoint{}, + endpointKey{ + Region: "us-west-2", + }: endpoint{}, + }, + }, "messaging-chime": service{ Endpoints: serviceEndpoints{ endpointKey{ @@ -14728,6 +15021,14 @@ var awsPartition = partition{ Region: "ap-northeast-2", }, }, + endpointKey{ + Region: "ap-northeast-3", + }: endpoint{ + Hostname: "oidc.ap-northeast-3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-3", + }, + }, endpointKey{ Region: "ap-south-1", }: endpoint{ @@ -15421,6 +15722,14 @@ var awsPartition = partition{ Region: "ap-northeast-2", }, }, + endpointKey{ + Region: "ap-northeast-3", + }: endpoint{ + Hostname: "portal.sso.ap-northeast-3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-3", + }, + }, endpointKey{ Region: "ap-south-1", }: endpoint{ @@ -23828,6 +24137,16 @@ var awscnPartition = partition{ }, }, }, + "memory-db": service{ + Endpoints: serviceEndpoints{ + endpointKey{ + Region: "cn-north-1", + }: endpoint{}, + endpointKey{ + Region: "cn-northwest-1", + }: endpoint{}, + }, + }, "monitoring": service{ Defaults: endpointDefaults{ defaultKey{}: endpoint{ @@ -25059,6 +25378,16 @@ var awsusgovPartition = partition{ }: endpoint{}, }, }, + "backup-gateway": service{ + Endpoints: serviceEndpoints{ + endpointKey{ + Region: "us-gov-east-1", + }: endpoint{}, + endpointKey{ + Region: "us-gov-west-1", + }: endpoint{}, + }, + }, "batch": service{ Defaults: endpointDefaults{ defaultKey{}: endpoint{}, @@ -28370,42 +28699,12 @@ var awsusgovPartition = partition{ }, }, Endpoints: serviceEndpoints{ - endpointKey{ - Region: "fips-us-gov-east-1", - }: endpoint{ - Hostname: "servicecatalog-appregistry.us-gov-east-1.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-gov-east-1", - }, - Deprecated: boxedTrue, - }, - endpointKey{ - Region: "fips-us-gov-west-1", - }: endpoint{ - Hostname: "servicecatalog-appregistry.us-gov-west-1.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-gov-west-1", - }, - Deprecated: boxedTrue, - }, endpointKey{ Region: "us-gov-east-1", }: endpoint{}, - endpointKey{ - Region: "us-gov-east-1", - Variant: fipsVariant, - }: endpoint{ - Hostname: "servicecatalog-appregistry.us-gov-east-1.amazonaws.com", - }, endpointKey{ Region: "us-gov-west-1", }: endpoint{}, - endpointKey{ - Region: "us-gov-west-1", - Variant: fipsVariant, - }: endpoint{ - Hostname: "servicecatalog-appregistry.us-gov-west-1.amazonaws.com", - }, }, }, "servicediscovery": service{ diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go index 086dbce9c..2b7294f78 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.44.20" +const SDKVersion = "1.44.28" diff --git a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go index 9e37d6598..04170f403 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/ec2/api.go @@ -5708,7 +5708,7 @@ func (c *EC2) CreateLaunchTemplateRequest(input *CreateLaunchTemplateInput) (req // A launch template contains the parameters to launch an instance. When you // launch an instance using RunInstances, you can specify a launch template // instead of providing the launch parameters in the request. For more information, -// see Launching an instance from a launch template (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html) +// see Launch an instance from a launch template (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html) // in the Amazon Elastic Compute Cloud User Guide. // // If you want to clone an existing launch template as the basis for creating @@ -5795,7 +5795,12 @@ func (c *EC2) CreateLaunchTemplateVersionRequest(input *CreateLaunchTemplateVers // Launch template versions are numbered in the order in which they are created. // You cannot specify, change, or replace the numbering of launch template versions. // -// For more information, see Managing launch template versions (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html#manage-launch-template-versions)in +// Launch templates are immutable; after you create a launch template, you can't +// modify it. Instead, you can create a new version of the launch template that +// includes any changes you require. +// +// For more information, see Modify a launch template (manage launch template +// versions) (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html#manage-launch-template-versions)in // the Amazon Elastic Compute Cloud User Guide. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions @@ -63576,7 +63581,7 @@ type CreateLaunchTemplateInput struct { _ struct{} `type:"structure"` // Unique, case-sensitive identifier you provide to ensure the idempotency of - // the request. For more information, see Ensuring Idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). + // the request. For more information, see Ensuring idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). // // Constraint: Maximum 128 ASCII characters. ClientToken *string `type:"string"` @@ -63728,7 +63733,7 @@ type CreateLaunchTemplateVersionInput struct { _ struct{} `type:"structure"` // Unique, case-sensitive identifier you provide to ensure the idempotency of - // the request. For more information, see Ensuring Idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). + // the request. For more information, see Ensuring idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). // // Constraint: Maximum 128 ASCII characters. ClientToken *string `type:"string"` @@ -83255,6 +83260,10 @@ type DescribeInstanceAttributeOutput struct { // The block device mapping of the instance. BlockDeviceMappings []*InstanceBlockDeviceMapping `locationName:"blockDeviceMapping" locationNameList:"item" type:"list"` + // To enable the instance for Amazon Web Services Stop Protection, set this + // parameter to true; otherwise, set it to false. + DisableApiStop *AttributeBooleanValue `locationName:"disableApiStop" type:"structure"` + // If the value is true, you can't terminate the instance through the Amazon // EC2 console, CLI, or API; otherwise, you can. DisableApiTermination *AttributeBooleanValue `locationName:"disableApiTermination" type:"structure"` @@ -83334,6 +83343,12 @@ func (s *DescribeInstanceAttributeOutput) SetBlockDeviceMappings(v []*InstanceBl return s } +// SetDisableApiStop sets the DisableApiStop field's value. +func (s *DescribeInstanceAttributeOutput) SetDisableApiStop(v *AttributeBooleanValue) *DescribeInstanceAttributeOutput { + s.DisableApiStop = v + return s +} + // SetDisableApiTermination sets the DisableApiTermination field's value. func (s *DescribeInstanceAttributeOutput) SetDisableApiTermination(v *AttributeBooleanValue) *DescribeInstanceAttributeOutput { s.DisableApiTermination = v @@ -121101,7 +121116,7 @@ func (s *LaunchTemplateHibernationOptions) SetConfigured(v bool) *LaunchTemplate } // Indicates whether the instance is configured for hibernation. This parameter -// is valid only if the instance meets the hibernation prerequisites (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html#hibernating-prerequisites). +// is valid only if the instance meets the hibernation prerequisites (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html). type LaunchTemplateHibernationOptionsRequest struct { _ struct{} `type:"structure"` @@ -126102,6 +126117,10 @@ type ModifyInstanceAttributeInput struct { // in the Amazon EC2 User Guide. BlockDeviceMappings []*InstanceBlockDeviceMappingSpecification `locationName:"blockDeviceMapping" locationNameList:"item" type:"list"` + // Indicates whether an instance is enabled for stop protection. For more information, + // see Stop Protection (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Stop_Start.html#Using_StopProtection). + DisableApiStop *AttributeBooleanValue `type:"structure"` + // If the value is true, you can't terminate the instance using the Amazon EC2 // console, CLI, or API; otherwise, you can. You cannot use this parameter for // Spot Instances. @@ -126229,6 +126248,12 @@ func (s *ModifyInstanceAttributeInput) SetBlockDeviceMappings(v []*InstanceBlock return s } +// SetDisableApiStop sets the DisableApiStop field's value. +func (s *ModifyInstanceAttributeInput) SetDisableApiStop(v *AttributeBooleanValue) *ModifyInstanceAttributeInput { + s.DisableApiStop = v + return s +} + // SetDisableApiTermination sets the DisableApiTermination field's value. func (s *ModifyInstanceAttributeInput) SetDisableApiTermination(v *AttributeBooleanValue) *ModifyInstanceAttributeInput { s.DisableApiTermination = v @@ -127779,7 +127804,7 @@ type ModifyLaunchTemplateInput struct { _ struct{} `type:"structure"` // Unique, case-sensitive identifier you provide to ensure the idempotency of - // the request. For more information, see Ensuring Idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). + // the request. For more information, see Ensuring idempotency (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html). // // Constraint: Maximum 128 ASCII characters. ClientToken *string `type:"string"` @@ -140812,6 +140837,10 @@ type RequestLaunchTemplateData struct { // instances only. CreditSpecification *CreditSpecificationRequest `type:"structure"` + // Indicates whether to enable the instance for stop protection. For more information, + // see Stop Protection (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Stop_Start.html#Using_StopProtection). + DisableApiStop *bool `type:"boolean"` + // If you set this parameter to true, you can't terminate the instance using // the Amazon EC2 console, CLI, or API; otherwise, you can. To change this attribute // after launch, use ModifyInstanceAttribute (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceAttribute.html). @@ -140841,7 +140870,7 @@ type RequestLaunchTemplateData struct { EnclaveOptions *LaunchTemplateEnclaveOptionsRequest `type:"structure"` // Indicates whether an instance is enabled for hibernation. This parameter - // is valid only if the instance meets the hibernation prerequisites (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html#hibernating-prerequisites). + // is valid only if the instance meets the hibernation prerequisites (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html). // For more information, see Hibernate your instance (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html) // in the Amazon Elastic Compute Cloud User Guide. HibernationOptions *LaunchTemplateHibernationOptionsRequest `type:"structure"` @@ -140867,7 +140896,7 @@ type RequestLaunchTemplateData struct { // If you specify InstanceRequirements, you can't specify InstanceTypes. InstanceRequirements *InstanceRequirementsRequest `type:"structure"` - // The instance type. For more information, see Instance Types (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html) + // The instance type. For more information, see Instance types (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html) // in the Amazon Elastic Compute Cloud User Guide. // // If you specify InstanceTypes, you can't specify InstanceRequirements. @@ -140876,7 +140905,7 @@ type RequestLaunchTemplateData struct { // The ID of the kernel. // // We recommend that you use PV-GRUB instead of kernels and RAM disks. For more - // information, see User Provided Kernels (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html) + // information, see User provided kernels (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html) // in the Amazon Elastic Compute Cloud User Guide. KernelId *string `type:"string"` @@ -140915,7 +140944,7 @@ type RequestLaunchTemplateData struct { // The ID of the RAM disk. // // We recommend that you use PV-GRUB instead of kernels and RAM disks. For more - // information, see User Provided Kernels (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html) + // information, see User provided kernels (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html) // in the Amazon Elastic Compute Cloud User Guide. RamDiskId *string `type:"string"` @@ -140937,10 +140966,10 @@ type RequestLaunchTemplateData struct { TagSpecifications []*LaunchTemplateTagSpecificationRequest `locationName:"TagSpecification" locationNameList:"LaunchTemplateTagSpecificationRequest" type:"list"` // The user data to make available to the instance. You must provide base64-encoded - // text. User data is limited to 16 KB. For more information, see Running Commands - // on Your Linux Instance at Launch (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) - // (Linux) or Adding User Data (https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-instance-metadata.html#instancedata-add-user-data) - // (Windows). + // text. User data is limited to 16 KB. For more information, see Run commands + // on your Linux instance at launch (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) + // (Linux) or Work with instance user data (https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/instancedata-add-user-data.html) + // (Windows) in the Amazon Elastic Compute Cloud User Guide. // // If you are creating the launch template for use with Batch, the user data // must be provided in the MIME multi-part archive format (https://cloudinit.readthedocs.io/en/latest/topics/format.html#mime-multi-part-archive). @@ -141031,6 +141060,12 @@ func (s *RequestLaunchTemplateData) SetCreditSpecification(v *CreditSpecificatio return s } +// SetDisableApiStop sets the DisableApiStop field's value. +func (s *RequestLaunchTemplateData) SetDisableApiStop(v bool) *RequestLaunchTemplateData { + s.DisableApiStop = &v + return s +} + // SetDisableApiTermination sets the DisableApiTermination field's value. func (s *RequestLaunchTemplateData) SetDisableApiTermination(v bool) *RequestLaunchTemplateData { s.DisableApiTermination = &v @@ -143549,6 +143584,10 @@ type ResponseLaunchTemplateData struct { // The credit option for CPU usage of the instance. CreditSpecification *CreditSpecification `locationName:"creditSpecification" type:"structure"` + // Indicates whether the instance is enabled for stop protection. For more information, + // see Stop Protection (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Stop_Start.html#Using_StopProtection). + DisableApiStop *bool `locationName:"disableApiStop" type:"boolean"` + // If set to true, indicates that the instance cannot be terminated using the // Amazon EC2 console, command line tool, or API. DisableApiTermination *bool `locationName:"disableApiTermination" type:"boolean"` @@ -143679,6 +143718,12 @@ func (s *ResponseLaunchTemplateData) SetCreditSpecification(v *CreditSpecificati return s } +// SetDisableApiStop sets the DisableApiStop field's value. +func (s *ResponseLaunchTemplateData) SetDisableApiStop(v bool) *ResponseLaunchTemplateData { + s.DisableApiStop = &v + return s +} + // SetDisableApiTermination sets the DisableApiTermination field's value. func (s *ResponseLaunchTemplateData) SetDisableApiTermination(v bool) *ResponseLaunchTemplateData { s.DisableApiTermination = &v @@ -145358,6 +145403,10 @@ type RunInstancesInput struct { // For T3 instances with host tenancy, only standard is supported. CreditSpecification *CreditSpecificationRequest `type:"structure"` + // Indicates whether an instance is enabled for stop protection. For more information, + // see Stop Protection (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Stop_Start.html#Using_StopProtection). + DisableApiStop *bool `type:"boolean"` + // If you set this parameter to true, you can't terminate the instance using // the Amazon EC2 console, CLI, or API; otherwise, you can. To change this attribute // after launch, use ModifyInstanceAttribute (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceAttribute.html). @@ -145686,6 +145735,12 @@ func (s *RunInstancesInput) SetCreditSpecification(v *CreditSpecificationRequest return s } +// SetDisableApiStop sets the DisableApiStop field's value. +func (s *RunInstancesInput) SetDisableApiStop(v bool) *RunInstancesInput { + s.DisableApiStop = &v + return s +} + // SetDisableApiTermination sets the DisableApiTermination field's value. func (s *RunInstancesInput) SetDisableApiTermination(v bool) *RunInstancesInput { s.DisableApiTermination = &v @@ -158183,12 +158238,12 @@ type ValidationError struct { _ struct{} `type:"structure"` // The error code that indicates why the parameter or parameter combination - // is not valid. For more information about error codes, see Error Codes (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html.html). + // is not valid. For more information about error codes, see Error codes (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html). Code *string `locationName:"code" type:"string"` // The error message that describes why the parameter or parameter combination - // is not valid. For more information about error messages, see Error Codes - // (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html.html). + // is not valid. For more information about error messages, see Error codes + // (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html). Message *string `locationName:"message" type:"string"` } @@ -163362,6 +163417,9 @@ const ( // InstanceAttributeNameEnclaveOptions is a InstanceAttributeName enum value InstanceAttributeNameEnclaveOptions = "enclaveOptions" + + // InstanceAttributeNameDisableApiStop is a InstanceAttributeName enum value + InstanceAttributeNameDisableApiStop = "disableApiStop" ) // InstanceAttributeName_Values returns all elements of the InstanceAttributeName enum @@ -163382,6 +163440,7 @@ func InstanceAttributeName_Values() []string { InstanceAttributeNameSriovNetSupport, InstanceAttributeNameEnaSupport, InstanceAttributeNameEnclaveOptions, + InstanceAttributeNameDisableApiStop, } } @@ -165179,6 +165238,39 @@ const ( // InstanceTypeI4i32xlarge is a InstanceType enum value InstanceTypeI4i32xlarge = "i4i.32xlarge" + + // InstanceTypeI4iMetal is a InstanceType enum value + InstanceTypeI4iMetal = "i4i.metal" + + // InstanceTypeX2idnMetal is a InstanceType enum value + InstanceTypeX2idnMetal = "x2idn.metal" + + // InstanceTypeX2iednMetal is a InstanceType enum value + InstanceTypeX2iednMetal = "x2iedn.metal" + + // InstanceTypeC7gMedium is a InstanceType enum value + InstanceTypeC7gMedium = "c7g.medium" + + // InstanceTypeC7gLarge is a InstanceType enum value + InstanceTypeC7gLarge = "c7g.large" + + // InstanceTypeC7gXlarge is a InstanceType enum value + InstanceTypeC7gXlarge = "c7g.xlarge" + + // InstanceTypeC7g2xlarge is a InstanceType enum value + InstanceTypeC7g2xlarge = "c7g.2xlarge" + + // InstanceTypeC7g4xlarge is a InstanceType enum value + InstanceTypeC7g4xlarge = "c7g.4xlarge" + + // InstanceTypeC7g8xlarge is a InstanceType enum value + InstanceTypeC7g8xlarge = "c7g.8xlarge" + + // InstanceTypeC7g12xlarge is a InstanceType enum value + InstanceTypeC7g12xlarge = "c7g.12xlarge" + + // InstanceTypeC7g16xlarge is a InstanceType enum value + InstanceTypeC7g16xlarge = "c7g.16xlarge" ) // InstanceType_Values returns all elements of the InstanceType enum @@ -165698,6 +165790,17 @@ func InstanceType_Values() []string { InstanceTypeI4i8xlarge, InstanceTypeI4i16xlarge, InstanceTypeI4i32xlarge, + InstanceTypeI4iMetal, + InstanceTypeX2idnMetal, + InstanceTypeX2iednMetal, + InstanceTypeC7gMedium, + InstanceTypeC7gLarge, + InstanceTypeC7gXlarge, + InstanceTypeC7g2xlarge, + InstanceTypeC7g4xlarge, + InstanceTypeC7g8xlarge, + InstanceTypeC7g12xlarge, + InstanceTypeC7g16xlarge, } } diff --git a/vendor/github.com/aws/smithy-go/CHANGELOG.md b/vendor/github.com/aws/smithy-go/CHANGELOG.md index a5b73cf60..01a237dff 100644 --- a/vendor/github.com/aws/smithy-go/CHANGELOG.md +++ b/vendor/github.com/aws/smithy-go/CHANGELOG.md @@ -1,3 +1,9 @@ +# Release (v1.11.3) + +## Module Highlights +* `github.com/aws/smithy-go`: v1.11.3 + * **Dependency Update**: Updates smithy-go unit test dependency go-cmp to 0.5.8. + # Release (v1.11.2) * No change notes available for this release. diff --git a/vendor/github.com/aws/smithy-go/go_module_metadata.go b/vendor/github.com/aws/smithy-go/go_module_metadata.go index 7e252ec8c..53424221a 100644 --- a/vendor/github.com/aws/smithy-go/go_module_metadata.go +++ b/vendor/github.com/aws/smithy-go/go_module_metadata.go @@ -3,4 +3,4 @@ package smithy // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.11.2" +const goModuleVersion = "1.11.3" diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go index d6dc29ab8..b52719baa 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/clone.go @@ -84,10 +84,20 @@ type CloneSource struct { type CloneStatus struct { State CloneState `json:"state"` Source CloneSource `json:"source"` + + // failure can be obtained through .GetFailure() + failure *CloneFailure +} + +// CloneFailure reports details of a failure after a subvolume clone failed. +type CloneFailure struct { + Errno string `json:"errno"` + ErrStr string `json:"errstr"` } type cloneStatusWrapper struct { - Status CloneStatus `json:"status"` + Status CloneStatus `json:"status"` + Failure CloneFailure `json:"failure"` } func parseCloneStatus(res response) (*CloneStatus, error) { @@ -95,6 +105,9 @@ func parseCloneStatus(res response) (*CloneStatus, error) { if err := res.NoStatus().Unmarshal(&status).End(); err != nil { return nil, err } + if status.Failure.Errno != "" || status.Failure.ErrStr != "" { + status.Status.failure = &status.Failure + } return &status.Status, nil } diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/clone_nautilus.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/clone_nautilus.go new file mode 100644 index 000000000..f4480be4b --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/clone_nautilus.go @@ -0,0 +1,13 @@ +//go:build ceph_preview +// +build ceph_preview + +package admin + +// GetFailure returns details about the CloneStatus when in CloneFailed state. +// +// Similar To: +// Reading the .failure object from the JSON returned by "ceph fs subvolume +// snapshot clone" +func (cs *CloneStatus) GetFailure() *CloneFailure { + return cs.failure +} diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go index 03b3323ae..28f39f3d7 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/fsadmin.go @@ -93,6 +93,15 @@ func parseListNames(res response) ([]string, error) { return vl, nil } +func parseListKeyValues(res response) (map[string]string, error) { + var x map[string]string + if err := res.NoStatus().Unmarshal(&x).End(); err != nil { + return nil, err + } + + return x, nil +} + // parsePathResponse returns a cleaned up path from requests that get a path // unless an error is encountered, then an error is returned. func parsePathResponse(res response) (string, error) { diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/metadata.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/metadata.go new file mode 100644 index 000000000..238dd6cf6 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/metadata.go @@ -0,0 +1,104 @@ +//go:build !(nautilus || octopus) && ceph_preview && ceph_ci_untested +// +build !nautilus,!octopus,ceph_preview,ceph_ci_untested + +package admin + +import "C" + +// GetMetadata gets custom metadata on the subvolume in a volume belonging to +// an optional subvolume group based on provided key name. +// +// Similar To: +// ceph fs subvolume metadata get [--group_name ] +func (fsa *FSAdmin) GetMetadata(volume, group, subvolume, key string) (string, error) { + m := map[string]string{ + "prefix": "fs subvolume metadata get", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "key_name": key, + } + + if group != NoGroup { + m["group_name"] = group + } + + return parsePathResponse(fsa.marshalMgrCommand(m)) +} + +// SetMetadata sets custom metadata on the subvolume in a volume belonging to +// an optional subvolume group as a key-value pair. +// +// Similar To: +// ceph fs subvolume metadata set [--group_name ] +func (fsa *FSAdmin) SetMetadata(volume, group, subvolume, key, value string) error { + m := map[string]string{ + "prefix": "fs subvolume metadata set", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "key_name": key, + "value": value, + } + + if group != NoGroup { + m["group_name"] = group + } + + return fsa.marshalMgrCommand(m).NoData().End() +} + +// RemoveMetadata removes custom metadata set on the subvolume in a volume +// belonging to an optional subvolume group using the metadata key. +// +// Similar To: +// ceph fs subvolume metadata rm [--group_name ] +func (fsa *FSAdmin) RemoveMetadata(volume, group, subvolume, key string) error { + return fsa.rmSubVolumeMetadata(volume, group, subvolume, key, commonRmFlags{}) +} + +// ForceRemoveMetadata attempt to forcefully remove custom metadata set on +// the subvolume in a volume belonging to an optional subvolume group using +// the metadata key. +// +// Similar To: +// ceph fs subvolume metadata rm [--group_name ] --force +func (fsa *FSAdmin) ForceRemoveMetadata(volume, group, subvolume, key string) error { + return fsa.rmSubVolumeMetadata(volume, group, subvolume, key, commonRmFlags{force: true}) +} + +func (fsa *FSAdmin) rmSubVolumeMetadata(volume, group, subvolume, key string, o commonRmFlags) error { + m := map[string]string{ + "prefix": "fs subvolume metadata rm", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "key_name": key, + } + + if group != NoGroup { + m["group_name"] = group + } + + return fsa.marshalMgrCommand(mergeFlags(m, o)).NoData().End() +} + +// ListMetadata lists custom metadata (key-value pairs) set on the subvolume +// in a volume belonging to an optional subvolume group. +// +// Similar To: +// ceph fs subvolume metadata ls [--group_name ] +func (fsa *FSAdmin) ListMetadata(volume, group, subvolume string) (map[string]string, error) { + m := map[string]string{ + "prefix": "fs subvolume metadata ls", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + } + + if group != NoGroup { + m["group_name"] = group + } + + return parseListKeyValues(fsa.marshalMgrCommand(m)) +} diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/mgrmodule.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/mgrmodule.go index 21b8dbfbc..ae27006e5 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/admin/mgrmodule.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/mgrmodule.go @@ -6,28 +6,6 @@ import ( const mirroring = "mirroring" -// EnableModule will enable the specified manager module. -// -// Deprecated: use the equivalent function in cluster/admin/manager. -// -// Similar To: -// ceph mgr module enable [--force] -func (fsa *FSAdmin) EnableModule(module string, force bool) error { - mgradmin := manager.NewFromConn(fsa.conn) - return mgradmin.EnableModule(module, force) -} - -// DisableModule will disable the specified manager module. -// -// Deprecated: use the equivalent function in cluster/admin/manager. -// -// Similar To: -// ceph mgr module disable -func (fsa *FSAdmin) DisableModule(module string) error { - mgradmin := manager.NewFromConn(fsa.conn) - return mgradmin.DisableModule(module) -} - // EnableMirroringModule will enable the mirroring module for cephfs. // // Similar To: diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/snapshot_metadata.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/snapshot_metadata.go new file mode 100644 index 000000000..39adf83d9 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/snapshot_metadata.go @@ -0,0 +1,109 @@ +//go:build !(nautilus || octopus) && ceph_preview && ceph_ci_untested +// +build !nautilus,!octopus,ceph_preview,ceph_ci_untested + +package admin + +import "C" + +// GetSnapshotMetadata gets custom metadata on the subvolume snapshot in a +// volume belonging to an optional subvolume group based on provided key name. +// +// Similar To: +// ceph fs subvolume snapshot metadata get [--group_name ] +func (fsa *FSAdmin) GetSnapshotMetadata(volume, group, subvolume, snapname, key string) (string, error) { + m := map[string]string{ + "prefix": "fs subvolume snapshot metadata get", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "snap_name": snapname, + "key_name": key, + } + + if group != NoGroup { + m["group_name"] = group + } + + return parsePathResponse(fsa.marshalMgrCommand(m)) +} + +// SetSnapshotMetadata sets custom metadata on the subvolume snapshot in a +// volume belonging to an optional subvolume group as a key-value pair. +// +// Similar To: +// ceph fs subvolume snapshot metadata set [--group_name ] +func (fsa *FSAdmin) SetSnapshotMetadata(volume, group, subvolume, snapname, key, value string) error { + m := map[string]string{ + "prefix": "fs subvolume snapshot metadata set", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "snap_name": snapname, + "key_name": key, + "value": value, + } + + if group != NoGroup { + m["group_name"] = group + } + + return fsa.marshalMgrCommand(m).NoData().End() +} + +// RemoveSnapshotMetadata removes custom metadata set on the subvolume +// snapshot in a volume belonging to an optional subvolume group using the +// metadata key. +// +// Similar To: +// ceph fs subvolume snapshot metadata rm [--group_name ] +func (fsa *FSAdmin) RemoveSnapshotMetadata(volume, group, subvolume, snapname, key string) error { + return fsa.rmSubVolumeSnapShotMetadata(volume, group, subvolume, snapname, key, commonRmFlags{}) +} + +// ForceRemoveSnapshotMetadata attempt to forcefully remove custom metadata +// set on the subvolume snapshot in a volume belonging to an optional +// subvolume group using the metadata key. +// +// Similar To: +// ceph fs subvolume snapshot metadata rm [--group_name ] --force +func (fsa *FSAdmin) ForceRemoveSnapshotMetadata(volume, group, subvolume, snapname, key string) error { + return fsa.rmSubVolumeSnapShotMetadata(volume, group, subvolume, snapname, key, commonRmFlags{force: true}) +} + +func (fsa *FSAdmin) rmSubVolumeSnapShotMetadata(volume, group, subvolume, snapname, key string, o commonRmFlags) error { + m := map[string]string{ + "prefix": "fs subvolume snapshot metadata rm", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "snap_name": snapname, + "key_name": key, + } + + if group != NoGroup { + m["group_name"] = group + } + + return fsa.marshalMgrCommand(mergeFlags(m, o)).NoData().End() +} + +// ListSnapshotMetadata lists custom metadata (key-value pairs) set on the subvolume +// snapshot in a volume belonging to an optional subvolume group. +// +// Similar To: +// ceph fs subvolume snapshot metadata ls [--group_name ] +func (fsa *FSAdmin) ListSnapshotMetadata(volume, group, subvolume, snapname string) (map[string]string, error) { + m := map[string]string{ + "prefix": "fs subvolume snapshot metadata ls", + "format": "json", + "vol_name": volume, + "sub_name": subvolume, + "snap_name": snapname, + } + + if group != NoGroup { + m["group_name"] = group + } + + return parseListKeyValues(fsa.marshalMgrCommand(m)) +} diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go index 3131892c2..d2f57420c 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/volume.go @@ -2,6 +2,7 @@ package admin import ( "bytes" + "encoding/json" ) var ( @@ -114,8 +115,40 @@ type VolumeStatus struct { Pools []VolumePool `json:"pools"` } -func parseVolumeStatus(res response) (*VolumeStatus, error) { - var vs VolumeStatus +type mdsVersionField struct { + Version string + Items []struct { + Version string `json:"version"` + } +} + +func (m *mdsVersionField) UnmarshalJSON(data []byte) (err error) { + if err = json.Unmarshal(data, &m.Version); err == nil { + return + } + return json.Unmarshal(data, &m.Items) +} + +// volumeStatusResponse deals with the changing output of the mgr +// api json +type volumeStatusResponse struct { + Pools []VolumePool `json:"pools"` + MDSVersion mdsVersionField `json:"mds_version"` +} + +func (v *volumeStatusResponse) volumeStatus() *VolumeStatus { + vstatus := &VolumeStatus{} + vstatus.Pools = v.Pools + if v.MDSVersion.Version != "" { + vstatus.MDSVersion = v.MDSVersion.Version + } else if len(v.MDSVersion.Items) > 0 { + vstatus.MDSVersion = v.MDSVersion.Items[0].Version + } + return vstatus +} + +func parseVolumeStatus(res response) (*volumeStatusResponse, error) { + var vs volumeStatusResponse res = res.NoStatus() if !res.Ok() { return nil, res.End() @@ -142,5 +175,9 @@ func (fsa *FSAdmin) VolumeStatus(name string) (*VolumeStatus, error) { "prefix": "fs status", "format": "json", }) - return parseVolumeStatus(res) + v, err := parseVolumeStatus(res) + if err != nil { + return nil, err + } + return v.volumeStatus(), nil } diff --git a/vendor/github.com/ceph/go-ceph/common/admin/nfs/admin.go b/vendor/github.com/ceph/go-ceph/common/admin/nfs/admin.go index 2f644d0e4..e109f8830 100644 --- a/vendor/github.com/ceph/go-ceph/common/admin/nfs/admin.go +++ b/vendor/github.com/ceph/go-ceph/common/admin/nfs/admin.go @@ -1,5 +1,5 @@ -//go:build !(nautilus || octopus) && ceph_preview && ceph_ci_untested -// +build !nautilus,!octopus,ceph_preview,ceph_ci_untested +//go:build !(nautilus || octopus) && ceph_preview +// +build !nautilus,!octopus,ceph_preview package nfs @@ -15,7 +15,6 @@ type Admin struct { // NewFromConn creates an new management object from a preexisting // rados connection. The existing connection can be rados.Conn or any // type implementing the RadosCommander interface. -// PREVIEW func NewFromConn(conn ccom.RadosCommander) *Admin { return &Admin{conn} } diff --git a/vendor/github.com/ceph/go-ceph/common/admin/nfs/export.go b/vendor/github.com/ceph/go-ceph/common/admin/nfs/export.go index 230946410..02e0d847e 100644 --- a/vendor/github.com/ceph/go-ceph/common/admin/nfs/export.go +++ b/vendor/github.com/ceph/go-ceph/common/admin/nfs/export.go @@ -1,5 +1,5 @@ -//go:build !(nautilus || octopus) && ceph_preview && ceph_ci_untested -// +build !nautilus,!octopus,ceph_preview,ceph_ci_untested +//go:build !(nautilus || octopus) && ceph_preview +// +build !nautilus,!octopus,ceph_preview package nfs @@ -108,7 +108,6 @@ func parseExportInfo(res commands.Response) (ExportInfo, error) { } // CreateCephFSExport will create a new NFS export for a CephFS file system. -// PREVIEW // // Similar To: // ceph nfs export create cephfs @@ -126,7 +125,6 @@ func (nfsa *Admin) CreateCephFSExport(spec CephFSExportSpec) ( const delSucc = "Successfully deleted export" // RemoveExport will remove an NFS export based on the pseudo-path of the export. -// PREVIEW // // Similar To: // ceph nfs export rm @@ -142,7 +140,6 @@ func (nfsa *Admin) RemoveExport(clusterID, pseudoPath string) error { } // ListDetailedExports will return a list of exports with details. -// PREVIEW // // Similar To: // ceph nfs export ls --detailed @@ -166,7 +163,6 @@ func (nfsa *Admin) ListDetailedExports(clusterID string) ([]ExportInfo, error) { // ExportInfo will return a structure describing the export specified by it's // pseudo-path. -// PREVIEW // // Similar To: // ceph nfs export info diff --git a/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer.go b/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer.go index b7cbf9b6d..d74cd2bcf 100644 --- a/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer.go +++ b/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer.go @@ -1,5 +1,5 @@ -//go:build ptrguard -// +build ptrguard +//go:build !no_ptrguard +// +build !no_ptrguard package cutil diff --git a/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer_memcpy.go b/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer_memcpy.go index 96fb32d64..cde47c9e9 100644 --- a/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer_memcpy.go +++ b/vendor/github.com/ceph/go-ceph/internal/cutil/sync_buffer_memcpy.go @@ -1,5 +1,5 @@ -//go:build !ptrguard -// +build !ptrguard +//go:build no_ptrguard +// +build no_ptrguard package cutil diff --git a/vendor/github.com/ceph/go-ceph/rados/ioctx.go b/vendor/github.com/ceph/go-ceph/rados/ioctx.go index f05251942..807552623 100644 --- a/vendor/github.com/ceph/go-ceph/rados/ioctx.go +++ b/vendor/github.com/ceph/go-ceph/rados/ioctx.go @@ -43,6 +43,11 @@ const ( // CreateIdempotent if used with IOContext.Create() and the object // already exists, the function will not return an error. CreateIdempotent = C.LIBRADOS_CREATE_IDEMPOTENT + + defaultListObjectsResultSize = 1000 + // listEndSentinel is the value returned by rados_list_object_list_is_end + // when a cursor has reached the end of a pool + listEndSentinel = 1 ) //revive:disable:var-naming old-yet-exported public api @@ -302,22 +307,36 @@ type ObjectListFunc func(oid string) // RadosAllNamespaces before calling this function to return objects from all // namespaces func (ioctx *IOContext) ListObjects(listFn ObjectListFunc) error { - var ctx C.rados_list_ctx_t - ret := C.rados_nobjects_list_open(ioctx.ioctx, &ctx) - if ret < 0 { - return getError(ret) + pageResults := C.size_t(defaultListObjectsResultSize) + var filterLen C.size_t + results := make([]C.rados_object_list_item, pageResults) + + next := C.rados_object_list_begin(ioctx.ioctx) + if next == nil { + return ErrNotFound } - defer func() { C.rados_nobjects_list_close(ctx) }() + defer C.rados_object_list_cursor_free(ioctx.ioctx, next) + finish := C.rados_object_list_end(ioctx.ioctx) + if finish == nil { + return ErrNotFound + } + defer C.rados_object_list_cursor_free(ioctx.ioctx, finish) for { - var cEntry *C.char - ret := C.rados_nobjects_list_next(ctx, &cEntry, nil, nil) - if ret == -C.ENOENT { - return nil - } else if ret < 0 { + ret := C.rados_object_list(ioctx.ioctx, next, finish, pageResults, nil, filterLen, (*C.rados_object_list_item)(unsafe.Pointer(&results[0])), &next) + if ret < 0 { return getError(ret) } - listFn(C.GoString(cEntry)) + + numEntries := int(ret) + for i := 0; i < numEntries; i++ { + item := results[i] + listFn(C.GoStringN(item.oid, (C.int)(item.oid_length))) + } + + if C.rados_object_list_is_end(ioctx.ioctx, next) == listEndSentinel { + return nil + } } } diff --git a/vendor/github.com/ceph/go-ceph/rados/rados_read_op_assert_version.go b/vendor/github.com/ceph/go-ceph/rados/rados_read_op_assert_version.go index ce67c6ae3..e58cd18d2 100644 --- a/vendor/github.com/ceph/go-ceph/rados/rados_read_op_assert_version.go +++ b/vendor/github.com/ceph/go-ceph/rados/rados_read_op_assert_version.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -12,7 +9,6 @@ import "C" // AssertVersion ensures that the object exists and that its internal version // number is equal to "ver" before reading. "ver" should be a version number // previously obtained with IOContext.GetLastVersion(). -// PREVIEW // // Implements: // void rados_read_op_assert_version(rados_read_op_t read_op, diff --git a/vendor/github.com/ceph/go-ceph/rados/rados_set_locator.go b/vendor/github.com/ceph/go-ceph/rados/rados_set_locator.go index e80286794..61d467502 100644 --- a/vendor/github.com/ceph/go-ceph/rados/rados_set_locator.go +++ b/vendor/github.com/ceph/go-ceph/rados/rados_set_locator.go @@ -16,7 +16,6 @@ import ( // SetLocator sets the key for mapping objects to pgs within an io context. // Until a different locator key is set, all objects in this io context will be placed in the same pg. // To reset the locator, an empty string must be set. -// PREVIEW // // Implements: // void rados_ioctx_locator_set_key(rados_ioctx_t io, const char *key); diff --git a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_assert_version.go b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_assert_version.go index f9400a35c..0a4d510a6 100644 --- a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_assert_version.go +++ b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_assert_version.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -12,7 +9,6 @@ import "C" // AssertVersion ensures that the object exists and that its internal version // number is equal to "ver" before writing. "ver" should be a version number // previously obtained with IOContext.GetLastVersion(). -// PREVIEW // // Implements: // void rados_read_op_assert_version(rados_read_op_t read_op, diff --git a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_remove.go b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_remove.go index 103433e24..ce6b1bf51 100644 --- a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_remove.go +++ b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_remove.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -10,7 +7,6 @@ package rados import "C" // Remove object. -// PREVIEW // // Implements: // void rados_write_op_remove(rados_write_op_t write_op) diff --git a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_setxattr.go b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_setxattr.go index ebacb714c..828d2d57b 100644 --- a/vendor/github.com/ceph/go-ceph/rados/rados_write_op_setxattr.go +++ b/vendor/github.com/ceph/go-ceph/rados/rados_write_op_setxattr.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -14,7 +11,6 @@ import ( ) // SetXattr sets an xattr. -// PREVIEW // // Implements: // void rados_write_op_setxattr(rados_write_op_t write_op, diff --git a/vendor/github.com/ceph/go-ceph/rados/read_op_omap_get_vals_by_keys.go b/vendor/github.com/ceph/go-ceph/rados/read_op_omap_get_vals_by_keys.go index feed5d295..8ca9a322f 100644 --- a/vendor/github.com/ceph/go-ceph/rados/read_op_omap_get_vals_by_keys.go +++ b/vendor/github.com/ceph/go-ceph/rados/read_op_omap_get_vals_by_keys.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -58,7 +55,6 @@ func (s *ReadOpOmapGetValsByKeysStep) update() error { // ReadOpOmapGetValsByKeysStep's internal iterator. // If there are no more elements to retrieve, (nil, nil) is returned. // May be called only after Operate() finished. -// PREVIEW func (s *ReadOpOmapGetValsByKeysStep) Next() (*OmapKeyValue, error) { if !s.canIterate { return nil, ErrOperationIncomplete @@ -88,7 +84,6 @@ func (s *ReadOpOmapGetValsByKeysStep) Next() (*OmapKeyValue, error) { } // GetOmapValuesByKeys starts iterating over specific key/value pairs. -// PREVIEW // // Implements: // void rados_read_op_omap_get_vals_by_keys2(rados_read_op_t read_op, diff --git a/vendor/github.com/ceph/go-ceph/rados/read_op_read.go b/vendor/github.com/ceph/go-ceph/rados/read_op_read.go index 0c4809360..47e029871 100644 --- a/vendor/github.com/ceph/go-ceph/rados/read_op_read.go +++ b/vendor/github.com/ceph/go-ceph/rados/read_op_read.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados // #cgo LDFLAGS: -lrados @@ -49,7 +46,6 @@ func newReadOpReadStep() *ReadOpReadStep { // Read bytes from offset into buffer. // len(buffer) is the maximum number of bytes read from the object. // buffer[:ReadOpReadStep.BytesRead] then contains object data. -// PREVIEW // // Implements: // void rados_read_op_read(rados_read_op_t read_op, diff --git a/vendor/github.com/ceph/go-ceph/rados/watcher.go b/vendor/github.com/ceph/go-ceph/rados/watcher.go index 3df1cd044..0fc362c3d 100644 --- a/vendor/github.com/ceph/go-ceph/rados/watcher.go +++ b/vendor/github.com/ceph/go-ceph/rados/watcher.go @@ -1,6 +1,3 @@ -//go:build ceph_preview -// +build ceph_preview - package rados /* @@ -69,7 +66,6 @@ var ( ) // Watch creates a Watcher for the specified object. -// PREVIEW // // A Watcher receives all notifications that are sent to the object on which it // has been created. It exposes two read-only channels: Events() receives all @@ -103,7 +99,6 @@ func (ioctx *IOContext) Watch(obj string) (*Watcher, error) { // WatchWithTimeout creates a watcher on an object. Same as Watcher(), but // different timeout than the default can be specified. -// PREVIEW // // Implements: // int rados_watch3(rados_ioctx_t io, const char *o, uint64_t *cookie, @@ -142,26 +137,22 @@ func (ioctx *IOContext) WatchWithTimeout(oid string, timeout time.Duration) (*Wa } // ID returns the WatcherId of the Watcher -// PREVIEW func (w *Watcher) ID() WatcherID { return w.id } // Events returns a read-only channel, that receives all notifications that are // sent to the object of the Watcher. -// PREVIEW func (w *Watcher) Events() <-chan NotifyEvent { return w.events } // Errors returns a read-only channel, that receives all errors for the Watcher. -// PREVIEW func (w *Watcher) Errors() <-chan error { return w.errors } // Check on the status of a Watcher. -// PREVIEW // // Returns the time since it was last confirmed. If there is an error, the // Watcher is no longer valid, and should be destroyed with the Delete() method. @@ -177,7 +168,6 @@ func (w *Watcher) Check() (time.Duration, error) { } // Delete the watcher. This closes both the event and error channel. -// PREVIEW // // Implements: // int rados_unwatch2(rados_ioctx_t io, uint64_t cookie) @@ -203,7 +193,6 @@ func (w *Watcher) Delete() error { // Notify sends a notification with the provided data to all Watchers of the // specified object. -// PREVIEW // // CAUTION: even if the error is not nil. the returned slices // might still contain data. @@ -213,7 +202,6 @@ func (ioctx *IOContext) Notify(obj string, data []byte) ([]NotifyAck, []NotifyTi // NotifyWithTimeout is like Notify() but with a different timeout than the // default. -// PREVIEW // // Implements: // int rados_notify2(rados_ioctx_t io, const char* o, const char* buf, int buf_len, @@ -246,7 +234,6 @@ func (ioctx *IOContext) NotifyWithTimeout(obj string, data []byte, timeout time. // Ack sends an acknowledgement with the specified response data to the notfier // of the NotifyEvent. If a notify is not ack'ed, the originating Notify() call // blocks and eventiually times out. -// PREVIEW // // Implements: // int rados_notify_ack(rados_ioctx_t io, const char *o, uint64_t notify_id, @@ -276,7 +263,6 @@ func (ne *NotifyEvent) Ack(response []byte) error { } // WatcherFlush flushes all pending notifications of the cluster. -// PREVIEW // // Implements: // int rados_watch_flush(rados_t cluster) diff --git a/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go b/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go index b97dc9fb6..67b800534 100644 --- a/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go +++ b/vendor/github.com/ceph/go-ceph/rbd/rbd_nautilus.go @@ -13,30 +13,28 @@ import "C" import ( "unsafe" - "github.com/ceph/go-ceph/internal/retry" ts "github.com/ceph/go-ceph/internal/timespec" "github.com/ceph/go-ceph/rados" ) // GetImageNames returns the list of current RBD images. func GetImageNames(ioctx *rados.IOContext) ([]string, error) { - var ( - err error - images []C.rbd_image_spec_t - size C.size_t - ) - retry.WithSizes(32, 4096, func(s int) retry.Hint { - size = C.size_t(s) + var images []C.rbd_image_spec_t + size := C.size_t(4096) + for { images = make([]C.rbd_image_spec_t, size) ret := C.rbd_list2( cephIoctx(ioctx), (*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), &size) - err = getErrorIfNegative(ret) - return retry.Size(int(size)).If(err == errRange) - }) - if err != nil { - return nil, err + err := getErrorIfNegative(ret) + if err != nil { + if err == errRange { + continue + } + return nil, err + } + break } defer C.rbd_image_spec_list_cleanup((*C.rbd_image_spec_t)(unsafe.Pointer(&images[0])), size) diff --git a/vendor/github.com/ceph/go-ceph/rbd/snapshot_rename.go b/vendor/github.com/ceph/go-ceph/rbd/snapshot_rename.go new file mode 100644 index 000000000..e5649165e --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/rbd/snapshot_rename.go @@ -0,0 +1,37 @@ +//go:build ceph_preview +// +build ceph_preview + +package rbd + +// #cgo LDFLAGS: -lrbd +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// Rename a snapshot. +// +// Implements: +// int rbd_snap_rename(rbd_image_t image, const char *snapname, +// const char* dstsnapsname); +func (snapshot *Snapshot) Rename(destName string) error { + if err := snapshot.validate(imageNeedsIOContext | imageIsOpen | imageNeedsName | snapshotNeedsName); err != nil { + return err + } + + cSrcName := C.CString(snapshot.name) + cDestName := C.CString(destName) + defer C.free(unsafe.Pointer(cSrcName)) + defer C.free(unsafe.Pointer(cDestName)) + + err := C.rbd_snap_rename(snapshot.image.image, cSrcName, cDestName) + if err != 0 { + return getError(err) + } + + snapshot.name = destName + return nil +} diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go index 2a5446762..fd2b3a42b 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -40,6 +40,8 @@ import ( "github.com/google/go-cmp/cmp/internal/value" ) +// TODO(≥go1.18): Use any instead of interface{}. + // Equal reports whether x and y are equal by recursively applying the // following rules in the given order to x and y and all of their sub-values: // diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go index 104bb3053..1ef65ac1d 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_compare.go +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go @@ -116,7 +116,10 @@ func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out } // For leaf nodes, format the value based on the reflect.Values alone. - if v.MaxDepth == 0 { + // As a special case, treat equal []byte as a leaf nodes. + isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == reflect.TypeOf(byte(0)) + isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0 + if v.MaxDepth == 0 || isEqualBytes { switch opts.DiffMode { case diffUnknown, diffIdentical: // Format Equal. diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go index 76c04fdbd..287b89358 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -211,7 +211,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { out = opts.formatString("", string(b)) skipType = true - return opts.WithTypeMode(emitType).FormatType(t, out) + return opts.FormatType(t, out) } } @@ -282,7 +282,12 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, } defer ptrs.Pop() - skipType = true // Let the underlying value print the type instead + // Skip the name only if this is an unnamed pointer type. + // Otherwise taking the address of a value does not reproduce + // the named pointer type. + if v.Type().Name() == "" { + skipType = true // Let the underlying value print the type instead + } out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) out = &textWrap{Prefix: "&", Value: out} @@ -293,7 +298,6 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, } // Interfaces accept different concrete types, // so configure the underlying value to explicitly print the type. - skipType = true // Print the concrete type instead return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) default: panic(fmt.Sprintf("%v kind not handled", v.Kind())) diff --git a/vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go b/vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go index 745836add..b3a932eb1 100644 --- a/vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go +++ b/vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "regexp" "strconv" "strings" @@ -92,6 +93,9 @@ func ParseCapacityString(in interface{}) (uint64, error) { return cap, nil } +// Parse a duration from an arbitrary value (a string or numeric value) into +// a time.Duration; when units are missing (such as when a numeric type is +// provided), the duration is assumed to be in seconds. func ParseDurationSecond(in interface{}) (time.Duration, error) { var dur time.Duration jsonIn, ok := in.(json.Number) @@ -105,20 +109,22 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) { if inp == "" { return dur, nil } + + if v, err := strconv.ParseInt(inp, 10, 64); err == nil { + return time.Duration(v) * time.Second, nil + } + + if strings.HasSuffix(inp, "d") { + v, err := strconv.ParseInt(inp[:len(inp)-1], 10, 64) + if err != nil { + return dur, err + } + return time.Duration(v) * 24 * time.Hour, nil + } + var err error - // Look for a suffix otherwise its a plain second value - if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") || strings.HasSuffix(inp, "ms") { - dur, err = time.ParseDuration(inp) - if err != nil { - return dur, err - } - } else { - // Plain integer - secs, err := strconv.ParseInt(inp, 10, 64) - if err != nil { - return dur, err - } - dur = time.Duration(secs) * time.Second + if dur, err = time.ParseDuration(inp); err != nil { + return dur, err } case int: dur = time.Duration(inp) * time.Second @@ -145,6 +151,9 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) { return dur, nil } +// Parse an absolute timestamp from the provided arbitrary value (string or +// numeric value). When an untyped numeric value is provided, it is assumed +// to be seconds from the Unix Epoch. func ParseAbsoluteTime(in interface{}) (time.Time, error) { var t time.Time switch inp := in.(type) { @@ -193,6 +202,13 @@ func ParseAbsoluteTime(in interface{}) (time.Time, error) { return t, nil } +// ParseInt takes an arbitrary value (either a string or numeric type) and +// parses it as an int64 value. This value is assumed to be larger than the +// provided type, but cannot safely be cast. +// +// When the end value is bounded (such as an int value), it is recommended +// to instead call SafeParseInt or SafeParseIntRange to safely cast to a +// more restrictive type. func ParseInt(in interface{}) (int64, error) { var ret int64 jsonIn, ok := in.(json.Number) @@ -230,6 +246,104 @@ func ParseInt(in interface{}) (int64, error) { return ret, nil } +// ParseDirectIntSlice behaves similarly to ParseInt, but accepts typed +// slices, returning a slice of int64s. +// +// If the starting value may not be in slice form (e.g.. a bare numeric value +// could be provided), it is suggested to call ParseIntSlice instead. +func ParseDirectIntSlice(in interface{}) ([]int64, error) { + var ret []int64 + + switch in.(type) { + case []int: + for _, v := range in.([]int) { + ret = append(ret, int64(v)) + } + case []int32: + for _, v := range in.([]int32) { + ret = append(ret, int64(v)) + } + case []int64: + // For consistency to ensure callers can always modify ret without + // impacting in. + for _, v := range in.([]int64) { + ret = append(ret, v) + } + case []uint: + for _, v := range in.([]uint) { + ret = append(ret, int64(v)) + } + case []uint32: + for _, v := range in.([]uint32) { + ret = append(ret, int64(v)) + } + case []uint64: + for _, v := range in.([]uint64) { + ret = append(ret, int64(v)) + } + case []json.Number: + for _, v := range in.([]json.Number) { + element, err := ParseInt(v) + if err != nil { + return nil, err + } + ret = append(ret, element) + } + case []string: + for _, v := range in.([]string) { + element, err := ParseInt(v) + if err != nil { + return nil, err + } + ret = append(ret, element) + } + default: + return nil, errors.New("could not parse value from input") + } + + return ret, nil +} + +// ParseIntSlice is a helper function for handling upgrades of optional +// slices; that is, if the API accepts a type similar to , +// nicely handle the common cases of providing only an int-ish, providing +// an actual slice of int-ishes, or providing a comma-separated list of +// numbers. +// +// When []int64 is not the desired final type (or the values should be +// range-bound), it is suggested to call SafeParseIntSlice or +// SafeParseIntSliceRange instead. +func ParseIntSlice(in interface{}) ([]int64, error) { + if ret, err := ParseInt(in); err == nil { + return []int64{ret}, nil + } + + if ret, err := ParseDirectIntSlice(in); err == nil { + return ret, nil + } + + if strings, err := ParseCommaStringSlice(in); err == nil { + var ret []int64 + for _, v := range strings { + if v == "" { + // Ignore empty fields + continue + } + + element, err := ParseInt(v) + if err != nil { + return nil, err + } + ret = append(ret, element) + } + + return ret, nil + } + + return nil, errors.New("could not parse value from input") +} + +// Parses the provided arbitrary value as a boolean-like value. func ParseBool(in interface{}) (bool, error) { var result bool if err := mapstructure.WeakDecode(in, &result); err != nil { @@ -238,6 +352,7 @@ func ParseBool(in interface{}) (bool, error) { return result, nil } +// Parses the provided arbitrary value as a string. func ParseString(in interface{}) (string, error) { var result string if err := mapstructure.WeakDecode(in, &result); err != nil { @@ -246,7 +361,13 @@ func ParseString(in interface{}) (string, error) { return result, nil } +// Parses the provided string-like value as a comma-separated list of values. func ParseCommaStringSlice(in interface{}) ([]string, error) { + jsonIn, ok := in.(json.Number) + if ok { + in = jsonIn.String() + } + rawString, ok := in.(string) if ok && rawString == "" { return []string{}, nil @@ -267,6 +388,7 @@ func ParseCommaStringSlice(in interface{}) ([]string, error) { return strutil.TrimStrings(result), nil } +// Parses the specified value as one or more addresses, separated by commas. func ParseAddrs(addrs interface{}) ([]*sockaddr.SockAddrMarshaler, error) { out := make([]*sockaddr.SockAddrMarshaler, 0) stringAddrs := make([]string, 0) @@ -306,3 +428,75 @@ func ParseAddrs(addrs interface{}) ([]*sockaddr.SockAddrMarshaler, error) { return out, nil } + +// Parses the provided arbitrary value (see ParseInt), ensuring it is within +// the specified range (inclusive of bounds). If this range corresponds to a +// smaller type, the returned value can then be safely cast without risking +// overflow. +func SafeParseIntRange(in interface{}, min int64, max int64) (int64, error) { + raw, err := ParseInt(in) + if err != nil { + return 0, err + } + + if raw < min || raw > max { + return 0, fmt.Errorf("error parsing int value; out of range [%v to %v]: %v", min, max, raw) + } + + return raw, nil +} + +// Parses the specified arbitrary value (see ParseInt), ensuring that the +// resulting value is within the range for an int value. If no error occurred, +// the caller knows no overflow occurred. +func SafeParseInt(in interface{}) (int, error) { + raw, err := SafeParseIntRange(in, math.MinInt, math.MaxInt) + return int(raw), err +} + +// Parses the provided arbitrary value (see ParseIntSlice) into a slice of +// int64 values, ensuring each is within the specified range (inclusive of +// bounds). If this range corresponds to a smaller type, the returned value +// can then be safely cast without risking overflow. +// +// If elements is positive, it is used to ensure the resulting slice is +// bounded above by that many number of elements (inclusive). +func SafeParseIntSliceRange(in interface{}, minValue int64, maxValue int64, elements int) ([]int64, error) { + raw, err := ParseIntSlice(in) + if err != nil { + return nil, err + } + + if elements > 0 && len(raw) > elements { + return nil, fmt.Errorf("error parsing value from input: got %v but expected at most %v elements", len(raw), elements) + } + + for index, value := range raw { + if value < minValue || value > maxValue { + return nil, fmt.Errorf("error parsing value from input: element %v was outside of range [%v to %v]: %v", index, minValue, maxValue, value) + } + } + + return raw, nil +} + +// Parses the provided arbitrary value (see ParseIntSlice) into a slice of +// int values, ensuring the each resulting value in the slice is within the +// range for an int value. If no error occurred, the caller knows no overflow +// occurred. +// +// If elements is positive, it is used to ensure the resulting slice is +// bounded above by that many number of elements (inclusive). +func SafeParseIntSlice(in interface{}, elements int) ([]int, error) { + raw, err := SafeParseIntSliceRange(in, math.MinInt, math.MaxInt, elements) + if err != nil || raw == nil { + return nil, err + } + + var result = make([]int, len(raw)) + for _, element := range raw { + result = append(result, int(element)) + } + + return result, nil +} diff --git a/vendor/github.com/hashicorp/go-secure-stdlib/strutil/strutil.go b/vendor/github.com/hashicorp/go-secure-stdlib/strutil/strutil.go index 55d3cdda7..102462dc6 100644 --- a/vendor/github.com/hashicorp/go-secure-stdlib/strutil/strutil.go +++ b/vendor/github.com/hashicorp/go-secure-stdlib/strutil/strutil.go @@ -230,16 +230,16 @@ func TrimStrings(items []string) []string { // strings. This also may convert the items in the slice to lower case and // returns a sorted slice. func RemoveDuplicates(items []string, lowercase bool) []string { - itemsMap := map[string]bool{} + itemsMap := make(map[string]struct{}, len(items)) for _, item := range items { item = strings.TrimSpace(item) - if lowercase { - item = strings.ToLower(item) - } if item == "" { continue } - itemsMap[item] = true + if lowercase { + item = strings.ToLower(item) + } + itemsMap[item] = struct{}{} } items = make([]string, 0, len(itemsMap)) for item := range itemsMap { @@ -254,18 +254,21 @@ func RemoveDuplicates(items []string, lowercase bool) []string { // In all cases, strings are compared after trimming whitespace // If caseInsensitive, strings will be compared after ToLower() func RemoveDuplicatesStable(items []string, caseInsensitive bool) []string { - itemsMap := make(map[string]bool, len(items)) + itemsMap := make(map[string]struct{}, len(items)) deduplicated := make([]string, 0, len(items)) for _, item := range items { key := strings.TrimSpace(item) + if _, ok := itemsMap[key]; ok || key == "" { + continue + } if caseInsensitive { key = strings.ToLower(key) } - if key == "" || itemsMap[key] { + if _, ok := itemsMap[key]; ok { continue } - itemsMap[key] = true + itemsMap[key] = struct{}{} deduplicated = append(deduplicated, item) } return deduplicated @@ -299,17 +302,18 @@ func EquivalentSlices(a, b []string) bool { } // First we'll build maps to ensure unique values - mapA := map[string]bool{} - mapB := map[string]bool{} + mapA := make(map[string]struct{}, len(a)) + mapB := make(map[string]struct{}, len(b)) for _, keyA := range a { - mapA[keyA] = true + mapA[keyA] = struct{}{} } for _, keyB := range b { - mapB[keyB] = true + mapB[keyB] = struct{}{} } // Now we'll build our checking slices - var sortedA, sortedB []string + sortedA := make([]string, 0, len(mapA)) + sortedB := make([]string, 0, len(mapB)) for keyA := range mapA { sortedA = append(sortedA, keyA) } @@ -434,23 +438,21 @@ func Difference(a, b []string, lowercase bool) []string { a = RemoveDuplicates(a, lowercase) b = RemoveDuplicates(b, lowercase) - itemsMap := map[string]bool{} + itemsMap := map[string]struct{}{} for _, aVal := range a { - itemsMap[aVal] = true + itemsMap[aVal] = struct{}{} } // Perform difference calculation for _, bVal := range b { if _, ok := itemsMap[bVal]; ok { - itemsMap[bVal] = false + delete(itemsMap, bVal) } } items := []string{} - for item, exists := range itemsMap { - if exists { - items = append(items, item) - } + for item := range itemsMap { + items = append(items, item) } sort.Strings(items) return items diff --git a/vendor/github.com/hashicorp/vault/api/auth.go b/vendor/github.com/hashicorp/vault/api/auth.go index 10af56bb9..fa92de4b3 100644 --- a/vendor/github.com/hashicorp/vault/api/auth.go +++ b/vendor/github.com/hashicorp/vault/api/auth.go @@ -31,16 +31,82 @@ func (a *Auth) Login(ctx context.Context, authMethod AuthMethod) (*Secret, error if authMethod == nil { return nil, fmt.Errorf("no auth method provided for login") } + return a.login(ctx, authMethod) +} - authSecret, err := authMethod.Login(ctx, a.c) +// MFALogin is a wrapper that helps satisfy Vault's MFA implementation. +// If optional credentials are provided a single-phase login will be attempted +// and the resulting Secret will contain a ClientToken if the authentication is successful. +// The client's token will also be set accordingly. +// +// If no credentials are provided a two-phase MFA login will be assumed and the resulting +// Secret will have a MFARequirement containing the MFARequestID to be used in a follow-up +// call to `sys/mfa/validate` or by passing it to the method (*Auth).MFAValidate. +func (a *Auth) MFALogin(ctx context.Context, authMethod AuthMethod, creds ...string) (*Secret, error) { + if len(creds) > 0 { + a.c.SetMFACreds(creds) + return a.login(ctx, authMethod) + } + + return a.twoPhaseMFALogin(ctx, authMethod) +} + +// MFAValidate validates an MFA request using the appropriate payload and a secret containing +// Auth.MFARequirement, like the one returned by MFALogin when credentials are not provided. +// Upon successful validation the client token will be set accordingly. +// +// The Secret returned is the authentication secret, which if desired can be +// passed as input to the NewLifetimeWatcher method in order to start +// automatically renewing the token. +func (a *Auth) MFAValidate(ctx context.Context, mfaSecret *Secret, payload map[string]interface{}) (*Secret, error) { + if mfaSecret == nil || mfaSecret.Auth == nil || mfaSecret.Auth.MFARequirement == nil { + return nil, fmt.Errorf("secret does not contain MFARequirements") + } + + s, err := a.c.Sys().MFAValidateWithContext(ctx, mfaSecret.Auth.MFARequirement.GetMFARequestID(), payload) + if err != nil { + return nil, err + } + + return a.checkAndSetToken(s) +} + +// login performs the (*AuthMethod).Login() with the configured client and checks that a ClientToken is returned +func (a *Auth) login(ctx context.Context, authMethod AuthMethod) (*Secret, error) { + s, err := authMethod.Login(ctx, a.c) if err != nil { return nil, fmt.Errorf("unable to log in to auth method: %w", err) } - if authSecret == nil || authSecret.Auth == nil || authSecret.Auth.ClientToken == "" { - return nil, fmt.Errorf("login response from auth method did not return client token") + + return a.checkAndSetToken(s) +} + +// twoPhaseMFALogin performs the (*AuthMethod).Login() with the configured client +// and checks that an MFARequirement is returned +func (a *Auth) twoPhaseMFALogin(ctx context.Context, authMethod AuthMethod) (*Secret, error) { + s, err := authMethod.Login(ctx, a.c) + if err != nil { + return nil, fmt.Errorf("unable to log in: %w", err) + } + if s == nil || s.Auth == nil || s.Auth.MFARequirement == nil { + if s != nil { + s.Warnings = append(s.Warnings, "expected secret to contain MFARequirements") + } + return s, fmt.Errorf("assumed two-phase MFA login, returned secret is missing MFARequirements") } - a.c.SetToken(authSecret.Auth.ClientToken) - - return authSecret, nil + return s, nil +} + +func (a *Auth) checkAndSetToken(s *Secret) (*Secret, error) { + if s == nil || s.Auth == nil || s.Auth.ClientToken == "" { + if s != nil { + s.Warnings = append(s.Warnings, "expected secret to contain ClientToken") + } + return s, fmt.Errorf("response did not return ClientToken, client token not set") + } + + a.c.SetToken(s.Auth.ClientToken) + + return s, nil } diff --git a/vendor/github.com/hashicorp/vault/api/client.go b/vendor/github.com/hashicorp/vault/api/client.go index 99813a21b..b5f7e9bb8 100644 --- a/vendor/github.com/hashicorp/vault/api/client.go +++ b/vendor/github.com/hashicorp/vault/api/client.go @@ -36,6 +36,7 @@ const ( EnvVaultAddress = "VAULT_ADDR" EnvVaultAgentAddr = "VAULT_AGENT_ADDR" EnvVaultCACert = "VAULT_CACERT" + EnvVaultCACertBytes = "VAULT_CACERT_BYTES" EnvVaultCAPath = "VAULT_CAPATH" EnvVaultClientCert = "VAULT_CLIENT_CERT" EnvVaultClientKey = "VAULT_CLIENT_KEY" @@ -50,6 +51,7 @@ const ( EnvVaultMFA = "VAULT_MFA" EnvRateLimit = "VAULT_RATE_LIMIT" EnvHTTPProxy = "VAULT_HTTP_PROXY" + EnvVaultProxyAddr = "VAULT_PROXY_ADDR" HeaderIndex = "X-Vault-Index" HeaderForward = "X-Vault-Forward" HeaderInconsistent = "X-Vault-Inconsistent" @@ -142,6 +144,14 @@ type Config struct { // with the same client. Cloning a client will not clone this value. OutputCurlString bool + // OutputPolicy causes the actual request to return an error of type + // *OutputPolicyError. Type asserting the error message will display + // an example of the required policy HCL needed for the operation. + // + // Note: It is not thread-safe to set this and make concurrent requests + // with the same client. Cloning a client will not clone this value. + OutputPolicy bool + // curlCACert, curlCAPath, curlClientCert and curlClientKey are used to keep // track of the name of the TLS certs and keys when OutputCurlString is set. // Cloning a client will also not clone those values. @@ -172,9 +182,14 @@ type Config struct { // used to communicate with Vault. type TLSConfig struct { // CACert is the path to a PEM-encoded CA cert file to use to verify the - // Vault server SSL certificate. + // Vault server SSL certificate. It takes precedence over CACertBytes + // and CAPath. CACert string + // CACertBytes is a PEM-encoded certificate or bundle. It takes precedence + // over CAPath. + CACertBytes []byte + // CAPath is the path to a directory of PEM-encoded CA cert files to verify // the Vault server SSL certificate. CAPath string @@ -266,12 +281,13 @@ func (c *Config) configureTLS(t *TLSConfig) error { return fmt.Errorf("both client cert and client key must be provided") } - if t.CACert != "" || t.CAPath != "" { + if t.CACert != "" || len(t.CACertBytes) != 0 || t.CAPath != "" { c.curlCACert = t.CACert c.curlCAPath = t.CAPath rootConfig := &rootcerts.Config{ - CAFile: t.CACert, - CAPath: t.CAPath, + CAFile: t.CACert, + CACertificate: t.CACertBytes, + CAPath: t.CAPath, } if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil { return err @@ -313,6 +329,7 @@ func (c *Config) ReadEnvironment() error { var envAddress string var envAgentAddress string var envCACert string + var envCACertBytes []byte var envCAPath string var envClientCert string var envClientKey string @@ -322,7 +339,7 @@ func (c *Config) ReadEnvironment() error { var envMaxRetries *uint64 var envSRVLookup bool var limit *rate.Limiter - var envHTTPProxy string + var envVaultProxy string // Parse the environment variables if v := os.Getenv(EnvVaultAddress); v != "" { @@ -343,6 +360,9 @@ func (c *Config) ReadEnvironment() error { if v := os.Getenv(EnvVaultCACert); v != "" { envCACert = v } + if v := os.Getenv(EnvVaultCACertBytes); v != "" { + envCACertBytes = []byte(v) + } if v := os.Getenv(EnvVaultCAPath); v != "" { envCAPath = v } @@ -392,12 +412,18 @@ func (c *Config) ReadEnvironment() error { } if v := os.Getenv(EnvHTTPProxy); v != "" { - envHTTPProxy = v + envVaultProxy = v + } + + // VAULT_PROXY_ADDR supersedes VAULT_HTTP_PROXY + if v := os.Getenv(EnvVaultProxyAddr); v != "" { + envVaultProxy = v } // Configure the HTTP clients TLS configuration. t := &TLSConfig{ CACert: envCACert, + CACertBytes: envCACertBytes, CAPath: envCAPath, ClientCert: envClientCert, ClientKey: envClientKey, @@ -431,14 +457,14 @@ func (c *Config) ReadEnvironment() error { c.Timeout = envClientTimeout } - if envHTTPProxy != "" { - url, err := url.Parse(envHTTPProxy) + if envVaultProxy != "" { + u, err := url.Parse(envVaultProxy) if err != nil { return err } transport := c.HttpClient.Transport.(*http.Transport) - transport.Proxy = http.ProxyURL(url) + transport.Proxy = http.ProxyURL(u) } return nil @@ -576,7 +602,6 @@ func (c *Client) CloneConfig() *Config { newConfig.CheckRetry = c.config.CheckRetry newConfig.Logger = c.config.Logger newConfig.Limiter = c.config.Limiter - newConfig.OutputCurlString = c.config.OutputCurlString newConfig.SRVLookup = c.config.SRVLookup newConfig.CloneHeaders = c.config.CloneHeaders newConfig.CloneToken = c.config.CloneToken @@ -589,7 +614,7 @@ func (c *Client) CloneConfig() *Config { return newConfig } -// Sets the address of Vault in the client. The format of address should be +// SetAddress sets the address of Vault in the client. The format of address should be // "://:". Setting this on a client will override the // value of VAULT_ADDR environment variable. func (c *Client) SetAddress(addr string) error { @@ -616,6 +641,16 @@ func (c *Client) Address() string { return c.addr.String() } +func (c *Client) SetCheckRedirect(f func(*http.Request, []*http.Request) error) { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + + c.config.modifyLock.Lock() + defer c.config.modifyLock.Unlock() + + c.config.HttpClient.CheckRedirect = f +} + // SetLimiter will set the rate limiter for this client. // This method is thread-safe. // rateLimit and burst are specified according to https://godoc.org/golang.org/x/time/rate#NewLimiter @@ -768,6 +803,24 @@ func (c *Client) SetOutputCurlString(curl bool) { c.config.OutputCurlString = curl } +func (c *Client) OutputPolicy() bool { + c.modifyLock.RLock() + defer c.modifyLock.RUnlock() + c.config.modifyLock.RLock() + defer c.config.modifyLock.RUnlock() + + return c.config.OutputPolicy +} + +func (c *Client) SetOutputPolicy(isSet bool) { + c.modifyLock.RLock() + defer c.modifyLock.RUnlock() + c.config.modifyLock.Lock() + defer c.config.modifyLock.Unlock() + + c.config.OutputPolicy = isSet +} + // CurrentWrappingLookupFunc sets a lookup function that returns desired wrap TTLs // for a given operation and path. func (c *Client) CurrentWrappingLookupFunc() WrappingLookupFunc { @@ -808,10 +861,39 @@ func (c *Client) setNamespace(namespace string) { c.headers.Set(consts.NamespaceHeaderName, namespace) } +// ClearNamespace removes the namespace header if set. func (c *Client) ClearNamespace() { c.modifyLock.Lock() defer c.modifyLock.Unlock() - c.headers.Del(consts.NamespaceHeaderName) + if c.headers != nil { + c.headers.Del(consts.NamespaceHeaderName) + } +} + +// Namespace returns the namespace currently set in this client. It will +// return an empty string if there is no namespace set. +func (c *Client) Namespace() string { + c.modifyLock.Lock() + defer c.modifyLock.Unlock() + if c.headers == nil { + return "" + } + return c.headers.Get(consts.NamespaceHeaderName) +} + +// WithNamespace makes a shallow copy of Client, modifies it to use +// the given namespace, and returns it. Passing an empty string will +// temporarily unset the namespace. +func (c *Client) WithNamespace(namespace string) *Client { + c2 := *c + c2.modifyLock = sync.RWMutex{} + c2.headers = c.Headers() + if namespace == "" { + c2.ClearNamespace() + } else { + c2.SetNamespace(namespace) + } + return &c2 } // Token returns the access token being used by this client. It will @@ -990,22 +1072,21 @@ func (c *Client) clone(cloneHeaders bool) (*Client, error) { defer config.modifyLock.RUnlock() newConfig := &Config{ - Address: config.Address, - HttpClient: config.HttpClient, - MinRetryWait: config.MinRetryWait, - MaxRetryWait: config.MaxRetryWait, - MaxRetries: config.MaxRetries, - Timeout: config.Timeout, - Backoff: config.Backoff, - CheckRetry: config.CheckRetry, - Logger: config.Logger, - Limiter: config.Limiter, - OutputCurlString: config.OutputCurlString, - AgentAddress: config.AgentAddress, - SRVLookup: config.SRVLookup, - CloneHeaders: config.CloneHeaders, - CloneToken: config.CloneToken, - ReadYourWrites: config.ReadYourWrites, + Address: config.Address, + HttpClient: config.HttpClient, + MinRetryWait: config.MinRetryWait, + MaxRetryWait: config.MaxRetryWait, + MaxRetries: config.MaxRetries, + Timeout: config.Timeout, + Backoff: config.Backoff, + CheckRetry: config.CheckRetry, + Logger: config.Logger, + Limiter: config.Limiter, + AgentAddress: config.AgentAddress, + SRVLookup: config.SRVLookup, + CloneHeaders: config.CloneHeaders, + CloneToken: config.CloneToken, + ReadYourWrites: config.ReadYourWrites, } client, err := NewClient(newConfig) if err != nil { @@ -1131,12 +1212,23 @@ func (c *Client) rawRequestWithContext(ctx context.Context, r *Request) (*Respon checkRetry := c.config.CheckRetry backoff := c.config.Backoff httpClient := c.config.HttpClient + ns := c.headers.Get(consts.NamespaceHeaderName) outputCurlString := c.config.OutputCurlString + outputPolicy := c.config.OutputPolicy logger := c.config.Logger c.config.modifyLock.RUnlock() c.modifyLock.RUnlock() + // ensure that the most current namespace setting is used at the time of the call + // e.g. calls using (*Client).WithNamespace + switch ns { + case "": + r.Headers.Del(consts.NamespaceHeaderName) + default: + r.Headers.Set(consts.NamespaceHeaderName, ns) + } + for _, cb := range c.requestCallbacks { cb(r) } @@ -1176,6 +1268,14 @@ START: return nil, LastOutputStringError } + if outputPolicy { + LastOutputPolicyError = &OutputPolicyError{ + method: req.Method, + path: strings.TrimPrefix(req.URL.Path, "/v1"), + } + return nil, LastOutputPolicyError + } + req.Request = req.Request.WithContext(ctx) if backoff == nil { @@ -1268,20 +1368,31 @@ func (c *Client) httpRequestWithContext(ctx context.Context, r *Request) (*Respo limiter := c.config.Limiter httpClient := c.config.HttpClient outputCurlString := c.config.OutputCurlString + outputPolicy := c.config.OutputPolicy + + // add headers if c.headers != nil { for header, vals := range c.headers { for _, val := range vals { req.Header.Add(header, val) } } + // explicitly set the namespace header to current client + if ns := c.headers.Get(consts.NamespaceHeaderName); ns != "" { + r.Headers.Set(consts.NamespaceHeaderName, ns) + } } + c.config.modifyLock.RUnlock() c.modifyLock.RUnlock() - // OutputCurlString logic relies on the request type to be retryable.Request as + // OutputCurlString and OutputPolicy logic rely on the request type to be retryable.Request if outputCurlString { return nil, fmt.Errorf("output-curl-string is not implemented for this request") } + if outputPolicy { + return nil, fmt.Errorf("output-policy is not implemented for this request") + } req.URL.User = r.URL.User req.URL.Scheme = r.URL.Scheme diff --git a/vendor/github.com/hashicorp/vault/api/lifetime_watcher.go b/vendor/github.com/hashicorp/vault/api/lifetime_watcher.go index f775dfb15..f06263526 100644 --- a/vendor/github.com/hashicorp/vault/api/lifetime_watcher.go +++ b/vendor/github.com/hashicorp/vault/api/lifetime_watcher.go @@ -113,7 +113,9 @@ type LifetimeWatcherInput struct { // The new TTL, in seconds, that should be set on the lease. The TTL set // here may or may not be honored by the vault server, based on Vault - // configuration or any associated max TTL values. + // configuration or any associated max TTL values. If specified, the + // minimum of this value and the remaining lease duration will be used + // for grace period calculations. Increment int // RenewBehavior controls what happens when a renewal errors or the @@ -257,7 +259,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, initialTime := time.Now() priorDuration := time.Duration(initLeaseDuration) * time.Second - r.calculateGrace(priorDuration) + r.calculateGrace(priorDuration, time.Duration(r.increment)*time.Second) var errorBackoff backoff.BackOff for { @@ -345,7 +347,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, // extending. Once it stops extending, we've hit the max and need to // rely on the grace duration. if remainingLeaseDuration > priorDuration { - r.calculateGrace(remainingLeaseDuration) + r.calculateGrace(remainingLeaseDuration, time.Duration(r.increment)*time.Second) } priorDuration = remainingLeaseDuration @@ -373,16 +375,21 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, } } -// calculateGrace calculates the grace period based on a reasonable set of -// assumptions given the total lease time; it also adds some jitter to not have -// clients be in sync. -func (r *LifetimeWatcher) calculateGrace(leaseDuration time.Duration) { - if leaseDuration <= 0 { +// calculateGrace calculates the grace period based on the minimum of the +// remaining lease duration and the token increment value; it also adds some +// jitter to not have clients be in sync. +func (r *LifetimeWatcher) calculateGrace(leaseDuration, increment time.Duration) { + minDuration := leaseDuration + if minDuration > increment && increment > 0 { + minDuration = increment + } + + if minDuration <= 0 { r.grace = 0 return } - leaseNanos := float64(leaseDuration.Nanoseconds()) + leaseNanos := float64(minDuration.Nanoseconds()) jitterMax := 0.1 * leaseNanos // For a given lease duration, we want to allow 80-90% of that to elapse, diff --git a/vendor/github.com/hashicorp/vault/api/logical.go b/vendor/github.com/hashicorp/vault/api/logical.go index 39d61b96a..747b9bc12 100644 --- a/vendor/github.com/hashicorp/vault/api/logical.go +++ b/vendor/github.com/hashicorp/vault/api/logical.go @@ -323,7 +323,7 @@ func (c *Logical) UnwrapWithContext(ctx context.Context, wrappingToken string) ( c.c.SetToken(wrappingToken) } - secret, err = c.Read(wrappedResponseLocation) + secret, err = c.ReadWithContext(ctx, wrappedResponseLocation) if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("error reading %q: {{err}}", wrappedResponseLocation), err) } diff --git a/vendor/github.com/hashicorp/vault/api/output_policy.go b/vendor/github.com/hashicorp/vault/api/output_policy.go new file mode 100644 index 000000000..85d1617e5 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/output_policy.go @@ -0,0 +1,82 @@ +package api + +import ( + "fmt" + "net/http" + "net/url" + "strings" +) + +const ( + ErrOutputPolicyRequest = "output a policy, please" +) + +var LastOutputPolicyError *OutputPolicyError + +type OutputPolicyError struct { + method string + path string + finalHCLString string +} + +func (d *OutputPolicyError) Error() string { + if d.finalHCLString == "" { + p, err := d.buildSamplePolicy() + if err != nil { + return err.Error() + } + d.finalHCLString = p + } + + return ErrOutputPolicyRequest +} + +func (d *OutputPolicyError) HCLString() (string, error) { + if d.finalHCLString == "" { + p, err := d.buildSamplePolicy() + if err != nil { + return "", err + } + d.finalHCLString = p + } + return d.finalHCLString, nil +} + +// Builds a sample policy document from the request +func (d *OutputPolicyError) buildSamplePolicy() (string, error) { + var capabilities []string + switch d.method { + case http.MethodGet, "": + capabilities = append(capabilities, "read") + case http.MethodPost, http.MethodPut: + capabilities = append(capabilities, "create") + capabilities = append(capabilities, "update") + case http.MethodPatch: + capabilities = append(capabilities, "patch") + case http.MethodDelete: + capabilities = append(capabilities, "delete") + case "LIST": + capabilities = append(capabilities, "list") + } + + // sanitize, then trim the Vault address and v1 from the front of the path + path, err := url.PathUnescape(d.path) + if err != nil { + return "", fmt.Errorf("failed to unescape request URL characters: %v", err) + } + + // determine whether to add sudo capability + if IsSudoPath(path) { + capabilities = append(capabilities, "sudo") + } + + // the OpenAPI response has a / in front of each path, + // but policies need the path without that leading slash + path = strings.TrimLeft(path, "/") + + capStr := strings.Join(capabilities, `", "`) + return fmt.Sprintf( + `path "%s" { + capabilities = ["%s"] +}`, path, capStr), nil +} diff --git a/vendor/github.com/hashicorp/vault/api/output_string.go b/vendor/github.com/hashicorp/vault/api/output_string.go index 9129ea0c3..b8c396ebc 100644 --- a/vendor/github.com/hashicorp/vault/api/output_string.go +++ b/vendor/github.com/hashicorp/vault/api/output_string.go @@ -19,58 +19,68 @@ type OutputStringError struct { TLSSkipVerify bool ClientCACert, ClientCAPath string ClientCert, ClientKey string - parsingError error - parsedCurlString string + finalCurlString string } func (d *OutputStringError) Error() string { - if d.parsedCurlString == "" { - d.parseRequest() - if d.parsingError != nil { - return d.parsingError.Error() + if d.finalCurlString == "" { + cs, err := d.buildCurlString() + if err != nil { + return err.Error() } + d.finalCurlString = cs } return ErrOutputStringRequest } -func (d *OutputStringError) parseRequest() { +func (d *OutputStringError) CurlString() (string, error) { + if d.finalCurlString == "" { + cs, err := d.buildCurlString() + if err != nil { + return "", err + } + d.finalCurlString = cs + } + return d.finalCurlString, nil +} + +func (d *OutputStringError) buildCurlString() (string, error) { body, err := d.Request.BodyBytes() if err != nil { - d.parsingError = err - return + return "", err } // Build cURL string - d.parsedCurlString = "curl " + finalCurlString := "curl " if d.TLSSkipVerify { - d.parsedCurlString += "--insecure " + finalCurlString += "--insecure " } if d.Request.Method != http.MethodGet { - d.parsedCurlString = fmt.Sprintf("%s-X %s ", d.parsedCurlString, d.Request.Method) + finalCurlString = fmt.Sprintf("%s-X %s ", finalCurlString, d.Request.Method) } if d.ClientCACert != "" { clientCACert := strings.Replace(d.ClientCACert, "'", "'\"'\"'", -1) - d.parsedCurlString = fmt.Sprintf("%s--cacert '%s' ", d.parsedCurlString, clientCACert) + finalCurlString = fmt.Sprintf("%s--cacert '%s' ", finalCurlString, clientCACert) } if d.ClientCAPath != "" { clientCAPath := strings.Replace(d.ClientCAPath, "'", "'\"'\"'", -1) - d.parsedCurlString = fmt.Sprintf("%s--capath '%s' ", d.parsedCurlString, clientCAPath) + finalCurlString = fmt.Sprintf("%s--capath '%s' ", finalCurlString, clientCAPath) } if d.ClientCert != "" { clientCert := strings.Replace(d.ClientCert, "'", "'\"'\"'", -1) - d.parsedCurlString = fmt.Sprintf("%s--cert '%s' ", d.parsedCurlString, clientCert) + finalCurlString = fmt.Sprintf("%s--cert '%s' ", finalCurlString, clientCert) } if d.ClientKey != "" { clientKey := strings.Replace(d.ClientKey, "'", "'\"'\"'", -1) - d.parsedCurlString = fmt.Sprintf("%s--key '%s' ", d.parsedCurlString, clientKey) + finalCurlString = fmt.Sprintf("%s--key '%s' ", finalCurlString, clientKey) } for k, v := range d.Request.Header { for _, h := range v { if strings.ToLower(k) == "x-vault-token" { h = `$(vault print token)` } - d.parsedCurlString = fmt.Sprintf("%s-H \"%s: %s\" ", d.parsedCurlString, k, h) + finalCurlString = fmt.Sprintf("%s-H \"%s: %s\" ", finalCurlString, k, h) } } @@ -78,15 +88,8 @@ func (d *OutputStringError) parseRequest() { // We need to escape single quotes since that's what we're using to // quote the body escapedBody := strings.Replace(string(body), "'", "'\"'\"'", -1) - d.parsedCurlString = fmt.Sprintf("%s-d '%s' ", d.parsedCurlString, escapedBody) + finalCurlString = fmt.Sprintf("%s-d '%s' ", finalCurlString, escapedBody) } - d.parsedCurlString = fmt.Sprintf("%s%s", d.parsedCurlString, d.Request.URL.String()) -} - -func (d *OutputStringError) CurlString() string { - if d.parsedCurlString == "" { - d.parseRequest() - } - return d.parsedCurlString + return fmt.Sprintf("%s%s", finalCurlString, d.Request.URL.String()), nil } diff --git a/vendor/github.com/hashicorp/vault/api/plugin_helpers.go b/vendor/github.com/hashicorp/vault/api/plugin_helpers.go index e7da60cc5..e8ceb9c2f 100644 --- a/vendor/github.com/hashicorp/vault/api/plugin_helpers.go +++ b/vendor/github.com/hashicorp/vault/api/plugin_helpers.go @@ -9,6 +9,7 @@ import ( "flag" "net/url" "os" + "regexp" squarejwt "gopkg.in/square/go-jose.v2/jwt" @@ -23,6 +24,49 @@ var ( // PluginUnwrapTokenEnv is the ENV name used to pass unwrap tokens to the // plugin. PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN" + + // sudoPaths is a map containing the paths that require a token's policy + // to have the "sudo" capability. The keys are the paths as strings, in + // the same format as they are returned by the OpenAPI spec. The values + // are the regular expressions that can be used to test whether a given + // path matches that path or not (useful specifically for the paths that + // contain templated fields.) + sudoPaths = map[string]*regexp.Regexp{ + "/auth/token/accessors/": regexp.MustCompile(`^/auth/token/accessors/$`), + "/pki/root": regexp.MustCompile(`^/pki/root$`), + "/pki/root/sign-self-issued": regexp.MustCompile(`^/pki/root/sign-self-issued$`), + "/sys/audit": regexp.MustCompile(`^/sys/audit$`), + "/sys/audit/{path}": regexp.MustCompile(`^/sys/audit/.+$`), + "/sys/auth/{path}": regexp.MustCompile(`^/sys/auth/.+$`), + "/sys/auth/{path}/tune": regexp.MustCompile(`^/sys/auth/.+/tune$`), + "/sys/config/auditing/request-headers": regexp.MustCompile(`^/sys/config/auditing/request-headers$`), + "/sys/config/auditing/request-headers/{header}": regexp.MustCompile(`^/sys/config/auditing/request-headers/.+$`), + "/sys/config/cors": regexp.MustCompile(`^/sys/config/cors$`), + "/sys/config/ui/headers/": regexp.MustCompile(`^/sys/config/ui/headers/$`), + "/sys/config/ui/headers/{header}": regexp.MustCompile(`^/sys/config/ui/headers/.+$`), + "/sys/leases": regexp.MustCompile(`^/sys/leases$`), + "/sys/leases/lookup/": regexp.MustCompile(`^/sys/leases/lookup/$`), + "/sys/leases/lookup/{prefix}": regexp.MustCompile(`^/sys/leases/lookup/.+$`), + "/sys/leases/revoke-force/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-force/.+$`), + "/sys/leases/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/leases/revoke-prefix/.+$`), + "/sys/plugins/catalog/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[^/]+$`), + "/sys/plugins/catalog/{type}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+$`), + "/sys/plugins/catalog/{type}/{name}": regexp.MustCompile(`^/sys/plugins/catalog/[\w-]+/[^/]+$`), + "/sys/raw": regexp.MustCompile(`^/sys/raw$`), + "/sys/raw/{path}": regexp.MustCompile(`^/sys/raw/.+$`), + "/sys/remount": regexp.MustCompile(`^/sys/remount$`), + "/sys/revoke-force/{prefix}": regexp.MustCompile(`^/sys/revoke-force/.+$`), + "/sys/revoke-prefix/{prefix}": regexp.MustCompile(`^/sys/revoke-prefix/.+$`), + "/sys/rotate": regexp.MustCompile(`^/sys/rotate$`), + + // enterprise-only paths + "/sys/replication/dr/primary/secondary-token": regexp.MustCompile(`^/sys/replication/dr/primary/secondary-token$`), + "/sys/replication/performance/primary/secondary-token": regexp.MustCompile(`^/sys/replication/performance/primary/secondary-token$`), + "/sys/replication/primary/secondary-token": regexp.MustCompile(`^/sys/replication/primary/secondary-token$`), + "/sys/replication/reindex": regexp.MustCompile(`^/sys/replication/reindex$`), + "/sys/storage/raft/snapshot-auto/config/": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/$`), + "/sys/storage/raft/snapshot-auto/config/{name}": regexp.MustCompile(`^/sys/storage/raft/snapshot-auto/config/[^/]+$`), + } ) // PluginAPIClientMeta is a helper that plugins can use to configure TLS connections @@ -192,3 +236,28 @@ func VaultPluginTLSProviderContext(ctx context.Context, apiTLSConfig *TLSConfig) return tlsConfig, nil } } + +func SudoPaths() map[string]*regexp.Regexp { + return sudoPaths +} + +// Determine whether the given path requires the sudo capability +func IsSudoPath(path string) bool { + // Return early if the path is any of the non-templated sudo paths. + if _, ok := sudoPaths[path]; ok { + return true + } + + // Some sudo paths have templated fields in them. + // (e.g. /sys/revoke-prefix/{prefix}) + // The values in the sudoPaths map are actually regular expressions, + // so we can check if our path matches against them. + for _, sudoPathRegexp := range sudoPaths { + match := sudoPathRegexp.MatchString(path) + if match { + return true + } + } + + return false +} diff --git a/vendor/github.com/hashicorp/vault/api/secret.go b/vendor/github.com/hashicorp/vault/api/secret.go index a3a288bf1..77e3ee9a9 100644 --- a/vendor/github.com/hashicorp/vault/api/secret.go +++ b/vendor/github.com/hashicorp/vault/api/secret.go @@ -94,12 +94,7 @@ func (s *Secret) TokenRemainingUses() (int, error) { return -1, nil } - uses, err := parseutil.ParseInt(s.Data["num_uses"]) - if err != nil { - return 0, err - } - - return int(uses), nil + return parseutil.SafeParseInt(s.Data["num_uses"]) } // TokenPolicies returns the standardized list of policies for the given secret. diff --git a/vendor/github.com/hashicorp/vault/api/sys_hastatus.go b/vendor/github.com/hashicorp/vault/api/sys_hastatus.go index 35bf40336..d89d59651 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_hastatus.go +++ b/vendor/github.com/hashicorp/vault/api/sys_hastatus.go @@ -37,4 +37,7 @@ type HANode struct { ClusterAddress string `json:"cluster_address"` ActiveNode bool `json:"active_node"` LastEcho *time.Time `json:"last_echo"` + Version string `json:"version"` + UpgradeVersion string `json:"upgrade_version,omitempty"` + RedundancyZone string `json:"redundancy_zone,omitempty"` } diff --git a/vendor/github.com/hashicorp/vault/api/sys_mfa.go b/vendor/github.com/hashicorp/vault/api/sys_mfa.go new file mode 100644 index 000000000..a1ba1bd80 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_mfa.go @@ -0,0 +1,45 @@ +package api + +import ( + "context" + "fmt" + "net/http" +) + +func (c *Sys) MFAValidate(requestID string, payload map[string]interface{}) (*Secret, error) { + return c.MFAValidateWithContext(context.Background(), requestID, payload) +} + +func (c *Sys) MFAValidateWithContext(ctx context.Context, requestID string, payload map[string]interface{}) (*Secret, error) { + ctx, cancelFunc := c.c.withConfiguredTimeout(ctx) + defer cancelFunc() + + body := map[string]interface{}{ + "mfa_request_id": requestID, + "mfa_payload": payload, + } + + r := c.c.NewRequest(http.MethodPost, fmt.Sprintf("/v1/sys/mfa/validate")) + if err := r.SetJSONBody(body); err != nil { + return nil, fmt.Errorf("failed to set request body: %w", err) + } + + resp, err := c.c.rawRequestWithContext(ctx, r) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + return nil, err + } + + secret, err := ParseSecret(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to parse secret from response: %w", err) + } + + if secret == nil { + return nil, fmt.Errorf("data from server response is empty") + } + + return secret, nil +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_monitor.go b/vendor/github.com/hashicorp/vault/api/sys_monitor.go index df2774672..6813799f0 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_monitor.go +++ b/vendor/github.com/hashicorp/vault/api/sys_monitor.go @@ -5,11 +5,13 @@ import ( "context" "fmt" "net/http" + + "github.com/hashicorp/vault/sdk/helper/logging" ) // Monitor returns a channel that outputs strings containing the log messages // coming from the server. -func (c *Sys) Monitor(ctx context.Context, logLevel string) (chan string, error) { +func (c *Sys) Monitor(ctx context.Context, logLevel string, logFormat string) (chan string, error) { r := c.c.NewRequest(http.MethodGet, "/v1/sys/monitor") if logLevel == "" { @@ -18,6 +20,12 @@ func (c *Sys) Monitor(ctx context.Context, logLevel string) (chan string, error) r.Params.Add("log_level", logLevel) } + if logFormat == "" || logFormat == logging.UnspecifiedFormat.String() { + r.Params.Add("log_format", "standard") + } else { + r.Params.Add("log_format", logFormat) + } + resp, err := c.c.RawRequestWithContext(ctx, r) if err != nil { return nil, err diff --git a/vendor/github.com/hashicorp/vault/api/sys_plugins.go b/vendor/github.com/hashicorp/vault/api/sys_plugins.go index 920af4c3c..004ee222b 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_plugins.go +++ b/vendor/github.com/hashicorp/vault/api/sys_plugins.go @@ -100,23 +100,24 @@ func (c *Sys) ListPluginsWithContext(ctx context.Context, i *ListPluginsInput) ( PluginsByType: make(map[consts.PluginType][]string), } if i.Type == consts.PluginTypeUnknown { - for pluginTypeStr, pluginsRaw := range secret.Data { - pluginType, err := consts.ParsePluginType(pluginTypeStr) - if err != nil { - return nil, err + for _, pluginType := range consts.PluginTypes { + pluginsRaw, ok := secret.Data[pluginType.String()] + if !ok { + continue } pluginsIfc, ok := pluginsRaw.([]interface{}) if !ok { - return nil, fmt.Errorf("unable to parse plugins for %q type", pluginTypeStr) + return nil, fmt.Errorf("unable to parse plugins for %q type", pluginType.String()) } - plugins := make([]string, len(pluginsIfc)) - for i, nameIfc := range pluginsIfc { + plugins := make([]string, 0, len(pluginsIfc)) + for _, nameIfc := range pluginsIfc { name, ok := nameIfc.(string) if !ok { + continue } - plugins[i] = name + plugins = append(plugins, name) } result.PluginsByType[pluginType] = plugins } diff --git a/vendor/github.com/hashicorp/vault/api/sys_raft.go b/vendor/github.com/hashicorp/vault/api/sys_raft.go index df10bf672..7806a1418 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_raft.go +++ b/vendor/github.com/hashicorp/vault/api/sys_raft.go @@ -44,6 +44,7 @@ type AutopilotConfig struct { MaxTrailingLogs uint64 `json:"max_trailing_logs" mapstructure:"max_trailing_logs"` MinQuorum uint `json:"min_quorum" mapstructure:"min_quorum"` ServerStabilizationTime time.Duration `json:"server_stabilization_time" mapstructure:"-"` + DisableUpgradeMigration bool `json:"disable_upgrade_migration" mapstructure:"disable_upgrade_migration"` } // MarshalJSON makes the autopilot config fields JSON compatible @@ -55,6 +56,7 @@ func (ac *AutopilotConfig) MarshalJSON() ([]byte, error) { "max_trailing_logs": ac.MaxTrailingLogs, "min_quorum": ac.MinQuorum, "server_stabilization_time": ac.ServerStabilizationTime.String(), + "disable_upgrade_migration": ac.DisableUpgradeMigration, }) } @@ -84,28 +86,59 @@ func (ac *AutopilotConfig) UnmarshalJSON(b []byte) error { // AutopilotState represents the response of the raft autopilot state API type AutopilotState struct { - Healthy bool `mapstructure:"healthy"` - FailureTolerance int `mapstructure:"failure_tolerance"` - Servers map[string]*AutopilotServer `mapstructure:"servers"` - Leader string `mapstructure:"leader"` - Voters []string `mapstructure:"voters"` - NonVoters []string `mapstructure:"non_voters"` + Healthy bool `mapstructure:"healthy"` + FailureTolerance int `mapstructure:"failure_tolerance"` + Servers map[string]*AutopilotServer `mapstructure:"servers"` + Leader string `mapstructure:"leader"` + Voters []string `mapstructure:"voters"` + NonVoters []string `mapstructure:"non_voters"` + RedundancyZones map[string]AutopilotZone `mapstructure:"redundancy_zones,omitempty"` + Upgrade *AutopilotUpgrade `mapstructure:"upgrade_info,omitempty"` + OptimisticFailureTolerance int `mapstructure:"optimistic_failure_tolerance,omitempty"` } // AutopilotServer represents the server blocks in the response of the raft // autopilot state API. type AutopilotServer struct { - ID string `mapstructure:"id"` - Name string `mapstructure:"name"` - Address string `mapstructure:"address"` - NodeStatus string `mapstructure:"node_status"` - LastContact string `mapstructure:"last_contact"` - LastTerm uint64 `mapstructure:"last_term"` - LastIndex uint64 `mapstructure:"last_index"` - Healthy bool `mapstructure:"healthy"` - StableSince string `mapstructure:"stable_since"` - Status string `mapstructure:"status"` - Meta map[string]string `mapstructure:"meta"` + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + Address string `mapstructure:"address"` + NodeStatus string `mapstructure:"node_status"` + LastContact string `mapstructure:"last_contact"` + LastTerm uint64 `mapstructure:"last_term"` + LastIndex uint64 `mapstructure:"last_index"` + Healthy bool `mapstructure:"healthy"` + StableSince string `mapstructure:"stable_since"` + Status string `mapstructure:"status"` + Version string `mapstructure:"version"` + UpgradeVersion string `mapstructure:"upgrade_version,omitempty"` + RedundancyZone string `mapstructure:"redundancy_zone,omitempty"` + NodeType string `mapstructure:"node_type,omitempty"` +} + +type AutopilotZone struct { + Servers []string `mapstructure:"servers,omitempty"` + Voters []string `mapstructure:"voters,omitempty"` + FailureTolerance int `mapstructure:"failure_tolerance,omitempty"` +} + +type AutopilotUpgrade struct { + Status string `mapstructure:"status"` + TargetVersion string `mapstructure:"target_version,omitempty"` + TargetVersionVoters []string `mapstructure:"target_version_voters,omitempty"` + TargetVersionNonVoters []string `mapstructure:"target_version_non_voters,omitempty"` + TargetVersionReadReplicas []string `mapstructure:"target_version_read_replicas,omitempty"` + OtherVersionVoters []string `mapstructure:"other_version_voters,omitempty"` + OtherVersionNonVoters []string `mapstructure:"other_version_non_voters,omitempty"` + OtherVersionReadReplicas []string `mapstructure:"other_version_read_replicas,omitempty"` + RedundancyZones map[string]AutopilotZoneUpgradeVersions `mapstructure:"redundancy_zones,omitempty"` +} + +type AutopilotZoneUpgradeVersions struct { + TargetVersionVoters []string `mapstructure:"target_version_voters,omitempty"` + TargetVersionNonVoters []string `mapstructure:"target_version_non_voters,omitempty"` + OtherVersionVoters []string `mapstructure:"other_version_voters,omitempty"` + OtherVersionNonVoters []string `mapstructure:"other_version_non_voters,omitempty"` } // RaftJoin wraps RaftJoinWithContext using context.Background. diff --git a/vendor/github.com/hashicorp/vault/api/sys_seal.go b/vendor/github.com/hashicorp/vault/api/sys_seal.go index dcd8d32a5..189d61469 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_seal.go +++ b/vendor/github.com/hashicorp/vault/api/sys_seal.go @@ -101,6 +101,7 @@ type SealStatusResponse struct { Progress int `json:"progress"` Nonce string `json:"nonce"` Version string `json:"version"` + BuildDate string `json:"build_date"` Migration bool `json:"migration"` ClusterName string `json:"cluster_name,omitempty"` ClusterID string `json:"cluster_id,omitempty"` diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go index b6bee2e34..27d056854 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go @@ -150,6 +150,46 @@ func ParsePKIJSON(input []byte) (*ParsedCertBundle, error) { return nil, errutil.UserError{Err: "unable to parse out of either secret data or a secret object"} } +func ParseDERKey(privateKeyBytes []byte) (signer crypto.Signer, format BlockType, err error) { + if signer, err = x509.ParseECPrivateKey(privateKeyBytes); err == nil { + format = ECBlock + return + } + + if signer, err = x509.ParsePKCS1PrivateKey(privateKeyBytes); err == nil { + format = PKCS1Block + return + } + + var rawKey interface{} + if rawKey, err = x509.ParsePKCS8PrivateKey(privateKeyBytes); err == nil { + switch rawSigner := rawKey.(type) { + case *rsa.PrivateKey: + signer = rawSigner + case *ecdsa.PrivateKey: + signer = rawSigner + case ed25519.PrivateKey: + signer = rawSigner + default: + return nil, UnknownBlock, errutil.InternalError{Err: "unknown type for parsed PKCS8 Private Key"} + } + + format = PKCS8Block + return + } + + return nil, UnknownBlock, err +} + +func ParsePEMKey(keyPem string) (crypto.Signer, BlockType, error) { + pemBlock, _ := pem.Decode([]byte(keyPem)) + if pemBlock == nil { + return nil, UnknownBlock, errutil.UserError{Err: "no data found in PEM block"} + } + + return ParseDERKey(pemBlock.Bytes) +} + // ParsePEMBundle takes a string of concatenated PEM-format certificate // and private key values and decodes/parses them, checking validity along // the way. The first certificate must be the subject certificate and issuing @@ -170,43 +210,19 @@ func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) { return nil, errutil.UserError{Err: "no data found in PEM block"} } - if signer, err := x509.ParseECPrivateKey(pemBlock.Bytes); err == nil { + if signer, format, err := ParseDERKey(pemBlock.Bytes); err == nil { if parsedBundle.PrivateKeyType != UnknownPrivateKey { return nil, errutil.UserError{Err: "more than one private key given; provide only one private key in the bundle"} } - parsedBundle.PrivateKeyFormat = ECBlock - parsedBundle.PrivateKeyType = ECPrivateKey + + parsedBundle.PrivateKeyFormat = format + parsedBundle.PrivateKeyType = GetPrivateKeyTypeFromSigner(signer) + if parsedBundle.PrivateKeyType == UnknownPrivateKey { + return nil, errutil.UserError{Err: "Unknown type of private key included in the bundle: %v"} + } + parsedBundle.PrivateKeyBytes = pemBlock.Bytes parsedBundle.PrivateKey = signer - - } else if signer, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes); err == nil { - if parsedBundle.PrivateKeyType != UnknownPrivateKey { - return nil, errutil.UserError{Err: "more than one private key given; provide only one private key in the bundle"} - } - parsedBundle.PrivateKeyType = RSAPrivateKey - parsedBundle.PrivateKeyFormat = PKCS1Block - parsedBundle.PrivateKeyBytes = pemBlock.Bytes - parsedBundle.PrivateKey = signer - } else if signer, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes); err == nil { - parsedBundle.PrivateKeyFormat = PKCS8Block - - if parsedBundle.PrivateKeyType != UnknownPrivateKey { - return nil, errutil.UserError{Err: "More than one private key given; provide only one private key in the bundle"} - } - switch signer := signer.(type) { - case *rsa.PrivateKey: - parsedBundle.PrivateKey = signer - parsedBundle.PrivateKeyType = RSAPrivateKey - parsedBundle.PrivateKeyBytes = pemBlock.Bytes - case *ecdsa.PrivateKey: - parsedBundle.PrivateKey = signer - parsedBundle.PrivateKeyType = ECPrivateKey - parsedBundle.PrivateKeyBytes = pemBlock.Bytes - case ed25519.PrivateKey: - parsedBundle.PrivateKey = signer - parsedBundle.PrivateKeyType = Ed25519PrivateKey - parsedBundle.PrivateKeyBytes = pemBlock.Bytes - } } else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil { certPath = append(certPath, &CertBlock{ Certificate: certificates[0], @@ -336,7 +352,21 @@ func generateSerialNumber(randReader io.Reader) (*big.Int, error) { return serial, nil } -// ComparePublicKeys compares two public keys and returns true if they match +// ComparePublicKeysAndType compares two public keys and returns true if they match, +// false if their types or contents differ, and an error on unsupported key types. +func ComparePublicKeysAndType(key1Iface, key2Iface crypto.PublicKey) (bool, error) { + equal, err := ComparePublicKeys(key1Iface, key2Iface) + if err != nil { + if strings.Contains(err.Error(), "key types do not match:") { + return false, nil + } + } + + return equal, err +} + +// ComparePublicKeys compares two public keys and returns true if they match, +// returns an error if public key types are mismatched, or they are an unsupported key type. func ComparePublicKeys(key1Iface, key2Iface crypto.PublicKey) (bool, error) { switch key1Iface.(type) { case *rsa.PublicKey: @@ -585,25 +615,17 @@ func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) { // certain internal circumstances. func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) { if keyType == "ec" { - // To comply with BSI recommendations Section 4.2 and Mozilla root - // store policy section 5.1.2, enforce that NIST P-curves use a hash - // length corresponding to curve length. Note that ed25519 does not - // the "ec" key type. - expectedHashBits := expectedNISTPCurveHashBits[keyBits] - - if expectedHashBits != hashBits && hashBits != 0 { - return hashBits, fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", hashBits, keyBits) - } else if hashBits == 0 { - hashBits = expectedHashBits - } + // Enforcement of curve moved to selectSignatureAlgorithmForECDSA. See + // note there about why. } else if keyType == "rsa" && hashBits == 0 { // To match previous behavior (and ignoring NIST's recommendations for // hash size to align with RSA key sizes), default to SHA-2-256. hashBits = 256 - } else if keyType == "ed25519" || keyType == "ed448" { + } else if keyType == "ed25519" || keyType == "ed448" || keyType == "any" { // No-op; ed25519 and ed448 internally specify their own hash and // we do not need to select one. Double hashing isn't supported in - // certificate signing and we must + // certificate signing. Additionally, the any key type can't know + // what hash algorithm to use yet, so default to zero. return 0, nil } @@ -642,7 +664,7 @@ func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, h // Validates that the length of the hash (in bits) used in the signature // calculation is a known, approved value. func ValidateSignatureLength(keyType string, hashBits int) error { - if keyType == "ed25519" || keyType == "ed448" { + if keyType == "any" || keyType == "ec" || keyType == "ed25519" || keyType == "ed448" { // ed25519 and ed448 include built-in hashing and is not externally // configurable. There are three modes for each of these schemes: // @@ -654,6 +676,13 @@ func ValidateSignatureLength(keyType string, hashBits int) error { // // In all cases, we won't have a hash algorithm to validate here, so // return nil. + // + // Additionally, when KeyType is any, we can't yet validate the + // signature algorithm size, so it takes the default zero value. + // + // When KeyType is ec, we also can't validate this value as we're + // forcefully ignoring the users' choice and specifying a value based + // on issuer type. return nil } @@ -842,16 +871,25 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen } if data.SigningBundle != nil { - if len(data.SigningBundle.Certificate.AuthorityKeyId) > 0 && - !bytes.Equal(data.SigningBundle.Certificate.AuthorityKeyId, data.SigningBundle.Certificate.SubjectKeyId) { + if (len(data.SigningBundle.Certificate.AuthorityKeyId) > 0 && + !bytes.Equal(data.SigningBundle.Certificate.AuthorityKeyId, data.SigningBundle.Certificate.SubjectKeyId)) || + data.Params.ForceAppendCaChain { + var chain []*CertBlock - result.CAChain = []*CertBlock{ - { + signingChain := data.SigningBundle.CAChain + // Some bundles already include the root included in the chain, so don't include it twice. + if len(signingChain) == 0 || !bytes.Equal(signingChain[0].Bytes, data.SigningBundle.CertificateBytes) { + chain = append(chain, &CertBlock{ Certificate: data.SigningBundle.Certificate, Bytes: data.SigningBundle.CertificateBytes, - }, + }) } - result.CAChain = append(result.CAChain, data.SigningBundle.CAChain...) + + if len(signingChain) > 0 { + chain = append(chain, signingChain...) + } + + result.CAChain = chain } } @@ -859,16 +897,25 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen } func selectSignatureAlgorithmForECDSA(pub crypto.PublicKey, signatureBits int) x509.SignatureAlgorithm { - // If signature bits are configured, prefer them to the default choice. - switch signatureBits { - case 256: - return x509.ECDSAWithSHA256 - case 384: - return x509.ECDSAWithSHA384 - case 512: - return x509.ECDSAWithSHA512 - } - + // Previously we preferred the user-specified signature bits for ECDSA + // keys. However, this could result in using a longer hash function than + // the underlying NIST P-curve will encode (e.g., a SHA-512 hash with a + // P-256 key). This isn't ideal: the hash is implicitly truncated + // (effectively turning it into SHA-512/256) and we then need to rely + // on the prefix security of the hash. Since both NIST and Mozilla guidance + // suggest instead using the correct hash function, we should prefer that + // over the operator-specified signatureBits. + // + // Lastly, note that pub above needs to be the _signer's_ public key; + // the issue with DefaultOrValueHashBits is that it is called at role + // configuration time, which might _precede_ issuer generation. Thus + // it only has access to the desired key type and not the actual issuer. + // The reference from that function is reproduced below: + // + // > To comply with BSI recommendations Section 4.2 and Mozilla root + // > store policy section 5.1.2, enforce that NIST P-curves use a hash + // > length corresponding to curve length. Note that ed25519 does not + // > implement the "ec" key type. key, ok := pub.(*ecdsa.PublicKey) if !ok { return x509.ECDSAWithSHA256 @@ -1120,7 +1167,7 @@ func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBun return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)} } - result.CAChain = data.SigningBundle.GetCAChain() + result.CAChain = data.SigningBundle.GetFullChain() return result, nil } @@ -1190,3 +1237,20 @@ func GetPublicKeySize(key crypto.PublicKey) int { return -1 } + +// CreateKeyBundle create a KeyBundle struct object which includes a generated key +// of keyType with keyBits leveraging the randomness from randReader. +func CreateKeyBundle(keyType string, keyBits int, randReader io.Reader) (KeyBundle, error) { + return CreateKeyBundleWithKeyGenerator(keyType, keyBits, randReader, generatePrivateKey) +} + +// CreateKeyBundleWithKeyGenerator create a KeyBundle struct object which includes +// a generated key of keyType with keyBits leveraging the randomness from randReader and +// delegates the actual key generation to keyGenerator +func CreateKeyBundleWithKeyGenerator(keyType string, keyBits int, randReader io.Reader, keyGenerator KeyGenerator) (KeyBundle, error) { + result := KeyBundle{} + if err := keyGenerator(keyType, keyBits, &result, randReader); err != nil { + return result, err + } + return result, nil +} diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go index 076a4e352..76587826e 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go @@ -78,9 +78,10 @@ type BlockType string // Well-known formats const ( - PKCS1Block BlockType = "RSA PRIVATE KEY" - PKCS8Block BlockType = "PRIVATE KEY" - ECBlock BlockType = "EC PRIVATE KEY" + UnknownBlock BlockType = "" + PKCS1Block BlockType = "RSA PRIVATE KEY" + PKCS8Block BlockType = "PRIVATE KEY" + ECBlock BlockType = "EC PRIVATE KEY" ) // ParsedPrivateKeyContainer allows common key setting for certs and CSRs @@ -137,6 +138,25 @@ type ParsedCSRBundle struct { CSR *x509.CertificateRequest } +type KeyBundle struct { + PrivateKeyType PrivateKeyType + PrivateKeyBytes []byte + PrivateKey crypto.Signer +} + +func GetPrivateKeyTypeFromSigner(signer crypto.Signer) PrivateKeyType { + switch signer.(type) { + case *rsa.PrivateKey: + return RSAPrivateKey + case *ecdsa.PrivateKey: + return ECPrivateKey + case ed25519.PrivateKey: + return Ed25519PrivateKey + default: + return UnknownPrivateKey + } +} + // ToPEMBundle converts a string-based certificate bundle // to a PEM-based string certificate bundle in trust path // order, leaf certificate first @@ -661,9 +681,32 @@ type URLEntries struct { OCSPServers []string `json:"ocsp_servers" structs:"ocsp_servers" mapstructure:"ocsp_servers"` } +type NotAfterBehavior int + +const ( + ErrNotAfterBehavior NotAfterBehavior = iota + TruncateNotAfterBehavior + PermitNotAfterBehavior +) + +var notAfterBehaviorNames = map[NotAfterBehavior]string{ + ErrNotAfterBehavior: "err", + TruncateNotAfterBehavior: "truncate", + PermitNotAfterBehavior: "permit", +} + +func (n NotAfterBehavior) String() string { + if name, ok := notAfterBehaviorNames[n]; ok && len(name) > 0 { + return name + } + + return "unknown" +} + type CAInfoBundle struct { ParsedCertBundle - URLs *URLEntries + URLs *URLEntries + LeafNotAfterBehavior NotAfterBehavior } func (b *CAInfoBundle) GetCAChain() []*CertBlock { @@ -675,13 +718,7 @@ func (b *CAInfoBundle) GetCAChain() []*CertBlock { (len(b.Certificate.AuthorityKeyId) == 0 && !bytes.Equal(b.Certificate.RawIssuer, b.Certificate.RawSubject)) { - chain = append(chain, &CertBlock{ - Certificate: b.Certificate, - Bytes: b.CertificateBytes, - }) - if b.CAChain != nil && len(b.CAChain) > 0 { - chain = append(chain, b.CAChain...) - } + chain = b.GetFullChain() } return chain @@ -690,10 +727,14 @@ func (b *CAInfoBundle) GetCAChain() []*CertBlock { func (b *CAInfoBundle) GetFullChain() []*CertBlock { var chain []*CertBlock - chain = append(chain, &CertBlock{ - Certificate: b.Certificate, - Bytes: b.CertificateBytes, - }) + // Some bundles already include the root included in the chain, + // so don't include it twice. + if len(b.CAChain) == 0 || !bytes.Equal(b.CAChain[0].Bytes, b.CertificateBytes) { + chain = append(chain, &CertBlock{ + Certificate: b.Certificate, + Bytes: b.CertificateBytes, + }) + } if len(b.CAChain) > 0 { chain = append(chain, b.CAChain...) @@ -738,6 +779,7 @@ type CreationParameters struct { PolicyIdentifiers []string BasicConstraintsValidForNonCA bool SignatureBits int + ForceAppendCaChain bool // Only used when signing a CA cert UseCSRValues bool @@ -825,3 +867,30 @@ func AddKeyUsages(data *CreationBundle, certTemplate *x509.Certificate) { certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageMicrosoftKernelCodeSigning) } } + +// SetParsedPrivateKey sets the private key parameters on the bundle +func (p *KeyBundle) SetParsedPrivateKey(privateKey crypto.Signer, privateKeyType PrivateKeyType, privateKeyBytes []byte) { + p.PrivateKey = privateKey + p.PrivateKeyType = privateKeyType + p.PrivateKeyBytes = privateKeyBytes +} + +func (p *KeyBundle) ToPrivateKeyPemString() (string, error) { + block := pem.Block{} + + if p.PrivateKeyBytes != nil && len(p.PrivateKeyBytes) > 0 { + block.Bytes = p.PrivateKeyBytes + switch p.PrivateKeyType { + case RSAPrivateKey: + block.Type = "RSA PRIVATE KEY" + case ECPrivateKey: + block.Type = "EC PRIVATE KEY" + default: + block.Type = "PRIVATE KEY" + } + privateKeyPemString := strings.TrimSpace(string(pem.EncodeToMemory(&block))) + return privateKeyPemString, nil + } + + return "", errutil.InternalError{Err: "No Private Key Bytes to Wrap"} +} diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/consts/consts.go b/vendor/github.com/hashicorp/vault/sdk/helper/consts/consts.go index b10f57a22..c431e2e59 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/consts/consts.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/consts/consts.go @@ -32,4 +32,6 @@ const ( // ReplicationResolverALPN is the negotiated protocol used for // resolving replicaiton addresses ReplicationResolverALPN = "replication_resolver_v1" + + VaultEnableFilePermissionsCheckEnv = "VAULT_ENABLE_FILE_PERMISSIONS_CHECK" ) diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/multiplexing.pb.go b/vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/multiplexing.pb.go index fa3357d49..d0ff51e57 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/multiplexing.pb.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/pluginutil/multiplexing.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/helper/pluginutil/multiplexing.proto package pluginutil diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/auth.go b/vendor/github.com/hashicorp/vault/sdk/logical/auth.go index 7f68bc936..62707e819 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/auth.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/auth.go @@ -8,7 +8,8 @@ import ( ) // Auth is the resulting authentication information that is part of -// Response for credential backends. +// Response for credential backends. It's also attached to Request objects and +// defines the authentication used for the request. This value is audit logged. type Auth struct { LeaseOptions @@ -101,10 +102,28 @@ type Auth struct { // Orphan is set if the token does not have a parent Orphan bool `json:"orphan"` + // PolicyResults is the set of policies that grant the token access to the + // requesting path. + PolicyResults *PolicyResults `json:"policy_results"` + // MFARequirement MFARequirement *MFARequirement `json:"mfa_requirement"` + + // EntityCreated is set to true if an entity is created as part of a login request + EntityCreated bool `json:"entity_created"` } func (a *Auth) GoString() string { return fmt.Sprintf("*%#v", *a) } + +type PolicyResults struct { + Allowed bool `json:"allowed"` + GrantingPolicies []PolicyInfo `json:"granting_policies"` +} + +type PolicyInfo struct { + Name string `json:"name"` + NamespaceId string `json:"namespace_id"` + Type string `json:"type"` +} diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/identity.pb.go b/vendor/github.com/hashicorp/vault/sdk/logical/identity.pb.go index c472b68a0..4b1a36b39 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/identity.pb.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/identity.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/identity.proto package logical diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/managed_key.go b/vendor/github.com/hashicorp/vault/sdk/logical/managed_key.go index 917d1c5e5..750459542 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/managed_key.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/managed_key.go @@ -40,17 +40,17 @@ type ( type ManagedKeySystemView interface { // WithManagedKeyByName retrieves an instantiated managed key for consumption by the given function. The // provided key can only be used within the scope of that function call - WithManagedKeyByName(ctx context.Context, keyName, mountPoint string, f ManagedKeyConsumer) error + WithManagedKeyByName(ctx context.Context, keyName, backendUUID string, f ManagedKeyConsumer) error // WithManagedKeyByUUID retrieves an instantiated managed key for consumption by the given function. The // provided key can only be used within the scope of that function call - WithManagedKeyByUUID(ctx context.Context, keyUuid, mountPoint string, f ManagedKeyConsumer) error + WithManagedKeyByUUID(ctx context.Context, keyUuid, backendUUID string, f ManagedKeyConsumer) error // WithManagedSigningKeyByName retrieves an instantiated managed signing key for consumption by the given function, // with the same semantics as WithManagedKeyByName - WithManagedSigningKeyByName(ctx context.Context, keyName, mountPoint string, f ManagedSigningKeyConsumer) error + WithManagedSigningKeyByName(ctx context.Context, keyName, backendUUID string, f ManagedSigningKeyConsumer) error // WithManagedSigningKeyByUUID retrieves an instantiated managed signing key for consumption by the given function, // with the same semantics as WithManagedKeyByUUID - WithManagedSigningKeyByUUID(ctx context.Context, keyUuid, mountPoint string, f ManagedSigningKeyConsumer) error + WithManagedSigningKeyByUUID(ctx context.Context, keyUuid, backendUUID string, f ManagedSigningKeyConsumer) error } type ManagedAsymmetricKey interface { diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/plugin.pb.go b/vendor/github.com/hashicorp/vault/sdk/logical/plugin.pb.go index d4722ce09..1fb53f9a7 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/plugin.pb.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/plugin.proto package logical @@ -27,6 +27,10 @@ type PluginEnvironment struct { // VaultVersion is the version of the Vault server VaultVersion string `protobuf:"bytes,1,opt,name=vault_version,json=vaultVersion,proto3" json:"vault_version,omitempty"` + // VaultVersionPrerelease is the prerelease information of the Vault server + VaultVersionPrerelease string `protobuf:"bytes,2,opt,name=vault_version_prerelease,json=vaultVersionPrerelease,proto3" json:"vault_version_prerelease,omitempty"` + // VaultVersionMetadata is the version metadata of the Vault server + VaultVersionMetadata string `protobuf:"bytes,3,opt,name=vault_version_metadata,json=vaultVersionMetadata,proto3" json:"vault_version_metadata,omitempty"` } func (x *PluginEnvironment) Reset() { @@ -68,18 +72,39 @@ func (x *PluginEnvironment) GetVaultVersion() string { return "" } +func (x *PluginEnvironment) GetVaultVersionPrerelease() string { + if x != nil { + return x.VaultVersionPrerelease + } + return "" +} + +func (x *PluginEnvironment) GetVaultVersionMetadata() string { + if x != nil { + return x.VaultVersionMetadata + } + return "" +} + var File_sdk_logical_plugin_proto protoreflect.FileDescriptor var file_sdk_logical_plugin_proto_rawDesc = []byte{ 0x0a, 0x18, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x22, 0x38, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, - 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, - 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, - 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, - 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x61, 0x6c, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, + 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, + 0x0a, 0x18, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, + 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x76, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x28, + 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/plugin.proto b/vendor/github.com/hashicorp/vault/sdk/logical/plugin.proto index 5992c2139..f2df6c75d 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/plugin.proto +++ b/vendor/github.com/hashicorp/vault/sdk/logical/plugin.proto @@ -7,4 +7,10 @@ package logical; message PluginEnvironment { // VaultVersion is the version of the Vault server string vault_version = 1; + + // VaultVersionPrerelease is the prerelease information of the Vault server + string vault_version_prerelease = 2; + + // VaultVersionMetadata is the version metadata of the Vault server + string vault_version_metadata = 3; } diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/response_util.go b/vendor/github.com/hashicorp/vault/sdk/logical/response_util.go index 92e3483d8..7454189f1 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/response_util.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/response_util.go @@ -120,6 +120,8 @@ func RespondErrorCommon(req *Request, resp *Response, err error) (int, error) { statusCode = http.StatusPreconditionFailed case errwrap.Contains(err, ErrPathFunctionalityRemoved.Error()): statusCode = http.StatusNotFound + case errwrap.Contains(err, ErrRelativePath.Error()): + statusCode = http.StatusBadRequest } } diff --git a/vendor/github.com/hashicorp/vault/sdk/version/version.go b/vendor/github.com/hashicorp/vault/sdk/version/version.go index 1a4521ae6..78b8eb829 100644 --- a/vendor/github.com/hashicorp/vault/sdk/version/version.go +++ b/vendor/github.com/hashicorp/vault/sdk/version/version.go @@ -11,6 +11,7 @@ type VersionInfo struct { Version string `json:"version,omitempty"` VersionPrerelease string `json:"version_prerelease,omitempty"` VersionMetadata string `json:"version_metadata,omitempty"` + BuildDate string `json:"build_date,omitempty"` } func GetVersion() *VersionInfo { @@ -29,6 +30,7 @@ func GetVersion() *VersionInfo { Version: ver, VersionPrerelease: rel, VersionMetadata: md, + BuildDate: BuildDate, } } @@ -70,5 +72,9 @@ func (c *VersionInfo) FullVersionNumber(rev bool) string { fmt.Fprintf(&versionString, " (%s)", c.Revision) } + if c.BuildDate != "" { + fmt.Fprintf(&versionString, ", built %s", c.BuildDate) + } + return versionString.String() } diff --git a/vendor/github.com/hashicorp/vault/sdk/version/version_base.go b/vendor/github.com/hashicorp/vault/sdk/version/version_base.go index 5211c15ba..2a21d313d 100644 --- a/vendor/github.com/hashicorp/vault/sdk/version/version_base.go +++ b/vendor/github.com/hashicorp/vault/sdk/version/version_base.go @@ -5,10 +5,13 @@ var ( GitCommit string GitDescribe string + // The compilation date. This will be filled in by the compiler. + BuildDate string + // Whether cgo is enabled or not; set at build time CgoEnabled bool - Version = "1.10.0" + Version = "1.11.0" VersionPrerelease = "dev1" VersionMetadata = "" ) diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/doc.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/doc.go deleted file mode 100644 index d8a1f5dad..000000000 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// +k8s:deepcopy-gen=package -// +groupName=snapshot.storage.k8s.io - -package v1beta1 diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/register.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/register.go deleted file mode 100644 index 91f451424..000000000 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/register.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1beta1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// GroupName is the group name use in this package. -const GroupName = "snapshot.storage.k8s.io" - -var ( - // SchemeBuilder is the new scheme builder - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - // AddToScheme adds to scheme - AddToScheme = SchemeBuilder.AddToScheme - // SchemeGroupVersion is the group version used to register these objects. - SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} -) - -// Resource takes an unqualified resource and returns a Group-qualified GroupResource. -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -func init() { - // We only register manually written functions here. The registration of the - // generated functions takes place in the generated files. The separation - // makes the code compile even when the generated files are missing. - SchemeBuilder.Register(addKnownTypes) -} - -// addKnownTypes adds the set of types defined in this package to the supplied scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &VolumeSnapshotClass{}, - &VolumeSnapshotClassList{}, - &VolumeSnapshot{}, - &VolumeSnapshotList{}, - &VolumeSnapshotContent{}, - &VolumeSnapshotContentList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/types.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/types.go deleted file mode 100644 index 1299d3655..000000000 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/types.go +++ /dev/null @@ -1,395 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// +kubebuilder:object:generate=true -package v1beta1 - -import ( - core_v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VolumeSnapshot is a user's request for either creating a point-in-time -// snapshot of a persistent volume, or binding to a pre-existing snapshot. -// +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Namespaced -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="ReadyToUse",type=boolean,JSONPath=`.status.readyToUse`,description="Indicates if a snapshot is ready to be used to restore a volume." -// +kubebuilder:printcolumn:name="SourcePVC",type=string,JSONPath=`.spec.source.persistentVolumeClaimName`,description="Name of the source PVC from where a dynamically taken snapshot will be created." -// +kubebuilder:printcolumn:name="SourceSnapshotContent",type=string,JSONPath=`.spec.source.volumeSnapshotContentName`,description="Name of the VolumeSnapshotContent which represents a pre-provisioned snapshot." -// +kubebuilder:printcolumn:name="RestoreSize",type=string,JSONPath=`.status.restoreSize`,description="Represents the complete size of the snapshot." -// +kubebuilder:printcolumn:name="SnapshotClass",type=string,JSONPath=`.spec.volumeSnapshotClassName`,description="The name of the VolumeSnapshotClass requested by the VolumeSnapshot." -// +kubebuilder:printcolumn:name="SnapshotContent",type=string,JSONPath=`.status.boundVolumeSnapshotContentName`,description="The name of the VolumeSnapshotContent to which this VolumeSnapshot is bound." -// +kubebuilder:printcolumn:name="CreationTime",type=date,JSONPath=`.status.creationTime`,description="Timestamp when the point-in-time snapshot is taken by the underlying storage system." -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -type VolumeSnapshot struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // spec defines the desired characteristics of a snapshot requested by a user. - // More info: https://kubernetes.io/docs/concepts/storage/volume-snapshots#volumesnapshots - // Required. - Spec VolumeSnapshotSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` - - // status represents the current information of a snapshot. - // NOTE: status can be modified by sources other than system controllers, - // and must not be depended upon for accuracy. - // Controllers should only use information from the VolumeSnapshotContent object - // after verifying that the binding is accurate and complete. - // +optional - Status *VolumeSnapshotStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// VolumeSnapshotList is a list of VolumeSnapshot objects -type VolumeSnapshotList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // List of VolumeSnapshots - Items []VolumeSnapshot `json:"items" protobuf:"bytes,2,rep,name=items"` -} - -// VolumeSnapshotSpec describes the common attributes of a volume snapshot. -type VolumeSnapshotSpec struct { - // source specifies where a snapshot will be created from. - // This field is immutable after creation. - // Required. - Source VolumeSnapshotSource `json:"source" protobuf:"bytes,1,opt,name=source"` - - // volumeSnapshotClassName is the name of the VolumeSnapshotClass requested by the VolumeSnapshot. - // If not specified, the default snapshot class will be used if one exists. - // If not specified, and there is no default snapshot class, dynamic snapshot creation will fail. - // Empty string is not allowed for this field. - // TODO(xiangqian): a webhook validation on empty string. - // More info: https://kubernetes.io/docs/concepts/storage/volume-snapshot-classes - // +optional - VolumeSnapshotClassName *string `json:"volumeSnapshotClassName,omitempty" protobuf:"bytes,2,opt,name=volumeSnapshotClassName"` -} - -// VolumeSnapshotSource specifies whether the underlying snapshot should be -// dynamically taken upon creation or if a pre-existing VolumeSnapshotContent -// object should be used. -// Exactly one of its members must be set. -// Members in VolumeSnapshotSource are immutable. -// TODO(xiangqian): Add a webhook to ensure that VolumeSnapshotSource members -// will not be updated once specified. -type VolumeSnapshotSource struct { - // persistentVolumeClaimName specifies the name of the PersistentVolumeClaim - // object in the same namespace as the VolumeSnapshot object where the - // snapshot should be dynamically taken from. - // This field is immutable. - // +optional - PersistentVolumeClaimName *string `json:"persistentVolumeClaimName,omitempty" protobuf:"bytes,1,opt,name=persistentVolumeClaimName"` - - // volumeSnapshotContentName specifies the name of a pre-existing VolumeSnapshotContent - // object. - // This field is immutable. - // +optional - VolumeSnapshotContentName *string `json:"volumeSnapshotContentName,omitempty" protobuf:"bytes,2,opt,name=volumeSnapshotContentName"` -} - -// VolumeSnapshotStatus is the status of the VolumeSnapshot -type VolumeSnapshotStatus struct { - // boundVolumeSnapshotContentName represents the name of the VolumeSnapshotContent - // object to which the VolumeSnapshot object is bound. - // If not specified, it indicates that the VolumeSnapshot object has not been - // successfully bound to a VolumeSnapshotContent object yet. - // NOTE: Specified boundVolumeSnapshotContentName alone does not mean binding - // is valid. Controllers MUST always verify bidirectional binding between - // VolumeSnapshot and VolumeSnapshotContent to avoid possible security issues. - // +optional - BoundVolumeSnapshotContentName *string `json:"boundVolumeSnapshotContentName,omitempty" protobuf:"bytes,1,opt,name=boundVolumeSnapshotContentName"` - - // creationTime is the timestamp when the point-in-time snapshot is taken - // by the underlying storage system. - // In dynamic snapshot creation case, this field will be filled in with the - // "creation_time" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "creation_time" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it. - // If not specified, it indicates that the creation time of the snapshot is unknown. - // +optional - CreationTime *metav1.Time `json:"creationTime,omitempty" protobuf:"bytes,2,opt,name=creationTime"` - - // readyToUse indicates if a snapshot is ready to be used to restore a volume. - // In dynamic snapshot creation case, this field will be filled in with the - // "ready_to_use" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "ready_to_use" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it, - // otherwise, this field will be set to "True". - // If not specified, it means the readiness of a snapshot is unknown. - // +optional - ReadyToUse *bool `json:"readyToUse,omitempty" protobuf:"varint,3,opt,name=readyToUse"` - - // restoreSize represents the complete size of the snapshot in bytes. - // In dynamic snapshot creation case, this field will be filled in with the - // "size_bytes" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "size_bytes" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it. - // When restoring a volume from this snapshot, the size of the volume MUST NOT - // be smaller than the restoreSize if it is specified, otherwise the restoration will fail. - // If not specified, it indicates that the size is unknown. - // +optional - RestoreSize *resource.Quantity `json:"restoreSize,omitempty" protobuf:"bytes,4,opt,name=restoreSize"` - - // error is the last observed error during snapshot creation, if any. - // This field could be helpful to upper level controllers(i.e., application controller) - // to decide whether they should continue on waiting for the snapshot to be created - // based on the type of error reported. - // +optional - Error *VolumeSnapshotError `json:"error,omitempty" protobuf:"bytes,5,opt,name=error,casttype=VolumeSnapshotError"` -} - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VolumeSnapshotClass specifies parameters that a underlying storage system uses when -// creating a volume snapshot. A specific VolumeSnapshotClass is used by specifying its -// name in a VolumeSnapshot object. -// VolumeSnapshotClasses are non-namespaced -// +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster -// +kubebuilder:printcolumn:name="Driver",type=string,JSONPath=`.driver` -// +kubebuilder:printcolumn:name="DeletionPolicy",type=string,JSONPath=`.deletionPolicy`,description="Determines whether a VolumeSnapshotContent created through the VolumeSnapshotClass should be deleted when its bound VolumeSnapshot is deleted." -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -type VolumeSnapshotClass struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // driver is the name of the storage driver that handles this VolumeSnapshotClass. - // Required. - Driver string `json:"driver" protobuf:"bytes,2,opt,name=driver"` - - // parameters is a key-value map with storage driver specific parameters for creating snapshots. - // These values are opaque to Kubernetes. - // +optional - Parameters map[string]string `json:"parameters,omitempty" protobuf:"bytes,3,rep,name=parameters"` - - // deletionPolicy determines whether a VolumeSnapshotContent created through - // the VolumeSnapshotClass should be deleted when its bound VolumeSnapshot is deleted. - // Supported values are "Retain" and "Delete". - // "Retain" means that the VolumeSnapshotContent and its physical snapshot on underlying storage system are kept. - // "Delete" means that the VolumeSnapshotContent and its physical snapshot on underlying storage system are deleted. - // Required. - DeletionPolicy DeletionPolicy `json:"deletionPolicy" protobuf:"bytes,4,opt,name=deletionPolicy"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VolumeSnapshotClassList is a collection of VolumeSnapshotClasses. -// +kubebuilder:object:root=true -type VolumeSnapshotClassList struct { - metav1.TypeMeta `json:",inline"` - // Standard list metadata - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // items is the list of VolumeSnapshotClasses - Items []VolumeSnapshotClass `json:"items" protobuf:"bytes,2,rep,name=items"` -} - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VolumeSnapshotContent represents the actual "on-disk" snapshot object in the -// underlying storage system -// +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster -// +kubebuilder:subresource:status -// +kubebuilder:printcolumn:name="ReadyToUse",type=boolean,JSONPath=`.status.readyToUse`,description="Indicates if a snapshot is ready to be used to restore a volume." -// +kubebuilder:printcolumn:name="RestoreSize",type=integer,JSONPath=`.status.restoreSize`,description="Represents the complete size of the snapshot in bytes" -// +kubebuilder:printcolumn:name="DeletionPolicy",type=string,JSONPath=`.spec.deletionPolicy`,description="Determines whether this VolumeSnapshotContent and its physical snapshot on the underlying storage system should be deleted when its bound VolumeSnapshot is deleted." -// +kubebuilder:printcolumn:name="Driver",type=string,JSONPath=`.spec.driver`,description="Name of the CSI driver used to create the physical snapshot on the underlying storage system." -// +kubebuilder:printcolumn:name="VolumeSnapshotClass",type=string,JSONPath=`.spec.volumeSnapshotClassName`,description="Name of the VolumeSnapshotClass to which this snapshot belongs." -// +kubebuilder:printcolumn:name="VolumeSnapshot",type=string,JSONPath=`.spec.volumeSnapshotRef.name`,description="Name of the VolumeSnapshot object to which this VolumeSnapshotContent object is bound." -// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -type VolumeSnapshotContent struct { - metav1.TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // spec defines properties of a VolumeSnapshotContent created by the underlying storage system. - // Required. - Spec VolumeSnapshotContentSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` - - // status represents the current information of a snapshot. - // +optional - Status *VolumeSnapshotContentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VolumeSnapshotContentList is a list of VolumeSnapshotContent objects -// +kubebuilder:object:root=true -type VolumeSnapshotContentList struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // items is the list of VolumeSnapshotContents - Items []VolumeSnapshotContent `json:"items" protobuf:"bytes,2,rep,name=items"` -} - -// VolumeSnapshotContentSpec is the specification of a VolumeSnapshotContent -type VolumeSnapshotContentSpec struct { - // volumeSnapshotRef specifies the VolumeSnapshot object to which this - // VolumeSnapshotContent object is bound. - // VolumeSnapshot.Spec.VolumeSnapshotContentName field must reference to - // this VolumeSnapshotContent's name for the bidirectional binding to be valid. - // For a pre-existing VolumeSnapshotContent object, name and namespace of the - // VolumeSnapshot object MUST be provided for binding to happen. - // This field is immutable after creation. - // Required. - VolumeSnapshotRef core_v1.ObjectReference `json:"volumeSnapshotRef" protobuf:"bytes,1,opt,name=volumeSnapshotRef"` - - // deletionPolicy determines whether this VolumeSnapshotContent and its physical snapshot on - // the underlying storage system should be deleted when its bound VolumeSnapshot is deleted. - // Supported values are "Retain" and "Delete". - // "Retain" means that the VolumeSnapshotContent and its physical snapshot on underlying storage system are kept. - // "Delete" means that the VolumeSnapshotContent and its physical snapshot on underlying storage system are deleted. - // In dynamic snapshot creation case, this field will be filled in with the "DeletionPolicy" field defined in the - // VolumeSnapshotClass the VolumeSnapshot refers to. - // For pre-existing snapshots, users MUST specify this field when creating the VolumeSnapshotContent object. - // Required. - DeletionPolicy DeletionPolicy `json:"deletionPolicy" protobuf:"bytes,2,opt,name=deletionPolicy"` - - // driver is the name of the CSI driver used to create the physical snapshot on - // the underlying storage system. - // This MUST be the same as the name returned by the CSI GetPluginName() call for - // that driver. - // Required. - Driver string `json:"driver" protobuf:"bytes,3,opt,name=driver"` - - // name of the VolumeSnapshotClass to which this snapshot belongs. - // +optional - VolumeSnapshotClassName *string `json:"volumeSnapshotClassName,omitempty" protobuf:"bytes,4,opt,name=volumeSnapshotClassName"` - - // source specifies from where a snapshot will be created. - // This field is immutable after creation. - // Required. - Source VolumeSnapshotContentSource `json:"source" protobuf:"bytes,5,opt,name=source"` -} - -// VolumeSnapshotContentSource represents the CSI source of a snapshot. -// Exactly one of its members must be set. -// Members in VolumeSnapshotContentSource are immutable. -// TODO(xiangqian): Add a webhook to ensure that VolumeSnapshotContentSource members -// will be immutable once specified. -type VolumeSnapshotContentSource struct { - // volumeHandle specifies the CSI "volume_id" of the volume from which a snapshot - // should be dynamically taken from. - // This field is immutable. - // +optional - VolumeHandle *string `json:"volumeHandle,omitempty" protobuf:"bytes,1,opt,name=volumeHandle"` - - // snapshotHandle specifies the CSI "snapshot_id" of a pre-existing snapshot on - // the underlying storage system. - // This field is immutable. - // +optional - SnapshotHandle *string `json:"snapshotHandle,omitempty" protobuf:"bytes,2,opt,name=snapshotHandle"` -} - -// VolumeSnapshotContentStatus is the status of a VolumeSnapshotContent object -type VolumeSnapshotContentStatus struct { - // snapshotHandle is the CSI "snapshot_id" of a snapshot on the underlying storage system. - // If not specified, it indicates that dynamic snapshot creation has either failed - // or it is still in progress. - // +optional - SnapshotHandle *string `json:"snapshotHandle,omitempty" protobuf:"bytes,1,opt,name=snapshotHandle"` - - // creationTime is the timestamp when the point-in-time snapshot is taken - // by the underlying storage system. - // In dynamic snapshot creation case, this field will be filled in with the - // "creation_time" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "creation_time" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it. - // If not specified, it indicates the creation time is unknown. - // The format of this field is a Unix nanoseconds time encoded as an int64. - // On Unix, the command `date +%s%N` returns the current time in nanoseconds - // since 1970-01-01 00:00:00 UTC. - // +optional - CreationTime *int64 `json:"creationTime,omitempty" protobuf:"varint,2,opt,name=creationTime"` - - // restoreSize represents the complete size of the snapshot in bytes. - // In dynamic snapshot creation case, this field will be filled in with the - // "size_bytes" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "size_bytes" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it. - // When restoring a volume from this snapshot, the size of the volume MUST NOT - // be smaller than the restoreSize if it is specified, otherwise the restoration will fail. - // If not specified, it indicates that the size is unknown. - // +kubebuilder:validation:Minimum=0 - // +optional - RestoreSize *int64 `json:"restoreSize,omitempty" protobuf:"bytes,3,opt,name=restoreSize"` - - // readyToUse indicates if a snapshot is ready to be used to restore a volume. - // In dynamic snapshot creation case, this field will be filled in with the - // "ready_to_use" value returned from CSI "CreateSnapshotRequest" gRPC call. - // For a pre-existing snapshot, this field will be filled with the "ready_to_use" - // value returned from the CSI "ListSnapshots" gRPC call if the driver supports it, - // otherwise, this field will be set to "True". - // If not specified, it means the readiness of a snapshot is unknown. - // +optional. - ReadyToUse *bool `json:"readyToUse,omitempty" protobuf:"varint,4,opt,name=readyToUse"` - - // error is the latest observed error during snapshot creation, if any. - // +optional - Error *VolumeSnapshotError `json:"error,omitempty" protobuf:"bytes,5,opt,name=error,casttype=VolumeSnapshotError"` -} - -// DeletionPolicy describes a policy for end-of-life maintenance of volume snapshot contents -// +kubebuilder:validation:Enum=Delete;Retain -type DeletionPolicy string - -const ( - // volumeSnapshotContentDelete means the snapshot will be deleted from the - // underlying storage system on release from its volume snapshot. - VolumeSnapshotContentDelete DeletionPolicy = "Delete" - - // volumeSnapshotContentRetain means the snapshot will be left in its current - // state on release from its volume snapshot. - VolumeSnapshotContentRetain DeletionPolicy = "Retain" -) - -// VolumeSnapshotError describes an error encountered during snapshot creation. -type VolumeSnapshotError struct { - // time is the timestamp when the error was encountered. - // +optional - Time *metav1.Time `json:"time,omitempty" protobuf:"bytes,1,opt,name=time"` - - // message is a string detailing the encountered error during snapshot - // creation if specified. - // NOTE: message may be logged, and it should not contain sensitive - // information. - // +optional - Message *string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"` -} diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/zz_generated.deepcopy.go deleted file mode 100644 index 550ff57f8..000000000 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1/zz_generated.deepcopy.go +++ /dev/null @@ -1,424 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1beta1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshot) DeepCopyInto(out *VolumeSnapshot) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(VolumeSnapshotStatus) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshot. -func (in *VolumeSnapshot) DeepCopy() *VolumeSnapshot { - if in == nil { - return nil - } - out := new(VolumeSnapshot) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshot) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotClass) DeepCopyInto(out *VolumeSnapshotClass) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotClass. -func (in *VolumeSnapshotClass) DeepCopy() *VolumeSnapshotClass { - if in == nil { - return nil - } - out := new(VolumeSnapshotClass) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshotClass) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotClassList) DeepCopyInto(out *VolumeSnapshotClassList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VolumeSnapshotClass, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotClassList. -func (in *VolumeSnapshotClassList) DeepCopy() *VolumeSnapshotClassList { - if in == nil { - return nil - } - out := new(VolumeSnapshotClassList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshotClassList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotContent) DeepCopyInto(out *VolumeSnapshotContent) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(VolumeSnapshotContentStatus) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotContent. -func (in *VolumeSnapshotContent) DeepCopy() *VolumeSnapshotContent { - if in == nil { - return nil - } - out := new(VolumeSnapshotContent) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshotContent) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotContentList) DeepCopyInto(out *VolumeSnapshotContentList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VolumeSnapshotContent, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotContentList. -func (in *VolumeSnapshotContentList) DeepCopy() *VolumeSnapshotContentList { - if in == nil { - return nil - } - out := new(VolumeSnapshotContentList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshotContentList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotContentSource) DeepCopyInto(out *VolumeSnapshotContentSource) { - *out = *in - if in.VolumeHandle != nil { - in, out := &in.VolumeHandle, &out.VolumeHandle - *out = new(string) - **out = **in - } - if in.SnapshotHandle != nil { - in, out := &in.SnapshotHandle, &out.SnapshotHandle - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotContentSource. -func (in *VolumeSnapshotContentSource) DeepCopy() *VolumeSnapshotContentSource { - if in == nil { - return nil - } - out := new(VolumeSnapshotContentSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotContentSpec) DeepCopyInto(out *VolumeSnapshotContentSpec) { - *out = *in - out.VolumeSnapshotRef = in.VolumeSnapshotRef - if in.VolumeSnapshotClassName != nil { - in, out := &in.VolumeSnapshotClassName, &out.VolumeSnapshotClassName - *out = new(string) - **out = **in - } - in.Source.DeepCopyInto(&out.Source) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotContentSpec. -func (in *VolumeSnapshotContentSpec) DeepCopy() *VolumeSnapshotContentSpec { - if in == nil { - return nil - } - out := new(VolumeSnapshotContentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotContentStatus) DeepCopyInto(out *VolumeSnapshotContentStatus) { - *out = *in - if in.SnapshotHandle != nil { - in, out := &in.SnapshotHandle, &out.SnapshotHandle - *out = new(string) - **out = **in - } - if in.CreationTime != nil { - in, out := &in.CreationTime, &out.CreationTime - *out = new(int64) - **out = **in - } - if in.RestoreSize != nil { - in, out := &in.RestoreSize, &out.RestoreSize - *out = new(int64) - **out = **in - } - if in.ReadyToUse != nil { - in, out := &in.ReadyToUse, &out.ReadyToUse - *out = new(bool) - **out = **in - } - if in.Error != nil { - in, out := &in.Error, &out.Error - *out = new(VolumeSnapshotError) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotContentStatus. -func (in *VolumeSnapshotContentStatus) DeepCopy() *VolumeSnapshotContentStatus { - if in == nil { - return nil - } - out := new(VolumeSnapshotContentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotError) DeepCopyInto(out *VolumeSnapshotError) { - *out = *in - if in.Time != nil { - in, out := &in.Time, &out.Time - *out = (*in).DeepCopy() - } - if in.Message != nil { - in, out := &in.Message, &out.Message - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotError. -func (in *VolumeSnapshotError) DeepCopy() *VolumeSnapshotError { - if in == nil { - return nil - } - out := new(VolumeSnapshotError) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotList) DeepCopyInto(out *VolumeSnapshotList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VolumeSnapshot, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotList. -func (in *VolumeSnapshotList) DeepCopy() *VolumeSnapshotList { - if in == nil { - return nil - } - out := new(VolumeSnapshotList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VolumeSnapshotList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotSource) DeepCopyInto(out *VolumeSnapshotSource) { - *out = *in - if in.PersistentVolumeClaimName != nil { - in, out := &in.PersistentVolumeClaimName, &out.PersistentVolumeClaimName - *out = new(string) - **out = **in - } - if in.VolumeSnapshotContentName != nil { - in, out := &in.VolumeSnapshotContentName, &out.VolumeSnapshotContentName - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotSource. -func (in *VolumeSnapshotSource) DeepCopy() *VolumeSnapshotSource { - if in == nil { - return nil - } - out := new(VolumeSnapshotSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotSpec) DeepCopyInto(out *VolumeSnapshotSpec) { - *out = *in - in.Source.DeepCopyInto(&out.Source) - if in.VolumeSnapshotClassName != nil { - in, out := &in.VolumeSnapshotClassName, &out.VolumeSnapshotClassName - *out = new(string) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotSpec. -func (in *VolumeSnapshotSpec) DeepCopy() *VolumeSnapshotSpec { - if in == nil { - return nil - } - out := new(VolumeSnapshotSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSnapshotStatus) DeepCopyInto(out *VolumeSnapshotStatus) { - *out = *in - if in.BoundVolumeSnapshotContentName != nil { - in, out := &in.BoundVolumeSnapshotContentName, &out.BoundVolumeSnapshotContentName - *out = new(string) - **out = **in - } - if in.CreationTime != nil { - in, out := &in.CreationTime, &out.CreationTime - *out = (*in).DeepCopy() - } - if in.ReadyToUse != nil { - in, out := &in.ReadyToUse, &out.ReadyToUse - *out = new(bool) - **out = **in - } - if in.RestoreSize != nil { - in, out := &in.RestoreSize, &out.RestoreSize - x := (*in).DeepCopy() - *out = &x - } - if in.Error != nil { - in, out := &in.Error, &out.Error - *out = new(VolumeSnapshotError) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotStatus. -func (in *VolumeSnapshotStatus) DeepCopy() *VolumeSnapshotStatus { - if in == nil { - return nil - } - out := new(VolumeSnapshotStatus) - in.DeepCopyInto(out) - return out -} diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/LICENSE b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/LICENSE similarity index 100% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/LICENSE rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/LICENSE diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/doc.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/doc.go similarity index 100% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/doc.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/doc.go diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/register.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/register.go similarity index 100% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/register.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/register.go diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/types.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/types.go similarity index 95% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/types.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/types.go index b9745df21..6490abaa4 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/types.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/types.go @@ -29,7 +29,7 @@ import ( // VolumeSnapshot is a user's request for either creating a point-in-time // snapshot of a persistent volume, or binding to a pre-existing snapshot. // +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Namespaced +// +kubebuilder:resource:scope=Namespaced,shortName=vs // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="ReadyToUse",type=boolean,JSONPath=`.status.readyToUse`,description="Indicates if the snapshot is ready to be used to restore a volume." // +kubebuilder:printcolumn:name="SourcePVC",type=string,JSONPath=`.spec.source.persistentVolumeClaimName`,description="If a new snapshot needs to be created, this contains the name of the source PVC from which this snapshot was (or will be) created." @@ -42,7 +42,7 @@ import ( type VolumeSnapshot struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -179,7 +179,7 @@ type VolumeSnapshotStatus struct { // This field could be helpful to upper level controllers(i.e., application controller) // to decide whether they should continue on waiting for the snapshot to be created // based on the type of error reported. - // The snapshot controller will keep retrying when an error occurrs during the + // The snapshot controller will keep retrying when an error occurs during the // snapshot creation. Upon success, this error field will be cleared. // +optional Error *VolumeSnapshotError `json:"error,omitempty" protobuf:"bytes,5,opt,name=error,casttype=VolumeSnapshotError"` @@ -194,14 +194,14 @@ type VolumeSnapshotStatus struct { // name in a VolumeSnapshot object. // VolumeSnapshotClasses are non-namespaced // +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster +// +kubebuilder:resource:scope=Cluster,shortName=vsclass;vsclasses // +kubebuilder:printcolumn:name="Driver",type=string,JSONPath=`.driver` // +kubebuilder:printcolumn:name="DeletionPolicy",type=string,JSONPath=`.deletionPolicy`,description="Determines whether a VolumeSnapshotContent created through the VolumeSnapshotClass should be deleted when its bound VolumeSnapshot is deleted." // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` type VolumeSnapshotClass struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -230,7 +230,7 @@ type VolumeSnapshotClass struct { type VolumeSnapshotClassList struct { metav1.TypeMeta `json:",inline"` // Standard list metadata - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -245,7 +245,7 @@ type VolumeSnapshotClassList struct { // VolumeSnapshotContent represents the actual "on-disk" snapshot object in the // underlying storage system // +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster +// +kubebuilder:resource:scope=Cluster,shortName=vsc;vscs // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="ReadyToUse",type=boolean,JSONPath=`.status.readyToUse`,description="Indicates if the snapshot is ready to be used to restore a volume." // +kubebuilder:printcolumn:name="RestoreSize",type=integer,JSONPath=`.status.restoreSize`,description="Represents the complete size of the snapshot in bytes" @@ -257,7 +257,7 @@ type VolumeSnapshotClassList struct { type VolumeSnapshotContent struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -328,13 +328,19 @@ type VolumeSnapshotContentSpec struct { // This field is immutable after creation. // Required. Source VolumeSnapshotContentSource `json:"source" protobuf:"bytes,5,opt,name=source"` + + // SourceVolumeMode is the mode of the volume whose snapshot is taken. + // Can be either “Filesystem” or “Block”. + // If not specified, it indicates the source volume's mode is unknown. + // This field is immutable. + // This field is an alpha field. + // +optional + SourceVolumeMode *core_v1.PersistentVolumeMode `json:"sourceVolumeMode" protobuf:"bytes,6,opt,name=sourceVolumeMode"` } // VolumeSnapshotContentSource represents the CSI source of a snapshot. // Exactly one of its members must be set. // Members in VolumeSnapshotContentSource are immutable. -// TODO(xiangqian): Add a webhook to ensure that VolumeSnapshotContentSource members -// will be immutable once specified. type VolumeSnapshotContentSource struct { // volumeHandle specifies the CSI "volume_id" of the volume from which a snapshot // should be dynamically taken from. diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/zz_generated.deepcopy.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/zz_generated.deepcopy.go similarity index 98% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/zz_generated.deepcopy.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/zz_generated.deepcopy.go index febab0cd4..901b2b080 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1/zz_generated.deepcopy.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1/zz_generated.deepcopy.go @@ -1,7 +1,8 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +22,7 @@ limitations under the License. package v1 import ( + corev1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -223,6 +225,11 @@ func (in *VolumeSnapshotContentSpec) DeepCopyInto(out *VolumeSnapshotContentSpec **out = **in } in.Source.DeepCopyInto(&out.Source) + if in.SourceVolumeMode != nil { + in, out := &in.SourceVolumeMode, &out.SourceVolumeMode + *out = new(corev1.PersistentVolumeMode) + **out = **in + } return } diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/doc.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/doc.go similarity index 94% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/doc.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/doc.go index 7d06c9402..288d3794d 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/doc.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/register.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/register.go similarity index 90% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/register.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/register.go index 46e6628fd..60f4addd4 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme/register.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,8 +19,7 @@ limitations under the License. package scheme import ( - snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - snapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1" + snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,7 +31,6 @@ var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ - snapshotv1beta1.AddToScheme, snapshotv1.AddToScheme, } diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/doc.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/doc.go similarity index 94% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/doc.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/doc.go index 1917a6294..01fa5fd65 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/doc.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go similarity index 94% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go index 88df72cf7..cae7c1cac 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go similarity index 98% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go index 36a02388c..6160690f0 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import ( "context" "time" - v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - scheme "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme" + v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + scheme "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go similarity index 74% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go index 996f1e16b..938dbdb75 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshot_client.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,8 +19,10 @@ limitations under the License. package v1 import ( - v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme" + "net/http" + + v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) @@ -49,12 +51,28 @@ func (c *SnapshotV1Client) VolumeSnapshotContents() VolumeSnapshotContentInterfa } // NewForConfig creates a new SnapshotV1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*SnapshotV1Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err } - client, err := rest.RESTClientFor(&config) + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new SnapshotV1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*SnapshotV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err } diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go similarity index 97% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go index 7463fb40e..71ef507f3 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotclass.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import ( "context" "time" - v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - scheme "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme" + v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + scheme "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" diff --git a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go similarity index 98% rename from vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go rename to vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go index 3e0504cb0..7f634e68c 100644 --- a/vendor/github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go +++ b/vendor/github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1/volumesnapshotcontent.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import ( "context" "time" - v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" - scheme "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme" + v1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + scheme "github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md index 9fe803a5e..c75823490 100644 --- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md +++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md @@ -1,3 +1,20 @@ +## 1.5.0 + +* New option `IgnoreUntaggedFields` to ignore decoding to any fields + without `mapstructure` (or the configured tag name) set [GH-277] +* New option `ErrorUnset` which makes it an error if any fields + in a target struct are not set by the decoding process. [GH-225] +* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240] +* Decoding to slice from array no longer crashes [GH-265] +* Decode nested struct pointers to map [GH-271] +* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280] +* Fix issue where fields with `,omitempty` would sometimes decode + into a map with an empty string key [GH-281] + +## 1.4.3 + +* Fix cases where `json.Number` didn't decode properly [GH-261] + ## 1.4.2 * Custom name matchers to support any sort of casing, formatting, etc. for diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go index 4d4bbc733..3a754ca72 100644 --- a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go +++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go @@ -77,6 +77,28 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { } } +// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. +// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. +func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { + return func(a, b reflect.Value) (interface{}, error) { + var allErrs string + var out interface{} + var err error + + for _, f := range ff { + out, err = DecodeHookExec(f, a, b) + if err != nil { + allErrs += err.Error() + "\n" + continue + } + + return out, nil + } + + return nil, errors.New(allErrs) + } +} + // StringToSliceHookFunc returns a DecodeHookFunc that converts // string to []string by splitting on the given sep. func StringToSliceHookFunc(sep string) DecodeHookFunc { diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go index dcee0f2d6..1efb22ac3 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -122,7 +122,7 @@ // field value is zero and a numeric type, the field is empty, and it won't // be encoded into the destination type. // -// type Source { +// type Source struct { // Age int `mapstructure:",omitempty"` // } // @@ -215,6 +215,12 @@ type DecoderConfig struct { // (extra keys). ErrorUnused bool + // If ErrorUnset is true, then it is an error for there to exist + // fields in the result that were not set in the decoding process + // (extra fields). This only applies to decoding to a struct. This + // will affect all nested structs as well. + ErrorUnset bool + // ZeroFields, if set to true, will zero fields before writing them. // For example, a map will be emptied before decoded values are put in // it. If this is false, a map will be merged. @@ -259,6 +265,10 @@ type DecoderConfig struct { // defaults to "mapstructure" TagName string + // IgnoreUntaggedFields ignores all struct fields without explicit + // TagName, comparable to `mapstructure:"-"` as default behaviour. + IgnoreUntaggedFields bool + // MatchName is the function used to match the map key to the struct // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. @@ -284,6 +294,11 @@ type Metadata struct { // Unused is a slice of keys that were found in the raw value but // weren't decoded since there was no matching field in the result interface Unused []string + + // Unset is a slice of field names that were found in the result interface + // but weren't set in the decoding process since there was no matching value + // in the input + Unset []string } // Decode takes an input structure and uses reflection to translate it to @@ -375,6 +390,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { if config.Metadata.Unused == nil { config.Metadata.Unused = make([]string, 0) } + + if config.Metadata.Unset == nil { + config.Metadata.Unset = make([]string, 0) + } } if config.TagName == "" { @@ -684,16 +703,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) - i, err := jn.Int64() + i, err := strconv.ParseUint(string(jn), 0, 64) if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } - if i < 0 && !d.config.WeaklyTypedInput { - return fmt.Errorf("cannot parse '%s', %d overflows uint", - name, i) - } - val.SetUint(uint64(i)) + val.SetUint(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", @@ -910,9 +925,15 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re tagValue := f.Tag.Get(d.config.TagName) keyName := f.Name + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } + // If Squash is set in the config, we squash the field down. squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous + v = dereferencePtrToStructIfNeeded(v, d.config.TagName) + // Determine the name of the key in the map if index := strings.Index(tagValue, ","); index != -1 { if tagValue[:index] == "-" { @@ -924,7 +945,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re } // If "squash" is specified in the tag, we squash the field down. - squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1 + squash = squash || strings.Index(tagValue[index+1:], "squash") != -1 if squash { // When squashing, the embedded type can be a pointer to a struct. if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { @@ -936,7 +957,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) } } - keyName = tagValue[:index] + if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { + keyName = keyNameTagValue + } } else if len(tagValue) > 0 { if tagValue == "-" { continue @@ -1092,7 +1115,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) } // If the input value is nil, then don't allocate since empty != nil - if dataVal.IsNil() { + if dataValKind != reflect.Array && dataVal.IsNil() { return nil } @@ -1254,6 +1277,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e dataValKeysUnused[dataValKey.Interface()] = struct{}{} } + targetValKeysUnused := make(map[interface{}]struct{}) errors := make([]string, 0) // This slice will keep track of all the structs we'll be decoding. @@ -1358,7 +1382,8 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e if !rawMapVal.IsValid() { // There was no matching key in the map for the value in - // the struct. Just ignore. + // the struct. Remember it for potential errors and metadata. + targetValKeysUnused[fieldName] = struct{}{} continue } } @@ -1418,6 +1443,17 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e errors = appendErrors(errors, err) } + if d.config.ErrorUnset && len(targetValKeysUnused) > 0 { + keys := make([]string, 0, len(targetValKeysUnused)) + for rawKey := range targetValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", ")) + errors = appendErrors(errors, err) + } + if len(errors) > 0 { return &Error{errors} } @@ -1432,6 +1468,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) } + for rawKey := range targetValKeysUnused { + key := rawKey.(string) + if name != "" { + key = name + "." + key + } + + d.config.Metadata.Unset = append(d.config.Metadata.Unset, key) + } } return nil @@ -1469,3 +1513,28 @@ func getKind(val reflect.Value) reflect.Kind { return kind } } + +func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool { + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields + return true + } + if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside + return true + } + } + return false +} + +func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return v + } + deref := v.Elem() + derefT := deref.Type() + if isStructTypeConvertibleToMap(derefT, true, tagName) { + return deref + } + return v +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go index df22c47fc..da867903e 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go @@ -9,7 +9,7 @@ package assert import "reflect" -// Wrapper around reflect.Value.CanConvert, for compatability +// Wrapper around reflect.Value.CanConvert, for compatibility // reasons. func canConvert(value reflect.Value, to reflect.Type) bool { return value.CanConvert(to) diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index ea9836d28..de6d41c23 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -801,16 +801,31 @@ func (ac *addrConn) connect() error { return nil } +func equalAddresses(a, b []resolver.Address) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if !v.Equal(b[i]) { + return false + } + } + return true +} + // tryUpdateAddrs tries to update ac.addrs with the new addresses list. // -// If ac is Connecting, it returns false. The caller should tear down the ac and -// create a new one. Note that the backoff will be reset when this happens. -// // If ac is TransientFailure, it updates ac.addrs and returns true. The updated // addresses will be picked up by retry in the next iteration after backoff. // // If ac is Shutdown or Idle, it updates ac.addrs and returns true. // +// If the addresses is the same as the old list, it does nothing and returns +// true. +// +// If ac is Connecting, it returns false. The caller should tear down the ac and +// create a new one. Note that the backoff will be reset when this happens. +// // If ac is Ready, it checks whether current connected address of ac is in the // new addrs list. // - If true, it updates ac.addrs and returns true. The ac will keep using @@ -827,6 +842,10 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool { return true } + if equalAddresses(ac.addrs, addrs) { + return true + } + if ac.state == connectivity.Connecting { return false } @@ -1219,6 +1238,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne ac.mu.Lock() defer ac.mu.Unlock() defer connClosed.Fire() + defer hcancel() if !hcStarted || hctx.Err() != nil { // We didn't start the health check or set the state to READY, so // no need to do anything else here. @@ -1229,7 +1249,6 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne // state, since there may be a new transport in this addrConn. return } - hcancel() ac.transport = nil // Refresh the name resolver ac.cc.resolveNow(resolver.ResolveNowOptions{}) @@ -1252,6 +1271,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, func() { prefaceReceived.Fire() }, onGoAway, onClose) if err != nil { // newTr is either nil, or closed. + hcancel() channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err) return err } diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go index 6d84f74c7..18e530fc9 100644 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/vendor/google.golang.org/grpc/encoding/encoding.go @@ -108,7 +108,7 @@ var registeredCodecs = make(map[string]Codec) // more details. // // NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Compressors are +// an init() function), and is not thread-safe. If multiple Codecs are // registered with the same name, the one registered last will take effect. func RegisterCodec(codec Codec) { if codec == nil { diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index 8394d252d..244f4b081 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -137,6 +137,7 @@ type earlyAbortStream struct { streamID uint32 contentSubtype string status *status.Status + rst bool } func (*earlyAbortStream) isTransportResponseFrame() bool { return false } @@ -786,6 +787,11 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error { if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil { return err } + if eas.rst { + if err := l.framer.fr.WriteRSTStream(eas.streamID, http2.ErrCodeNo); err != nil { + return err + } + } return nil } diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 0956b500c..45d7bd145 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -21,7 +21,6 @@ package transport import ( "bytes" "context" - "errors" "fmt" "io" "math" @@ -53,10 +52,10 @@ import ( var ( // ErrIllegalHeaderWrite indicates that setting header is illegal because of // the stream's state. - ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called") + ErrIllegalHeaderWrite = status.Error(codes.Internal, "transport: SendHeader called multiple times") // ErrHeaderListSizeLimitViolation indicates that the header list size is larger // than the limit set by peer. - ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer") + ErrHeaderListSizeLimitViolation = status.Error(codes.Internal, "transport: trying to send header list size larger than the limit set by peer") ) // serverConnectionCounter counts the number of connections a server has seen @@ -449,6 +448,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( streamID: streamID, contentSubtype: s.contentSubtype, status: status.New(codes.Internal, errMsg), + rst: !frame.StreamEnded(), }) return false } @@ -522,14 +522,16 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( } if httpMethod != http.MethodPost { t.mu.Unlock() + errMsg := fmt.Sprintf("http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) if logger.V(logLevel) { - logger.Infof("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod) + logger.Infof("transport: %v", errMsg) } - t.controlBuf.put(&cleanupStream{ - streamID: streamID, - rst: true, - rstCode: http2.ErrCodeProtocol, - onWrite: func() {}, + t.controlBuf.put(&earlyAbortStream{ + httpStatus: 405, + streamID: streamID, + contentSubtype: s.contentSubtype, + status: status.New(codes.Internal, errMsg), + rst: !frame.StreamEnded(), }) s.cancel() return false @@ -550,6 +552,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func( streamID: s.id, contentSubtype: s.contentSubtype, status: stat, + rst: !frame.StreamEnded(), }) return false } @@ -931,11 +934,25 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool { return true } +func (t *http2Server) streamContextErr(s *Stream) error { + select { + case <-t.done: + return ErrConnClosing + default: + } + return ContextErr(s.ctx.Err()) +} + // WriteHeader sends the header metadata md back to the client. func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { - if s.updateHeaderSent() || s.getState() == streamDone { + if s.updateHeaderSent() { return ErrIllegalHeaderWrite } + + if s.getState() == streamDone { + return t.streamContextErr(s) + } + s.hdrMu.Lock() if md.Len() > 0 { if s.header.Len() > 0 { @@ -946,7 +963,7 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { } if err := t.writeHeaderLocked(s); err != nil { s.hdrMu.Unlock() - return err + return status.Convert(err).Err() } s.hdrMu.Unlock() return nil @@ -1062,23 +1079,12 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error { func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { if !s.isHeaderSent() { // Headers haven't been written yet. if err := t.WriteHeader(s, nil); err != nil { - if _, ok := err.(ConnectionError); ok { - return err - } - // TODO(mmukhi, dfawley): Make sure this is the right code to return. - return status.Errorf(codes.Internal, "transport: %v", err) + return err } } else { // Writing headers checks for this condition. if s.getState() == streamDone { - // TODO(mmukhi, dfawley): Should the server write also return io.EOF? - s.cancel() - select { - case <-t.done: - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) + return t.streamContextErr(s) } } df := &dataFrame{ @@ -1088,12 +1094,7 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e onEachWrite: t.setResetPingStrikes, } if err := s.wq.get(int32(len(hdr) + len(data))); err != nil { - select { - case <-t.done: - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) + return t.streamContextErr(s) } return t.controlBuf.put(df) } @@ -1229,10 +1230,6 @@ func (t *http2Server) Close() { // deleteStream deletes the stream s from transport's active streams. func (t *http2Server) deleteStream(s *Stream, eosReceived bool) { - // In case stream sending and receiving are invoked in separate - // goroutines (e.g., bi-directional streaming), cancel needs to be - // called to interrupt the potential blocking on other goroutines. - s.cancel() t.mu.Lock() if _, ok := t.activeStreams[s.id]; ok { @@ -1254,6 +1251,11 @@ func (t *http2Server) deleteStream(s *Stream, eosReceived bool) { // finishStream closes the stream and puts the trailing headerFrame into controlbuf. func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) { + // In case stream sending and receiving are invoked in separate + // goroutines (e.g., bi-directional streaming), cancel needs to be + // called to interrupt the potential blocking on other goroutines. + s.cancel() + oldState := s.swapState(streamDone) if oldState == streamDone { // If the stream was already done, return. @@ -1273,6 +1275,11 @@ func (t *http2Server) finishStream(s *Stream, rst bool, rstCode http2.ErrCode, h // closeStream clears the footprint of a stream when the stream is not needed any more. func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, eosReceived bool) { + // In case stream sending and receiving are invoked in separate + // goroutines (e.g., bi-directional streaming), cancel needs to be + // called to interrupt the potential blocking on other goroutines. + s.cancel() + s.swapState(streamDone) t.deleteStream(s, eosReceived) diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 96431a058..65de84b30 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -1801,12 +1801,26 @@ func (s *Server) getCodec(contentSubtype string) baseCodec { return codec } -// SetHeader sets the header metadata. -// When called multiple times, all the provided metadata will be merged. -// All the metadata will be sent out when one of the following happens: -// - grpc.SendHeader() is called; -// - The first response is sent out; -// - An RPC status is sent out (error or success). +// SetHeader sets the header metadata to be sent from the server to the client. +// The context provided must be the context passed to the server's handler. +// +// Streaming RPCs should prefer the SetHeader method of the ServerStream. +// +// When called multiple times, all the provided metadata will be merged. All +// the metadata will be sent out when one of the following happens: +// +// - grpc.SendHeader is called, or for streaming handlers, stream.SendHeader. +// - The first response message is sent. For unary handlers, this occurs when +// the handler returns; for streaming handlers, this can happen when stream's +// SendMsg method is called. +// - An RPC status is sent out (error or success). This occurs when the handler +// returns. +// +// SetHeader will fail if called after any of the events above. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SetHeader(ctx context.Context, md metadata.MD) error { if md.Len() == 0 { return nil @@ -1818,8 +1832,14 @@ func SetHeader(ctx context.Context, md metadata.MD) error { return stream.SetHeader(md) } -// SendHeader sends header metadata. It may be called at most once. -// The provided md and headers set by SetHeader() will be sent. +// SendHeader sends header metadata. It may be called at most once, and may not +// be called after any event that causes headers to be sent (see SetHeader for +// a complete list). The provided md and headers set by SetHeader() will be +// sent. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SendHeader(ctx context.Context, md metadata.MD) error { stream := ServerTransportStreamFromContext(ctx) if stream == nil { @@ -1833,6 +1853,10 @@ func SendHeader(ctx context.Context, md metadata.MD) error { // SetTrailer sets the trailer metadata that will be sent when an RPC returns. // When called more than once, all the provided metadata will be merged. +// +// The error returned is compatible with the status package. However, the +// status code will often not match the RPC status as seen by the client +// application, and therefore, should not be relied upon for this purpose. func SetTrailer(ctx context.Context, md metadata.MD) error { if md.Len() == 0 { return nil diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index be5140535..236fc17ec 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -1381,8 +1381,10 @@ func (as *addrConnStream) finish(err error) { // ServerStream defines the server-side behavior of a streaming RPC. // -// All errors returned from ServerStream methods are compatible with the -// status package. +// Errors returned from ServerStream methods are compatible with the status +// package. However, the status code will often not match the RPC status as +// seen by the client application, and therefore, should not be relied upon for +// this purpose. type ServerStream interface { // SetHeader sets the header metadata. It may be called multiple times. // When call multiple times, all the provided metadata will be merged. diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index f17192e6a..5bc03f9b3 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.46.2" +const Version = "1.47.0" diff --git a/vendor/gopkg.in/yaml.v3/decode.go b/vendor/gopkg.in/yaml.v3/decode.go index df36e3a30..0173b6982 100644 --- a/vendor/gopkg.in/yaml.v3/decode.go +++ b/vendor/gopkg.in/yaml.v3/decode.go @@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } - if !yaml_parser_parse(&p.parser, &p.event) { + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ @@ -320,6 +323,8 @@ type decoder struct { decodeCount int aliasCount int aliasDepth int + + mergedFields map[interface{}]bool } var ( @@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) @@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if mergedFields[ki] { + continue + } + mergedFields[ki] = true + } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() @@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + d.stringMapType = stringMapType d.generalMapType = generalMapType return true @@ -844,7 +867,8 @@ func isStringMap(n *Node) bool { } l := len(n.Content) for i := 0; i < l; i += 2 { - if n.Content[i].ShortTag() != strTag { + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { return false } } @@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } @@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.prepare(n, field) } + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node var doneFields []bool if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) @@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { for i := 0; i < l; i += 2 { ni := n.Content[i] if isMerge(ni) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } - if info, ok := sinfo.FieldsMap[name.String()]; ok { + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { if d.uniqueKeys { if doneFields[info.Id] { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) @@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } return true } @@ -918,19 +956,29 @@ func failWantMap() { failf("map merge requires map or sequence of maps as the value") } -func (d *decoder) merge(n *Node, out reflect.Value) { - switch n.Kind { +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.mergedFields[k.Interface()] = true + } + } + } + + switch merge.Kind { case MappingNode: - d.unmarshal(n, out) + d.unmarshal(merge, out) case AliasNode: - if n.Alias != nil && n.Alias.Kind != MappingNode { + if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } - d.unmarshal(n, out) + d.unmarshal(merge, out) case SequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.Content) - 1; i >= 0; i-- { - ni := n.Content[i] + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] if ni.Kind == AliasNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() @@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) { default: failWantMap() } + + d.mergedFields = mergedFields } func isMerge(n *Node) bool { diff --git a/vendor/gopkg.in/yaml.v3/parserc.go b/vendor/gopkg.in/yaml.v3/parserc.go index ac66fccc0..268558a0d 100644 --- a/vendor/gopkg.in/yaml.v3/parserc.go +++ b/vendor/gopkg.in/yaml.v3/parserc.go @@ -687,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -786,7 +789,7 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } token := peek_token(parser) - if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { + if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { return } @@ -813,6 +816,9 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } @@ -922,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) + if token == nil { + return false + } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 0eb94cd8a..d935b3a9b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -14,7 +14,7 @@ github.com/armon/go-metrics # github.com/armon/go-radix v1.0.0 ## explicit github.com/armon/go-radix -# github.com/aws/aws-sdk-go v1.44.20 +# github.com/aws/aws-sdk-go v1.44.28 ## explicit; go 1.11 github.com/aws/aws-sdk-go/aws github.com/aws/aws-sdk-go/aws/awserr @@ -59,7 +59,7 @@ github.com/aws/aws-sdk-go/service/sso github.com/aws/aws-sdk-go/service/sso/ssoiface github.com/aws/aws-sdk-go/service/sts github.com/aws/aws-sdk-go/service/sts/stsiface -# github.com/aws/aws-sdk-go-v2 v1.16.3 +# github.com/aws/aws-sdk-go-v2 v1.16.5 ## explicit; go 1.15 github.com/aws/aws-sdk-go-v2/aws github.com/aws/aws-sdk-go-v2/aws/defaults @@ -76,21 +76,21 @@ github.com/aws/aws-sdk-go-v2/internal/sdk github.com/aws/aws-sdk-go-v2/internal/strings github.com/aws/aws-sdk-go-v2/internal/sync/singleflight github.com/aws/aws-sdk-go-v2/internal/timeconv -# github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 +# github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 ## explicit; go 1.15 github.com/aws/aws-sdk-go-v2/internal/configsources -# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 +# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 ## explicit; go 1.15 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 -# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 +# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.6 ## explicit; go 1.15 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url -# github.com/aws/aws-sdk-go-v2/service/sts v1.16.5 +# github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 ## explicit; go 1.15 github.com/aws/aws-sdk-go-v2/service/sts github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sts/types -# github.com/aws/smithy-go v1.11.2 +# github.com/aws/smithy-go v1.11.3 ## explicit; go 1.15 github.com/aws/smithy-go github.com/aws/smithy-go/document @@ -119,7 +119,7 @@ github.com/cenkalti/backoff/v3 github.com/ceph/ceph-csi/api/deploy/kubernetes/nfs github.com/ceph/ceph-csi/api/deploy/kubernetes/rbd github.com/ceph/ceph-csi/api/deploy/ocp -# github.com/ceph/go-ceph v0.15.0 +# github.com/ceph/go-ceph v0.16.0 ## explicit; go 1.12 github.com/ceph/go-ceph/cephfs/admin github.com/ceph/go-ceph/common/admin/manager @@ -219,8 +219,8 @@ github.com/google/gnostic/extensions github.com/google/gnostic/jsonschema github.com/google/gnostic/openapiv2 github.com/google/gnostic/openapiv3 -# github.com/google/go-cmp v0.5.7 -## explicit; go 1.11 +# github.com/google/go-cmp v0.5.8 +## explicit; go 1.13 github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags @@ -271,10 +271,10 @@ github.com/hashicorp/go-rootcerts # github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 ## explicit; go 1.16 github.com/hashicorp/go-secure-stdlib/mlock -# github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1 +# github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 ## explicit; go 1.16 github.com/hashicorp/go-secure-stdlib/parseutil -# github.com/hashicorp/go-secure-stdlib/strutil v0.1.1 +# github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 ## explicit; go 1.16 github.com/hashicorp/go-secure-stdlib/strutil # github.com/hashicorp/go-sockaddr v1.0.2 @@ -305,10 +305,10 @@ github.com/hashicorp/hcl/json/token ## explicit; go 1.13 github.com/hashicorp/vault/command/agent/auth github.com/hashicorp/vault/command/agent/auth/kubernetes -# github.com/hashicorp/vault/api v1.5.0 +# github.com/hashicorp/vault/api v1.6.0 ## explicit; go 1.13 github.com/hashicorp/vault/api -# github.com/hashicorp/vault/sdk v0.4.1 +# github.com/hashicorp/vault/sdk v0.5.0 ## explicit; go 1.16 github.com/hashicorp/vault/sdk/helper/certutil github.com/hashicorp/vault/sdk/helper/compressutil @@ -352,12 +352,11 @@ github.com/kubernetes-csi/csi-lib-utils/connection github.com/kubernetes-csi/csi-lib-utils/metrics github.com/kubernetes-csi/csi-lib-utils/protosanitizer github.com/kubernetes-csi/csi-lib-utils/rpc -# github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 -## explicit; go 1.15 -github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1 -github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1 -github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/scheme -github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned/typed/volumesnapshot/v1 +# github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0 +## explicit; go 1.17 +github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1 +github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/scheme +github.com/kubernetes-csi/external-snapshotter/client/v6/clientset/versioned/typed/volumesnapshot/v1 # github.com/libopenstorage/secrets v0.0.0-20210908194121-a1d19aa9713a ## explicit; go 1.13 github.com/libopenstorage/secrets @@ -386,7 +385,7 @@ github.com/mitchellh/go-homedir # github.com/mitchellh/go-testing-interface v1.0.0 ## explicit github.com/mitchellh/go-testing-interface -# github.com/mitchellh/mapstructure v1.4.2 +# github.com/mitchellh/mapstructure v1.5.0 ## explicit; go 1.14 github.com/mitchellh/mapstructure # github.com/mitchellh/reflectwalk v1.0.1 @@ -503,7 +502,7 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/stretchr/testify v1.7.1 +# github.com/stretchr/testify v1.7.2 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/require @@ -663,7 +662,7 @@ google.golang.org/appengine/urlfetch google.golang.org/genproto/googleapis/api/httpbody google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/protobuf/field_mask -# google.golang.org/grpc v1.46.2 +# google.golang.org/grpc v1.47.0 ## explicit; go 1.14 google.golang.org/grpc google.golang.org/grpc/attributes @@ -770,10 +769,10 @@ gopkg.in/tomb.v1 # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +# gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/api v0.24.0 => k8s.io/api v0.24.0 +# k8s.io/api v0.24.1 => k8s.io/api v0.24.1 ## explicit; go 1.16 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -822,11 +821,11 @@ k8s.io/api/scheduling/v1beta1 k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 -# k8s.io/apiextensions-apiserver v0.23.0 => k8s.io/apiextensions-apiserver v0.24.0 +# k8s.io/apiextensions-apiserver v0.23.0 => k8s.io/apiextensions-apiserver v0.24.1 ## explicit; go 1.16 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 -# k8s.io/apimachinery v0.24.0 => k8s.io/apimachinery v0.24.0 +# k8s.io/apimachinery v0.24.1 => k8s.io/apimachinery v0.24.1 ## explicit; go 1.16 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -882,7 +881,7 @@ k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.24.0 => k8s.io/apiserver v0.24.0 +# k8s.io/apiserver v0.24.1 => k8s.io/apiserver v0.24.1 ## explicit; go 1.16 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/admission/configuration @@ -921,7 +920,7 @@ k8s.io/apiserver/pkg/util/feature k8s.io/apiserver/pkg/util/webhook k8s.io/apiserver/pkg/util/x509metrics k8s.io/apiserver/pkg/warning -# k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.24.0 +# k8s.io/client-go v12.0.0+incompatible => k8s.io/client-go v0.24.1 ## explicit; go 1.16 k8s.io/client-go/applyconfigurations/admissionregistration/v1 k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1 @@ -1165,12 +1164,12 @@ k8s.io/client-go/util/homedir k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/workqueue -# k8s.io/cloud-provider v0.24.0 => k8s.io/cloud-provider v0.24.0 +# k8s.io/cloud-provider v0.24.1 => k8s.io/cloud-provider v0.24.1 ## explicit; go 1.16 k8s.io/cloud-provider k8s.io/cloud-provider/volume k8s.io/cloud-provider/volume/helpers -# k8s.io/component-base v0.24.0 => k8s.io/component-base v0.24.0 +# k8s.io/component-base v0.24.1 => k8s.io/component-base v0.24.1 ## explicit; go 1.16 k8s.io/component-base/cli/flag k8s.io/component-base/config @@ -1181,7 +1180,7 @@ k8s.io/component-base/metrics/legacyregistry k8s.io/component-base/metrics/testutil k8s.io/component-base/traces k8s.io/component-base/version -# k8s.io/component-helpers v0.24.0 => k8s.io/component-helpers v0.24.0 +# k8s.io/component-helpers v0.24.1 => k8s.io/component-helpers v0.24.1 ## explicit; go 1.16 k8s.io/component-helpers/node/util/sysctl k8s.io/component-helpers/scheduling/corev1 @@ -1205,14 +1204,14 @@ k8s.io/kube-openapi/pkg/schemamutation k8s.io/kube-openapi/pkg/spec3 k8s.io/kube-openapi/pkg/util/proto k8s.io/kube-openapi/pkg/validation/spec -# k8s.io/kubectl v0.0.0 => k8s.io/kubectl v0.24.0 +# k8s.io/kubectl v0.0.0 => k8s.io/kubectl v0.24.1 ## explicit; go 1.16 k8s.io/kubectl/pkg/scale k8s.io/kubectl/pkg/util/podutils -# k8s.io/kubelet v0.0.0 => k8s.io/kubelet v0.24.0 +# k8s.io/kubelet v0.0.0 => k8s.io/kubelet v0.24.1 ## explicit; go 1.16 k8s.io/kubelet/pkg/apis/stats/v1alpha1 -# k8s.io/kubernetes v1.24.0 +# k8s.io/kubernetes v1.24.1 ## explicit; go 1.16 k8s.io/kubernetes/pkg/api/legacyscheme k8s.io/kubernetes/pkg/api/service @@ -1273,10 +1272,10 @@ k8s.io/kubernetes/test/e2e/storage/utils k8s.io/kubernetes/test/e2e/testing-manifests k8s.io/kubernetes/test/utils k8s.io/kubernetes/test/utils/image -# k8s.io/mount-utils v0.24.0 => k8s.io/mount-utils v0.24.0 +# k8s.io/mount-utils v0.24.1 => k8s.io/mount-utils v0.24.1 ## explicit; go 1.16 k8s.io/mount-utils -# k8s.io/pod-security-admission v0.0.0 => k8s.io/pod-security-admission v0.24.0 +# k8s.io/pod-security-admission v0.0.0 => k8s.io/pod-security-admission v0.24.1 ## explicit; go 1.16 k8s.io/pod-security-admission/api # k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 @@ -1356,29 +1355,29 @@ sigs.k8s.io/yaml # github.com/golang/protobuf => github.com/golang/protobuf v1.4.3 # github.com/portworx/sched-ops => github.com/portworx/sched-ops v0.20.4-openstorage-rc3 # gomodules.xyz/jsonpatch/v2 => github.com/gomodules/jsonpatch/v2 v2.2.0 -# k8s.io/api => k8s.io/api v0.24.0 -# k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.0 -# k8s.io/apimachinery => k8s.io/apimachinery v0.24.0 -# k8s.io/apiserver => k8s.io/apiserver v0.24.0 -# k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.0 -# k8s.io/client-go => k8s.io/client-go v0.24.0 -# k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.0 -# k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.0 -# k8s.io/code-generator => k8s.io/code-generator v0.24.0 -# k8s.io/component-base => k8s.io/component-base v0.24.0 -# k8s.io/component-helpers => k8s.io/component-helpers v0.24.0 -# k8s.io/controller-manager => k8s.io/controller-manager v0.24.0 -# k8s.io/cri-api => k8s.io/cri-api v0.24.0 -# k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.0 -# k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.0 -# k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.0 -# k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.0 -# k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.0 -# k8s.io/kubectl => k8s.io/kubectl v0.24.0 -# k8s.io/kubelet => k8s.io/kubelet v0.24.0 -# k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.0 -# k8s.io/metrics => k8s.io/metrics v0.24.0 -# k8s.io/mount-utils => k8s.io/mount-utils v0.24.0 -# k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.0 -# k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.0 +# k8s.io/api => k8s.io/api v0.24.1 +# k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.1 +# k8s.io/apimachinery => k8s.io/apimachinery v0.24.1 +# k8s.io/apiserver => k8s.io/apiserver v0.24.1 +# k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.1 +# k8s.io/client-go => k8s.io/client-go v0.24.1 +# k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.1 +# k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.1 +# k8s.io/code-generator => k8s.io/code-generator v0.24.1 +# k8s.io/component-base => k8s.io/component-base v0.24.1 +# k8s.io/component-helpers => k8s.io/component-helpers v0.24.1 +# k8s.io/controller-manager => k8s.io/controller-manager v0.24.1 +# k8s.io/cri-api => k8s.io/cri-api v0.24.1 +# k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.1 +# k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.1 +# k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.1 +# k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.1 +# k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.1 +# k8s.io/kubectl => k8s.io/kubectl v0.24.1 +# k8s.io/kubelet => k8s.io/kubelet v0.24.1 +# k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.1 +# k8s.io/metrics => k8s.io/metrics v0.24.1 +# k8s.io/mount-utils => k8s.io/mount-utils v0.24.1 +# k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.1 +# k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.1 # layeh.com/radius => github.com/layeh/radius v0.0.0-20190322222518-890bc1058917