| 805 | |
| 806 | |
| 807 | Future<Nothing> VolumeManagerProcess::_attachVolume(const string& volumeId) |
| 808 | { |
| 809 | CHECK(volumes.contains(volumeId)); |
| 810 | VolumeState& volumeState = volumes.at(volumeId).state; |
| 811 | |
| 812 | if (volumeState.state() == VolumeState::NODE_READY) { |
| 813 | return Nothing(); |
| 814 | } |
| 815 | |
| 816 | if (volumeState.state() != VolumeState::CREATED && |
| 817 | volumeState.state() != VolumeState::CONTROLLER_PUBLISH && |
| 818 | volumeState.state() != VolumeState::CONTROLLER_UNPUBLISH) { |
| 819 | return Failure( |
| 820 | "Cannot attach volume '" + volumeId + "' in " + |
| 821 | stringify(volumeState.state()) + " state"); |
| 822 | } |
| 823 | |
| 824 | if (!controllerCapabilities->publishUnpublishVolume) { |
| 825 | // Since this is a no-op, no need to checkpoint here. |
| 826 | volumeState.set_state(VolumeState::NODE_READY); |
| 827 | return Nothing(); |
| 828 | } |
| 829 | |
| 830 | // A previously failed `ControllerUnpublishVolume` call can be recovered |
| 831 | // through an extra `ControllerUnpublishVolume` call. See: |
| 832 | // https://github.com/container-storage-interface/spec/blob/v1.1.0/spec.md#controllerunpublishvolume // NOLINT |
| 833 | if (volumeState.state() == VolumeState::CONTROLLER_UNPUBLISH) { |
| 834 | // Retry after recovering the volume to `CREATED` state. |
| 835 | return _detachVolume(volumeId) |
| 836 | .then(process::defer(self(), &Self::_attachVolume, volumeId)); |
| 837 | } |
| 838 | |
| 839 | if (volumeState.state() == VolumeState::CREATED) { |
| 840 | volumeState.set_state(VolumeState::CONTROLLER_PUBLISH); |
| 841 | checkpointVolumeState(volumeId); |
| 842 | } |
| 843 | |
| 844 | LOG(INFO) |
| 845 | << "Calling '/csi.v1.Controller/ControllerPublishVolume' for volume '" |
| 846 | << volumeId << "'"; |
| 847 | |
| 848 | ControllerPublishVolumeRequest request; |
| 849 | request.set_volume_id(volumeId); |
| 850 | request.set_node_id(CHECK_NOTNONE(nodeId)); |
| 851 | *request.mutable_volume_capability() = |
| 852 | evolve(volumeState.volume_capability()); |
| 853 | request.set_readonly(volumeState.readonly()); |
| 854 | *request.mutable_volume_context() = volumeState.volume_context(); |
| 855 | |
| 856 | return call( |
| 857 | CONTROLLER_SERVICE, &Client::controllerPublishVolume, std::move(request)) |
| 858 | .then(process::defer(self(), [this, volumeId]( |
| 859 | const ControllerPublishVolumeResponse& response) { |
| 860 | CHECK(volumes.contains(volumeId)); |
| 861 | VolumeState& volumeState = volumes.at(volumeId).state; |
| 862 | volumeState.set_state(VolumeState::NODE_READY); |
| 863 | *volumeState.mutable_publish_context() = response.publish_context(); |
| 864 | |