├── config ├── vc_creds.json └── sv_kubeconfig ├── NOTICE ├── .gitignore ├── client-sdk └── go │ ├── client │ ├── model_error.go │ ├── model_one_of_job_result_job_status.go │ ├── model_snapshots_for_volume_result.go │ ├── model_one_of_job_result_job_parameters.go │ ├── model_migrate_volumes_result.go │ ├── model_snapshot_delete_result.go │ ├── model_fault.go │ ├── model_resume_volume_provisioning_result.go │ ├── model_suspend_volume_provisioning_result.go │ ├── model_vm_details.go │ ├── model_registercluster_body.go │ ├── model_fcd_attachment_details.go │ ├── model_register_cluster_result.go │ ├── model_deregister_cluster_result.go │ ├── model_orphan_volume_details.go │ ├── model_orphan_volume_delete_failure.go │ ├── model_volume_details.go │ ├── model_job_result.go │ ├── model_volume_migration_job_parameters.go │ ├── model_volume_migration_task_status.go │ ├── model_volume_migration_job_status.go │ ├── model_snapshot_deletion_job_parameters.go │ ├── model_orphan_snapshot.go │ ├── model_datastore_resources_result.go │ ├── model_snapshot_details.go │ ├── model_orphan_snapshot_result.go │ ├── model_orphan_volume_delete_result.go │ ├── model_snapshot_deletion_task_status.go │ ├── model_snapshot_deletion_job_status.go │ ├── model_orphan_volume_result.go │ ├── model_orphan_volume.go │ ├── configuration.go │ ├── api_job_details.go │ ├── api_orphan_volume.go │ ├── api_orphan_snapshot.go │ ├── client.go │ └── api_cluster_record_keeping.go │ ├── go.mod │ ├── examples │ └── basicauth_client.go │ └── go.sum ├── docs └── book │ ├── supported_scale.md │ ├── deployment │ ├── upgrade.md │ ├── tls-certs.md │ ├── tls-enable.md │ ├── basicauth.md │ └── oauth2.md │ └── features │ ├── storage_vmotion.md │ ├── orphan_volumes.md │ └── orphan_snapshots.md ├── deploy ├── basic-auth │ └── nginx.conf ├── deploy.sh └── oauth2 │ └── nginx.conf ├── CONTRIBUTING_CLA.md ├── CODE_OF_CONDUCT.md ├── scripts └── get-kubeconfig.sh ├── README.md └── LICENSE /config/vc_creds.json: -------------------------------------------------------------------------------- 1 | { 2 | "vc": "", 3 | "user": "", 4 | "password": "" 5 | } -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 VMware, Inc. 2 | 3 | This product is licensed to you under the Apache License, V2.0 (the "License"). You may not use this product except in compliance with the License. 4 | 5 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. -------------------------------------------------------------------------------- /config/sv_kubeconfig: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | clusters: 3 | - cluster: 4 | certificate-authority-data: 5 | server: 6 | name: kubernetes 7 | contexts: 8 | - context: 9 | cluster: kubernetes 10 | user: kubernetes-admin 11 | name: kubernetes-admin@kubernetes 12 | current-context: kubernetes-admin@kubernetes 13 | kind: Config 14 | preferences: {} 15 | users: 16 | - name: kubernetes-admin 17 | user: 18 | client-certificate-data: 19 | client-key-data: 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | ### Go template 4 | # If you prefer the allow list template instead of the deny list, see community template: 5 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 6 | # 7 | # Binaries for programs and plugins 8 | *.exe 9 | *.exe~ 10 | *.dll 11 | *.so 12 | *.dylib 13 | 14 | # Test binary, built with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | 20 | # Dependency directories (remove the comment below to include it) 21 | # vendor/ 22 | 23 | # Go workspace file 24 | go.work 25 | go.work.sum 26 | 27 | # env file 28 | .env 29 | 30 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type ModelError struct { 19 | Message string `json:"message"` 20 | Error_ string `json:"error"` 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_one_of_job_result_job_status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type OneOfJobResultJobStatus struct { 19 | VolumeMigrationJobStatus 20 | SnapshotDeletionJobStatus 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshots_for_volume_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type SnapshotsForVolumeResult struct { 19 | Snapshots []SnapshotDetails `json:"Snapshots,omitempty"` 20 | } 21 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_one_of_job_result_job_parameters.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type OneOfJobResultJobParameters struct { 19 | VolumeMigrationJobParameters 20 | SnapshotDeletionJobParameters 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_migrate_volumes_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type MigrateVolumesResult struct { 19 | // Identifier of the volume migration job submitted 20 | JobId string `json:"jobId,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshot_delete_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type SnapshotDeleteResult struct { 19 | // Identifier of the snapshot deletion job submitted 20 | JobId string `json:"jobId,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_fault.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type Fault struct { 19 | // Error message for the fault. 20 | Message string `json:"message,omitempty"` 21 | // Type of fault. 22 | FaultType string `json:"faultType,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_resume_volume_provisioning_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type ResumeVolumeProvisioningResult struct { 19 | // Result of resuming volume provisioning on datastore. 20 | Message string `json:"message,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_suspend_volume_provisioning_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type SuspendVolumeProvisioningResult struct { 19 | // Result of suspending volume provisioning on datastore. 20 | Message string `json:"message,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_vm_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type VmDetails struct { 19 | // Name of the virtual machine. 20 | VmName string `json:"vmName,omitempty"` 21 | // Id of the virtual machine. 22 | VmId string `json:"vmId,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_registercluster_body.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "os" 20 | ) 21 | 22 | type RegisterclusterBody struct { 23 | // A file with cluster kubeconfig content. 24 | ClusterKubeConfigFile **os.File `json:"clusterKubeConfigFile"` 25 | } 26 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_fcd_attachment_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type FcdAttachmentDetails struct { 19 | // Indicates whether the volume is attached to a VM or not. 20 | Attached bool `json:"attached,omitempty"` 21 | // The name of VM to which the volume is attached. 22 | Vm string `json:"vm,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_register_cluster_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type RegisterClusterResult struct { 19 | // Status to indicate if registration was successful. 20 | Status string `json:"status,omitempty"` 21 | // Indicates the clusterId which got registered with CNS Manager. 22 | ClusterId string `json:"clusterId,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_deregister_cluster_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type DeregisterClusterResult struct { 19 | // Status to indicate if deregistration was successful. 20 | Status string `json:"status,omitempty"` 21 | // Indicates the clusterId which got deregistered with CNS Manager. 22 | ClusterId string `json:"clusterId,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_volume_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package swagger 18 | 19 | // OrphanVolumeDetails represents if the orphan volume is attached to a VM or not. 20 | type OrphanVolumeDetails struct { 21 | // Indicates whether the orphan volume is attached to a VM or not. 22 | Attached bool `json:"attached"` 23 | // The name of VM to which the orphan volume is attached. 24 | Vm string `json:"vm,omitempty"` 25 | } 26 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_volume_delete_failure.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | // OrphanVolumeDeleteFailure is the result of a failed orphan volume deletion. It contains the ID of the orphan volume and the reason for deletion failure. 19 | type OrphanVolumeDeleteFailure struct { 20 | // ID of the orphan volume whose deletion failed. 21 | VolumeId string `json:"volumeId"` 22 | // Reason for deletion failure. 23 | Reason string `json:"reason"` 24 | } 25 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_volume_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type VolumeDetails struct { 19 | // ID of the FCD. 20 | FcdId string `json:"fcdId,omitempty"` 21 | // Name of the FCD. 22 | FcdName string `json:"fcdName,omitempty"` 23 | AttachmentDetails *FcdAttachmentDetails `json:"attachmentDetails,omitempty"` 24 | // Host owning the node vm to which the volume is attached. 25 | Host string `json:"host,omitempty"` 26 | } 27 | -------------------------------------------------------------------------------- /docs/book/supported_scale.md: -------------------------------------------------------------------------------- 1 | ## Configuration Limits for CNS Manager 2 | 3 | This topic provides the configuration limits for CNS manager and the features it offers. When you use CNS manager in your environment, stay within the supported and recommended limits. 4 | 5 | **Number of Kubernetes clusters in vCenter registered with CNS manager** 6 | 32 7 | 8 | 9 | **Number of concurrent PV migrations** 10 | On CNS manager application level, there can be 8 volume migrations that can be invoked in parallel across all clusters. 11 | And on vCenter level, the limits for simultaneous migrations can be be derived from this document - https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vcenterhost.doc/GUID-25EA5833-03B5-4EDD-A167-87578B8009B3.html 12 | 13 | This translates to concurrent migration limits to be 1 per VM(for attached volumes), 2 per host & 8 per datastore. 14 | If there are parallel FCD migrations invoked beyond these limits, they will be queued based on the limits for each type of resource. CNS manager supports queueing upto 400 volumes at any given time. -------------------------------------------------------------------------------- /client-sdk/go/client/model_job_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type JobResult struct { 19 | // ID of the job. 20 | JobId string `json:"jobId,omitempty"` 21 | // Current phase of the job. 22 | Phase string `json:"phase,omitempty"` 23 | // Input parameters of the job. 24 | JobParameters *OneOfJobResultJobParameters `json:"jobParameters,omitempty"` 25 | // Status of individual tasks and the overall job status. 26 | JobStatus *OneOfJobResultJobStatus `json:"jobStatus,omitempty"` 27 | } 28 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_volume_migration_job_parameters.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type VolumeMigrationJobParameters struct { 19 | // Datacenter on which source and target datastores reside. 20 | Datacenter string `json:"datacenter,omitempty"` 21 | // Name of the source datastore for volume migration. 22 | SourceDatastore string `json:"sourceDatastore,omitempty"` 23 | // Name of the target datastore for volume migration. 24 | TargetDatastore string `json:"targetDatastore,omitempty"` 25 | // Array of volumes provided to be migrated. 26 | VolumesToMigrate []string `json:"volumesToMigrate,omitempty"` 27 | } 28 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_volume_migration_task_status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "time" 20 | ) 21 | 22 | type VolumeMigrationTaskStatus struct { 23 | // ID of the FCD being migrated 24 | FcdId string `json:"fcdId,omitempty"` 25 | // Current phase of the volume migration. 26 | Phase string `json:"phase,omitempty"` 27 | // The timestamp at which the task was invoked. 28 | TaskStartTime time.Time `json:"taskStartTime,omitempty"` 29 | // The timestamp at which the task finished. 30 | TaskEndTime time.Time `json:"taskEndTime,omitempty"` 31 | Error_ *Fault `json:"error,omitempty"` 32 | } 33 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_volume_migration_job_status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "time" 20 | ) 21 | 22 | type VolumeMigrationJobStatus struct { 23 | // Overall phase of the volume migration job. 24 | OverallPhase string `json:"overallPhase,omitempty"` 25 | // Time at which the job started processing. 26 | StartTime time.Time `json:"startTime,omitempty"` 27 | // Time at which the job completed processing. 28 | EndTime time.Time `json:"endTime,omitempty"` 29 | // Array of status of individual volume migration tasks in the job. 30 | VolumeMigrationTasks []VolumeMigrationTaskStatus `json:"volumeMigrationTasks,omitempty"` 31 | } 32 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshot_deletion_job_parameters.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type SnapshotDeletionJobParameters struct { 19 | // fcdId is the identifier of FCD. 20 | FcdId string `json:"fcdId,omitempty"` 21 | // snapshotId is the identifier of snapshot. 22 | SnapshotId string `json:"snapshotId,omitempty"` 23 | // datacenter to which snapshots belong. 24 | Datacenter string `json:"datacenter,omitempty"` 25 | // datastores to which snapshots belong. 26 | Datastores []string `json:"datastores,omitempty"` 27 | // snapshotPrefix is the prefix used in the snapshot description. 28 | SnapshotPrefix string `json:"snapshotPrefix,omitempty"` 29 | } 30 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_snapshot.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type OrphanSnapshot struct { 19 | // FCD Id of the orphan snapshot. 20 | VolumeId string `json:"volumeId,omitempty"` 21 | // Snapshot Id of the orphan snapshot. 22 | VolumeSnapshotId string `json:"volumeSnapshotId,omitempty"` 23 | // Datacenter where the orphan snapshot is located. 24 | Datacenter string `json:"datacenter,omitempty"` 25 | // Datastore where the orphan snapshot is located. 26 | Datastore string `json:"datastore,omitempty"` 27 | // Create time of the orphan snapshot. 28 | CreateTime string `json:"createTime,omitempty"` 29 | // Description of orphan snapshot 30 | SnapshotDescription string `json:"snapshotDescription,omitempty"` 31 | } 32 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_datastore_resources_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type DatastoreResourcesResult struct { 19 | // Datacenter on which datastore resides. 20 | Datacenter string `json:"datacenter,omitempty"` 21 | // Datastore on which container volumes are being queried. 22 | Datastore string `json:"datastore,omitempty"` 23 | // The number of volumes on the datastore. 24 | TotalVolumes int64 `json:"totalVolumes,omitempty"` 25 | // Array of CNS volumes with the FCD id and vm attachment details. 26 | ContainerVolumes []VolumeDetails `json:"containerVolumes,omitempty"` 27 | // Array of non-CNS volumes with the FCD id and vm attachment details. 28 | OtherVolumes []VolumeDetails `json:"otherVolumes,omitempty"` 29 | // Array of virtual machines on the datastore. 30 | VirtualMachines []VmDetails `json:"virtualMachines,omitempty"` 31 | } 32 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshot_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type SnapshotDetails struct { 19 | // Id of the snapshot. 20 | SnapshotId string `json:"snapshotId,omitempty"` 21 | // Id of the volume. 22 | VolumeId string `json:"volumeId,omitempty"` 23 | // Time when snapshot is created. 24 | CreateTime string `json:"createTime,omitempty"` 25 | // Description of the snapshot. 26 | SnapshotDescription string `json:"snapshotDescription,omitempty"` 27 | // Phase of the sanpshot if it is created by Velero vSphere plugin. 28 | VelerovSpherePluginSnapshotPhase string `json:"velerovSpherePluginSnapshotPhase,omitempty"` 29 | // Associated VolumeSnapshotContent name if snapshot is created by CSI driver. 30 | AssociatedVolumeSnapshotContent string `json:"associatedVolumeSnapshotContent,omitempty"` 31 | // Owner who created this snapshot. e.g. vSphere CSI driver, Velero vSphere plugin etc. 32 | Owner string `json:"owner,omitempty"` 33 | } 34 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_snapshot_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type OrphanSnapshotResult struct { 19 | // The total orphan snapshots returned. 20 | TotalOrphanSnapshots int64 `json:"totalOrphanSnapshots,omitempty"` 21 | // Limit specifies the maximum number of entries that are displayed in single request. 22 | Limit int64 `json:"limit,omitempty"` 23 | // Offset specifies the starting point of the result set. 24 | Offset int64 `json:"offset,omitempty"` 25 | // Array of orphan snapshots 26 | OrphanSnapshots []OrphanSnapshot `json:"orphanSnapshots,omitempty"` 27 | // Since the detection of orphan snapshots is an expensive operation, the operation is performed asynchronously at regular intervals. This API returns the list of orphan snapshots found in the last run of the operation. `retryAfterMinutes` indicates the time in minutes after which the next retry should be attempted to get the updated orphan snapshot list. 28 | RetryAfterMinutes int64 `json:"retryAfterMinutes,omitempty"` 29 | } 30 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_volume_delete_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package swagger 18 | 19 | // OrphanVolumeDeleteResult is the result of deleting orphan volumes. 20 | type OrphanVolumeDeleteResult struct { 21 | // Number of orphan volumes detected. 22 | TotalOrphansDetected int64 `json:"totalOrphansDetected"` 23 | // Number of orphan volumes deleted. 24 | TotalOrphansDeleted int64 `json:"totalOrphansDeleted"` 25 | // Number of deleted orphan volumes that were detached. 26 | TotalDetachedOrphansDeleted int64 `json:"totalDetachedOrphansDeleted"` 27 | // Number of deleted orphan volumes that were attached to a VM. 28 | TotalAttachedOrphansDeleted int64 `json:"totalAttachedOrphansDeleted"` 29 | // Array of successfully deleted orphan volume IDs. 30 | SuccessfulOrphanDeletions []string `json:"successfulOrphanDeletions"` 31 | // Array of failed orphan volume deletions with the reason for failure for each orphan volume. 32 | FailedOrphanDeletions []OrphanVolumeDeleteFailure `json:"failedOrphanDeletions,omitempty"` 33 | } 34 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshot_deletion_task_status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | import ( 18 | "time" 19 | ) 20 | 21 | type SnapshotDeletionTaskStatus struct { 22 | // Id of the FCD to which snapshot belongs. 23 | FcdId string `json:"fcdId,omitempty"` 24 | // Id of the snapshot to be deleted. 25 | SnapshotId string `json:"snapshotId,omitempty"` 26 | // datacenter to which snapshot belongs. 27 | Datacenter string `json:"datacenter,omitempty"` 28 | // datastore to which snapshot belongs. 29 | Datastore string `json:"datastore,omitempty"` 30 | // description of the snapshot. 31 | SnapshotDescription string `json:"snapshotDescription,omitempty"` 32 | // Current phase of the snapshot deletion task. 33 | Phase string `json:"phase,omitempty"` 34 | // The timestamp at which the task was invoked. 35 | TaskStartTime time.Time `json:"taskStartTime,omitempty"` 36 | // The timestamp at which the task finished. 37 | TaskEndTime time.Time `json:"taskEndTime,omitempty"` 38 | Error_ *Fault `json:"error,omitempty"` 39 | } 40 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_snapshot_deletion_job_status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | import ( 18 | "time" 19 | ) 20 | 21 | type SnapshotDeletionJobStatus struct { 22 | // Total snapshots which will be deleted as part of this job. 23 | TotalSnapshotsPlannedForDeletion int32 `json:"totalSnapshotsPlannedForDeletion,omitempty"` 24 | // Total snapshots which got successfully deleted as part of this job. 25 | TotalSnapshotsSuccessfullyDeleted int32 `json:"totalSnapshotsSuccessfullyDeleted,omitempty"` 26 | // Total snapshots whose deletion failed as part of this job. 27 | TotalSnapshotsWithFailedDeletion int32 `json:"totalSnapshotsWithFailedDeletion,omitempty"` 28 | // Time at which the job started processing. 29 | StartTime time.Time `json:"startTime,omitempty"` 30 | // Time at which the job completed processing. 31 | EndTime time.Time `json:"endTime,omitempty"` 32 | // Array of status of individual snapshot deletion tasks in the job. 33 | SnapshotDeletionTasks []SnapshotDeletionTaskStatus `json:"snapshotDeletionTasks,omitempty"` 34 | } 35 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_volume_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | type OrphanVolumeResult struct { 19 | // The total number of orphan volumes detected in the vCenter for the input. 20 | TotalOrphans int32 `json:"totalOrphans"` 21 | // The total number of orphan volumes that are attached to a VM. This field is returned only if includeDetails is set to true. 22 | TotalOrphansAttached int32 `json:"totalOrphansAttached,omitempty"` 23 | // The total number of orphan volumes that are not attached to a VM. This field is returned only if includeDetails is set to true. 24 | TotalOrphansDetached int32 `json:"totalOrphansDetached,omitempty"` 25 | // Array of orphan volumes. 26 | OrphanVolumes []OrphanVolume `json:"orphanVolumes"` 27 | // The time in minutes after which the next retry should be attempted to get the updated orphan volume list. 28 | RetryAfterMinutes int32 `json:"retryAfterMinutes"` 29 | // The maximum number of orphan volumes returned. 30 | Limit int32 `json:"limit,omitempty"` 31 | // The offset of the next page if there are more orphan volumes to query. 32 | NextOffset int32 `json:"nextOffset,omitempty"` 33 | } 34 | -------------------------------------------------------------------------------- /client-sdk/go/client/model_orphan_volume.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package swagger 18 | 19 | // Orphan volumes are volumes that are present in the vSphere datastore but have no corresponding PersistentVolume in the Kubernetes cluster. Primarily, Orphan volumes are created when the CNS solution creates more than one volume for a Persistent Volume in the Kubernetes cluster. This can occur when the vCenter components are slow, storage is slow, vCenter service restarts, or there are connectivity issues between vCenter and ESXi hosts 20 | type OrphanVolume struct { 21 | // ID of the orphan volume. 22 | VolumeId string `json:"volumeId,omitempty"` 23 | // Name of the orphan volume. 24 | VolumeName string `json:"volumeName,omitempty"` 25 | // Datacenter where the orphan volume is located. 26 | Datacenter string `json:"datacenter,omitempty"` 27 | // Datastore where the orphan volume is located. 28 | Datastore string `json:"datastore,omitempty"` 29 | // Create time of the orphan volume. 30 | CreateTime string `json:"createTime,omitempty"` 31 | // Capacity of the orphan volume. 32 | CapacityInMb int64 `json:"capacityInMb,omitempty"` 33 | 34 | Details *OrphanVolumeDetails `json:"details,omitempty"` 35 | } 36 | -------------------------------------------------------------------------------- /deploy/basic-auth/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | # This is the location where Nginx will store the PID file. 6 | # The default location where Nginx stores the PID file is not accessible in certain flavors of K8s like OCP. 7 | pid /tmp/nginx.pid; 8 | 9 | http { 10 | server { 11 | listen 8081; 12 | # Use below instead to configure an https server 13 | # listen 443 ssl; 14 | 15 | server_name cnsmanager.cns.vmware.com; 16 | 17 | #Uncomment this to enable ssl/tls communication over https. 18 | #ssl_certificate /etc/nginx/tls/tls.crt; 19 | #ssl_certificate_key /etc/nginx/tls/tls.key; 20 | 21 | auth_basic "Restricted Content"; 22 | auth_basic_user_file /etc/nginx/basicauth_creds; 23 | 24 | location /ui { 25 | proxy_pass http://localhost:8080; 26 | proxy_set_header Host $host; 27 | proxy_buffering on; 28 | } 29 | 30 | location /1.0.0 { 31 | proxy_pass http://localhost:8100; 32 | proxy_set_header Host $host; 33 | proxy_buffering on; 34 | 35 | proxy_read_timeout 300; 36 | proxy_connect_timeout 300; 37 | proxy_send_timeout 300; 38 | } 39 | 40 | #Since /waitforjob is supposed to be a blocking API call, set a long timeout of 1d 41 | location /1.0.0/waitforjob { 42 | proxy_pass http://localhost:8100; 43 | proxy_set_header Host $host; 44 | proxy_buffering on; 45 | 46 | proxy_read_timeout 1d; 47 | proxy_connect_timeout 1d; 48 | proxy_send_timeout 1d; 49 | } 50 | 51 | # Since DELETE /orphanvolumes could be a long running operation, 52 | # set a timeout of 30m for /orphanvolumes endpoints 53 | location /1.0.0/orphanvolumes { 54 | proxy_pass http://localhost:8100; 55 | proxy_set_header Host $host; 56 | proxy_buffering on; 57 | 58 | proxy_read_timeout 30m; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/book/deployment/upgrade.md: -------------------------------------------------------------------------------- 1 | ## Upgrading cns-manager 2 | 3 | The easiest way to upgrade cns-manager would be to completely undeploy the current version, checkout the targetted release & use deployment artifacts from the that release. The steps to deploy are already available [here](../../../README.md#deploying-cns-manager). 4 | 5 | But if you want to preserve some of the earlier configurations such as oAuth2 configuration, clusters that were already registered etc., you can perform below steps: 6 | 7 | **1.** Update Swagger config to reflect the newly added API endpoints(if any) in Swagger UI. 8 | This can be done using following commands. 9 | 10 | ``` 11 | > git checkout 12 | > kubectl -n delete configmap swagger-api 13 | > export CNS_MANAGER_ENDPOINT= 14 | > sed "s/%CNS_MANAGER_ENDPOINT%/$CNS_MANAGER_ENDPOINT/g" deploy/swagger-template.yaml > swagger.yaml 15 | > kubectl -n create configmap swagger-api --from-file=swagger.yaml 16 | > rm swagger.yaml 17 | ``` 18 | 19 | Here `CNS_MANAGER_ENDPOINT` will be the endpoint over which CNS manager service is accessible (<> or FQDN set during deployment). 20 | 21 | **2.** Update nginx config to reflect any changes done for nginx proxy. 22 | ``` 23 | > git checkout 24 | > kubectl -n delete configmap nginx-conf 25 | > kubectl -n create configmap nginx-conf --from-file=/nginx.conf 26 | ``` 27 | 28 | Here `auth-folder` is the folder corresponding to your deployment type - `deploy/basic-auth` or `deploy/oauth2`. 29 | 30 | **3.** Check if orphan volume auto-deletion is disabled. It's recommended to keep it disabled until you fully understand its usage (Read the [orphan volume feature documentation](../features/orphan_volumes.md#setting-up-auto-monitoring-for-orphan-volumes-deletion) for details). 31 | 32 | The desired value can be set in `auto-delete-ov` field in `cnsmanager-config` configmap. 33 | 34 | ``` 35 | kubectl edit configmap cnsmanager-config -n 36 | ``` 37 | 38 | **4.** Update the new release image in cns-manager deployment. For instance, for upgrading to release 0.2.0: 39 | ``` 40 | kubectl set image deployment/cns-manager cns-manager=projects.registry.vmware.com/cns_manager/cns-manager:r0.2.0 -n 41 | ``` 42 | 43 | This will restart the deployment including updating the nginx config as well as new API endpoints in Swagger UI. -------------------------------------------------------------------------------- /client-sdk/go/go.mod: -------------------------------------------------------------------------------- 1 | module cns.vmware.com/cns-manager 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/antihax/optional v1.0.0 7 | github.com/go-logr/zapr v1.2.3 8 | go.uber.org/zap v1.24.0 9 | golang.org/x/oauth2 v0.5.0 10 | sigs.k8s.io/controller-runtime v0.13.0 11 | ) 12 | 13 | require ( 14 | github.com/fsnotify/fsnotify v1.6.0 // indirect 15 | github.com/go-logr/logr v1.2.3 // indirect 16 | github.com/golang/protobuf v1.5.2 // indirect 17 | github.com/google/go-cmp v0.5.9 // indirect 18 | github.com/onsi/gomega v1.27.0 // indirect 19 | github.com/stretchr/testify v1.8.1 // indirect 20 | go.uber.org/atomic v1.7.0 // indirect 21 | go.uber.org/multierr v1.6.0 // indirect 22 | golang.org/x/net v0.17.0 // indirect 23 | google.golang.org/appengine v1.6.7 // indirect 24 | google.golang.org/protobuf v1.28.1 // indirect 25 | k8s.io/apimachinery v0.25.2 // indirect 26 | ) 27 | 28 | replace ( 29 | github.com/go-logr/logr => github.com/go-logr/logr v1.2.0 30 | github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.4.1 31 | github.com/kubernetes-csi/csi-lib-utils => github.com/kubernetes-csi/csi-lib-utils v0.11.0 32 | k8s.io/api => k8s.io/api v0.25.2 33 | k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.25.2 34 | k8s.io/apimachinery => k8s.io/apimachinery v0.25.2 35 | k8s.io/apiserver => k8s.io/apiserver v0.25.2 36 | k8s.io/cli-runtime => k8s.io/cli-runtime v0.25.2 37 | k8s.io/client-go => k8s.io/client-go v0.25.2 38 | k8s.io/cloud-provider => k8s.io/cloud-provider v0.25.2 39 | k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.25.2 40 | k8s.io/code-generator => k8s.io/code-generator v0.25.2 41 | k8s.io/component-base => k8s.io/component-base v0.25.2 42 | k8s.io/component-helpers => k8s.io/component-helpers v0.25.2 43 | k8s.io/controller-manager => k8s.io/controller-manager v0.25.2 44 | k8s.io/cri-api => k8s.io/cri-api v0.25.2 45 | k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.25.2 46 | k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.25.2 47 | k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.25.2 48 | k8s.io/kube-proxy => k8s.io/kube-proxy v0.25.2 49 | k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.25.2 50 | k8s.io/kubectl => k8s.io/kubectl v0.25.2 51 | k8s.io/kubelet => k8s.io/kubelet v0.25.2 52 | k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.25.2 53 | k8s.io/metrics => k8s.io/metrics v0.25.2 54 | k8s.io/mount-utils => k8s.io/mount-utils v0.24.6 55 | k8s.io/node-api => k8s.io/node-api v0.25.2 56 | k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.25.2 57 | k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.25.2 58 | k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.25.2 59 | k8s.io/sample-controller => k8s.io/sample-controller v0.25.2 60 | ) 61 | -------------------------------------------------------------------------------- /client-sdk/go/client/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "net/http" 20 | ) 21 | 22 | // contextKeys are used to identify the type of value in the context. 23 | // Since these are string, it is possible to get a short description of the 24 | // context key for logging and debugging using key.String(). 25 | 26 | type contextKey string 27 | 28 | func (c contextKey) String() string { 29 | return "auth " + string(c) 30 | } 31 | 32 | var ( 33 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 34 | ContextOAuth2 = contextKey("token") 35 | 36 | // ContextBasicAuth takes BasicAuth as authentication for the request. 37 | ContextBasicAuth = contextKey("basic") 38 | 39 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 40 | ContextAccessToken = contextKey("accesstoken") 41 | 42 | // ContextAPIKey takes an APIKey as authentication for the request 43 | ContextAPIKey = contextKey("apikey") 44 | ) 45 | 46 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 47 | type BasicAuth struct { 48 | UserName string `json:"userName,omitempty"` 49 | Password string `json:"password,omitempty"` 50 | } 51 | 52 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 53 | type APIKey struct { 54 | Key string 55 | Prefix string 56 | } 57 | 58 | type Configuration struct { 59 | BasePath string `json:"basePath,omitempty"` 60 | Host string `json:"host,omitempty"` 61 | Scheme string `json:"scheme,omitempty"` 62 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 63 | UserAgent string `json:"userAgent,omitempty"` 64 | HTTPClient *http.Client 65 | } 66 | 67 | func NewConfiguration() *Configuration { 68 | cfg := &Configuration{ 69 | BasePath: "http://localhost:8080/v1", 70 | DefaultHeader: make(map[string]string), 71 | UserAgent: "Swagger-Codegen/1.0.0/go", 72 | } 73 | return cfg 74 | } 75 | 76 | func (c *Configuration) AddDefaultHeader(key string, value string) { 77 | c.DefaultHeader[key] = value 78 | } 79 | -------------------------------------------------------------------------------- /docs/book/deployment/tls-certs.md: -------------------------------------------------------------------------------- 1 | 2 | ## Generating TLS key and certificate 3 | 4 | * Pre-requisites - An internal/private CA(Certificate Authority) that is trusted by all machines in your private network. 5 | 6 | The Internal CA section below is just used for demonstration purposes. Skip to [Generate CSR](#generate-csr) section directly if you already have a functioning CA that can issue certificates. 7 | 8 | ### Set up an internal CA 9 | First step is to set up an internal CA. Ignore this section if you already have a CA. 10 | 11 | Steps: 12 | 1. Generate private key for for the internal CA. 13 | ``` 14 | openssl genrsa -des3 -out myCA.key 2048 15 | ``` 16 | 17 | 2. Generate root certificate for the CA using the private key above. 18 | ``` 19 | openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem 20 | ``` 21 | 22 | This root CA needs to be added to client machine's trust store so that browsers recognise this as a valid root CA. 23 | 24 | 25 | ### Generate CSR 26 | Now that the CA is ready, generate a CSR which can be sent to the Internal CA to issue a valid certificate. 27 | 28 | Steps: 29 | 1. Generate private key. 30 | ``` 31 | openssl genrsa -out helloabc.com.key 2048 32 | ``` 33 | Private key is stored in helloabc.com.key file 34 | 35 | 2. Create CSR with the above private key 36 | ``` 37 | openssl req -new -key helloabc.com.key -out helloabc.com.csr 38 | ``` 39 | The CSR is stored in helloabc.com.csr file 40 | 41 | Make sure to give a proper Common Name(CN), that is, the domain you expect CNS manager to be accessible on. For example, if you give Common Name as helloabc.com then CNS manager will be available on https://helloabc.com 42 | 43 | 3. Create a .ext file, say `helloabc.com.ext` having config details and a Subject Alternative Name. The SAN should be same as CN provided in the above step. 44 | ``` 45 | authorityKeyIdentifier=keyid,issuer 46 | basicConstraints=CA:FALSE 47 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 48 | subjectAltName = @alt_names 49 | 50 | [alt_names] 51 | DNS.1 = helloabc.com 52 | ``` 53 | 54 | ### Generate certificate 55 | Generate the certificate with the following command: 56 | ``` 57 | openssl x509 -req -in helloabc.com.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out helloabc.com.pem -days 825 -sha256 -extfile helloabc.com.ext 58 | ``` 59 | 60 | It takes as input: 61 | - CSR - helloabc.com.csr 62 | - Internal CA certificate - myCA.pem 63 | - Private key - myCA.key 64 | - extfile - helloabc.com.ext 65 | 66 | Output(issued certificate) is stored in file `helloabc.com.pem` 67 | 68 | 69 | ### Important Notes 70 | 1. The key and certificate files generated `helloabc.com.key` and `helloabc.com.pem` can be used during CNS manager deployment to enable TLS. 71 | 2. CNS manager currently does not manage certificate lifecycle. Thus, the responsibility to renew certificates must be taken care of manually or by some other preferred way. 72 | 3. After deployment, ensure there's a DNS entry for the the domain name used here to the CNS manager service ExternalIP/NodePort running in Kubernetes cluster. -------------------------------------------------------------------------------- /docs/book/deployment/tls-enable.md: -------------------------------------------------------------------------------- 1 | ## Deploying CNS Manager with TLS enabled 2 | 3 | Below are the changes you need to make during deployment to enable TLS on CNS manager. This is on top of the deployment instructions for basicauth/oauth2, and assumes you have the TLS key and certificate already generated. 4 | (If you want to generate self-signed certificates using an internal/private CA, the instructions are provided [here](tls-certs.md) just as a reference.) 5 | 6 | ### Add ssl settings to nginx config 7 | Make changes in `nginx.conf` to add ssl config. 8 | For basicauth, make changes in `deploy/basic-auth/nginx.conf`. 9 | For oauth2, make changes in `deploy/oauth2/nginx.conf`. 10 | 11 | * Uncomment the ssl-related settings in nginx.conf 12 | ``` 13 | http { 14 | server { 15 | listen 80; 16 | # Use below instead to configure an https server 17 | #listen 443 ssl; 18 | . 19 | . 20 | #Uncomment this to enable ssl/tls communication over https. 21 | #ssl_certificate /etc/nginx/tls/tls.crt; 22 | #ssl_certificate_key /etc/nginx/tls/tls.key; 23 | . 24 | . 25 | . 26 | } 27 | } 28 | ``` 29 | You can also add any additional settings in nginx config by following their documentation. 30 | 31 | 32 | ### Add TLS related changes to deployment yaml 33 | Make changes in `deploy-template.yaml` to add TLS related changes. Select the file to change depending on the authentication mechanism. 34 | For basicauth, make changes in `deploy/basic-auth/deploy-template.yaml`. 35 | For oauth2, make changes in `deploy/oauth2/deploy-template.yaml`. 36 | 37 | * Uncomment in deployment yaml the sections to define and mount the volume containing tls certificates. 38 | ``` 39 | . 40 | . 41 | --- 42 | apiVersion: apps/v1 43 | kind: Deployment 44 | . 45 | . 46 | containers: 47 | - name: nginx-proxy 48 | . 49 | . 50 | volumeMounts: 51 | . 52 | . 53 | #uncomment below volume mount if tls is enabled. 54 | #- name: cnsmanager-tls 55 | # mountPath: /etc/nginx/tls 56 | # readOnly: true 57 | . 58 | . 59 | volumes: 60 | . 61 | . 62 | #uncomment below volume creation if tls is enabled. 63 | #- name: cnsmanager-tls 64 | # secret: 65 | # secretName: cnsmanager-tls 66 | . 67 | . 68 | . 69 | ``` 70 | 71 | ### Pass additional command line arguments to deploy script 72 | * After making the above changes, add TLS-related arguments while running the deployment script. You need to set `tls-flag` to `true` and provide the filepaths to TLS certificate and key. 73 | 74 | ``` 75 | > cd deploy 76 | > ./deploy.sh <(tls flag)true|false> 77 | ``` 78 | 79 | For example 80 | ``` 81 | > cd deploy 82 | > ./deploy.sh cns-manager ../config/sv_kubeconfig ../config/vc_creds.json :30009 basicauth 'Administrator' 'Admin123@' /etc/tls/helloabc.com.key /etc/tls/helloabc.com.pem 83 | ``` 84 | 85 | For detailed deployment instructions, refer to the parent document for [basicauth](basicauth.md) and [oauth2](oauth2.md) deployments. 86 | -------------------------------------------------------------------------------- /deploy/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 6 ] 4 | then 5 | echo "./deploy.sh <(tls flag)true|false> 6 | 7 | " 8 | exit 1 9 | fi 10 | 11 | NAMESPACE=$1 12 | SV_KUBECONFIG_FILE=$2 13 | VC_CREDS_FILE=$3 14 | CNS_MANAGER_ENDPOINT=$4 15 | AUTH_MECHANISM=$5 16 | TLS_FLAG=$6 17 | 18 | if ! [ $AUTH_MECHANISM == "basicauth" -o $AUTH_MECHANISM == "oauth2" ] 19 | then 20 | echo "Auth mechanism needs to be either basicauth or oauth2." 21 | exit 1 22 | fi 23 | 24 | # Set variables depending on the values of auth mechanism and tls flag. 25 | if [ $AUTH_MECHANISM == "basicauth" ] 26 | then 27 | BASICAUTH_USERNAME=$7 28 | BASICAUTH_PASSWORD=$8 29 | 30 | if [ $TLS_FLAG == "true" ] 31 | then 32 | TLS_KEY=$9 33 | TLS_CERT=${10} 34 | fi 35 | else 36 | if [ $TLS_FLAG == "true" ] 37 | then 38 | TLS_KEY=$7 39 | TLS_CERT=$8 40 | fi 41 | fi 42 | 43 | # Create a namespace where CNS manager will be deployed. 44 | kubectl create ns $NAMESPACE 45 | if [ $? -ne 0 ] 46 | then 47 | echo "Failed to create namespace $NAMESPACE." 48 | exit 1 49 | fi 50 | 51 | # Create a secret that has SV cluster's admin kubeconfig. 52 | # Edit the sv_kubeconfig file as per your deployment. 53 | kubectl -n $NAMESPACE create secret generic sv-kubeconfig --from-file=$SV_KUBECONFIG_FILE 54 | if [ $? -ne 0 ] 55 | then 56 | echo "Failed to create sv-kubeconfig secret." 57 | exit 1 58 | fi 59 | 60 | # Create a secret that has the vCenter's admin creds. 61 | # Edit vc_creds.json as per your requirement. 62 | kubectl -n $NAMESPACE create secret generic vc-creds --from-file=$VC_CREDS_FILE 63 | if [ $? -ne 0 ] 64 | then 65 | echo "Failed to create vc-creds secret." 66 | exit 1 67 | fi 68 | 69 | # If auth mechanism is basicauth, create a secret that has basic auth credentials hashed using MD5-based 70 | # password algorithm. Create a temp file with credentials and remove it after creating the secret. 71 | if [ $AUTH_MECHANISM == "basicauth" ] 72 | then 73 | # Assign manifest folder 74 | MANIFEST_FOLDER="basic-auth" 75 | echo -n $BASICAUTH_USERNAME: >> basicauth_creds 76 | openssl passwd -apr1 $BASICAUTH_PASSWORD >> basicauth_creds 77 | kubectl -n $NAMESPACE create secret generic basicauth-creds --from-file=basicauth_creds 78 | if [ $? -ne 0 ] 79 | then 80 | echo "Failed to create basicauth-creds secret." 81 | exit 1 82 | fi 83 | rm basicauth_creds 84 | else 85 | # Assign manifest folder 86 | MANIFEST_FOLDER="oauth2" 87 | fi 88 | 89 | # Create a config map for nginx-conf 90 | kubectl -n $NAMESPACE create configmap nginx-conf --from-file=$MANIFEST_FOLDER/nginx.conf 91 | if [ $? -ne 0 ] 92 | then 93 | echo "Failed to create nginx-conf config map." 94 | exit 1 95 | fi 96 | 97 | # If ssl is set to true, create a secret to store ssl key and cert. 98 | if [ $TLS_FLAG == "true" ] 99 | then 100 | kubectl -n $NAMESPACE create secret tls cnsmanager-tls --key $TLS_KEY --cert $TLS_CERT 101 | if [ $? -ne 0 ] 102 | then 103 | echo "Failed to create cnsmanager-tls secret." 104 | exit 1 105 | fi 106 | fi 107 | 108 | # Create a config map for the CNS manager swagger API spec. 109 | sed "s/%CNS_MANAGER_ENDPOINT%/$CNS_MANAGER_ENDPOINT/g" swagger-template.yaml > swagger.yaml 110 | kubectl -n $NAMESPACE create configmap swagger-api --from-file=swagger.yaml 111 | if [ $? -ne 0 ] 112 | then 113 | echo "Failed to create swagger-api config map." 114 | exit 1 115 | fi 116 | rm swagger.yaml 117 | 118 | # Deploy CNS manager 119 | sed -e "s#%CNS_MANAGER_ENDPOINT%#$CNS_MANAGER_ENDPOINT#g" \ 120 | -e "s#%CNS_MANAGER_NAMESPACE%#$NAMESPACE#g" \ 121 | -e "s#%AUTH_TYPE%#$AUTH_MECHANISM#g" \ 122 | $MANIFEST_FOLDER/deploy-template.yaml > deploy.yaml 123 | kubectl -n $NAMESPACE apply -f deploy.yaml 124 | if [ $? -ne 0 ] 125 | then 126 | echo "Failed to deploy CNS manager." 127 | exit 1 128 | fi 129 | rm deploy.yaml 130 | -------------------------------------------------------------------------------- /docs/book/deployment/basicauth.md: -------------------------------------------------------------------------------- 1 | ## Deploying CNS Manager with basic auth 2 | 3 | Below are the steps to configure, deploy & run cns-manager on a vanilla Kubernetes cluster with basic auth. 4 | 5 | ### Prepare the config 6 | 1. Capture kubeconfig of the cluster in which CNS manager is being deployed in a file named `sv_kubeconfig`. 7 | Refer to sample config file provided under config folder. 8 | 9 | 2. Create a file named vc_creds.json and copy into it the credentials to your VC. 10 | Refer to sample config file provided under config folder. 11 | ``` 12 | { 13 | "vc": "", 14 | "user": "", 15 | "password": "" 16 | } 17 | ``` 18 | 19 | 3. Choose any of the worker nodes to act as an endpoint for CNS manager APIs. Let's call this worker node's IP as WORKER_NODE_IP. 20 | The nodePort is set to 30008 by default in *deploy-template.yaml*. 21 | So the CNS manager endpoint would be :30008. 22 | 23 | Note : If your cloud provider supports a load balancer, you can choose to deploy a load balancer service instead. In that case, the CNS manager endpoint would be :30008 24 | 25 | Also if you need to change kubeconfig or VC creds after the deployment script has run, then you can either: 26 | a. Recreate the secrets `sv-kubeconfig` & `vc-creds` created from these files and restart the cns- manager deployment, OR 27 | b. Delete the namespace and run the deployment script again. 28 | 29 | ### Deploy the application 30 | * After preparing the config, use the following command to deploy CNS manager on the cluster. 31 | 32 | ``` 33 | > cd deploy 34 | > ./deploy.sh <(tls flag)true|false> 35 | ``` 36 | 37 | Note: The basicauth username and password used here is what admin chooses while deploying this application, and it may not be same as the vCenter username and password. 38 | 39 | For example 40 | ``` 41 | > ./deploy.sh cns-manager ../config/sv_kubeconfig ../config/vc_creds.json :30008 basicauth false 'Administrator' 'Admin123@' 42 | ``` 43 | Please ensure to use single quotes around username and password. This will ensure that any special characters in username or password are escaped. 44 | 45 | * The deployment script will create a bunch of Kubernetes objects. The sample output should look like as described below. 46 | Once the deployment is successful, verify that CNS manager pod is running in the namespace. 47 | ``` 48 | > ./deploy.sh cns-manager ../config/sv_kubeconfig ../config/vc_creds.json 10.184.71.61:30008 basicauth false 'Administrator' 'Admin123@' 49 | 50 | namespace/cns-manager created 51 | secret/sv-kubeconfig created 52 | secret/vc-creds created 53 | secret/basicauth-creds created 54 | configmap/swagger-api created 55 | serviceaccount/cns-manager created 56 | rolebinding.rbac.authorization.k8s.io/cns-manager created 57 | customresourcedefinition.apiextensions.k8s.io/orphanvolumestats.cnsmanager.cns.vmware.com configured 58 | customresourcedefinition.apiextensions.k8s.io/volumemigrationjobs.cnsmanager.cn s.vmware.com configured 59 | customresourcedefinition.apiextensions.k8s.io/volumemigrationtasks.cnsmanager.c ns.vmware.com configured 60 | clusterrole.rbac.authorization.k8s.io/cns-manager configured 61 | clusterrolebinding.rbac.authorization.k8s.io/cns-manager-rolebinding unchanged 62 | configmap/cnsmanager-config created 63 | configmap/nginx-conf created 64 | deployment.apps/cns-manager created 65 | service/cns-manager created 66 | 67 | > kubectl get pods -n cns-manager 68 | NAME READY STATUS RESTARTS AGE 69 | cns-manager-6ff456dc97-nrj65 3/3 Running 0 54s 70 | ``` 71 | 72 | * After the deployment, the Swagger UI for invoking APIs can be accessed at /ui/ 73 | This will need basic auth credentials selected during deployment. 74 | 75 | Alternatively, the APIs can also be invoked using some other client like *curl*, with basicauth credentials passed in command line arguments. 76 | For instance: 77 | ``` 78 | curl -X 'GET' 'http://10.184.71.61:30008/1.0.0/datastoreresources?datacenter=VSAN- DC&datastore=vsanDatastore' -H 'accept: application/json' -u "Admistrator:Admin123@" 79 | ``` 80 | 81 | * Please note that the Swagger UI is accessible through URL /ui/ 82 | But the backend APIs are accessible with URL prefix /1.0.0/ -------------------------------------------------------------------------------- /CONTRIBUTING_CLA.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional documentation, the cloud-native-storage-self-service-manager project team welcomes contributions from the community. Before you start working with this project please read and sign our Contributor License Agreement (https://cla.vmware.com/cla/1/preview). If you wish to contribute code and you have not signed our Contributor Licence Agreement (CLA), our bot will prompt you to do so when you open a Pull Request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq). 4 | 5 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution. 6 | 7 | ## Reporting Bugs, Features, and Enhancements 8 | 9 | We welcome you to use the GitHub issue tracker to report bugs or suggest features and enhancements. 10 | 11 | When filing an issue, please check existing open, or recently closed, issues to make sure someone else hasn't already 12 | reported the issue. 13 | 14 | Please try to include as much information as you can. Details like these are incredibly useful: 15 | 16 | * A reproducible test case or series of steps. 17 | * Any modifications you've made relevant to the bug. 18 | * Anything unusual about your environment or deployment. 19 | 20 | ## Contributing via Pull Requests 21 | 22 | Contributions via pull requests are appreciated. Before sending us a pull request, please ensure that: 23 | 24 | 1. You [open a discussion](https://github.com/vmware-samples/packer-examples-for-vsphere/discussions) to discuss any significant work with the maintainer(s). 25 | 1. You open an issue and link your pull request to the issue for context. 26 | 1. You are working against the latest source on the `main` branch. 27 | 1. You check existing open, and recently merged, pull requests to make sure someone else hasn't already addressed 28 | the problem. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 1. Modify the source; please focus on the **specific** change you are contributing. 34 | 1. Update the documentation, if required. 35 | 1. Commit to your fork using a clear commit messages. 36 | 1. Send us a pull request, answering any default questions in the pull request. 37 | 1. Pay attention to any automated failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | ### Contributor Flow 43 | 44 | This is a rough outline of what a contributor's workflow looks like: 45 | 46 | * Create a topic branch from where you want to base your work. 47 | * Make commits of logical units. 48 | * Make sure your commit messages are in the proper format. 49 | * Push your changes to a topic branch in your fork of the repository. 50 | * Submit a pull request. 51 | 52 | Example: 53 | 54 | ``` shell 55 | git remote add upstream https://github.com/vmware-samples/cloud-native-storage-self-service-manager.git 56 | git checkout -b feat/foo main 57 | git commit -s -a 58 | git push origin feat/foo 59 | ``` 60 | 61 | ### Staying In Sync With Upstream 62 | 63 | When your branch gets out of sync with the vmware-samples/main branch, use the following to update: 64 | 65 | ``` shell 66 | git checkout feat/foo 67 | git fetch upstream 68 | git rebase upstream/main 69 | ``` 70 | 71 | ### Updating Pull Requests 72 | 73 | If your pull request fails to pass or needs changes based on code review, you'll most likely want to squash these 74 | changes into existing commits. 75 | 76 | If your pull request contains a single commit or your changes are related to the most recent commit, you can simply 77 | amend the commit. 78 | 79 | ``` shell 80 | git add . 81 | git commit --amend 82 | git push -f origin feat/foo 83 | ``` 84 | 85 | If you need to squash changes into an earlier commit, you can use: 86 | 87 | ``` shell 88 | git add . 89 | git commit --fixup 90 | git rebase -i --autosquash main 91 | git push --force-with-lease origin feat/foo 92 | ``` 93 | 94 | Be sure to add a comment to the pull request indicating your new changes are ready to review, as GitHub does not 95 | generate a notification when you `git push`. 96 | 97 | ## Finding Contributions to Work On 98 | 99 | Looking at the existing issues is a great way to find something to contribute on. If you have an idea you'd like to 100 | discuss, [open a discussion](https://github.com/vmware-samples/cloud-native-storage-self-service-manager/discussions). 101 | 102 | ## Licensing 103 | 104 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 105 | 106 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) 107 | for larger changes. 108 | -------------------------------------------------------------------------------- /docs/book/features/storage_vmotion.md: -------------------------------------------------------------------------------- 1 | ## Storage vMotion for CNS volumes 2 | This feature involves moving volumes from one datastore to another. It supports both offline and online migration of CNS volumes. Also to complement this functionality, there is a set of APIs provided that includes: 3 | 1. Querying resources on a datastore. 4 | 2. Suspend CNS volume provisioning on a datastore. 5 | 3. Migrate volumes across datastores. 6 | 4. Resume CNS volume provisioning on a datastore. 7 | 8 | ### Prerequisites 9 | * vSphere 7.0.u3+ 10 | * CSI driver version 2.5+ 11 | * Source and target datastores should be mounted on at least a single common ESX host. 12 | 13 | ### Functionality Not Supported 14 | * The following are not supported with current release of CNS manager, hence please ensure that storage vMotion be not performed for: 15 | 1. Migration of non-CNS volumes 16 | 2. Migration of file volumes 17 | 18 | * While migration of VMs is not possible with this tool yet, the worker node VMs can be migrated using vMotion from VCenter UI. This should be done after all the volumes attached to a worker VM have been migrated to destination datastore. 19 | 20 | #### Caveats 21 | * If vcp-to-csi migrated volumes are relocated to a different datastore, then it won't be possible to switch back to in-tree VCP plug-in as the volumes will lose mapping with VCP. 22 | * While the migration of a volume attached to a node is in progress, any operations attaching/detaching volumes to this node will be queued(volume relocation acquires a VM lock). 23 | What this implies that if a pod scheduled on this node gets killed, or any other pod is trying to attach a volume to this node, they might not come up until the volume migration is finished and VM lock is released, implying a possibility of some downtime for the application. 24 | But Kubernetes will continue to retry the attach/detach operations which should succeed after the volume has been relocated. 25 | * If volumes are attached to a VM, their migration to a vVol datastore is not supported. 26 | 27 | ### Concurrency Limits & Scale 28 | On CNS manager application level, there can be 8 volume migrations that can be invoked in parallel across all clusters. 29 | And on vCenter level, the limits for simultaneous migrations can be be derived from this document - https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vcenterhost.doc/GUID-25EA5833-03B5-4EDD-A167-87578B8009B3.html 30 | 31 | This translates to concurrent migration limits to be 1 per VM(for attached volumes), 2 per host & 8 per datastore. If there are parallel FCD migrations invoked beyond these limits, they will be queued based on the limits for each type of resource. 32 | 33 | ### Using the APIs 34 | 1. **Identifying resources on a datastore** 35 | We're providing a helper API for the user to know the volume details on a datastore level. This API can be used to know what are the resources on a datastore that need to be moved if a datastore needs to be decommissioned. 36 | 37 | API Details: 38 | */datastoreresources* 39 | This gives details of the resources on the datastore. The resources can include 40 | 41 | a. List of CNS volumes 42 | b. List of non-container volumes(created out of band) 43 | c. List of VMs on the datastore. 44 | 45 | 2. **Suspend volume provisioning on a datastore** 46 | Before migrating volumes from a datastore, we don't want to create any new volumes on that datastore. So it will be better to put the datastore in a mode that suspends volume provisioning from CSI. 47 | This can be achieved by invoking `SuspendVolumeProvisioning` API. This needs all Kubernetes clusters registered with CNS manager to be upgraded to CSI driver version 2.5+ (with `cnsmgr-suspend-create-volume` feature state switch enabled), otherwise the operation will fail. 48 | 49 | API Details: 50 | */suspendvolumeprovisioning* 51 | 52 | For **file volumes**, the provisioning suspension takes a few minutes to come into effect. This time interval depends on the value for the parameter `csi-auth-check-intervalinmin` set in vsphere csi configuration file while setting up vsphere-csi-driver. The default time interval is 5 minutes. 53 | For **block volumes**, provisioning is suspended immediately. 54 | 55 | 3. **Migrate Volumes** 56 | Once the list of volumes to be migrated is identified either from the VC UI or the helper API, the fcdIds can be inputted to the `MigrateVolumes` API along with target datastore. 57 | 58 | API Details: 59 | */migratevolumes* 60 | 61 | This API is an asynchronous API and will return a job Id that's migrating volumes in the background. The status of this job can be queried anytime using `getjobstatus` API. 62 | 63 | 4. **Checking status of the volume migration job** 64 | Two APIs are being offered to check the status of the volume migration job (A job corresponds to a single invocation of the API, that can have multiple tasks corresponding to each volume Id passed in the API request) 65 | 66 | * */getjobstatus* 67 | This returns the current status of the job. A job can be in one of the following status: 68 | 69 | * Queued - Job has been created but hasn't started processing. 70 | * Running - Job is currently executing. 71 | * Success - Job has completed successfully with all tasks succeeding. 72 | * Error - Job ran but some or all of its tasks failed. 73 | 74 | 75 | * */waitforjob* 76 | This is a blocking API that waits for job to be successful or fail. 77 | Unlike `getjobstatus` API, this will wait for the job to finish before returning the job result response. -------------------------------------------------------------------------------- /docs/book/deployment/oauth2.md: -------------------------------------------------------------------------------- 1 | ## Deploying CNS Manager with OAuth2 2 | 3 | CNS Manager uses oauth2-proxy for providing authentication using OIDC providers such Gitlab, GitHub,Google and others). Please see the list of supported OIDC providers [here](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider) 4 | 5 | Below are the steps to configure, deploy & run cns-manager on a vanilla Kubernetes cluster with OAuth2. 6 | 7 | ### Prepare the config 8 | 1. Capture kubeconfig of the cluster in which CNS manager is being deployed in a file named `sv_kubeconfig`. 9 | Refer to sample config file provided under config folder. 10 | 11 | 2. Create a file named vc_creds.json and copy into it the credentials to your VC. 12 | Refer to sample config file provided under config folder. 13 | ``` 14 | { 15 | "vc": "", 16 | "user": "", 17 | "password": "" 18 | } 19 | ``` 20 | 21 | 3. Choose any of the worker nodes to act as an endpoint for CNS manager APIs. Let's call this worker node's IP as WORKER_NODE_IP. 22 | The nodePort is set to 30008 by default in *deploy-template.yaml*. 23 | So the CNS manager endpoint would be :30008. 24 | 25 | Note : If your cloud provider supports a load balancer, you can choose to deploy a load balancer service instead. In that case, the CNS manager endpoint would be :30008 26 | 27 | Also if you need to change kubeconfig or VC creds after the deployment script has run, then you can either: 28 | a. Recreate the secrets `sv-kubeconfig` & `vc-creds` created from these files and restart the cns- manager deployment, OR 29 | b. Delete the namespace and run the deployment script again. 30 | 31 | ### Register an OAuth application and Update configuration 32 | 1. Select a provider and register an OAuth application with it. 33 | Use `http://:30008/oauth2/callback` for Redirect URL. 34 | 35 | 2. In provided oauth2 [deployment yaml](../../../deploy/oauth2/deploy-template.yaml) that uses Gitlab as an example, update the provider, client-id & client-secret arguments with corresponding values from your registered OAuth application. 36 | Check [oauth2-proxy documentation](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/#gitlab-auth-provider) for configuration for each provider. 37 | 38 | ### Add authorized users for the application 39 | * Add users who can invoke the APIs by editing the application config. In provided oauth2 [deployment yaml](../../../deploy/oauth2/deploy-template.yaml), update the `allowedUsers` field in `cnsmanager-config` configmap definition. 40 | It accepts a list of comma-separated values. 41 | 42 | 43 | 44 | ### Deploy the application 45 | * Use the following command to deploy CNS manager on the cluster. 46 | 47 | ``` 48 | > cd deploy 49 | > ./deploy.sh <(tls flag)true|false> 50 | ``` 51 | 52 | For example 53 | ``` 54 | > ./deploy.sh cns-manager ../config/sv_kubeconfig ../config/vc_creds.json :30008 oauth2 false 55 | ``` 56 | 57 | * The deployment script will create a bunch of Kubernetes objects. The sample output should look like as described below. 58 | Once the deployment is successful, verify that CNS manager pod is running in the namespace. 59 | ``` 60 | > ./deploy.sh cns-manager ../config/sv_kubeconfig ../config/vc_creds.json 10.184.71.61:30008 oauth2 false 61 | 62 | namespace/cns-manager created 63 | secret/sv-kubeconfig created 64 | secret/vc-creds created 65 | secret/basicauth-creds created 66 | configmap/swagger-api created 67 | serviceaccount/cns-manager created 68 | rolebinding.rbac.authorization.k8s.io/cns-manager created 69 | customresourcedefinition.apiextensions.k8s.io/orphanvolumestats.cnsmanager.cns.vmware.com configured 70 | customresourcedefinition.apiextensions.k8s.io/volumemigrationjobs.cnsmanager.cn s.vmware.com configured 71 | customresourcedefinition.apiextensions.k8s.io/volumemigrationtasks.cnsmanager.c ns.vmware.com configured 72 | clusterrole.rbac.authorization.k8s.io/cns-manager configured 73 | clusterrolebinding.rbac.authorization.k8s.io/cns-manager-rolebinding unchanged 74 | configmap/cnsmanager-config created 75 | configmap/nginx-conf created 76 | deployment.apps/cns-manager created 77 | service/cns-manager created 78 | 79 | > kubectl get pods -n cns-manager 80 | NAME READY STATUS RESTARTS AGE 81 | cns-manager-6ff456dc97-nrj65 4/4 Running 0 37s 82 | ``` 83 | 84 | * After the deployment, the Swagger UI for invoking APIs can be accessed at /ui/ 85 | This will redirect to the OIDC provider for authentication. 86 | 87 | After authentication, the APIs can be directly invoked from Swagger UI. 88 | 89 | 90 | ## Day 2 Operations 91 | * **Authorize additional users to invoke APIs** 92 | 93 | If you have deployed CNS manager with oAuth2 authentication and you want to 94 | add more authorized users to invoke the APIs , it can be achieved by updating the `allowedUsers` field in `cnsmanager-config` configmap definition. 95 | It accepts a list of comma-separated values. 96 | Restart the deployment after updating the configmap 97 | 98 | ``` 99 | > kubectl edit configmap cnsmanager-config -n 100 | > kubectl rollout restart deployment cns-manager -n 101 | 102 | ``` -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in cloud-native-storage-self-service-manager project and our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at oss-coc@vmware.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /deploy/oauth2/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | # This is the location where Nginx will store the PID file. 6 | # The default location where Nginx stores the PID file is not accessible in certain flavors of K8s like OCP. 7 | pid /tmp/nginx.pid; 8 | 9 | http { 10 | server { 11 | listen 8081; 12 | #Use below instead to configure an https server 13 | #listen 443 ssl; 14 | server_name cnsmanager.cns.vmware.com; 15 | 16 | #Uncomment this to enable ssl/tls communication over https. 17 | #ssl_certificate /etc/nginx/tls/tls.crt; 18 | #ssl_certificate_key /etc/nginx/tls/tls.key; 19 | 20 | location /ui { 21 | auth_request /oauth2/auth; 22 | error_page 401 = /oauth2/sign_in; 23 | 24 | # pass information via X-User and X-Forwarded-Email headers to backend, 25 | # requires running with --set-xauthrequest flag 26 | auth_request_set $user $upstream_http_x_auth_request_user; 27 | auth_request_set $email $upstream_http_x_auth_request_email; 28 | proxy_set_header X-User $user; 29 | proxy_set_header X-Forwarded-Email $email; 30 | 31 | # if you enabled --cookie-refresh, this is needed for it to work with auth_request 32 | auth_request_set $auth_cookie $upstream_http_set_cookie; 33 | add_header Set-Cookie $auth_cookie; 34 | 35 | proxy_pass http://localhost:8080; 36 | proxy_set_header X-Forwarded-For $remote_addr; 37 | proxy_set_header Host $http_host; 38 | } 39 | 40 | location /1.0.0 { 41 | auth_request /oauth2/auth; 42 | 43 | # pass information via X-User and X-Forwarded-Email headers to backend, 44 | # requires running with --set-xauthrequest flag 45 | auth_request_set $user $upstream_http_x_auth_request_user; 46 | auth_request_set $email $upstream_http_x_auth_request_email; 47 | proxy_set_header X-User $user; 48 | proxy_set_header X-Forwarded-Email $email; 49 | 50 | # if you enabled --cookie-refresh, this is needed for it to work with auth_request 51 | auth_request_set $auth_cookie $upstream_http_set_cookie; 52 | add_header Set-Cookie $auth_cookie; 53 | 54 | proxy_pass http://localhost:8100; 55 | proxy_set_header Host $host; 56 | proxy_buffering on; 57 | 58 | proxy_read_timeout 300; 59 | proxy_connect_timeout 300; 60 | proxy_send_timeout 300; 61 | } 62 | 63 | #Since /waitforjob is supposed to be a blocking API call, set a long timeout of 1d 64 | location /1.0.0/waitforjob { 65 | auth_request /oauth2/auth; 66 | 67 | # pass information via X-User and X-Forwarded-Email headers to backend, 68 | # requires running with --set-xauthrequest flag 69 | auth_request_set $user $upstream_http_x_auth_request_user; 70 | auth_request_set $email $upstream_http_x_auth_request_email; 71 | proxy_set_header X-User $user; 72 | proxy_set_header X-Forwarded-Email $email; 73 | 74 | # if you enabled --cookie-refresh, this is needed for it to work with auth_request 75 | auth_request_set $auth_cookie $upstream_http_set_cookie; 76 | add_header Set-Cookie $auth_cookie; 77 | 78 | proxy_pass http://localhost:8100; 79 | proxy_set_header Host $host; 80 | proxy_buffering on; 81 | 82 | proxy_read_timeout 1d; 83 | proxy_connect_timeout 1d; 84 | proxy_send_timeout 1d; 85 | } 86 | 87 | # Since DELETE /orphanvolumes could be a long running operation, 88 | # set a timeout of 30m for /orphanvolumes endpoints 89 | location /1.0.0/orphanvolumes { 90 | auth_request /oauth2/auth; 91 | 92 | # pass information via X-User and X-Forwarded-Email headers to backend, 93 | # requires running with --set-xauthrequest flag 94 | auth_request_set $user $upstream_http_x_auth_request_user; 95 | auth_request_set $email $upstream_http_x_auth_request_email; 96 | proxy_set_header X-User $user; 97 | proxy_set_header X-Forwarded-Email $email; 98 | 99 | # if you enabled --cookie-refresh, this is needed for it to work with auth_request 100 | auth_request_set $auth_cookie $upstream_http_set_cookie; 101 | add_header Set-Cookie $auth_cookie; 102 | 103 | proxy_pass http://localhost:8100; 104 | proxy_set_header Host $host; 105 | proxy_buffering on; 106 | 107 | proxy_read_timeout 30m; 108 | } 109 | 110 | location /oauth2/ { 111 | proxy_pass http://127.0.0.1:4180; 112 | proxy_set_header Host $host; 113 | proxy_set_header X-Real-IP $remote_addr; 114 | proxy_set_header X-Scheme $scheme; 115 | proxy_set_header X-Auth-Request-Redirect $request_uri; 116 | # or, if you are handling multiple domains: 117 | # proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; 118 | } 119 | 120 | location = /oauth2/auth { 121 | proxy_pass http://127.0.0.1:4180; 122 | proxy_set_header Host $host; 123 | proxy_set_header X-Real-IP $remote_addr; 124 | proxy_set_header X-Scheme $scheme; 125 | # nginx auth_request includes headers but not body 126 | proxy_set_header Content-Length ""; 127 | proxy_pass_request_body off; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /scripts/get-kubeconfig.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -lt 2 ] 4 | then 5 | echo "Usage: ./get-kubeconfig.sh " 6 | exit 1 7 | fi 8 | 9 | if [ $# -eq 3 ] 10 | then 11 | echo "Invalid input params. Server URL is a mandatory input param if K8s context is provided." 12 | exit 1 13 | fi 14 | 15 | KUBECONFIG_FILE_PATH=$1 16 | OUTPUT_FILE=$2 17 | CONTEXT=$3 18 | SERVER_URL=$4 19 | 20 | export KUBECONFIG=$KUBECONFIG_FILE_PATH 21 | 22 | # If context is provided, set it 23 | if [ -n "$CONTEXT" ] 24 | then 25 | kubectl config use-context $CONTEXT 26 | if [ $? -ne 0 ] 27 | then 28 | echo "Error occurred in setting context" 29 | exit 1 30 | fi 31 | 32 | fi 33 | 34 | # This clean up function is called to clean up all resources that were created as part of this script. 35 | clean_up() 36 | { 37 | kubectl delete sa cnsmanager-sa > /dev/null 2>&1 38 | kubectl delete ClusterRole cnsmanager-sa-role > /dev/null 2>&1 39 | kubectl delete ClusterRoleBinding cnsmanager-sa-rb > /dev/null 2>&1 40 | kubectl config delete-user cnsmanager-sa > /dev/null 2>&1 41 | rm -f cnsmanagerrbac.yaml > /dev/null 2>&1 42 | rm -f cnsmanagerkubeconfig > /dev/null 2>&1 43 | rm -f secret_output > /dev/null 2>&1 44 | rm cnsmanagerkubeconfig.bak > /dev/null 2>&1 45 | } 46 | 47 | # Clean up env before proceeding 48 | clean_up 49 | 50 | echo "Starting creation of kubeconfig..." 51 | 52 | # Create service account for kubeconfig 53 | kubectl create sa cnsmanager-sa 54 | if [ $? -ne 0 ] 55 | then 56 | echo "Failed to create service account. Cleaning up resources before exiting." 57 | clean_up 58 | exit 1 59 | fi 60 | 61 | token_secretname=$(kubectl get secret 2> /dev/null | grep "cnsmanager-sa-token" | awk '{print $1}') 62 | 63 | # Contents of token secret if required to be created explicitly 64 | cat < cnsmanager-token-secret.yaml 65 | apiVersion: v1 66 | kind: Secret 67 | metadata: 68 | name: cnsmgr-sa-token 69 | annotations: 70 | kubernetes.io/service-account.name: cnsmanager-sa 71 | type: kubernetes.io/service-account-token 72 | EOF 73 | 74 | # If token secret is not autogenerated, create it 75 | if [ -z "$token_secretname" ] 76 | then 77 | token_secretname="cnsmgr-sa-token" 78 | # Create token secret for cnsmanager-sa 79 | kubectl apply -f cnsmanager-token-secret.yaml 80 | if [ $? -ne 0 ] 81 | then 82 | echo "Failed to create token secret for service account. Cleaning up resources before exiting." 83 | clean_up 84 | exit 1 85 | fi 86 | fi 87 | 88 | sleep 3 89 | 90 | # Get the token secret created for CNS manager SA 91 | kubectl get secret $token_secretname -oyaml > secret_output 92 | if [ $? -ne 0 ] 93 | then 94 | echo "Failed to find token secret for cnsmanager service account. Cleaning up resources before exiting." 95 | clean_up 96 | exit 1 97 | fi 98 | 99 | token=$(cat secret_output | grep "token:" | awk -F ' ' '{print $2}' | base64 -d) 100 | 101 | # Set config for cns manager SA 102 | kubectl config set-credentials cnsmanager-sa --token=$token 103 | if [ $? -ne 0 ] 104 | then 105 | echo "Failed to set credentials in config. Cleaning up resources before exiting" 106 | clean_up 107 | exit 1 108 | fi 109 | 110 | # Extract values needed to contruct canmanager kubeconfig 111 | clusterAuthData=$(cat secret_output | grep "ca.crt:" | awk -F ' ' '{print $2}') 112 | 113 | # If server URL was provided in input, we don't need to extract it from kubeconfig file 114 | if [ -z $SERVER_URL ] 115 | then 116 | 117 | num_of_clusters=$(cat $KUBECONFIG_FILE_PATH | grep -c "server:") 118 | 119 | if [ $num_of_clusters -ne 1 ] 120 | then 121 | echo "Invalid configuration provided. If multiple clusters are concerned, provide the context and server URL also in input parameters." 122 | clean_up 123 | exit 1 124 | fi 125 | 126 | serverUrl=$(cat $KUBECONFIG_FILE_PATH | grep "server:" | awk -F ' ' '{print $2}') 127 | else 128 | serverUrl=$SERVER_URL 129 | fi 130 | 131 | cat < cnsmanagerkubeconfig 132 | apiVersion: v1 133 | kind: Config 134 | clusters: 135 | - cluster: 136 | certificate-authority-data: clusterAuthDataPlaceholder 137 | server: serverUrlPlaceholder 138 | name: cnsmgr-cluster 139 | contexts: 140 | - context: 141 | cluster: cnsmgr-cluster 142 | user: cnsmanager-sa 143 | name: cnsmanager-sa 144 | current-context: cnsmanager-sa 145 | users: 146 | - name: cnsmanager-sa 147 | user: 148 | token: tokenPlaceholder 149 | EOF 150 | 151 | sed -i'.bak' -e "s~clusterAuthDataPlaceholder~$clusterAuthData~g" cnsmanagerkubeconfig 152 | sed -i'.bak' -r "s~serverUrlPlaceholder~$serverUrl~g" cnsmanagerkubeconfig 153 | sed -i'.bak' -e "s/tokenPlaceholder/$token/g" cnsmanagerkubeconfig 154 | 155 | cat < cnsmanagerrbac.yaml 156 | --- 157 | apiVersion: rbac.authorization.k8s.io/v1 158 | kind: ClusterRole 159 | metadata: 160 | name: cnsmanager-sa-role 161 | rules: 162 | - apiGroups: ["rbac.authorization.k8s.io"] 163 | resources: ["clusterroles"] 164 | verbs: ["get", "list", "update", "escalate", "patch", "delete"] 165 | - apiGroups: [""] 166 | resources: ["persistentvolumeclaims"] 167 | verbs: ["get", "list"] 168 | - apiGroups: [""] 169 | resources: ["persistentvolumes"] 170 | verbs: ["get", "list"] 171 | - apiGroups: [""] 172 | resources: ["nodes"] 173 | verbs: ["get", "list"] 174 | - apiGroups: [""] 175 | resources: ["secrets","configmaps"] 176 | verbs: ["get", "list"] 177 | - apiGroups: ["cns.vmware.com"] 178 | resources: ["cnsvspherevolumemigrations"] 179 | verbs: ["get", "list"] 180 | - apiGroups: ["cns.vmware.com"] 181 | resources: ["csinodetopologies"] 182 | verbs: ["list"] 183 | --- 184 | apiVersion: rbac.authorization.k8s.io/v1 185 | kind: ClusterRoleBinding 186 | metadata: 187 | name: cnsmanager-sa-rb 188 | roleRef: 189 | apiGroup: rbac.authorization.k8s.io 190 | kind: ClusterRole 191 | name: cnsmanager-sa-role 192 | subjects: 193 | - kind: ServiceAccount 194 | name: cnsmanager-sa 195 | namespace: default 196 | EOF 197 | 198 | # Apply RBAC rules 199 | kubectl create -f cnsmanagerrbac.yaml 200 | if [ $? -ne 0 ] 201 | then 202 | echo "Failed to create RBAC rules. Cleaning up resources before exiting" 203 | clean_up 204 | exit 1 205 | fi 206 | 207 | echo "\n" 208 | cat cnsmanagerkubeconfig > $OUTPUT_FILE 209 | echo "Generated kubeconfig stored in output file $OUTPUT_FILE" 210 | echo '\n' 211 | 212 | rm cnsmanagerrbac.yaml 213 | rm cnsmanagerkubeconfig 214 | rm secret_output 215 | rm cnsmanager-token-secret.yaml 216 | rm cnsmanagerkubeconfig.bak 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cloud-native-storage-self-service-manager 2 | 3 | CNS Manager is a diagnostic and self-service tool that helps detect and auto-remediate some of the known issues in storage control plane in vCenter. It also provides certain table stake features, such as svMotion and datastore decomission to complement the Cloud Native Storage solution offered in vCenter. 4 | CNS Manager exposes APIs that can be invoked by authorized users to detect issues. 5 | 6 | 7 | This repository provides artifacts for deploying CNS manager in vanilla Kubernetes cluster, as well as the client sdk to invoke its endpoints. 8 | 9 | ## Deploying cns-manager 10 | CNS manager needs to be deployed in one of the Kubernetes clusters in the vCenter. 11 | If there are multiple Kubernetes clusters in a vCenter, it's recommended that it be deployed in a dedicated admin-managed cluster, but it's not a must. However, the admin should be responsible to secure the Kubernetes cluster where CNS manager is deployed since it will have credentials to vCenter and the Kubernetes cluster. 12 | Also if you want CNS manager to be highly available, deploy it on a Kubernetes cluster that's highly available itself. 13 | 14 | Note : To deploy CNS manager from this repo, you can clone it on your machine and then set kubeconfig to point to the remote Kubernetes cluster where CNS manager needs to be deployed. Then follow the instructions for deployment. 15 | 16 | The deployment is supported with two authentication mechanisms to limit who can access CNS manager APIs: 17 | 1. Basic Auth - The CNS manager admin can choose fixed credentials at the time of deployment. This auth mechanism is less secure than OAuth2 to be used in Production. Nevertheless, it can be used for a quick deployment to test the application and in air-gapped environments where the vCenter is not connected to the internet. 18 | See these [instructions](docs/book/deployment/basicauth.md) for basic auth deployment. 19 | 20 | 2. OAuth2 - With OAuth2, the authentication is delegated to an OIDC provider such as Gitlab, Github,Google etc. It does require creating an OAuth application on the OIDC provider before deploying CNS manager. 21 | See these [instructions](docs/book/deployment/oauth2.md) for OAuth2 deployment. 22 | 23 | ## Enabling TLS for your deployment 24 | You can enable TLS for your CNS Manager deployment with a few tweaks, so that the communication is encrypted between client(a browser, for instance) and the application. 25 | See these [deployment changes](docs/book/deployment/tls-enable.md) to enable TLS on CNS Manager. It can be done for both basicauth & OAuth2 deployments, and assumes you have the TLS key and certificate generated. 26 | 27 | ## Register Kubernetes clusters before you start! 28 | CNS manager relies on communicating with Kubernetes clusters for several functionalities it offers. It is therefore a pre-requisite to register all Kubernetes clusters in vCenter with CNS manager. 29 | 30 | Note: CNS manager can support upto 32 Kubernetes clusters per vCenter. Please see [supported scale](docs/book/supported_scale.md) for any recommended configurations for CNS manager. 31 | 32 | The following section explains how to register a Kubernetes cluster with CNS manager. These steps are applicable to all Kubernetes clusters in the vCenter. 33 | 34 | **1. Generate a kubeconfig with minimal privileges for CNS manager:** 35 | * The provided script `scripts/get-kubeconfig.sh` generates a kubeconfig for CNS manager with minimal privileges required for its functioning. But if you're fine with providing admin kubeconfig for the cluster to be registered, you can skip kubeconfig generation part mentioned below and directly jump to cluster registration part. 36 | 37 | Note : The script may not work on all Kubernetes distributions if they don't adhere to the [recommended steps](https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-A1982536-F741-4614-A6F2-ADEE21AA4588.html) for deploying vSphere CSI driver. 38 | 39 | * The script takes 2 mandatory input parameters. First is the path to the cluster's kubeconfig file and the second is the name of the file where the generated kubeconfig file with minimal privileges should be stored. Here is how you can run the script: 40 | ``` 41 | ./get-kubeconfig.sh 42 | ``` 43 | * If you have a combined kubeconfig with multiple contexts defined to access multiple servers, you will also need to provide context-name and server URL to access the desired Kubernetes cluster. 44 | Example: 45 | ``` 46 | ./get-kubeconfig.sh 47 | ``` 48 | * The script will output the newly generated kubeconfig in the provided output_file_name. 49 | 50 | **2. Register cluster with CNS manager using kubeconfig**: 51 | * Invoke `/registercluster` API on CNS manager by uploading the kubeconfig file. You may also modify other input parameters for the API based on your cluster configuration. 52 | The API can also be invoked from command line. Here is an example: 53 | ``` 54 | curl -X 'POST' "http://CNS-MANAGER-ENDPOINT/1.0.0/registercluster?csiDriverSecretName=vsphere-config-secret&csiDriverSecretNamespace=vmware-system-csi" -H 'accept: application/json' -H 'Content-Type: multipart/form-data' -F 'clusterKubeConfigFile=@output_file_name' -u "Admistrator:Admin123@" 55 | ``` 56 | * Once the cluster is registered, you may delete this file from the machine. 57 | 58 | **Note**: If a registered cluster later gets decommissioned or deleted from the vCenter, don't forget to deregister it from CNS manager as well. This will ensure a smooth execution of functionalities offered through CNS manager. 59 | 60 | ## Upgrading cns-manager 61 | See the [upgrade instructions](docs/book/deployment/upgrade.md) if you're upgrading previously deployed cns-manager instance to a newer release. 62 | ## Functionalities currently offered through cns-manager 63 | 64 | * **Storage vMotion for CNS volumes** 65 | This feature allows migrating volumes from one datastore to another. Read [here](docs/book/features/storage_vmotion.md) for more details about this feature. 66 | 67 | * **Orphan volumes detection & deletion** 68 | This feature allows detecting/deleting orphan volumes that are not being used in any of the registered Kubernetes clusters on the vCenter. Read [here](docs/book/features/orphan_volumes.md) for more details about this feature. 69 | 70 | * **Orphan snapshots detection & deletion** 71 | This feature allows detecting/deleting orphan snapshots that are not being used in any of the registered Kubernetes clusters on the vCenter. Read [here](docs/book/features/orphan_snapshots.md) for more details about this feature. -------------------------------------------------------------------------------- /docs/book/features/orphan_volumes.md: -------------------------------------------------------------------------------- 1 | ## Orphan volumes detection and clean-up 2 | Orphan volumes are vSphere volumes that are present on a vSphere datastore but there is no corresponding PersistentVolume in Kubernetes clusters on the vCenter. 3 | 4 | Orphan volumes are often created when CNS solution creates more than one vSphere volume for a Persistent Volume in the Kubernetes cluster. This could occur when the vCenter components are slow, storage is slow, vCenter service restarts, connectivity issues between vCenter and ESXi hosts etc. Since these orphan volumes occupy space in the datastore and are not really used in Kubernetes, it's useful to identify and cleanup orphan volumes periodically. 5 | 6 | This functionality provides a set of APIs to detect and delete orphan volumes on-demand, and also provide an option to turn on automatic deletion periodically. 7 | 8 | ### What qualifies as an orphan volume ? 9 | A volume qualifies as an orphan volume if it meets all the conditions listed below: 10 | 11 | 1) A PersistentVolume in any of the registered kubernetes clusters is not using the volume. 12 | 2) The volume was dynamically provisioned using vSphere CSI driver. 13 | 3) The volume is no longer classified as a container volume in vCenter. 14 | 4) Volume exists for more than 50 minutes(or `orphan-volume-detection-interval-mins` value configured in `cnsmanager-config` configmap) at the time of orphan volumes detection. 15 | [**Note:** The worst case time for a volume to be detected as an orphan after its creation is twice `orphan-volume-detection-interval-mins` (i.e. 100 minutes by default).] 16 | 17 | ### Which orphan volumes are skipped from detection/deletion ? 18 | Some volumes, even if a kubernetes PersistentVolume doesn't map to them, will not be considered during orphan volume detection/deletion. These include: 19 | * Volumes created out of band (not using vSphere CSI driver). 20 | * Statically provisioned CNS volume whose name doesn't start with `pvc-`. 21 | * File volumes. 22 | * Orphan volumes that have snapshots will be detected as orphans, but they cannot be deleted using Orphan volume delete API. 23 | 24 | ### A reminder to register all Kubernetes clusters! 25 | Before you start using orphan volume functionality, it's imperative that you [register all the Kubernetes clusters](../../../README.md#register-kubernetes-clusters-before-you-start) in vCenter with CNS Manager, so that orphan volumes are detected correctly. Any newly added kubernetes cluster should also be immediately registered. 26 | 27 | ### APIs provided 28 | 1. *GET /orphanvolumes* 29 | 30 | This API takes optional parameters, datacenter & list of datastores, and returns orphan volumes for them. 31 | - If datacenter is not specified, then it returns all orphan volumes in the vCenter (all datastores on all datacenters). 32 | - If only datacenter is specified, then it returns orphan volumes in all datastores in the datacenter. 33 | - If both datacenter & list of datastores is specified, it returns orphan volumes in specified datastores on the datacenter. 34 | 35 | This API also takes optional parameter includeDetails, it should be set to `true` to get a detailed dump of the orphan volumes. 36 | 37 | There could be hundreds of orphan volumes, so this API supports pagination as well. It takes optional parameters limit and offset. Limit specifies the maximum entries that should be displayed in single request, whereas offset specifies the starting point of the result set. 38 | 39 | Detection of orphan volumes can be a time-consuming operation if there are large number of orphans. Hence, it is performed asynchronously at regular intervals and the response is cached. This API returns list of orphan volumes computed in the last run, along with the next operation interval(`RetryAfterMinutes`). 40 | **Note:** For newly deployed CNS manager application when orphan volumes are being computed in the background for the first time, the API may return no orphan volumes. It should then be re-tried after `RetryAfterMinutes` to get orphan volumes computed in the latest run. 41 | 42 | 43 | 2. *DELETE /orphanvolumes* 44 | 45 | This API is used to delete orphan volumes. It also takes optional parameters, datacenter & list of datastores, and deletes orphan volumes from them. 46 | You can also specify whether you want to delete orphan volumes attached to a virtual machine or not. If set to `true`, the API will detach the orphan volume from the VM before deleting it. 47 | 48 | Please note if there are large number of orphan volumes in the system or if there's a slowness in vCenter networking/storage, the orphan deletion may take longer. If it takes longer than 30 minutes, the API client will time out. But be assured that orphan volumes are being deleted in the background which can also be verified by listing the orphans again using `GET /orphanvolumes` API. 49 | 50 | 51 | ### Setting up auto-monitoring for orphan volumes deletion 52 | There's also an option to automatically monitor and delete orphan volumes periodically. It's controlled using `auto-delete-ov` configuration in `cnsmanager-config` configmap. It can take one of the 3 values: 53 | a. `disable`: Orphan volumes will not be deleted automatically. 54 | b. `enable-only-detached-ov`: Delete only detached orphan volumes. 55 | c. `enable`: Delete all the detected orphan volumes(both attached & detached). 56 | 57 | By default, it is disabled. 58 | 59 | ## Known Issues 60 | 61 | ### False Positives Due to Non-CSI Created Volumes 62 | CNS Manager relies on naming conventions to identify orphan volumes, which can lead to false positives in certain cases. 63 | - If a volume is created by something other than the CSI driver but has a name prefixed with pvc-, it may be mistakenly classified as an orphan. 64 | - For example, First Class Disks (FCDs) backing Image Disks are created by directly invoking the VPXD endpoint and do not contain metadata in CNS or VSLM. 65 | - If any FCD backing an Image Disk has a name starting with pvc-, CNS Manager might incorrectly detect it as an orphan volume. 66 | ##### Recommendation: 67 | Before deleting orphan volumes, VI administrators should verify that no such FCDs exist to prevent accidental data loss. 68 | 69 | ### Undetectable Orphan Volumes from Unresponsive Clusters 70 | If a Kubernetes cluster becomes unresponsive, the CNS metadata for its volumes remains intact. As a result, CNS Manager is unable to detect these volumes as orphans, even if they are no longer in use. 71 | 72 | ⚠ Limitation: Currently, there is no automated way to identify and clean up such volumes within CNS Manager. 73 | 74 | ### Deletion Failure for Orphan Volumes with Snapshots 75 | If an orphan volume has snapshots attached, the delete operation will fail because CNS Manager does not currently account for volumes with existing snapshots. 76 | 77 | #### Workaround: 78 | Users must manually delete the snapshots before removing the orphan volume by invoking: 79 | 80 | - DELETE /volumes/{volumeId}/snapshots (to delete all snapshots for a volume) 81 | 82 | - DELETE /volumes/{volumeId}/snapshots/{snapshotId} (to delete a specific snapshot) 83 | 84 | #### 🚀 Planned Improvement: The detection algorithm will be enhanced to exclude such volumes in future updates. 85 | 86 | ### Slow Deletion on vCenter with Many Orphan Volumes 87 | On a vCenter with a large number of orphan volumes, the delete operation may take a long time to complete, potentially causing a timeout when invoking the endpoint. 88 | 89 | - Despite the timeout, CNS Manager continues deleting the volumes in the background. 90 | - Users may not receive immediate confirmation of successful deletion. 91 | #### Workaround: 92 | To track progress, periodically invoke the LIST orphan volumes endpoint to check the current state. 93 | 94 | #### 🚀 Planned Improvement: There are plans to convert this process into an asynchronous job to improve efficiency and avoid timeouts. 95 | -------------------------------------------------------------------------------- /client-sdk/go/examples/basicauth_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import ( 19 | "context" 20 | 21 | apiclient "cns.vmware.com/cns-manager/client" 22 | "github.com/antihax/optional" 23 | "github.com/go-logr/zapr" 24 | "go.uber.org/zap" 25 | "sigs.k8s.io/controller-runtime/pkg/log" 26 | ) 27 | 28 | func main() { 29 | ctx := context.TODO() 30 | devLog, _ := zap.NewDevelopment() 31 | logger := zapr.NewLogger(devLog) 32 | logger = logger.WithName("Main") 33 | ctx = log.IntoContext(ctx, logger) 34 | 35 | //Set BasicAuth credentials in ctx. 36 | ctx = context.WithValue(ctx, apiclient.ContextBasicAuth, apiclient.BasicAuth{ 37 | UserName: "Administrator", 38 | Password: "Admin123@", 39 | }) 40 | //Set API server basepath and create a client. 41 | cfg := &apiclient.Configuration{ 42 | BasePath: "http://10.185.227.74:30008/1.0.0", 43 | } 44 | client := apiclient.NewAPIClient(cfg) 45 | 46 | //================API Invocation Examples=========== 47 | 48 | //======Datastore Resources============ 49 | // datacenter := "VSAN-DC" 50 | // datastore := "vsanDatastore" 51 | // res, resp, err := client.DatastoreOperationsApi.GetDatastoreResources(ctx, datacenter, datastore) 52 | // if err != nil { 53 | // logger.Error(err, "failed to get datastore resources") 54 | // } 55 | 56 | // logger.Info("Result", "result", res) //This gives the result in json format 57 | // logger.Info("HTTP status", "status", resp.Status) 58 | //======================================================= 59 | 60 | //====== Migrate Volumes======== 61 | // datacenter := "VSAN-DC" 62 | // srcDatastore := "vsanDatastore" 63 | // targetDatastore := "agohil-nfs" 64 | // fcdIdsToMigrate := &apiclient.DatastoreOperationsApiMigrateVolumesOpts{ 65 | // FcdIdsToMigrate: optional.NewInterface([]string{"ae903e93-b589-4cda-a3af-e59ef4660325"}), 66 | // } 67 | 68 | // res, resp, err := client.DatastoreOperationsApi.MigrateVolumes(ctx, datacenter, srcDatastore, targetDatastore, fcdIdsToMigrate) 69 | // if err != nil { 70 | // logger.Error(err, "failed to migrate volumes") 71 | // } 72 | 73 | // logger.Info("Result", "result", res) //This gives the result in json format 74 | // logger.Info("HTTP status", "status", resp.Status) 75 | //======================================================= 76 | 77 | //====== Job status ======== 78 | // jobId := "volumemigrationjob-64248e96-7cf4-11ec-94b8-165555b18a9c" 79 | 80 | // res, resp, err := client.JobDetailsApi.GetJobStatus(ctx, jobId) 81 | // if err != nil { 82 | // logger.Error(err, "failed to get job status") 83 | // } 84 | 85 | // logger.Info("Result", "result", res) //This gives the result in json format 86 | // logger.Info("HTTP status", "status", resp.Status) 87 | //======================================================= 88 | 89 | //======Suspend Volume Provisioning on Datastore====== 90 | // datacenter := "VSAN-DC" 91 | // datastore := "local-0" 92 | // 93 | // res, resp, err := client.DatastoreOperationsApi.SuspendVolumeProvisioning(ctx, datacenter, datastore) 94 | // if err != nil { 95 | // logger.Error(err, "failed to suspend volume provisioning on datastore") 96 | // } 97 | // logger.Info("Result", "result", res) //This gives the result in json format 98 | // logger.Info("HTTP status", "status", resp.Status) 99 | //======================================================= 100 | 101 | //======Resume Volume Provisioning on Datastore====== 102 | // datacenter := "VSAN-DC" 103 | // datastore := "local-0" 104 | // 105 | // res, resp, err := client.DatastoreOperationsApi.ResumeVolumeProvisioning(ctx, datacenter, datastore) 106 | // if err != nil { 107 | // logger.Error(err, "failed to resume provisioing volumes on datastore") 108 | // } 109 | // logger.Info("Result", "result", res) //This gives the result in json format 110 | // logger.Info("HTTP status", "status", resp.Status) 111 | //======================================================= 112 | 113 | //======List orphan volumes====== 114 | includeDetails := true 115 | opts := &apiclient.OrphanVolumeApiOrphanVolumeListOpts{ 116 | Datacenter: optional.NewString("VSAN-DC"), 117 | Datastores: optional.NewString("vsanDatastore"), 118 | IncludeDetails: optional.NewBool(includeDetails), 119 | Limit: optional.NewInt32(50), 120 | Offset: optional.NewInt32(0), 121 | } 122 | 123 | res, resp, err := client.OrphanVolumeApi.OrphanVolumeList(ctx, opts) 124 | if err != nil { 125 | logger.Error(err, "failed to list orphan volumes") 126 | } 127 | logger.Info("Result", "result", res) //This gives the result in json format 128 | logger.Info("HTTP status", "status", resp.Status) 129 | //======================================================= 130 | 131 | //======Delete orphan volumes====== 132 | // deleteAttachedOrphans := false 133 | // opts := &apiclient.OrphanVolumeApiOrphanVolumeDeleteOpts{ 134 | // Datacenter: optional.NewString("VSAN-DC"), 135 | // Datastores: optional.NewString("vsanDatastore"), 136 | // } 137 | 138 | // res, resp, err := client.OrphanVolumeApi.OrphanVolumeDelete(ctx, deleteAttachedOrphans, opts) 139 | // if err != nil { 140 | // logger.Error(err, "failed to delete orphan volumes") 141 | // } 142 | // logger.Info("Result", "result", res) //This gives the result in json format 143 | // logger.Info("HTTP status", "status", resp.Status) 144 | //======================================================= 145 | 146 | //======List orphan snapshots====== 147 | // osopts := &apiclient.OrphanSnapshotApiOrphanSnapshotsListOpts{ 148 | // Datacenter: optional.NewString("VSAN-DC"), 149 | // Datastores: optional.NewString("vsanDatastore"), 150 | // SnapshotPrefix: optional.NewString("snapshot"), 151 | // Limit: optional.NewInt64(50), 152 | // Offset: optional.NewInt64(0), 153 | // } 154 | 155 | // osres, resp, err := client.OrphanSnapshotApi.OrphanSnapshotsList(ctx, osopts) 156 | // if err != nil { 157 | // logger.Error(err, "failed to list orphan snapshots") 158 | // } 159 | // logger.Info("Result", "result", osres) //This gives the result in json format 160 | // logger.Info("HTTP status", "status", resp.Status) 161 | //======================================================= 162 | 163 | //======Delete orphan snapshots====== 164 | // osdopts := &apiclient.OrphanSnapshotApiOrphanSnapshotsDeleteOpts{ 165 | // Datacenter: optional.NewString("VSAN-DC"), 166 | // Datastores: optional.NewString("vsanDatastore"), 167 | // SnapshotPrefix: optional.NewString("snapshot"), 168 | // } 169 | 170 | // osdres, resp, err := client.OrphanSnapshotApi.OrphanSnapshotsDelete(ctx, osdopts) 171 | // if err != nil { 172 | // logger.Error(err, "failed to delete orphan snapshots") 173 | // } 174 | // logger.Info("Result", "result", osdres) //This gives the result in json format 175 | // logger.Info("HTTP status", "status", resp.Status) 176 | //======================================================= 177 | 178 | //====== Job status ======== 179 | // jobId := "snapshotdeletionjob-94539b64-7cf4-11ec-94b8-165555b18754" 180 | 181 | // res, resp, err := client.JobDetailsApi.GetJobStatus(ctx, jobId) 182 | // if err != nil { 183 | // logger.Error(err, "failed to get job status snapshot deletion job") 184 | // } 185 | 186 | // logger.Info("Result", "result", res) //This gives the result in json format 187 | // logger.Info("HTTP status", "status", resp.Status) 188 | //======================================================= 189 | } 190 | -------------------------------------------------------------------------------- /client-sdk/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= 2 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 3 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 4 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 9 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 10 | github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= 11 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 12 | github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= 13 | github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= 14 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 15 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 16 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 17 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 18 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 19 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 20 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 21 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 22 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 23 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 24 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 25 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 26 | github.com/onsi/gomega v1.27.0 h1:QLidEla4bXUuZVFa4KX6JHCsuGgbi85LC/pCHrt/O08= 27 | github.com/onsi/gomega v1.27.0/go.mod h1:i189pavgK95OSIipFBa74gC2V4qrQuvjuyGEr3GmbXA= 28 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 29 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 30 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 31 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 32 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 33 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 34 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 35 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 36 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 37 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 38 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 39 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 40 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 41 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 42 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 43 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 44 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 45 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 46 | go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= 47 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 48 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 49 | go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= 50 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 51 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 52 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 53 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 54 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 55 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 56 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 57 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 58 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 59 | golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= 60 | golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= 61 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 63 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 64 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 65 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 66 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 67 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 68 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 69 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 70 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 71 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 72 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 73 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 74 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 75 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 76 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 77 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 78 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 81 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 82 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 83 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 84 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 85 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 87 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 88 | k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= 89 | k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= 90 | sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= 91 | sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= 92 | -------------------------------------------------------------------------------- /docs/book/features/orphan_snapshots.md: -------------------------------------------------------------------------------- 1 | ## Orphan snapshots detection and clean-up 2 | From vSphere CSI driver's perspective, orphan snapshots are vSphere snapshots that were initiated through the vSphere CSI driver but do not have a corresponding VolumeSnapshotContent object in Kubernetes clusters on the vCenter. 3 | 4 | From Velero vSphere plugin's perspective, orphan snapshots are vSphere snapshots whose local deletion is failing after successful upload to the remote repository. 5 | Velero vSphere plugin performs following steps as part of backup: 6 | 1. Take a snapshot of the volume. 7 | 2. Upload the snapshot to a remote repository. 8 | 3. Delete the snapshot. 9 | 10 | In these steps, deletion of local snapshot could fail, which leaves behind a snapshot. It can be considered as an orphan snapshot. 11 | 12 | Orphan snapshot detection algorithm considers snapshot prefix for listing a snapshot as an orphan snapshot. Snapshot prefix is the prefix used in the snapshot description (snapshot description can be fetched using the GET /volumes/{volumeId}/snapshots API or will be available in CNS spec). 13 | For example, vSphere CSI driver creates snapshots with default prefix of “snapshot”. So, all these snapshot’s description has prefix “snapshot”. Similarly, Velero vSphere plugin uses snapshot prefix as “AstrolabeSnapshot”. 14 | CNS manager by default considers [“snapshot”, “AstrolableSnapshot”] as snapshot prefixes. These values are configurable using `snapshot-prefixes` parameter in the `cnsmanager-config` ConfigMap. 15 | 16 | Since these orphan snapshots occupy space in the datastore and are not really used in Kubernetes, it's useful to identify and cleanup orphan snapshots periodically. 17 | This functionality provides a set of APIs to detect and delete orphan snapshots on-demand. 18 | 19 | ### Supported versions 20 | * vSphere version: 7.0.u3+ 21 | * Kubernetes version: 1.26+ 22 | * vSphere CSI driver version: 2.6+ 23 | * Velero vSphere plugin version: v1.4.3+ 24 | 25 | ### Which orphan snapshots are skipped from detection/deletion ? 26 | Some snapshots will not be considered during orphan snapshot detection/deletion. These include: 27 | * Snapshots created out of band (not using vSphere CSI driver or Velero vSphere plugin). 28 | * Statically provisioned CNS snapshots whose description doesn't start with snapshotPrefix(es) registered with CNS manager. 29 | * Snapshots belonging to Kubernetes clusters which are not registered with CNS manager. 30 | 31 | ### A reminder to register all Kubernetes clusters! 32 | Before you start using orphan snapshot functionality, it's imperative that you [register all the Kubernetes clusters](../../../README.md#register-kubernetes-clusters-before-you-start) in vCenter with CNS Manager, so that orphan snapshots are detected correctly. Any newly added Kubernetes cluster should also be immediately registered. 33 | 34 | ### APIs provided 35 | 1. *GET /orphansnapshots* 36 | 37 | This API is used to get list of orphan snapshots. It takes optional parameters, datacenter & list of datastores, and returns orphan snapshots for them. 38 | - If datacenter is not specified, then it returns all orphan snapshots in the vCenter (all datastores on all datacenters ). 39 | - If only datacenter is specified, then it returns orphan snapshots in all datastores in the datacenter. 40 | - If both datacenter & list of datastores is specified, it returns orphan snapshots in specified datastores on the datacenter. 41 | 42 | This API also takes optional parameter snapshotPrefix, which indicates the prefix used in the snapshot description. So, this API returns orphan snapshots whose description’s prefix matches with the specified snapshot prefix. Default value of this parameter is “snapshot”. 43 | 44 | There could be hundreds of orphan snapshots, so this API supports pagination as well. It takes optional parameters limit (default value 50) and offset (default value 0). Limit specifies the maximum entries that should be displayed in single request, whereas offset specifies the starting point of the result set. 45 | 46 | Detection of orphan snapshots can be a time-consuming operation if there are large number of orphans. Hence it is performed asynchronously at regular intervals and the response is cached. This API returns list of orphan snapshots computed in the last run, along with the next operation interval (`RetryAfterMinutes`). 47 | 48 | **Note:** For newly deployed CNS manager application, when orphan snapshots are being computed in the background for the first time, the API may return no orphan snapshots. It should then be re-tried after `RetryAfterMinutes` to get orphan snapshots computed in the latest run. 49 | 50 | 2. *DELETE /orphansnapshots* 51 | 52 | This API is used to delete orphan snapshots. It takes optional parameters, datacenter & list of datastores, and deletes orphan snapshots from them with the same logic explained above. 53 | This API also takes optional parameter snapshotPrefix, which indicates the prefix used in the snapshot description. 54 | 55 | Please note if there are large number of orphan snapshots in the system or if there's a slowness in vCenter networking/storage, the orphan snapshot deletion may take longer. That is why, orphan snapshot deletion operation is performed asynchronously. 56 | It creates a job to delete these orphan snapshots in the background and returns the job Id to user, the status of this job can be retrieved using `getjobstatus` API. 57 | 58 | 59 | Apart from APIs provided for detection/deletion of orphan snapshots, there are separate APIs to list/delete snapshots of a specific volume or to delete an individual snapshot. 60 | 61 | 3. *GET /volumes/{volumeId}/snapshots* 62 | 63 | This API lists all the snapshots for a specific volume. It takes required parameters datacenter and datastore name to which volume and snapshots belong to. 64 | This API also takes optional parameter snapshotPrefix which indicates the prefix used in snapshot description. If snapshot prefix is not specified, then it lists all snapshots of a volume. Otherwise it lists only those snapshots of volume whose description’s prefix matches with the specified snapshot prefix. 65 | 66 | 4. *DELETE /volumes/{volumeId}/snapshots* 67 | 68 | This API deletes all snapshots of a specific volume. Similar to GET API, it takes required parameters datacenter and datastore and optional parameter snapshotPrefix. 69 | Deletion of all snapshots of a volume is performed asynchronously. It creates a job to delete these snapshots in the background and returns the job Id to user, the status of this job can be retrieved using `getjobstatus` API. 70 | 71 | 5. *DELETE /volumes/{volumeId}/snapshots/{snapshotId}* 72 | 73 | This API deletes a specific snapshot belonging to a volume. It takes required parameters datacenter and datastore name to which volume and snapshot belongs to. 74 | Deletion of a snapshot is performed asynchronously. It creates a job to delete this snapshot in the background and returns the job Id to user, the status of this job can be retrieved using `getjobstatus` API. 75 | 76 | ### Checking status of the snapshot deletion job 77 | Two APIs are being offered to check the status of the snapshot deletion job (A job corresponds to a single invocation of the API, that can have multiple tasks corresponding to separate snapshot deletion). 78 | 79 | 1. *GET /getjobstatus* 80 | 81 | This API returns the current status of the job. A job can be in one of the following status: 82 | 83 | * Queued - Job has been created but hasn't started processing. 84 | * Running - Job is currently executing. 85 | * Success - Job has completed successfully with all tasks succeeding. 86 | * Error - Job ran but some or all of its tasks failed. 87 | 88 | 2. *GET /waitforjob* 89 | 90 | This is a blocking API that waits for job to be successful or fail. 91 | Unlike `getjobstatus` API, this will wait for the job to finish before returning the job result response. 92 | 93 | ## Known Issues 94 | 95 | ### Deletion Failure for Orphan Snapshots of Orphan Volumes 96 | When attempting to delete an orphan snapshot that belongs to an orphan volume using the DELETE /orphansnapshots API, the operation fails with the error: 97 | 98 | > "Volume with ID xxxx is not registered as a CNS Volume." 99 | 100 | #### Workaround: 101 | To delete orphan snapshots, use the following APIs instead: 102 | 103 | - DELETE /volumes/{volumeId}/snapshots (to delete all snapshots for a volume) 104 | 105 | - DELETE /volumes/{volumeId}/snapshots/{snapshotId} (to delete a specific snapshot) 106 | 107 | #### 🚀 Planned Improvement: The detection algorithm will be enhanced to handle these scenarios without having to manually invoke the above APIs in future updates. 108 | -------------------------------------------------------------------------------- /client-sdk/go/client/api_job_details.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "context" 20 | "io/ioutil" 21 | "net/http" 22 | "net/url" 23 | "strings" 24 | ) 25 | 26 | // Linger please 27 | var ( 28 | _ context.Context 29 | ) 30 | 31 | type JobDetailsApiService service 32 | 33 | /* 34 | JobDetailsApiService Get status of an asynchronous job. 35 | There are a few functionalities offered through this tool that are long-running and may be running in background. This API helps to fetch the status of a job that's submitted, in progress or completed. A job can be in one of the following status: Queued - Job has been created but hasn't started processing. Running - Job is currently executing. Success - Job has completed successfully with all tasks succeeding. Error - Job ran but some or all of its tasks failed. 36 | - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 37 | - @param jobId Job Id for which the details need to be fetched. 38 | 39 | @return JobResult 40 | */ 41 | func (a *JobDetailsApiService) GetJobStatus(ctx context.Context, jobId string) (JobResult, *http.Response, error) { 42 | var ( 43 | localVarHttpMethod = strings.ToUpper("Get") 44 | localVarPostBody interface{} 45 | localVarFileName string 46 | localVarFileBytes []byte 47 | localVarReturnValue JobResult 48 | ) 49 | 50 | // create path and map variables 51 | localVarPath := a.client.cfg.BasePath + "/getjobstatus" 52 | 53 | localVarHeaderParams := make(map[string]string) 54 | localVarQueryParams := url.Values{} 55 | localVarFormParams := url.Values{} 56 | 57 | localVarQueryParams.Add("jobId", parameterToString(jobId, "")) 58 | // to determine the Content-Type header 59 | localVarHttpContentTypes := []string{} 60 | 61 | // set Content-Type header 62 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 63 | if localVarHttpContentType != "" { 64 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 65 | } 66 | 67 | // to determine the Accept header 68 | localVarHttpHeaderAccepts := []string{"application/json"} 69 | 70 | // set Accept header 71 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 72 | if localVarHttpHeaderAccept != "" { 73 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 74 | } 75 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 76 | if err != nil { 77 | return localVarReturnValue, nil, err 78 | } 79 | 80 | localVarHttpResponse, err := a.client.callAPI(r) 81 | if err != nil || localVarHttpResponse == nil { 82 | return localVarReturnValue, localVarHttpResponse, err 83 | } 84 | 85 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 86 | localVarHttpResponse.Body.Close() 87 | if err != nil { 88 | return localVarReturnValue, localVarHttpResponse, err 89 | } 90 | 91 | if localVarHttpResponse.StatusCode < 300 { 92 | // If we succeed, return the data, otherwise pass on to decode error. 93 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 94 | if err == nil { 95 | return localVarReturnValue, localVarHttpResponse, err 96 | } 97 | } 98 | 99 | if localVarHttpResponse.StatusCode >= 300 { 100 | newErr := GenericSwaggerError{ 101 | body: localVarBody, 102 | error: localVarHttpResponse.Status, 103 | } 104 | if localVarHttpResponse.StatusCode == 200 { 105 | var v JobResult 106 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 107 | if err != nil { 108 | newErr.error = err.Error() 109 | return localVarReturnValue, localVarHttpResponse, newErr 110 | } 111 | newErr.model = v 112 | return localVarReturnValue, localVarHttpResponse, newErr 113 | } 114 | if localVarHttpResponse.StatusCode == 0 { 115 | var v ModelError 116 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 117 | if err != nil { 118 | newErr.error = err.Error() 119 | return localVarReturnValue, localVarHttpResponse, newErr 120 | } 121 | newErr.model = v 122 | return localVarReturnValue, localVarHttpResponse, newErr 123 | } 124 | return localVarReturnValue, localVarHttpResponse, newErr 125 | } 126 | 127 | return localVarReturnValue, localVarHttpResponse, nil 128 | } 129 | 130 | /* 131 | JobDetailsApiService Wait until a job is successful or failed. 132 | This is a blocking API that waits for job to get successful or fail. Unlike `getJobStatus` API which fetches the current status of the job, this will wait for the job to finish before returning the job result response. 133 | - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 134 | - @param jobId Job Id for which to wait to complete. 135 | 136 | @return JobResult 137 | */ 138 | func (a *JobDetailsApiService) WaitForJob(ctx context.Context, jobId string) (JobResult, *http.Response, error) { 139 | var ( 140 | localVarHttpMethod = strings.ToUpper("Get") 141 | localVarPostBody interface{} 142 | localVarFileName string 143 | localVarFileBytes []byte 144 | localVarReturnValue JobResult 145 | ) 146 | 147 | // create path and map variables 148 | localVarPath := a.client.cfg.BasePath + "/waitforjob" 149 | 150 | localVarHeaderParams := make(map[string]string) 151 | localVarQueryParams := url.Values{} 152 | localVarFormParams := url.Values{} 153 | 154 | localVarQueryParams.Add("jobId", parameterToString(jobId, "")) 155 | // to determine the Content-Type header 156 | localVarHttpContentTypes := []string{} 157 | 158 | // set Content-Type header 159 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 160 | if localVarHttpContentType != "" { 161 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 162 | } 163 | 164 | // to determine the Accept header 165 | localVarHttpHeaderAccepts := []string{"application/json"} 166 | 167 | // set Accept header 168 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 169 | if localVarHttpHeaderAccept != "" { 170 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 171 | } 172 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 173 | if err != nil { 174 | return localVarReturnValue, nil, err 175 | } 176 | 177 | localVarHttpResponse, err := a.client.callAPI(r) 178 | if err != nil || localVarHttpResponse == nil { 179 | return localVarReturnValue, localVarHttpResponse, err 180 | } 181 | 182 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 183 | localVarHttpResponse.Body.Close() 184 | if err != nil { 185 | return localVarReturnValue, localVarHttpResponse, err 186 | } 187 | 188 | if localVarHttpResponse.StatusCode < 300 { 189 | // If we succeed, return the data, otherwise pass on to decode error. 190 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 191 | if err == nil { 192 | return localVarReturnValue, localVarHttpResponse, err 193 | } 194 | } 195 | 196 | if localVarHttpResponse.StatusCode >= 300 { 197 | newErr := GenericSwaggerError{ 198 | body: localVarBody, 199 | error: localVarHttpResponse.Status, 200 | } 201 | if localVarHttpResponse.StatusCode == 200 { 202 | var v JobResult 203 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 204 | if err != nil { 205 | newErr.error = err.Error() 206 | return localVarReturnValue, localVarHttpResponse, newErr 207 | } 208 | newErr.model = v 209 | return localVarReturnValue, localVarHttpResponse, newErr 210 | } 211 | return localVarReturnValue, localVarHttpResponse, newErr 212 | } 213 | 214 | return localVarReturnValue, localVarHttpResponse, nil 215 | } 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2022 VMware, Inc. 3 | 4 | The Apache 2.0 license (the "License") set forth below applies to all parts of the cloud service broker project. You may not use this file except in compliance with the License. 5 | 6 | Apache License 7 | 8 | Version 2.0, January 2004 9 | http://www.apache.org/licenses/ 10 | 11 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 12 | 13 | 1. Definitions. 14 | 15 | "License" shall mean the terms and conditions for use, reproduction, 16 | and distribution as defined by Sections 1 through 9 of this document. 17 | 18 | "Licensor" shall mean the copyright owner or entity authorized by the 19 | copyright owner that is granting the License. 20 | 21 | "Legal Entity" shall mean the union of the acting entity and all other 22 | entities that control, are controlled by, or are under common control 23 | with that entity. For the purposes of this definition, "control" means 24 | (i) the power, direct or indirect, to cause the direction or management 25 | of such entity, whether by contract or otherwise, or (ii) ownership 26 | of fifty percent (50%) or more of the outstanding shares, or (iii) 27 | beneficial ownership of such entity. 28 | 29 | "You" (or "Your") shall mean an individual or Legal Entity exercising 30 | permissions granted by this License. 31 | 32 | "Source" form shall mean the preferred form for making modifications, 33 | including but not limited to software source code, documentation source, 34 | and configuration files. 35 | 36 | "Object" form shall mean any form resulting from mechanical transformation 37 | or translation of a Source form, including but not limited to compiled 38 | object code, generated documentation, and conversions to other media 39 | types. 40 | 41 | "Work" shall mean the work of authorship, whether in Source or 42 | Object form, made available under the License, as indicated by a copyright 43 | notice that is included in or attached to the work (an example is provided 44 | in the Appendix below). 45 | 46 | "Derivative Works" shall mean any work, whether in Source or Object form, 47 | that is based on (or derived from) the Work and for which the editorial 48 | revisions, annotations, elaborations, or other modifications represent, 49 | as a whole, an original work of authorship. For the purposes of this 50 | License, Derivative Works shall not include works that remain separable 51 | from, or merely link (or bind by name) to the interfaces of, the Work 52 | and Derivative Works thereof. 53 | 54 | "Contribution" shall mean any work of authorship, including the 55 | original version of the Work and any modifications or additions to 56 | that Work or Derivative Works thereof, that is intentionally submitted 57 | to Licensor for inclusion in the Work by the copyright owner or by an 58 | individual or Legal Entity authorized to submit on behalf of the copyright 59 | owner. For the purposes of this definition, "submitted" means any form of 60 | electronic, verbal, or written communication sent to the Licensor or its 61 | representatives, including but not limited to communication on electronic 62 | mailing lists, source code control systems, and issue tracking systems 63 | that are managed by, or on behalf of, the Licensor for the purpose of 64 | discussing and improving the Work, but excluding communication that is 65 | conspicuously marked or otherwise designated in writing by the copyright 66 | owner as "Not a Contribution." 67 | 68 | "Contributor" shall mean Licensor and any individual or Legal Entity 69 | on behalf of whom a Contribution has been received by Licensor and 70 | subsequently incorporated within the Work. 71 | 72 | 2. Grant of Copyright License. 73 | Subject to the terms and conditions of this License, each Contributor 74 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 75 | royalty-free, irrevocable copyright license to reproduce, prepare 76 | Derivative Works of, publicly display, publicly perform, sublicense, and 77 | distribute the Work and such Derivative Works in Source or Object form. 78 | 79 | 3. Grant of Patent License. 80 | Subject to the terms and conditions of this License, each Contributor 81 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 82 | royalty- free, irrevocable (except as stated in this section) patent 83 | license to make, have made, use, offer to sell, sell, import, and 84 | otherwise transfer the Work, where such license applies only to those 85 | patent claims licensable by such Contributor that are necessarily 86 | infringed by their Contribution(s) alone or by combination of 87 | their Contribution(s) with the Work to which such Contribution(s) 88 | was submitted. If You institute patent litigation against any entity 89 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 90 | Work or a Contribution incorporated within the Work constitutes direct 91 | or contributory patent infringement, then any patent licenses granted 92 | to You under this License for that Work shall terminate as of the date 93 | such litigation is filed. 94 | 95 | 4. Redistribution. 96 | You may reproduce and distribute copies of the Work or Derivative Works 97 | thereof in any medium, with or without modifications, and in Source or 98 | Object form, provided that You meet the following conditions: 99 | 100 | a. You must give any other recipients of the Work or Derivative Works 101 | a copy of this License; and 102 | 103 | b. You must cause any modified files to carry prominent notices stating 104 | that You changed the files; and 105 | 106 | c. You must retain, in the Source form of any Derivative Works that 107 | You distribute, all copyright, patent, trademark, and attribution 108 | notices from the Source form of the Work, excluding those notices 109 | that do not pertain to any part of the Derivative Works; and 110 | 111 | d. If the Work includes a "NOTICE" text file as part of its 112 | distribution, then any Derivative Works that You distribute must 113 | include a readable copy of the attribution notices contained 114 | within such NOTICE file, excluding those notices that do not 115 | pertain to any part of the Derivative Works, in at least one of 116 | the following places: within a NOTICE text file distributed as part 117 | of the Derivative Works; within the Source form or documentation, 118 | if provided along with the Derivative Works; or, within a display 119 | generated by the Derivative Works, if and wherever such third-party 120 | notices normally appear. The contents of the NOTICE file are for 121 | informational purposes only and do not modify the License. You 122 | may add Your own attribution notices within Derivative Works that 123 | You distribute, alongside or as an addendum to the NOTICE text 124 | from the Work, provided that such additional attribution notices 125 | cannot be construed as modifying the License. You may add Your own 126 | copyright statement to Your modifications and may provide additional 127 | or different license terms and conditions for use, reproduction, or 128 | distribution of Your modifications, or for any such Derivative Works 129 | as a whole, provided Your use, reproduction, and distribution of the 130 | Work otherwise complies with the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. 133 | Unless You explicitly state otherwise, any Contribution intentionally 134 | submitted for inclusion in the Work by You to the Licensor shall be 135 | under the terms and conditions of this License, without any additional 136 | terms or conditions. Notwithstanding the above, nothing herein shall 137 | supersede or modify the terms of any separate license agreement you may 138 | have executed with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. 141 | This License does not grant permission to use the trade names, trademarks, 142 | service marks, or product names of the Licensor, except as required for 143 | reasonable and customary use in describing the origin of the Work and 144 | reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. 147 | Unless required by applicable law or agreed to in writing, Licensor 148 | provides the Work (and each Contributor provides its Contributions) on 149 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 150 | express or implied, including, without limitation, any warranties or 151 | conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 152 | A PARTICULAR PURPOSE. You are solely responsible for determining the 153 | appropriateness of using or redistributing the Work and assume any risks 154 | associated with Your exercise of permissions under this License. 155 | 156 | 8. Limitation of Liability. 157 | In no event and under no legal theory, whether in tort (including 158 | negligence), contract, or otherwise, unless required by applicable law 159 | (such as deliberate and grossly negligent acts) or agreed to in writing, 160 | shall any Contributor be liable to You for damages, including any direct, 161 | indirect, special, incidental, or consequential damages of any character 162 | arising as a result of this License or out of the use or inability to 163 | use the Work (including but not limited to damages for loss of goodwill, 164 | work stoppage, computer failure or malfunction, or any and all other 165 | commercial damages or losses), even if such Contributor has been advised 166 | of the possibility of such damages. 167 | 168 | 9. Accepting Warranty or Additional Liability. 169 | While redistributing the Work or Derivative Works thereof, You may 170 | choose to offer, and charge a fee for, acceptance of support, warranty, 171 | indemnity, or other liability obligations and/or rights consistent with 172 | this License. However, in accepting such obligations, You may act only 173 | on Your own behalf and on Your sole responsibility, not on behalf of 174 | any other Contributor, and only if You agree to indemnify, defend, and 175 | hold each Contributor harmless for any liability incurred by, or claims 176 | asserted against, such Contributor by reason of your accepting any such 177 | warranty or additional liability. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "[]" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright [yyyy] [name of copyright owner] 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. -------------------------------------------------------------------------------- /client-sdk/go/client/api_orphan_volume.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "context" 20 | "github.com/antihax/optional" 21 | "io/ioutil" 22 | "net/http" 23 | "net/url" 24 | "strings" 25 | ) 26 | 27 | // Linger please 28 | var ( 29 | _ context.Context 30 | ) 31 | 32 | type OrphanVolumeApiService service 33 | 34 | /* 35 | OrphanVolumeApiService Delete orphan volumes. 36 | Delete the orphan volumes for the given criteria. 37 | * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 38 | * @param deleteAttachedOrphans Set to `true` to delete attached orphans. When set to `true`, the API will detach the orphan volume from the VM before deleting it. 39 | * @param optional nil or *OrphanVolumeApiOrphanVolumeDeleteOpts - Optional Parameters: 40 | * @param "Datacenter" (optional.String) - (Optional) Datacenter name to narrow down the deletion of orphan volumes to. 41 | * @param "Datastores" (optional.String) - (Optional) List of comma-separated datastores to narrow down the deletion of orphan volumes to. Specify only if the `datacenter` param is specified. 42 | @return OrphanVolumeDeleteResult 43 | */ 44 | 45 | type OrphanVolumeApiOrphanVolumeDeleteOpts struct { 46 | Datacenter optional.String 47 | Datastores optional.String 48 | } 49 | 50 | func (a *OrphanVolumeApiService) OrphanVolumeDelete(ctx context.Context, deleteAttachedOrphans bool, localVarOptionals *OrphanVolumeApiOrphanVolumeDeleteOpts) (OrphanVolumeDeleteResult, *http.Response, error) { 51 | var ( 52 | localVarHttpMethod = strings.ToUpper("Delete") 53 | localVarPostBody interface{} 54 | localVarFileName string 55 | localVarFileBytes []byte 56 | localVarReturnValue OrphanVolumeDeleteResult 57 | ) 58 | 59 | // create path and map variables 60 | localVarPath := a.client.cfg.BasePath + "/orphanvolumes" 61 | 62 | localVarHeaderParams := make(map[string]string) 63 | localVarQueryParams := url.Values{} 64 | localVarFormParams := url.Values{} 65 | 66 | localVarQueryParams.Add("deleteAttachedOrphans", parameterToString(deleteAttachedOrphans, "")) 67 | if localVarOptionals != nil && localVarOptionals.Datacenter.IsSet() { 68 | localVarQueryParams.Add("datacenter", parameterToString(localVarOptionals.Datacenter.Value(), "")) 69 | } 70 | if localVarOptionals != nil && localVarOptionals.Datastores.IsSet() { 71 | localVarQueryParams.Add("datastores", parameterToString(localVarOptionals.Datastores.Value(), "")) 72 | } 73 | // to determine the Content-Type header 74 | localVarHttpContentTypes := []string{} 75 | 76 | // set Content-Type header 77 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 78 | if localVarHttpContentType != "" { 79 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 80 | } 81 | 82 | // to determine the Accept header 83 | localVarHttpHeaderAccepts := []string{"application/json"} 84 | 85 | // set Accept header 86 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 87 | if localVarHttpHeaderAccept != "" { 88 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 89 | } 90 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 91 | if err != nil { 92 | return localVarReturnValue, nil, err 93 | } 94 | 95 | localVarHttpResponse, err := a.client.callAPI(r) 96 | if err != nil || localVarHttpResponse == nil { 97 | return localVarReturnValue, localVarHttpResponse, err 98 | } 99 | 100 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 101 | localVarHttpResponse.Body.Close() 102 | if err != nil { 103 | return localVarReturnValue, localVarHttpResponse, err 104 | } 105 | 106 | if localVarHttpResponse.StatusCode < 300 { 107 | // If we succeed, return the data, otherwise pass on to decode error. 108 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 109 | if err == nil { 110 | return localVarReturnValue, localVarHttpResponse, err 111 | } 112 | } 113 | 114 | if localVarHttpResponse.StatusCode >= 300 { 115 | newErr := GenericSwaggerError{ 116 | body: localVarBody, 117 | error: localVarHttpResponse.Status, 118 | } 119 | if localVarHttpResponse.StatusCode == 200 { 120 | var v OrphanVolumeDeleteResult 121 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 122 | if err != nil { 123 | newErr.error = err.Error() 124 | return localVarReturnValue, localVarHttpResponse, newErr 125 | } 126 | newErr.model = v 127 | return localVarReturnValue, localVarHttpResponse, newErr 128 | } 129 | if localVarHttpResponse.StatusCode == 0 { 130 | var v ModelError 131 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 132 | if err != nil { 133 | newErr.error = err.Error() 134 | return localVarReturnValue, localVarHttpResponse, newErr 135 | } 136 | newErr.model = v 137 | return localVarReturnValue, localVarHttpResponse, newErr 138 | } 139 | return localVarReturnValue, localVarHttpResponse, newErr 140 | } 141 | 142 | return localVarReturnValue, localVarHttpResponse, nil 143 | } 144 | 145 | /* 146 | OrphanVolumeApiService List all the orphan volumes. 147 | Returns a list of orphan volumes for the given input parameters, which could be attached or detached. Since the detection of orphan volumes is an expensive operation, the operation is performed asynchronously at regular intervals. This API returns the list of orphan volumes found in the last run of the operation. If the request is successful, the response will contain the following: 1. `TotalOrphans` - The total number of orphan volumes found. 2. `OrphanVolumes` - The list of orphan volumes found. 3. `RetryAfterMinutes` - The time in minutes after which the next retry should be attempted to get the updated orphan volume list. 4. `TotalOrphansAttached` - The total number of orphan volumes found that are attached to a VM. 5. `TotalOrphansDetached` - The total number of orphan volumes found that are detached. 6. `Limit` - The maximum number of orphan volumes to be returned. 7. `NextOffset` - The offset of the next page if there are more orphan volumes to query. Orphan volumes are safe to delete since there is no PersistentVolume in the Kubernetes cluster referring to them. 148 | * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 149 | * @param optional nil or *OrphanVolumeApiOrphanVolumeListOpts - Optional Parameters: 150 | * @param "IncludeDetails" (optional.Bool) - (Optional) Set to \"true\" to get a detailed dump of the orphan volume. 151 | * @param "Datacenter" (optional.String) - (Optional) Datacenter name to narrow down the orphan volume search. 152 | * @param "Datastores" (optional.String) - (Optional) List of comma-separated datastores. Specify only if the `datacenter` param is specified. 153 | * @param "Offset" (optional.Int32) - (Optional) The offset indicates the starting point of the result set. 154 | * @param "Limit" (optional.Int32) - (Optional) The limit indicates the maximum number of orphan volumes to be returned. 155 | @return OrphanVolumeResult 156 | */ 157 | 158 | type OrphanVolumeApiOrphanVolumeListOpts struct { 159 | IncludeDetails optional.Bool 160 | Datacenter optional.String 161 | Datastores optional.String 162 | Offset optional.Int32 163 | Limit optional.Int32 164 | } 165 | 166 | func (a *OrphanVolumeApiService) OrphanVolumeList(ctx context.Context, localVarOptionals *OrphanVolumeApiOrphanVolumeListOpts) (OrphanVolumeResult, *http.Response, error) { 167 | var ( 168 | localVarHttpMethod = strings.ToUpper("Get") 169 | localVarPostBody interface{} 170 | localVarFileName string 171 | localVarFileBytes []byte 172 | localVarReturnValue OrphanVolumeResult 173 | ) 174 | 175 | // create path and map variables 176 | localVarPath := a.client.cfg.BasePath + "/orphanvolumes" 177 | 178 | localVarHeaderParams := make(map[string]string) 179 | localVarQueryParams := url.Values{} 180 | localVarFormParams := url.Values{} 181 | 182 | if localVarOptionals != nil && localVarOptionals.IncludeDetails.IsSet() { 183 | localVarQueryParams.Add("includeDetails", parameterToString(localVarOptionals.IncludeDetails.Value(), "")) 184 | } 185 | if localVarOptionals != nil && localVarOptionals.Datacenter.IsSet() { 186 | localVarQueryParams.Add("datacenter", parameterToString(localVarOptionals.Datacenter.Value(), "")) 187 | } 188 | if localVarOptionals != nil && localVarOptionals.Datastores.IsSet() { 189 | localVarQueryParams.Add("datastores", parameterToString(localVarOptionals.Datastores.Value(), "")) 190 | } 191 | if localVarOptionals != nil && localVarOptionals.Offset.IsSet() { 192 | localVarQueryParams.Add("offset", parameterToString(localVarOptionals.Offset.Value(), "")) 193 | } 194 | if localVarOptionals != nil && localVarOptionals.Limit.IsSet() { 195 | localVarQueryParams.Add("limit", parameterToString(localVarOptionals.Limit.Value(), "")) 196 | } 197 | // to determine the Content-Type header 198 | localVarHttpContentTypes := []string{} 199 | 200 | // set Content-Type header 201 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 202 | if localVarHttpContentType != "" { 203 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 204 | } 205 | 206 | // to determine the Accept header 207 | localVarHttpHeaderAccepts := []string{"application/json"} 208 | 209 | // set Accept header 210 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 211 | if localVarHttpHeaderAccept != "" { 212 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 213 | } 214 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 215 | if err != nil { 216 | return localVarReturnValue, nil, err 217 | } 218 | 219 | localVarHttpResponse, err := a.client.callAPI(r) 220 | if err != nil || localVarHttpResponse == nil { 221 | return localVarReturnValue, localVarHttpResponse, err 222 | } 223 | 224 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 225 | localVarHttpResponse.Body.Close() 226 | if err != nil { 227 | return localVarReturnValue, localVarHttpResponse, err 228 | } 229 | 230 | if localVarHttpResponse.StatusCode < 300 { 231 | // If we succeed, return the data, otherwise pass on to decode error. 232 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 233 | if err == nil { 234 | return localVarReturnValue, localVarHttpResponse, err 235 | } 236 | } 237 | 238 | if localVarHttpResponse.StatusCode >= 300 { 239 | newErr := GenericSwaggerError{ 240 | body: localVarBody, 241 | error: localVarHttpResponse.Status, 242 | } 243 | if localVarHttpResponse.StatusCode == 200 { 244 | var v OrphanVolumeResult 245 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 246 | if err != nil { 247 | newErr.error = err.Error() 248 | return localVarReturnValue, localVarHttpResponse, newErr 249 | } 250 | newErr.model = v 251 | return localVarReturnValue, localVarHttpResponse, newErr 252 | } 253 | if localVarHttpResponse.StatusCode == 0 { 254 | var v ModelError 255 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 256 | if err != nil { 257 | newErr.error = err.Error() 258 | return localVarReturnValue, localVarHttpResponse, newErr 259 | } 260 | newErr.model = v 261 | return localVarReturnValue, localVarHttpResponse, newErr 262 | } 263 | return localVarReturnValue, localVarHttpResponse, newErr 264 | } 265 | 266 | return localVarReturnValue, localVarHttpResponse, nil 267 | } 268 | -------------------------------------------------------------------------------- /client-sdk/go/client/api_orphan_snapshot.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "context" 20 | "io/ioutil" 21 | "net/http" 22 | "net/url" 23 | "strings" 24 | "github.com/antihax/optional" 25 | ) 26 | 27 | // Linger please 28 | var ( 29 | _ context.Context 30 | ) 31 | 32 | type OrphanSnapshotApiService service 33 | /* 34 | OrphanSnapshotApiService Delete orphan snapshots. 35 | Use this API to identify and delete orphan snapshots. From vSphere CSI plugin's perspective, orphan snapshots are FCD snapshots that were initiated through the vSphere CSI driver but do not have a corresponding VolumeSnapshotContent object in the Kubernetes cluster. snapshotPrefix is the prefix used in the snapshot description. Its default value is “snapshot”, which is also the default value used by snapshot sidecar in CSI and it can be configured based on prefix used in the snapshot sidecar. Use the `snapshotPrefix` parameter to specify alternate prefix. From Velero vSphere plugin's perspective, orphan snapshots are snapshots whose upload is failing with multiple attempts or snapshots whose local deletion is failing after successful upload. For Velero vSphere plugin, user has to specify “AstrolabeSnapshot” as the snapshotPrefix. Orphan snapshot deletion operation is performed asynchronously. It returns a job id, the status of which can be retrieved using `jobStatus` API. 36 | * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 37 | * @param optional nil or *OrphanSnapshotApiOrphanSnapshotsDeleteOpts - Optional Parameters: 38 | * @param "Datacenter" (optional.String) - (Optional) Datacenter name to narrow down the orphan snapshots search. 39 | * @param "Datastores" (optional.String) - (Optional) List of comma-separated datastores. Specify only if the `datacenter` param is specified. 40 | * @param "SnapshotPrefix" (optional.String) - (Optional) The snapshot prefix indicates the prefix used in snapshot description. 41 | @return SnapshotDeleteResult 42 | */ 43 | 44 | type OrphanSnapshotApiOrphanSnapshotsDeleteOpts struct { 45 | Datacenter optional.String 46 | Datastores optional.String 47 | SnapshotPrefix optional.String 48 | } 49 | 50 | func (a *OrphanSnapshotApiService) OrphanSnapshotsDelete(ctx context.Context, localVarOptionals *OrphanSnapshotApiOrphanSnapshotsDeleteOpts) (SnapshotDeleteResult, *http.Response, error) { 51 | var ( 52 | localVarHttpMethod = strings.ToUpper("Delete") 53 | localVarPostBody interface{} 54 | localVarFileName string 55 | localVarFileBytes []byte 56 | localVarReturnValue SnapshotDeleteResult 57 | ) 58 | 59 | // create path and map variables 60 | localVarPath := a.client.cfg.BasePath + "/orphansnapshots" 61 | 62 | localVarHeaderParams := make(map[string]string) 63 | localVarQueryParams := url.Values{} 64 | localVarFormParams := url.Values{} 65 | 66 | if localVarOptionals != nil && localVarOptionals.Datacenter.IsSet() { 67 | localVarQueryParams.Add("datacenter", parameterToString(localVarOptionals.Datacenter.Value(), "")) 68 | } 69 | if localVarOptionals != nil && localVarOptionals.Datastores.IsSet() { 70 | localVarQueryParams.Add("datastores", parameterToString(localVarOptionals.Datastores.Value(), "")) 71 | } 72 | if localVarOptionals != nil && localVarOptionals.SnapshotPrefix.IsSet() { 73 | localVarQueryParams.Add("snapshotPrefix", parameterToString(localVarOptionals.SnapshotPrefix.Value(), "")) 74 | } 75 | // to determine the Content-Type header 76 | localVarHttpContentTypes := []string{} 77 | 78 | // set Content-Type header 79 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 80 | if localVarHttpContentType != "" { 81 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 82 | } 83 | 84 | // to determine the Accept header 85 | localVarHttpHeaderAccepts := []string{"application/json"} 86 | 87 | // set Accept header 88 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 89 | if localVarHttpHeaderAccept != "" { 90 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 91 | } 92 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 93 | if err != nil { 94 | return localVarReturnValue, nil, err 95 | } 96 | 97 | localVarHttpResponse, err := a.client.callAPI(r) 98 | if err != nil || localVarHttpResponse == nil { 99 | return localVarReturnValue, localVarHttpResponse, err 100 | } 101 | 102 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 103 | localVarHttpResponse.Body.Close() 104 | if err != nil { 105 | return localVarReturnValue, localVarHttpResponse, err 106 | } 107 | 108 | if localVarHttpResponse.StatusCode < 300 { 109 | // If we succeed, return the data, otherwise pass on to decode error. 110 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 111 | if err == nil { 112 | return localVarReturnValue, localVarHttpResponse, err 113 | } 114 | } 115 | 116 | if localVarHttpResponse.StatusCode >= 300 { 117 | newErr := GenericSwaggerError{ 118 | body: localVarBody, 119 | error: localVarHttpResponse.Status, 120 | } 121 | if localVarHttpResponse.StatusCode == 202 { 122 | var v SnapshotDeleteResult 123 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 124 | if err != nil { 125 | newErr.error = err.Error() 126 | return localVarReturnValue, localVarHttpResponse, newErr 127 | } 128 | newErr.model = v 129 | return localVarReturnValue, localVarHttpResponse, newErr 130 | } 131 | if localVarHttpResponse.StatusCode == 0 { 132 | var v ModelError 133 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 134 | if err != nil { 135 | newErr.error = err.Error() 136 | return localVarReturnValue, localVarHttpResponse, newErr 137 | } 138 | newErr.model = v 139 | return localVarReturnValue, localVarHttpResponse, newErr 140 | } 141 | return localVarReturnValue, localVarHttpResponse, newErr 142 | } 143 | 144 | return localVarReturnValue, localVarHttpResponse, nil 145 | } 146 | /* 147 | OrphanSnapshotApiService List all the orphan snapshots. 148 | Use this API to identify orphan snapshots. From vSphere CSI plugin's perspective, orphan snapshots are FCD snapshots that were initiated through the vSphere CSI driver but do not have a corresponding VolumeSnapshotContent object in the Kubernetes cluster. snapshotPrefix is the prefix used in the snapshot description. Its default value is “snapshot”, which is also the default value used by snapshot sidecar in CSI and it can be configured based on prefix used in the snapshot sidecar. Use the `snapshotPrefix` parameter to specify alternate prefix. From Velero vSphere plugin's perspective, orphan snapshots are snapshots whose upload is failing with multiple attempts or snapshots whose local deletion is failing after successful upload. For Velero vSphere plugin, user has to specify “AstrolabeSnapshot” as the snapshotPrefix. GET API for orphan snapshots support pagination. The response body contains totalOrphanSnapshots, limit and offset values. Also, response header contains X-Limit and X-Next-Offset values. Based on these values user can decide if there are more results to be fetched. Since the detection of orphan snapshots is an expensive operation, the operation is performed asynchronously at regular intervals. This API returns the list of orphan snapshots found in the last run of the operation. `retryAfterMinutes` in response body indicates the time in minutes after which the next retry should be attempted to get the updated orphan snapshot list. 149 | * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 150 | * @param optional nil or *OrphanSnapshotApiOrphanSnapshotsListOpts - Optional Parameters: 151 | * @param "Datacenter" (optional.String) - (Optional) Datacenter name to narrow down the orphan snapshots search. 152 | * @param "Datastores" (optional.String) - (Optional) List of comma-separated datastores. Specify only if the `datacenter` param is specified. 153 | * @param "SnapshotPrefix" (optional.String) - (Optional) The snapshot prefix indicates the prefix used in snapshot description. 154 | * @param "Limit" (optional.Int64) - (Optional) Limit specifies the maximum entries that should be displayed in single request. 155 | * @param "Offset" (optional.Int64) - (Optional) Offset specifies the starting point of the result set. 156 | @return OrphanSnapshotResult 157 | */ 158 | 159 | type OrphanSnapshotApiOrphanSnapshotsListOpts struct { 160 | Datacenter optional.String 161 | Datastores optional.String 162 | SnapshotPrefix optional.String 163 | Limit optional.Int64 164 | Offset optional.Int64 165 | } 166 | 167 | func (a *OrphanSnapshotApiService) OrphanSnapshotsList(ctx context.Context, localVarOptionals *OrphanSnapshotApiOrphanSnapshotsListOpts) (OrphanSnapshotResult, *http.Response, error) { 168 | var ( 169 | localVarHttpMethod = strings.ToUpper("Get") 170 | localVarPostBody interface{} 171 | localVarFileName string 172 | localVarFileBytes []byte 173 | localVarReturnValue OrphanSnapshotResult 174 | ) 175 | 176 | // create path and map variables 177 | localVarPath := a.client.cfg.BasePath + "/orphansnapshots" 178 | 179 | localVarHeaderParams := make(map[string]string) 180 | localVarQueryParams := url.Values{} 181 | localVarFormParams := url.Values{} 182 | 183 | if localVarOptionals != nil && localVarOptionals.Datacenter.IsSet() { 184 | localVarQueryParams.Add("datacenter", parameterToString(localVarOptionals.Datacenter.Value(), "")) 185 | } 186 | if localVarOptionals != nil && localVarOptionals.Datastores.IsSet() { 187 | localVarQueryParams.Add("datastores", parameterToString(localVarOptionals.Datastores.Value(), "")) 188 | } 189 | if localVarOptionals != nil && localVarOptionals.SnapshotPrefix.IsSet() { 190 | localVarQueryParams.Add("snapshotPrefix", parameterToString(localVarOptionals.SnapshotPrefix.Value(), "")) 191 | } 192 | if localVarOptionals != nil && localVarOptionals.Limit.IsSet() { 193 | localVarQueryParams.Add("limit", parameterToString(localVarOptionals.Limit.Value(), "")) 194 | } 195 | if localVarOptionals != nil && localVarOptionals.Offset.IsSet() { 196 | localVarQueryParams.Add("offset", parameterToString(localVarOptionals.Offset.Value(), "")) 197 | } 198 | // to determine the Content-Type header 199 | localVarHttpContentTypes := []string{} 200 | 201 | // set Content-Type header 202 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 203 | if localVarHttpContentType != "" { 204 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 205 | } 206 | 207 | // to determine the Accept header 208 | localVarHttpHeaderAccepts := []string{"application/json"} 209 | 210 | // set Accept header 211 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 212 | if localVarHttpHeaderAccept != "" { 213 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 214 | } 215 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 216 | if err != nil { 217 | return localVarReturnValue, nil, err 218 | } 219 | 220 | localVarHttpResponse, err := a.client.callAPI(r) 221 | if err != nil || localVarHttpResponse == nil { 222 | return localVarReturnValue, localVarHttpResponse, err 223 | } 224 | 225 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 226 | localVarHttpResponse.Body.Close() 227 | if err != nil { 228 | return localVarReturnValue, localVarHttpResponse, err 229 | } 230 | 231 | if localVarHttpResponse.StatusCode < 300 { 232 | // If we succeed, return the data, otherwise pass on to decode error. 233 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 234 | if err == nil { 235 | return localVarReturnValue, localVarHttpResponse, err 236 | } 237 | } 238 | 239 | if localVarHttpResponse.StatusCode >= 300 { 240 | newErr := GenericSwaggerError{ 241 | body: localVarBody, 242 | error: localVarHttpResponse.Status, 243 | } 244 | if localVarHttpResponse.StatusCode == 200 { 245 | var v OrphanSnapshotResult 246 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 247 | if err != nil { 248 | newErr.error = err.Error() 249 | return localVarReturnValue, localVarHttpResponse, newErr 250 | } 251 | newErr.model = v 252 | return localVarReturnValue, localVarHttpResponse, newErr 253 | } 254 | if localVarHttpResponse.StatusCode == 0 { 255 | var v ModelError 256 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")); 257 | if err != nil { 258 | newErr.error = err.Error() 259 | return localVarReturnValue, localVarHttpResponse, newErr 260 | } 261 | newErr.model = v 262 | return localVarReturnValue, localVarHttpResponse, newErr 263 | } 264 | return localVarReturnValue, localVarHttpResponse, newErr 265 | } 266 | 267 | return localVarReturnValue, localVarHttpResponse, nil 268 | } 269 | -------------------------------------------------------------------------------- /client-sdk/go/client/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "bytes" 20 | "context" 21 | "encoding/json" 22 | "encoding/xml" 23 | "errors" 24 | "fmt" 25 | "io" 26 | "mime/multipart" 27 | "net/http" 28 | "net/url" 29 | "os" 30 | "path/filepath" 31 | "reflect" 32 | "regexp" 33 | "strconv" 34 | "strings" 35 | "time" 36 | "unicode/utf8" 37 | 38 | "golang.org/x/oauth2" 39 | ) 40 | 41 | var ( 42 | jsonCheck = regexp.MustCompile("(?i:[application|text]/json)") 43 | xmlCheck = regexp.MustCompile("(?i:[application|text]/xml)") 44 | ) 45 | 46 | // APIClient manages communication with the CNS Manager API v1.0.0 47 | // In most cases there should be only one, shared, APIClient. 48 | type APIClient struct { 49 | cfg *Configuration 50 | common service // Reuse a single struct instead of allocating one for each service on the heap. 51 | 52 | // API Services 53 | 54 | ClusterRecordKeepingApi *ClusterRecordKeepingApiService 55 | 56 | DatastoreOperationsApi *DatastoreOperationsApiService 57 | 58 | JobDetailsApi *JobDetailsApiService 59 | 60 | OrphanSnapshotApi *OrphanSnapshotApiService 61 | 62 | OrphanVolumeApi *OrphanVolumeApiService 63 | 64 | SnapshotForVolumeApi *SnapshotForVolumeApiService 65 | } 66 | 67 | type service struct { 68 | client *APIClient 69 | } 70 | 71 | // NewAPIClient creates a new API client. Requires a userAgent string describing your application. 72 | // optionally a custom http.Client to allow for advanced features such as caching. 73 | func NewAPIClient(cfg *Configuration) *APIClient { 74 | if cfg.HTTPClient == nil { 75 | cfg.HTTPClient = http.DefaultClient 76 | } 77 | 78 | c := &APIClient{} 79 | c.cfg = cfg 80 | c.common.client = c 81 | 82 | // API Services 83 | c.ClusterRecordKeepingApi = (*ClusterRecordKeepingApiService)(&c.common) 84 | c.DatastoreOperationsApi = (*DatastoreOperationsApiService)(&c.common) 85 | c.JobDetailsApi = (*JobDetailsApiService)(&c.common) 86 | c.OrphanSnapshotApi = (*OrphanSnapshotApiService)(&c.common) 87 | c.OrphanVolumeApi = (*OrphanVolumeApiService)(&c.common) 88 | c.SnapshotForVolumeApi = (*SnapshotForVolumeApiService)(&c.common) 89 | 90 | return c 91 | } 92 | 93 | func atoi(in string) (int, error) { 94 | return strconv.Atoi(in) 95 | } 96 | 97 | // selectHeaderContentType select a content type from the available list. 98 | func selectHeaderContentType(contentTypes []string) string { 99 | if len(contentTypes) == 0 { 100 | return "" 101 | } 102 | if contains(contentTypes, "application/json") { 103 | return "application/json" 104 | } 105 | return contentTypes[0] // use the first content type specified in 'consumes' 106 | } 107 | 108 | // selectHeaderAccept join all accept types and return 109 | func selectHeaderAccept(accepts []string) string { 110 | if len(accepts) == 0 { 111 | return "" 112 | } 113 | 114 | if contains(accepts, "application/json") { 115 | return "application/json" 116 | } 117 | 118 | return strings.Join(accepts, ",") 119 | } 120 | 121 | // contains is a case insenstive match, finding needle in a haystack 122 | func contains(haystack []string, needle string) bool { 123 | for _, a := range haystack { 124 | if strings.ToLower(a) == strings.ToLower(needle) { 125 | return true 126 | } 127 | } 128 | return false 129 | } 130 | 131 | // Verify optional parameters are of the correct type. 132 | func typeCheckParameter(obj interface{}, expected string, name string) error { 133 | // Make sure there is an object. 134 | if obj == nil { 135 | return nil 136 | } 137 | 138 | // Check the type is as expected. 139 | if reflect.TypeOf(obj).String() != expected { 140 | return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) 141 | } 142 | return nil 143 | } 144 | 145 | // parameterToString convert interface{} parameters to string, using a delimiter if format is provided. 146 | func parameterToString(obj interface{}, collectionFormat string) string { 147 | var delimiter string 148 | 149 | switch collectionFormat { 150 | case "pipes": 151 | delimiter = "|" 152 | case "ssv": 153 | delimiter = " " 154 | case "tsv": 155 | delimiter = "\t" 156 | case "csv": 157 | delimiter = "," 158 | } 159 | 160 | if reflect.TypeOf(obj).Kind() == reflect.Slice { 161 | return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") 162 | } 163 | 164 | return fmt.Sprintf("%v", obj) 165 | } 166 | 167 | // callAPI do the request. 168 | func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { 169 | return c.cfg.HTTPClient.Do(request) 170 | } 171 | 172 | // Change base path to allow switching to mocks 173 | func (c *APIClient) ChangeBasePath(path string) { 174 | c.cfg.BasePath = path 175 | } 176 | 177 | // prepareRequest build the request 178 | func (c *APIClient) prepareRequest( 179 | ctx context.Context, 180 | path string, method string, 181 | postBody interface{}, 182 | headerParams map[string]string, 183 | queryParams url.Values, 184 | formParams url.Values, 185 | fileName string, 186 | fileBytes []byte) (localVarRequest *http.Request, err error) { 187 | 188 | var body *bytes.Buffer 189 | 190 | // Detect postBody type and post. 191 | if postBody != nil { 192 | contentType := headerParams["Content-Type"] 193 | if contentType == "" { 194 | contentType = detectContentType(postBody) 195 | headerParams["Content-Type"] = contentType 196 | } 197 | 198 | body, err = setBody(postBody, contentType) 199 | if err != nil { 200 | return nil, err 201 | } 202 | } 203 | 204 | // add form parameters and file if available. 205 | if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { 206 | if body != nil { 207 | return nil, errors.New("Cannot specify postBody and multipart form at the same time.") 208 | } 209 | body = &bytes.Buffer{} 210 | w := multipart.NewWriter(body) 211 | 212 | for k, v := range formParams { 213 | for _, iv := range v { 214 | if strings.HasPrefix(k, "@") { // file 215 | err = addFile(w, k[1:], iv) 216 | if err != nil { 217 | return nil, err 218 | } 219 | } else { // form value 220 | w.WriteField(k, iv) 221 | } 222 | } 223 | } 224 | if len(fileBytes) > 0 && fileName != "" { 225 | w.Boundary() 226 | //_, fileNm := filepath.Split(fileName) 227 | part, err := w.CreateFormFile("file", filepath.Base(fileName)) 228 | if err != nil { 229 | return nil, err 230 | } 231 | _, err = part.Write(fileBytes) 232 | if err != nil { 233 | return nil, err 234 | } 235 | // Set the Boundary in the Content-Type 236 | headerParams["Content-Type"] = w.FormDataContentType() 237 | } 238 | 239 | // Set Content-Length 240 | headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) 241 | w.Close() 242 | } 243 | 244 | if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { 245 | if body != nil { 246 | return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") 247 | } 248 | body = &bytes.Buffer{} 249 | body.WriteString(formParams.Encode()) 250 | // Set Content-Length 251 | headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) 252 | } 253 | 254 | // Setup path and query parameters 255 | url, err := url.Parse(path) 256 | if err != nil { 257 | return nil, err 258 | } 259 | 260 | // Adding Query Param 261 | query := url.Query() 262 | for k, v := range queryParams { 263 | for _, iv := range v { 264 | query.Add(k, iv) 265 | } 266 | } 267 | 268 | // Encode the parameters. 269 | url.RawQuery = query.Encode() 270 | 271 | // Generate a new request 272 | if body != nil { 273 | localVarRequest, err = http.NewRequest(method, url.String(), body) 274 | } else { 275 | localVarRequest, err = http.NewRequest(method, url.String(), nil) 276 | } 277 | if err != nil { 278 | return nil, err 279 | } 280 | 281 | // add header parameters, if any 282 | if len(headerParams) > 0 { 283 | headers := http.Header{} 284 | for h, v := range headerParams { 285 | headers.Set(h, v) 286 | } 287 | localVarRequest.Header = headers 288 | } 289 | 290 | // Override request host, if applicable 291 | if c.cfg.Host != "" { 292 | localVarRequest.Host = c.cfg.Host 293 | } 294 | 295 | // Add the user agent to the request. 296 | localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) 297 | 298 | if ctx != nil { 299 | // add context to the request 300 | localVarRequest = localVarRequest.WithContext(ctx) 301 | 302 | // Walk through any authentication. 303 | 304 | // OAuth2 authentication 305 | if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { 306 | // We were able to grab an oauth2 token from the context 307 | var latestToken *oauth2.Token 308 | if latestToken, err = tok.Token(); err != nil { 309 | return nil, err 310 | } 311 | 312 | latestToken.SetAuthHeader(localVarRequest) 313 | } 314 | 315 | // Basic HTTP Authentication 316 | if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { 317 | localVarRequest.SetBasicAuth(auth.UserName, auth.Password) 318 | } 319 | 320 | // AccessToken Authentication 321 | if auth, ok := ctx.Value(ContextAccessToken).(string); ok { 322 | localVarRequest.Header.Add("Authorization", "Bearer "+auth) 323 | } 324 | } 325 | 326 | for header, value := range c.cfg.DefaultHeader { 327 | localVarRequest.Header.Add(header, value) 328 | } 329 | 330 | return localVarRequest, nil 331 | } 332 | 333 | func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { 334 | if strings.Contains(contentType, "application/xml") { 335 | if err = xml.Unmarshal(b, v); err != nil { 336 | return err 337 | } 338 | return nil 339 | } else if strings.Contains(contentType, "application/json") { 340 | if err = json.Unmarshal(b, v); err != nil { 341 | return err 342 | } 343 | return nil 344 | } 345 | return errors.New("undefined response type") 346 | } 347 | 348 | // Add a file to the multipart request 349 | func addFile(w *multipart.Writer, fieldName, path string) error { 350 | file, err := os.Open(path) 351 | if err != nil { 352 | return err 353 | } 354 | defer file.Close() 355 | 356 | part, err := w.CreateFormFile(fieldName, filepath.Base(path)) 357 | if err != nil { 358 | return err 359 | } 360 | _, err = io.Copy(part, file) 361 | 362 | return err 363 | } 364 | 365 | // Prevent trying to import "fmt" 366 | func reportError(format string, a ...interface{}) error { 367 | return fmt.Errorf(format, a...) 368 | } 369 | 370 | // Set request body from an interface{} 371 | func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { 372 | if bodyBuf == nil { 373 | bodyBuf = &bytes.Buffer{} 374 | } 375 | 376 | if reader, ok := body.(io.Reader); ok { 377 | _, err = bodyBuf.ReadFrom(reader) 378 | } else if b, ok := body.([]byte); ok { 379 | _, err = bodyBuf.Write(b) 380 | } else if s, ok := body.(string); ok { 381 | _, err = bodyBuf.WriteString(s) 382 | } else if s, ok := body.(*string); ok { 383 | _, err = bodyBuf.WriteString(*s) 384 | } else if jsonCheck.MatchString(contentType) { 385 | err = json.NewEncoder(bodyBuf).Encode(body) 386 | } else if xmlCheck.MatchString(contentType) { 387 | xml.NewEncoder(bodyBuf).Encode(body) 388 | } 389 | 390 | if err != nil { 391 | return nil, err 392 | } 393 | 394 | if bodyBuf.Len() == 0 { 395 | err = fmt.Errorf("Invalid body type %s\n", contentType) 396 | return nil, err 397 | } 398 | return bodyBuf, nil 399 | } 400 | 401 | // detectContentType method is used to figure out `Request.Body` content type for request header 402 | func detectContentType(body interface{}) string { 403 | contentType := "text/plain; charset=utf-8" 404 | kind := reflect.TypeOf(body).Kind() 405 | 406 | switch kind { 407 | case reflect.Struct, reflect.Map, reflect.Ptr: 408 | contentType = "application/json; charset=utf-8" 409 | case reflect.String: 410 | contentType = "text/plain; charset=utf-8" 411 | default: 412 | if b, ok := body.([]byte); ok { 413 | contentType = http.DetectContentType(b) 414 | } else if kind == reflect.Slice { 415 | contentType = "application/json; charset=utf-8" 416 | } 417 | } 418 | 419 | return contentType 420 | } 421 | 422 | // Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go 423 | type cacheControl map[string]string 424 | 425 | func parseCacheControl(headers http.Header) cacheControl { 426 | cc := cacheControl{} 427 | ccHeader := headers.Get("Cache-Control") 428 | for _, part := range strings.Split(ccHeader, ",") { 429 | part = strings.Trim(part, " ") 430 | if part == "" { 431 | continue 432 | } 433 | if strings.ContainsRune(part, '=') { 434 | keyval := strings.Split(part, "=") 435 | cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") 436 | } else { 437 | cc[part] = "" 438 | } 439 | } 440 | return cc 441 | } 442 | 443 | // CacheExpires helper function to determine remaining time before repeating a request. 444 | func CacheExpires(r *http.Response) time.Time { 445 | // Figure out when the cache expires. 446 | var expires time.Time 447 | now, err := time.Parse(time.RFC1123, r.Header.Get("date")) 448 | if err != nil { 449 | return time.Now() 450 | } 451 | respCacheControl := parseCacheControl(r.Header) 452 | 453 | if maxAge, ok := respCacheControl["max-age"]; ok { 454 | lifetime, err := time.ParseDuration(maxAge + "s") 455 | if err != nil { 456 | expires = now 457 | } 458 | expires = now.Add(lifetime) 459 | } else { 460 | expiresHeader := r.Header.Get("Expires") 461 | if expiresHeader != "" { 462 | expires, err = time.Parse(time.RFC1123, expiresHeader) 463 | if err != nil { 464 | expires = now 465 | } 466 | } 467 | } 468 | return expires 469 | } 470 | 471 | func strlen(s string) int { 472 | return utf8.RuneCountInString(s) 473 | } 474 | 475 | // GenericSwaggerError Provides access to the body, error and model on returned errors. 476 | type GenericSwaggerError struct { 477 | body []byte 478 | error string 479 | model interface{} 480 | } 481 | 482 | // Error returns non-empty string if there was an error. 483 | func (e GenericSwaggerError) Error() string { 484 | return e.error 485 | } 486 | 487 | // Body returns the raw bytes of the response 488 | func (e GenericSwaggerError) Body() []byte { 489 | return e.body 490 | } 491 | 492 | // Model returns the unpacked model of the error 493 | func (e GenericSwaggerError) Model() interface{} { 494 | return e.model 495 | } 496 | -------------------------------------------------------------------------------- /client-sdk/go/client/api_cluster_record_keeping.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package swagger 17 | 18 | import ( 19 | "context" 20 | "github.com/antihax/optional" 21 | "io/ioutil" 22 | "net/http" 23 | "net/url" 24 | "os" 25 | "strings" 26 | ) 27 | 28 | // Linger please 29 | var ( 30 | _ context.Context 31 | ) 32 | 33 | type ClusterRecordKeepingApiService service 34 | 35 | /* 36 | ClusterRecordKeepingApiService Deregister a cluster with the CNS Manager. 37 | The API takes unique clusterID as input and de-registers the cluster from CNS Manager. 38 | - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 39 | - @param clusterId Refers to cluster-id available in decoded data field from vsphere-config-secret. 40 | 41 | @return DeregisterClusterResult 42 | */ 43 | func (a *ClusterRecordKeepingApiService) Deregistercluster(ctx context.Context, clusterId string) (DeregisterClusterResult, *http.Response, error) { 44 | var ( 45 | localVarHttpMethod = strings.ToUpper("Post") 46 | localVarPostBody interface{} 47 | localVarFileName string 48 | localVarFileBytes []byte 49 | localVarReturnValue DeregisterClusterResult 50 | ) 51 | 52 | // create path and map variables 53 | localVarPath := a.client.cfg.BasePath + "/deregistercluster" 54 | 55 | localVarHeaderParams := make(map[string]string) 56 | localVarQueryParams := url.Values{} 57 | localVarFormParams := url.Values{} 58 | 59 | localVarQueryParams.Add("clusterId", parameterToString(clusterId, "")) 60 | // to determine the Content-Type header 61 | localVarHttpContentTypes := []string{} 62 | 63 | // set Content-Type header 64 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 65 | if localVarHttpContentType != "" { 66 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 67 | } 68 | 69 | // to determine the Accept header 70 | localVarHttpHeaderAccepts := []string{"application/json"} 71 | 72 | // set Accept header 73 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 74 | if localVarHttpHeaderAccept != "" { 75 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 76 | } 77 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 78 | if err != nil { 79 | return localVarReturnValue, nil, err 80 | } 81 | 82 | localVarHttpResponse, err := a.client.callAPI(r) 83 | if err != nil || localVarHttpResponse == nil { 84 | return localVarReturnValue, localVarHttpResponse, err 85 | } 86 | 87 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 88 | localVarHttpResponse.Body.Close() 89 | if err != nil { 90 | return localVarReturnValue, localVarHttpResponse, err 91 | } 92 | 93 | if localVarHttpResponse.StatusCode < 300 { 94 | // If we succeed, return the data, otherwise pass on to decode error. 95 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 96 | if err == nil { 97 | return localVarReturnValue, localVarHttpResponse, err 98 | } 99 | } 100 | 101 | if localVarHttpResponse.StatusCode >= 300 { 102 | newErr := GenericSwaggerError{ 103 | body: localVarBody, 104 | error: localVarHttpResponse.Status, 105 | } 106 | if localVarHttpResponse.StatusCode == 200 { 107 | var v DeregisterClusterResult 108 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 109 | if err != nil { 110 | newErr.error = err.Error() 111 | return localVarReturnValue, localVarHttpResponse, newErr 112 | } 113 | newErr.model = v 114 | return localVarReturnValue, localVarHttpResponse, newErr 115 | } 116 | if localVarHttpResponse.StatusCode == 0 { 117 | var v ModelError 118 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 119 | if err != nil { 120 | newErr.error = err.Error() 121 | return localVarReturnValue, localVarHttpResponse, newErr 122 | } 123 | newErr.model = v 124 | return localVarReturnValue, localVarHttpResponse, newErr 125 | } 126 | return localVarReturnValue, localVarHttpResponse, newErr 127 | } 128 | 129 | return localVarReturnValue, localVarHttpResponse, nil 130 | } 131 | 132 | /* 133 | ClusterRecordKeepingApiService Get the list of registered k8s clusters from CNS manager inventory. 134 | CNS manager does a record keeping of all the clusters in a vCenter. The registered cluster config is stored in the CNS manager inventory by using ClusterId as the key. The listregisteredclusters API will return the list of all the registered clusterIds as an array. 135 | - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 136 | 137 | @return []string 138 | */ 139 | func (a *ClusterRecordKeepingApiService) Listregisteredclusters(ctx context.Context) ([]string, *http.Response, error) { 140 | var ( 141 | localVarHttpMethod = strings.ToUpper("Get") 142 | localVarPostBody interface{} 143 | localVarFileName string 144 | localVarFileBytes []byte 145 | localVarReturnValue []string 146 | ) 147 | 148 | // create path and map variables 149 | localVarPath := a.client.cfg.BasePath + "/listregisteredclusters" 150 | 151 | localVarHeaderParams := make(map[string]string) 152 | localVarQueryParams := url.Values{} 153 | localVarFormParams := url.Values{} 154 | 155 | // to determine the Content-Type header 156 | localVarHttpContentTypes := []string{} 157 | 158 | // set Content-Type header 159 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 160 | if localVarHttpContentType != "" { 161 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 162 | } 163 | 164 | // to determine the Accept header 165 | localVarHttpHeaderAccepts := []string{"application/json"} 166 | 167 | // set Accept header 168 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 169 | if localVarHttpHeaderAccept != "" { 170 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 171 | } 172 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 173 | if err != nil { 174 | return localVarReturnValue, nil, err 175 | } 176 | 177 | localVarHttpResponse, err := a.client.callAPI(r) 178 | if err != nil || localVarHttpResponse == nil { 179 | return localVarReturnValue, localVarHttpResponse, err 180 | } 181 | 182 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 183 | localVarHttpResponse.Body.Close() 184 | if err != nil { 185 | return localVarReturnValue, localVarHttpResponse, err 186 | } 187 | 188 | if localVarHttpResponse.StatusCode < 300 { 189 | // If we succeed, return the data, otherwise pass on to decode error. 190 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 191 | if err == nil { 192 | return localVarReturnValue, localVarHttpResponse, err 193 | } 194 | } 195 | 196 | if localVarHttpResponse.StatusCode >= 300 { 197 | newErr := GenericSwaggerError{ 198 | body: localVarBody, 199 | error: localVarHttpResponse.Status, 200 | } 201 | if localVarHttpResponse.StatusCode == 200 { 202 | var v []string 203 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 204 | if err != nil { 205 | newErr.error = err.Error() 206 | return localVarReturnValue, localVarHttpResponse, newErr 207 | } 208 | newErr.model = v 209 | return localVarReturnValue, localVarHttpResponse, newErr 210 | } 211 | if localVarHttpResponse.StatusCode == 0 { 212 | var v ModelError 213 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 214 | if err != nil { 215 | newErr.error = err.Error() 216 | return localVarReturnValue, localVarHttpResponse, newErr 217 | } 218 | newErr.model = v 219 | return localVarReturnValue, localVarHttpResponse, newErr 220 | } 221 | return localVarReturnValue, localVarHttpResponse, newErr 222 | } 223 | 224 | return localVarReturnValue, localVarHttpResponse, nil 225 | } 226 | 227 | /* 228 | ClusterRecordKeepingApiService Register a Kubernetes cluster with CNS Manager. 229 | The API takes kubeconfig of a given cluster as an input. Make sure to copy the contents of the Cluster KubeConfig to a file. The kubeconfig is stored securely inside a k8s secret on the cluster where CNS manager is deployed. The API additionally takes optional params like CSI driver clusterId or CSI driver namespace and config secret name to read cluster-id from the CSI secret. 230 | * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). 231 | * @param optional nil or *ClusterRecordKeepingApiRegisterclusterOpts - Optional Parameters: 232 | * @param "ClusterKubeConfigFile" (optional.*os.File) - 233 | * @param "CsiDriverSecretName" (optional.String) - (Optional) Refers to the name of the config secret of vsphere-csi-driver. 234 | * @param "CsiDriverSecretNamespace" (optional.String) - (Optional) Refers to the namespace of the config secret of vsphere-csi-driver. 235 | * @param "ClusterId" (optional.String) - (Optional) Cluster Id of the cluster to be registered with the CNS Manager. This cluster Id needs to be the same that's used in the vSphere CSI driver config of the Kubernetes cluster. For vanilla Kubernetes distributions deployed using [VMware's recommended way](https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/2.0/vmware-vsphere-csp-getting-started/GUID-BFF39F1D-F70A-4360-ABC9-85BDAFBE8864.html), it will be present in the CSI secret and will be auto-computed from the provided kubeConfig. For other Kubernetes distributions, please check their docs to determine how they're configuring clusterId for the vSphere CSI driver in the Kubernetes cluster. 236 | @return RegisterClusterResult 237 | */ 238 | 239 | type ClusterRecordKeepingApiRegisterclusterOpts struct { 240 | ClusterKubeConfigFile optional.Interface 241 | CsiDriverSecretName optional.String 242 | CsiDriverSecretNamespace optional.String 243 | ClusterId optional.String 244 | } 245 | 246 | func (a *ClusterRecordKeepingApiService) Registercluster(ctx context.Context, localVarOptionals *ClusterRecordKeepingApiRegisterclusterOpts) (RegisterClusterResult, *http.Response, error) { 247 | var ( 248 | localVarHttpMethod = strings.ToUpper("Post") 249 | localVarPostBody interface{} 250 | localVarFileName string 251 | localVarFileBytes []byte 252 | localVarReturnValue RegisterClusterResult 253 | ) 254 | 255 | // create path and map variables 256 | localVarPath := a.client.cfg.BasePath + "/registercluster" 257 | 258 | localVarHeaderParams := make(map[string]string) 259 | localVarQueryParams := url.Values{} 260 | localVarFormParams := url.Values{} 261 | 262 | if localVarOptionals != nil && localVarOptionals.CsiDriverSecretName.IsSet() { 263 | localVarQueryParams.Add("csiDriverSecretName", parameterToString(localVarOptionals.CsiDriverSecretName.Value(), "")) 264 | } 265 | if localVarOptionals != nil && localVarOptionals.CsiDriverSecretNamespace.IsSet() { 266 | localVarQueryParams.Add("csiDriverSecretNamespace", parameterToString(localVarOptionals.CsiDriverSecretNamespace.Value(), "")) 267 | } 268 | if localVarOptionals != nil && localVarOptionals.ClusterId.IsSet() { 269 | localVarQueryParams.Add("clusterId", parameterToString(localVarOptionals.ClusterId.Value(), "")) 270 | } 271 | // to determine the Content-Type header 272 | localVarHttpContentTypes := []string{"multipart/form-data"} 273 | 274 | // set Content-Type header 275 | localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes) 276 | if localVarHttpContentType != "" { 277 | localVarHeaderParams["Content-Type"] = localVarHttpContentType 278 | } 279 | 280 | // to determine the Accept header 281 | localVarHttpHeaderAccepts := []string{"application/json"} 282 | 283 | // set Accept header 284 | localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts) 285 | if localVarHttpHeaderAccept != "" { 286 | localVarHeaderParams["Accept"] = localVarHttpHeaderAccept 287 | } 288 | var localVarFile *os.File 289 | if localVarOptionals != nil && localVarOptionals.ClusterKubeConfigFile.IsSet() { 290 | localVarFileOk := false 291 | localVarFile, localVarFileOk = localVarOptionals.ClusterKubeConfigFile.Value().(*os.File) 292 | if !localVarFileOk { 293 | return localVarReturnValue, nil, reportError("clusterKubeConfigFile should be *os.File") 294 | } 295 | } 296 | if localVarFile != nil { 297 | fbs, _ := ioutil.ReadAll(localVarFile) 298 | localVarFileBytes = fbs 299 | localVarFileName = localVarFile.Name() 300 | localVarFile.Close() 301 | } 302 | r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) 303 | if err != nil { 304 | return localVarReturnValue, nil, err 305 | } 306 | 307 | localVarHttpResponse, err := a.client.callAPI(r) 308 | if err != nil || localVarHttpResponse == nil { 309 | return localVarReturnValue, localVarHttpResponse, err 310 | } 311 | 312 | localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) 313 | localVarHttpResponse.Body.Close() 314 | if err != nil { 315 | return localVarReturnValue, localVarHttpResponse, err 316 | } 317 | 318 | if localVarHttpResponse.StatusCode < 300 { 319 | // If we succeed, return the data, otherwise pass on to decode error. 320 | err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 321 | if err == nil { 322 | return localVarReturnValue, localVarHttpResponse, err 323 | } 324 | } 325 | 326 | if localVarHttpResponse.StatusCode >= 300 { 327 | newErr := GenericSwaggerError{ 328 | body: localVarBody, 329 | error: localVarHttpResponse.Status, 330 | } 331 | if localVarHttpResponse.StatusCode == 200 { 332 | var v RegisterClusterResult 333 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 334 | if err != nil { 335 | newErr.error = err.Error() 336 | return localVarReturnValue, localVarHttpResponse, newErr 337 | } 338 | newErr.model = v 339 | return localVarReturnValue, localVarHttpResponse, newErr 340 | } 341 | if localVarHttpResponse.StatusCode == 0 { 342 | var v ModelError 343 | err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type")) 344 | if err != nil { 345 | newErr.error = err.Error() 346 | return localVarReturnValue, localVarHttpResponse, newErr 347 | } 348 | newErr.model = v 349 | return localVarReturnValue, localVarHttpResponse, newErr 350 | } 351 | return localVarReturnValue, localVarHttpResponse, newErr 352 | } 353 | 354 | return localVarReturnValue, localVarHttpResponse, nil 355 | } 356 | --------------------------------------------------------------------------------