diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 673f6da0ad11..3ba812e0a8a8 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1617,6 +1617,8 @@ "label.offeringid": "Offering ID", "label.offeringtype": "Compute Offering type", "label.ok": "OK", +"label.ssvm.open.cert.page": "Open Certificate Page", +"label.retry.upload": "Retry Upload", "label.only.end.date.and.time": "Only end date and time", "label.only.start.date.and.time": "Only start date and time", "label.open.documentation": "Open documentation", @@ -3667,6 +3669,9 @@ "message.upload.iso.failed.description": "Failed to upload ISO.", "message.upload.template.failed.description": "Failed to upload Template", "message.upload.volume.failed": "Volume upload failed", +"message.ssvm.cert.untrusted": "Unable to reach the upload server.", +"message.ssvm.cert.trust.instructions": "The upload server may be using a self-signed or untrusted certificate. Click 'Open Certificate Page' to open the server in a new browser tab, accept the certificate warning, then return here and click 'Retry Upload'. If the server remains unreachable, contact your administrator.", +"message.ssvm.unreachable.retry": "The upload server is still unreachable. If it uses a self-signed certificate, please accept it in the opened tab and try again.", "message.user.not.permitted.api": "User is not permitted to use the API", "message.validate.equalto": "Please enter the same value again.", "message.validate.max": "Please enter a value less than or equal to {0}.", diff --git a/ui/src/utils/ssvmProbe.js b/ui/src/utils/ssvmProbe.js new file mode 100644 index 000000000000..55690aea8981 --- /dev/null +++ b/ui/src/utils/ssvmProbe.js @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +const SSVM_PROBE_TIMEOUT_MS = 5000 +export async function probeSsvmCert (origin) { + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), SSVM_PROBE_TIMEOUT_MS) + try { + await fetch(origin, { method: 'HEAD', mode: 'no-cors', signal: controller.signal }) + return true + } catch (e) { + return false + } finally { + clearTimeout(timeoutId) + } +} diff --git a/ui/src/views/image/RegisterOrUploadIso.vue b/ui/src/views/image/RegisterOrUploadIso.vue index 37ae369727fe..3fa2519d5deb 100644 --- a/ui/src/views/image/RegisterOrUploadIso.vue +++ b/ui/src/views/image/RegisterOrUploadIso.vue @@ -19,11 +19,27 @@
- + {{ $t('message.upload.file.processing') }} +
+ +
+ {{ $t('label.cancel') }} + + {{ $t('label.ssvm.open.cert.page') }} + + + {{ $t('label.retry.upload') }} + +
+
1) { @@ -502,6 +533,7 @@ export default { fileList.forEach(file => { formData.append('files[]', file) }) + this.uploading = true this.uploadPercentage = 0 axios.post(this.uploadParams.postURL, formData, @@ -529,6 +561,8 @@ export default { description: `${this.$t('message.upload.iso.failed.description')} - ${e}`, duration: 0 }) + }).finally(() => { + this.uploading = false }) }, handleSubmit (e) { @@ -583,18 +617,18 @@ export default { } params.format = 'ISO' this.loading = true - api('getUploadParamsForIso', params).then(json => { + api('getUploadParamsForIso', params).then(async json => { this.uploadParams = (json.postuploadisoresponse && json.postuploadisoresponse.getuploadparams) ? json.postuploadisoresponse.getuploadparams : '' - const response = this.handleUpload() if (this.userdataid !== null) { this.linkUserdataToTemplate(this.userdataid, json.postuploadisoresponse.iso[0].id) } - if (response === 'upload successful') { - this.$notification.success({ - message: this.$t('message.success.upload'), - description: this.$t('message.success.upload.iso.description') - }) + this.ssvmOrigin = new URL(this.uploadParams.postURL).origin + const trusted = await probeSsvmCert(this.ssvmOrigin) + if (!trusted) { + this.ssvmCertUntrusted = true + return } + this.handleUpload() }).catch(error => { this.$notifyError(error) }).finally(() => { diff --git a/ui/src/views/image/RegisterOrUploadTemplate.vue b/ui/src/views/image/RegisterOrUploadTemplate.vue index 3ada9f6fd531..82df4d80c0e2 100644 --- a/ui/src/views/image/RegisterOrUploadTemplate.vue +++ b/ui/src/views/image/RegisterOrUploadTemplate.vue @@ -19,11 +19,27 @@
- + {{ $t('message.upload.file.processing') }} +
+ +
+ {{ $t('label.cancel') }} + + {{ $t('label.ssvm.open.cert.page') }} + + + {{ $t('label.retry.upload') }} + +
+
{ formData.append('files[]', file) }) + this.uploading = true this.uploadPercentage = 0 axios.post(this.uploadParams.postURL, formData, @@ -639,6 +670,8 @@ export default { this.closeAction() }).catch(e => { this.$notifyError(e) + }).finally(() => { + this.uploading = false }) }, fetchCustomHypervisorName () { @@ -1124,12 +1157,18 @@ export default { duration: 0 }) } - api('getUploadParamsForTemplate', params).then(json => { + api('getUploadParamsForTemplate', params).then(async json => { this.uploadParams = (json.postuploadtemplateresponse && json.postuploadtemplateresponse.getuploadparams) ? json.postuploadtemplateresponse.getuploadparams : '' - this.handleUpload() if (this.userdataid !== null) { this.linkUserdataToTemplate(this.userdataid, json.postuploadtemplateresponse.template[0].id) } + this.ssvmOrigin = new URL(this.uploadParams.postURL).origin + const trusted = await probeSsvmCert(this.ssvmOrigin) + if (!trusted) { + this.ssvmCertUntrusted = true + return + } + this.handleUpload() }).catch(error => { this.$notifyError(error) }).finally(() => { diff --git a/ui/src/views/storage/UploadLocalVolume.vue b/ui/src/views/storage/UploadLocalVolume.vue index 3a0bf4e129fe..9a94a58dc968 100644 --- a/ui/src/views/storage/UploadLocalVolume.vue +++ b/ui/src/views/storage/UploadLocalVolume.vue @@ -16,13 +16,29 @@ // under the License.