diff --git a/app/scripts/controllers/attachPVC.js b/app/scripts/controllers/attachPVC.js index e719e4444b..978ee722da 100644 --- a/app/scripts/controllers/attachPVC.js +++ b/app/scripts/controllers/attachPVC.js @@ -118,6 +118,58 @@ angular.module('openshiftConsole') $scope.$watchGroup(['attach.resource', 'attach.allContainers'], updateMountPaths); $scope.$watch('attach.containers', updateMountPaths, true); + var setVolumeMount = function(podTemplate, name, mountPath, subPath, readOnly) { + var noError = true; + _.each(podTemplate.spec.containers, function(container) { + if (!isContainerSelected(container)) { + return; + } + + var stopIteration = false; + var newVolumeMount = + StorageService.createVolumeMount(name, mountPath, subPath, readOnly); + if (!container.volumeMounts) { + container.volumeMounts = []; + } + + _.each(container.volumeMounts, function(mount) { + // if a new volumeMount matches an existing mountPath, + // fail if their names differ. This can happen when the + // "overwrite" option is specified. + if (mount.mountPath === newVolumeMount.mountPath && mount.name !== newVolumeMount.name) { + displayError('The volume mount "' + mount.mountPath + '" with name "' + mount.name +'" already exists for container "' + container.name + '"'); + noError = false; + stopIteration = true; + return false; + } + }); + + if (stopIteration) { + return false; + } + + _.each(container.volumeMounts, function(mount, idx) { + // if the volume mount we are trying to add already exists, + // replace the existing mount with the newly created one. + // This can happen when the "overwrite" option is specified. + if (mount.name === newVolumeMount.name) { + container.volumeMounts[idx] = newVolumeMount; + noError = true; + stopIteration = true; + return false; + } + }); + + if (stopIteration) { + return false; + } + + container.volumeMounts.push(newVolumeMount); + }); + + return noError; + }; + // load resources required to show the page (list of pvcs and deployment or deployment config) var load = function() { DataService.get(resourceGroupVersion, $routeParams.name, context).then( @@ -175,16 +227,11 @@ angular.module('openshiftConsole') var readOnly = $scope.attach.readOnly; if (mountPath) { // for each container in the pod spec, add the new volume mount - angular.forEach(podTemplate.spec.containers, function(container) { - if (isContainerSelected(container)) { - var newVolumeMount = - StorageService.createVolumeMount(name, mountPath, subPath, readOnly); - if (!container.volumeMounts) { - container.volumeMounts = []; - } - container.volumeMounts.push(newVolumeMount); - } - }); + // angular.forEach(podTemplate.spec.containers, function(container) { + if(!setVolumeMount(podTemplate, name, mountPath, subPath, readOnly)) { + $scope.disableInputs = false; + return; + } } // add the new volume to the pod template @@ -192,7 +239,15 @@ angular.module('openshiftConsole') if (!podTemplate.spec.volumes) { podTemplate.spec.volumes = []; } - podTemplate.spec.volumes.push(newVolume); + + // if the newly created volume already exists, only + // fail if the "overwrite" option was not set + var volumeExists = _.some(podTemplate.spec.volumes, { name: newVolume.name }); + + + if (!$scope.attach.overwrite || ($scope.attach.overwrite && !volumeExists)) { + podTemplate.spec.volumes.push(newVolume); + } DataService.update(resourceGroupVersion, resource.metadata.name, $scope.attach.resource, context).then( function() { diff --git a/app/views/attach-pvc.html b/app/views/attach-pvc.html index f0eb14a80f..26cca90ef1 100644 --- a/app/views/attach-pvc.html +++ b/app/views/attach-pvc.html @@ -83,6 +83,7 @@

Volume

ng-model="attach.mountPath" ng-pattern="/^\/.*$/" osc-unique="existingMountPaths" + osc-unique-disabled="attach.overwrite" placeholder="example: /data" autocorrect="off" autocapitalize="none" @@ -143,6 +144,7 @@

Volume

name="volumeName" ng-model="attach.volumeName" osc-unique="existingVolumeNames" + osc-unique-disabled="attach.overwrite" ng-pattern="/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/" maxlength="63" placeholder="(generated if empty)" @@ -171,6 +173,18 @@

Volume

+
+
+ +
+ Overwrite the volume mount config (if it already exists). +
+
+
+
\n" + "
\n" + "\n" + - "\n" + + "\n" + "
\n" + "Mount path for the volume inside the container. If not specified, the volume will not be mounted automatically.\n" + "
\n" + @@ -1064,7 +1064,7 @@ angular.module('openshiftConsoleTemplates', []).run(['$templateCache', function( "
\n" + "\n" + "\n" + - "\n" + + "\n" + "
\n" + "Unique name used to identify this volume. If not specified, a volume name is generated.\n" + "
\n" + @@ -1087,6 +1087,17 @@ angular.module('openshiftConsoleTemplates', []).run(['$templateCache', function( "
\n" + "
\n" + "\n" + + "
\n" + + "Overwrite the volume mount config (if it already exists).\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "\n" + diff --git a/dist/scripts/vendor.js b/dist/scripts/vendor.js index 7e9f6dcadd..bb21ae96ee 100644 --- a/dist/scripts/vendor.js +++ b/dist/scripts/vendor.js @@ -77269,7 +77269,7 @@ e.exports = '
\n
\n }, function(e, t) { e.exports = '
\n
\n
\n \n
\n
\n
\n {{$ctrl.imageStream.name}}\n {{$ctrl.istag.name}}\n
\n
\n \n {{tag}}\n \n
\n
\n
\n
\n

\n

\n Sample Repository:\n \x3c!-- TODO: Use Git link filter, needs to be added to origin-web-common --\x3e\n \n

\n
\n
\n'; }, function(e, t) { -e.exports = '
\n
\n
\n \n Pending\n

\n {{$ctrl.name}} is being created in {{$ctrl.selectedProject | displayName}}.\n

\n
\n
\n
\n
\n \n Success\n

\n {{$ctrl.name}} has been created in {{$ctrl.selectedProject | displayName}} successfully.\n

\n
\n
\n
\n \n \n
\n
\n

\n Continue to the project overview to check the status of your application as it builds and deploys.\n

\n
\n
\n
\n \n

\n {{$ctrl.name}} failed to create in {{$ctrl.selectedProject | displayName}}.\n

\n
\n
\n \n {{$ctrl.error.data.message | upperFirst}}\n \n \n An error occurred creating the application.\n \n
\n \x3c!-- TODO: Improve error message presentation --\x3e\n
    \n
  • \n {{failure.data.message}}\n
  • \n
\n
\n
\n'; +e.exports = '
\n
\n

\n \n

\n

\n The application is being created\n

\n
\n
\n
\n \n Success\n

\n \n {{$ctrl.name}} has been created in {{$ctrl.selectedProject.metadata.name}} successfully\n \n

\n
\n
\n
\n \n \n
\n
\n

\n Continue to your project to check the status of your application as it builds and deploys.\n

\n
\n
\n
\n \n

\n Error creating {{$ctrl.name}} in\n {{$ctrl.selectedProject | displayName}}\n

\n
\n
\n \n {{$ctrl.error.data.message | upperFirst}}\n \n \n An error occurred creating the application.\n \n
\n \x3c!-- TODO: Improve error message presentation --\x3e\n
    \n
  • \n {{failure.data.message}}\n
  • \n
\n
\n \n
\n'; }, function(e, t) { e.exports = '
\n
\n
\n \n \n \n
\n
\n'; }, function(e, t) { @@ -77277,11 +77277,11 @@ e.exports = '
\n \n \n Plan {{$ctrl.selectedPlan.externalMetadata.displayName}}\n \n \n {{$ctrl.selectedPlan.description}}\n

\n

\n

\n
\n
\n'; +e.exports = '
\n
\n
\n \n \n
\n
\n
\n {{$ctrl.serviceName}}\n
\n
\n \n {{tag}}\n \n
\n \n
\n
\n
\n

\n \n Plan {{$ctrl.selectedPlan.externalMetadata.displayName}}\n \n \n {{$ctrl.selectedPlan.description}}\n

\n

\n

\n
\n
\n'; }, function(e, t) { e.exports = '
\n
\n
\n

Select a Plan

\n
\n \n
\n
\n
\n
\n'; }, function(e, t) { -e.exports = '
\n
\n
\n
\n \n Pending\n

\n {{$ctrl.serviceClass.name}} is being provisioned in {{$ctrl.projectDisplayName}}.\n

\n
\n

The binding will be created after the service has been provisioned.

\n

This may take several minutes.

\n

Continue to the project overview to check the status of your service.

\n
\n
\n
\n
\n \n Error\n

\n {{$ctrl.serviceClass.name}} failed to provision in {{$ctrl.projectDisplayName}}.\n

\n
\n
\n \n {{$ctrl.error.message}}\n \n \n An error occurred provisioning the service.\n \n
\n
\n
\n
\n \n Success\n

\n {{$ctrl.serviceInstance.metadata.name}} has been added to {{$ctrl.projectDisplayName}} successfully.\n

\n
\n
\n \n
\n

Continue to the project overview to bind this service to your application. Binding this service creates a secret containing the information necessary for your application to use the service.

\n
\n
\n

or

\n

Browse resources for {{$ctrl.serviceClass.name}}:

\n \n
\n
\n'; +e.exports = '
\n
\n
\n

\n \n

\n

\n The service is being provisioned\n

\n
\n
\n
\n
\n \n

\n Error provisioning {{$ctrl.serviceClass.name}} in\n {{$ctrl.projectDisplayName}}\n

\n
\n
\n \n {{$ctrl.error.message}}\n \n \n An error occurred provisioning the service.\n \n
\n
\n
\n
\n \n Success\n

\n \n {{$ctrl.serviceInstance.metadata.name}} has been added to {{$ctrl.projectDisplayName}} successfully\n \n

\n
\n
\n
\n \n \n
\n
\n \n Info\n Continue to your project to bind this service to your application. Binding this service creates a secret containing the information necessary for your application to use the service.\n
\n \n
\n'; }, function(e, t) { e.exports = '
\n \n
    \n
  1. \n \n
  2. \n
\n
\n
\n\n \n
\n
\n'; }, function(e, t) { diff --git a/dist/styles/main.css b/dist/styles/main.css index 3a4be646c1..95a5400e0e 100644 --- a/dist/styles/main.css +++ b/dist/styles/main.css @@ -3,7 +3,6 @@ body,figure{margin:0} .text-left,caption,th{text-align:left} .navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.pre-scrollable{max-height:340px} .c3 svg,html{-webkit-tap-highlight-color:transparent} -.list-view-pf-top-align .list-view-pf-actions,.list-view-pf-top-align .list-view-pf-checkbox{align-self:flex-start} .fa,.font-icon,.glyphicon{-moz-osx-font-smoothing:grayscale} @font-face{font-family:"Open Sans";font-style:normal;font-weight:300;src:url(../styles/fonts/OpenSans-Light-webfont.eot);src:local("Open Sans Light"),local("OpenSans-Light"),url(../styles/fonts/OpenSans-Light-webfont.eot?#iefix) format("embedded-opentype"),url(../styles/fonts/OpenSans-Light-webfont.woff2) format("woff2"),url(../styles/fonts/OpenSans-Light-webfont.woff) format("woff"),url(../styles/fonts/OpenSans-Light-webfont.ttf) format("truetype"),url(../styles/fonts/OpenSans-Light-webfont.svg#OpenSans) format("svg")} @font-face{font-family:"Open Sans";font-style:normal;font-weight:400;src:url(../styles/fonts/OpenSans-Regular-webfont.eot);src:local("Open Sans"),local("OpenSans"),url(../styles/fonts/OpenSans-Regular-webfont.eot?#iefix) format("embedded-opentype"),url(../styles/fonts/OpenSans-Regular-webfont.woff2) format("woff2"),url(../styles/fonts/OpenSans-Regular-webfont.woff) format("woff"),url(../styles/fonts/OpenSans-Regular-webfont.ttf) format("truetype"),url(../styles/fonts/OpenSans-Regular-webfont.svg#OpenSans) format("svg")} @@ -3144,6 +3143,7 @@ a.disabled{color:#8b8d8f;cursor:not-allowed;text-decoration:none} } .list-view-pf-actions{float:right;margin-bottom:20px;margin-left:20px;margin-top:20px;order:2} .list-view-pf-actions .dropdown-kebab-pf,.list-view-pf-actions button,.list-view-pf-actions>a{margin-left:10px} +.list-view-pf-top-align .list-view-pf-actions{align-self:flex-start} .list-view-pf-additional-info{align-items:center;display:flex;flex-wrap:wrap} @media (min-width:992px){.list-view-pf-additional-info{flex:1 0 auto;float:left;width:50%} } @@ -3158,6 +3158,7 @@ a.disabled{color:#8b8d8f;cursor:not-allowed;text-decoration:none} @media (min-width:992px){.list-view-pf-body{align-items:center;display:flex;flex-direction:row} } .list-view-pf-checkbox{border-right:1px solid #d1d1d1;float:left;margin-right:15px;padding:3px 10px 3px 0} +.list-view-pf-top-align .list-view-pf-checkbox{align-self:flex-start} .list-view-pf-stacked .list-view-pf-description{display:block;flex:none} @media (min-width:992px){.list-view-pf-description{align-items:center;display:flex;float:left;width:50%} } @@ -3349,6 +3350,7 @@ a.disabled{color:#8b8d8f;cursor:not-allowed;text-decoration:none} .navbar-pf-vertical .nav .nav-item-iconic:focus,.navbar-pf-vertical .nav .nav-item-iconic:hover{color:#fff;background-color:transparent} .navbar-pf-vertical .nav .nav-item-iconic:focus .caret,.navbar-pf-vertical .nav .nav-item-iconic:focus .fa,.navbar-pf-vertical .nav .nav-item-iconic:focus .glyphicon,.navbar-pf-vertical .nav .nav-item-iconic:focus .pficon,.navbar-pf-vertical .nav .nav-item-iconic:hover .caret,.navbar-pf-vertical .nav .nav-item-iconic:hover .fa,.navbar-pf-vertical .nav .nav-item-iconic:hover .glyphicon,.navbar-pf-vertical .nav .nav-item-iconic:hover .pficon{color:#fff} .navbar-pf-vertical .nav .nav-item-iconic .badge{background-color:#0088ce;border-radius:20px;color:#fff;cursor:pointer;font-size:10px;font-weight:700;margin-top:-15px;margin-left:-12px;padding:2px 4px;min-width:10px;min-height:10px} +.bind-form .bind-choice strong,.delete-resource-modal h1,.results-message strong,.toast-notifications-list-pf,.word-break{word-wrap:break-word;min-width:0} .navbar-pf-vertical .nav .nav-item-iconic .caret,.navbar-pf-vertical .nav .nav-item-iconic .fa,.navbar-pf-vertical .nav .nav-item-iconic .pficon{color:#d1d1d1;font-size:17px} .navbar-pf-vertical .nav .nav-item-iconic .caret{font-size:13px;width:auto} .navbar-pf-vertical .nav .open>.nav-item-iconic,.navbar-pf-vertical .nav .open>.nav-item-iconic:focus,.navbar-pf-vertical .nav .open>.nav-item-iconic:hover{background:0 0} @@ -3953,18 +3955,18 @@ div.hopscotch-bubble .hopscotch-bubble-arrow-container.left,div.hopscotch-bubble .bind-form .bind-description{padding-left:20px} .bind-form .bind-choice{margin-top:10px} .bind-form .bind-choice:first-of-type{margin-top:0} -.bind-form .bind-choice strong{word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0} +.bind-form .bind-choice strong{word-break:break-word;overflow-wrap:break-word} .bind-form .help-block.service-instance-name{display:inline-block;margin-top:0} .bind-form .radio label[disabled]{cursor:not-allowed} .bind-service-selection{margin-bottom:5px} .results-info{margin-bottom:20px;margin-top:30px} .results-message{line-height:1.4;margin-top:0;padding-left:10px} -.results-message strong{word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0} +.results-message strong{word-break:break-word;overflow-wrap:break-word} .results-status{display:flex;font-size:16px} .results-status .pficon{margin-top:3px} .results-status .spinner.spinner-sm{height:16px;margin:3px 0 0;width:16px} .delete-resource-modal{background-color:#f1f1f1} -.delete-resource-modal h1{font-size:21px;font-weight:500;margin-bottom:20px;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0} +.delete-resource-modal h1{font-size:21px;font-weight:500;margin-bottom:20px;word-break:break-word;overflow-wrap:break-word} .delete-resource-modal p{font-size:16px} .delete-resource-modal .help-block{margin-top:0px;margin-bottom:10px} .dialog-btn{float:right!important;margin-right:10px} @@ -4002,7 +4004,7 @@ div.hopscotch-bubble .hopscotch-bubble-number{background:#0088ce;border-radius:1 div.hopscotch-bubble .hopscotch-nav-button{border-radius:0;font-family:"Open Sans";font-size:12px} div.hopscotch-bubble .hopscotch-nav-button.next{background-color:#0088ce;background-image:none;margin-left:5px} div.hopscotch-bubble .hopscotch-nav-button.prev{color:#030303} -.word-break{word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0} +.word-break{word-break:break-word;overflow-wrap:break-word} .word-break-all{word-break:break-all;word-break:break-word;overflow-wrap:break-word} .origin-modal-popup{background-color:#fff;bottom:0;color:#363636;left:0;position:fixed;padding:0 20px 20px;right:0;top:0;z-index:1050} @media (min-width:768px){.origin-modal-popup{border:1px solid #bbb;bottom:auto;box-shadow:0 6px 12px rgba(0,0,0,.175);left:auto;margin-top:15px;right:auto;top:auto} @@ -4021,7 +4023,7 @@ div.hopscotch-bubble .hopscotch-nav-button.prev{color:#030303} .toast-action-divider{color:#9c9c9c} .toast-notification-details .truncated-content{white-space:pre-line} .toast-notification-message{font-weight:700;margin-right:5px} -.toast-notifications-list-pf{top:28px;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0;z-index:1055} +.toast-notifications-list-pf{top:28px;word-break:break-word;overflow-wrap:break-word;z-index:1055} @media (max-width:767px){.toast-notifications-list-pf{max-width:calc(100% - 40px)} } .toast-notifications-list-pf>div.ng-enter{animation:toastSlideIn .2s ease-out} @@ -4110,9 +4112,10 @@ div.hopscotch-bubble .hopscotch-nav-button.prev{color:#030303} .order-service-details .order-service-details-top .service-icon .image img{max-height:60px;max-width:60px} } .order-service-details .order-service-details-top .service-title{font-size:18px;font-weight:600;display:-webkit-box;line-height:1.4em;max-height:4.2em;overflow:hidden;padding:0!important;-webkit-box-orient:vertical;-webkit-line-clamp:3} +.order-service-config .review-message strong,.order-service-details .order-service-details-top .service-title-area{overflow-wrap:break-word;min-width:0;word-break:break-word;word-wrap:break-word} @media (min-width:450px){.order-service-details .order-service-details-top .service-title{font-size:22px} } -.order-service-details .order-service-details-top .service-title-area{flex:1 1 0%;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;min-width:0} +.order-service-details .order-service-details-top .service-title-area{flex:1 1 0%} .order-service-details .order-service-details-top .sub-title{font-size:20px;font-weight:600;color:#72767b} .order-service-details .order-service-description-block{margin-top:15px} .order-service-details .order-service-description-block .description{white-space:pre-wrap} @@ -4132,7 +4135,6 @@ div.hopscotch-bubble .hopscotch-nav-button.prev{color:#030303} .order-service-config .footer-panel a:hover{color:#fff;text-decoration:none} .order-service-config h3{line-height:1.4;margin-top:0} .order-service-config h3+.alert{margin-top:20px} -.order-service-config .or{margin-left:15px} .order-service-config .select-plans .plan-name{display:inline-block;font-size:14px;margin-bottom:5px;margin-top:-2px} .order-service-config .select-plans .radio{margin-top:15px} .order-service-config .sub-title{margin:-5px 0 10px 25px} @@ -4140,6 +4142,10 @@ div.hopscotch-bubble .hopscotch-nav-button.prev{color:#030303} .order-service-config .related-services-container{background-color:#ededed;margin-top:62px;padding:14px;display:flex;align-items:center} .order-service-config .related-services-container .related-services-label{font-size:14px;font-weight:600;padding-right:14px} .order-service-config .related-services-container .related-services-row .card{background-color:#fff;border:1px solid #bbb;float:right;margin-right:6px;padding:11px} +.order-service-config .review-message{padding-left:10px} +.order-service-config .review-status{display:flex;font-size:16px} +.order-service-config .review-status .pficon{margin-top:3px} +.order-service-config .review-status .spinner.spinner-sm{height:16px;margin:3px 0 0;width:16px} .order-service-wizard-step .schema-form-fieldset{margin-bottom:40px;margin-top:40px} .order-service-wizard-step .schema-form-fieldset legend{border-bottom:none;border-top:solid 1px rgba(0,0,0,.15);margin-bottom:10px;padding-top:20px} body.overlay-open,body.overlay-open .landing,body.overlay-open .landing-side-bar{overflow:hidden}