@spinnaker/appengine 2026.0.3 → 2026.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{module as e,extend as n,copy as t}from"angular";import{HelpContentsRegistry as a,ConfirmationModalService as i,RecentHistoryService as r,InstanceReader as o,InstanceWriter as l,StageConstants as s,AppListExtractor as c,LoadBalancerWriter as p,TaskMonitor as d,AccountService as g,Registry as u,CloudProviderRegistry as m,FormikFormField as h,ReactSelectInput as v,StageArtifactSelectorDelegate as f,excludeAllTypesExcept as b,ArtifactTypePatterns as y,FormikStageConfig as C,FormValidator as S,ExecutionDetailsTasks as w,ExecutionArtifactTab as G,SETTINGS as A,StorageAccountReader as k,NgAppEngineDeployArtifactDelegate as $,ExpectedArtifactSelectorViewController as B,NgAppengineConfigArtifactDelegate as x,SERVER_GROUP_WRITER as T,StageArtifactSelector as D,withErrorBoundary as E,TaskExecutor as F,ServerGroupWarningMessageService as L,ServerGroupReader as P,ApplicationNameValidator as I,DeploymentStrategyRegistry as O}from"@spinnaker/core";import{cloneDeep as N,flattenDeep as z,uniq as R,difference as M,filter as U,chain as H,has as q,camelCase as V,get as j,reduce as W,set as _,merge as J,map as K,mapValues as Y}from"lodash";import Q from"react";import{Subject as X,from as Z}from"rxjs";import{takeUntil as ee}from"rxjs/operators";import{react2angular as ne}from"react2angular";import te from"classnames";const ae={bindings:{component:"<"},template:'\n <dt>HTTPS</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpsUrl}}" target="_blank">{{$ctrl.component.httpsUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpsUrl"></copy-to-clipboard>\n </dl>\n <dt>HTTP</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpUrl}}" target="_blank">{{$ctrl.component.httpUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpUrl"></copy-to-clipboard>\n </dl>\n '},ie="spinnaker.appengine.componentUrlDetails.component";e(ie,[]).component("appengineComponentUrlDetails",ae);class re{constructor(e){this.$filter=e}$onInit(){this.label||(this.label=this.$filter("robotToHuman")(this.key))}}re.$inject=["$filter"];const oe={bindings:{label:"@",key:"@",component:"<"},transclude:{keyLabel:"?keyText",valueLabel:"?valueLabel"},template:'\n <dt ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.label}}<span ng-transclude="keyLabel"></span></dt>\n <dd ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.component[$ctrl.key]}}<span ng-transclude="valueLabel"></span></dd>\n ',controller:re},le="spinnaker.appengine.conditionalDescriptionListItem";e(le,[]).component("appengineConditionalDtDd",oe);const se="spinnaker.appengine.loadBalancer.createMessage.component";e(se,[]).component("appengineLoadBalancerMessage",{bindings:{showCreateMessage:"<",columnOffset:"@",columns:"@"},templateUrl:"appengine/src/common/loadBalancerMessage.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/common/loadBalancerMessage.component.html",'<div class="row">\n <div class="col-md-offset-{{ $ctrl.columnOffset || 0 }} col-md-{{ $ctrl.columns || 12 }}">\n <div class="well">\n <p>\n <span ng-if="$ctrl.showCreateMessage">Spinnaker cannot create a load balancer for App Engine.</span>\n A Spinnaker load balancer maps to an App Engine service, which is specified in a version\'s\n <code>app.yaml</code>.\n </p>\n <p>For example, the following <code>app.yaml</code></p>\n <pre class="text-left" style="white-space: pre-line">\n \t\t\truntime: python27\n \t\t\tapi_version: 1\n …\n \t\t\tservice: mobile\n …\n \t\t</pre\n >\n <p>\n deploys to the <code>mobile</code> service. If you do not specify a service, your version will be deployed to\n the <code>default</code> service.\n </p>\n <p>\n If a service does not exist when a version is deployed, it will be created. It will then be editable as a load\n balancer within Spinnaker.\n </p>\n <p>\n <a href="https://cloud.google.com/appengine/docs/python/config/appref" target="_blank"\n >App Engine Documentation</a\n >\n </p>\n </div>\n </div>\n</div>\n')}]);[{key:"appengine.serverGroup.gcs.repositoryUrl",value:"The full URL to the GCS bucket or TAR file containing the source files for this deployment,\n including 'gs://'. For example, <b>gs://my-bucket/my-app</b> or <b>gs://my-bucket/app.tar</b>."},{key:"appengine.serverGroup.git.repositoryUrl",value:"The full URL to the git repository containing the source files for this deployment,\n including protocol. For example, <b>https://github.com/spinnaker/deck.git</b>"},{key:"appengine.serverGroup.gitCredentialType",value:"The credential type that will be used to access the git repository for this deployment.\n You can configure these credentials alongside your App Engine credentials."},{key:"appengine.serverGroup.branch",value:"The name of the branch in the above git repository to be used for this deployment."},{key:"appengine.serverGroup.applicationDirectoryRoot",value:"(Optional) Path to the directory root of the application to be deployed,\n starting from the root of the git repository. This is the directory from which\n <code>gcloud app deploy</code> will be run."},{key:"appengine.serverGroup.configFilepaths",value:"Paths to App Engine application configuration files, starting from the application directory root,\n specified above. In most cases, this will be <code>app.yaml</code> or <code>cron.yaml</code>,\n but could be <code>path/to/app.yaml</code> or <code>path/to/cron.yaml</code>."},{key:"appengine.serverGroup.configFiles",value:"<p>(Optional) The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file). These files should not conflict with the config filepaths above:\n if you include, for example, the contents of an <code>app.yaml</code>\n file here, you should <b>not</b> specify the file path to an <code>app.yaml</code> above.<br></p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.configFilesRequired",value:"<p>The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file).</p>\n <p>An <code>app.yaml</code> file is required and must have <code>runtime: custom</code> in\n order to deploy successfully.</p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.matchBranchOnRegex",value:"(Optional) A Jenkins trigger may produce details from multiple repositories and branches.\n Spinnaker will use the regex specified here to help resolve a branch for the deployment.\n If Spinnaker cannot resolve exactly one branch from the trigger, this pipeline will fail."},{key:"appengine.serverGroup.promote",value:"If selected, the newly deployed server group will receive all traffic."},{key:"appengine.serverGroup.stopPreviousVersion",value:"If selected, the previously running server group in this server group's <b>service</b>\n (Spinnaker load balancer) will be stopped. This option will be respected only if this server group will\n be receiving all traffic and the previous server group is using manual scaling."},{key:"appengine.serverGroup.containerImageUrl",value:"The full URL to the container image to use for deployment. The URL must be one of the valid GCR hostnames,\n for example <b>gcr.io/my-project/image:tag</b>"},{key:"appengine.serverGroup.suppress-version-string",value:'Spinnaker automatically versions your server groups. This means deployments through Spinnaker receive a\n short version string at the end of their name, like "v001". In most cases you will want to keep this\n version as part of the name. Preventing this string from being added to your server group will stop\n it from being considered when rolling back, promoting new versions or executing deployment strategies.\n If you are certain that you do not want the version string applied to this server group then check\n this box.'},{key:"appengine.loadBalancer.shardBy.cookie",value:'Diversion based on a specially named cookie, "GOOGAPPUID." The cookie must be set by the application itself or no diversion will occur.'},{key:"appengine.loadBalancer.shardBy.ip",value:"Diversion based on applying the modulus operation to a fingerprint of the IP address."},{key:"appengine.loadBalancer.migrateTraffic",value:"If selected, traffic will be gradually shifted to a single version. For gradual traffic migration,\n the target version must be located within instances that are configured for\n both warmup requests and automatic scaling.\n Gradual traffic migration is not supported in the App Engine flexible environment."},{key:"appengine.loadBalancer.allocations",value:"An allocation is the percent of traffic directed to a server group."},{key:"appengine.loadBalancer.textLocator",value:"Either the name of a server group, or a Spinnaker Pipeline Expression\n that resolves to the name of a server group."},{key:"appengine.instance.availability",value:"\n An instance's <b>availability</b> is determined by its version (Spinnaker server group).\n <ul>\n <li>Manual scaling versions use resident instances</li>\n <li>Basic scaling versions use dynamic instances</li>\n <li>Auto scaling versions use dynamic instances - but if you specify a number, N,\n of minimum idle instances, the first N instances will be resident,\n and additional dynamic instances will be created as necessary.\n </li>\n </ul>"},{key:"appengine.instance.averageLatency",value:"Average latency over the last minute in milliseconds."},{key:"appengine.instance.vmStatus",value:"Status of the virtual machine where this instance lives."},{key:"appengine.instance.qps",value:"Average queries per second over the last minute."},{key:"appengine.instance.errors",value:"Number of errors since this instance was started."},{key:"appengine.instance.requests",value:"Number of requests since this instance was started."}].forEach((e=>a.register(e.key,e.value)));class ce{constructor(e,n,t){this.$q=e,this.app=n,this.state={loading:!0},this.upToolTip="An App Engine instance is 'Up' if a load balancer is directing traffic to its server group.",this.outOfServiceToolTip="\n An App Engine instance is 'Out Of Service' if no load balancers are directing traffic to its server group.",this.app.ready().then((()=>this.retrieveInstance(t))).then((e=>{this.instance=e,this.state.loading=!1})).catch((()=>{this.instanceIdNotFound=t.instanceId,this.state.loading=!1}))}terminateInstance(){const e=N(this.instance),n=`${this.instance.name.substring(0,10)}...`;e.placement={},e.instanceId=e.name;const t={application:this.app,title:"Terminating "+n,onTaskComplete(){this.$state.includes("**.instanceDetails",{instanceId:e.name})&&this.$state.go("^")}};i.confirm({header:"Really terminate "+n+"?",buttonText:"Terminate "+n,account:e.account,taskMonitorConfig:t,submitMethod:()=>l.terminateInstance(e,this.app,{cloudProvider:"appengine"})})}retrieveInstance(e){const n=z([this.app.getDataSource("serverGroups").data,this.app.getDataSource("loadBalancers").data,this.app.getDataSource("loadBalancers").data.map((e=>e.serverGroups))]).find((n=>n.instances.some((n=>n.id===e.instanceId))));if(n){const t={region:n.region,account:n.account};return"serverGroup"===n.category&&(t.serverGroup=n.name),r.addExtraDataToLatest("instances",t),o.getInstanceDetails(n.account,n.region,e.instanceId).then((e=>(e.account=n.account,e.region=n.region,e)))}return this.$q.reject()}}ce.$inject=["$q","app","instance"];const pe="spinnaker.appengine.instanceDetails.controller";e(pe,[]).controller("appengineInstanceDetailsCtrl",ce);const de={bindings:{loadBalancer:"=",application:"<"},template:'\n <ng-form name="advancedSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Migrate Traffic <help-field key="appengine.loadBalancer.migrateTraffic"></help-field>\n </div>\n <div class="col-md-9">\n <div class="checkbox">\n <input type="checkbox" ng-disabled="$ctrl.disableMigrateTraffic() && !($ctrl.loadBalancer.migrateTraffic = false)" ng-model="$ctrl.loadBalancer.migrateTraffic">\n </div>\n </div>\n </div>\n </div>\n </ng-form>\n ',controller:class{constructor(){this.state={error:!1}}disableMigrateTraffic(){if(1!==this.loadBalancer.splitDescription.allocationDescriptions.length)return!0;{const e=this.loadBalancer.splitDescription.allocationDescriptions[0].serverGroupName,n=this.loadBalancer.serverGroups.find((n=>n.name===e));return!!n&&!n.allowsGradualTrafficMigration}}}},ge="spinnaker.appengine.loadBalancer.advancedSettings.component";e(ge,[]).component("appengineLoadBalancerAdvancedSettings",de);const ue={bindings:{allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},template:'\n <div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <ui-select ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n {{$select.selected}}\n </ui-select-match>\n <ui-select-choices repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search">\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"/>\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n </div>\n ',controller:class{getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}}},me="spinnaker.appengine.allocationConfigurationRow.component";e(me,[]).component("appengineAllocationConfigurationRow",ue);const he={bindings:{loadBalancer:"=",forPipelineConfig:"<",application:"<"},controller:class{$onInit(){this.updateServerGroupOptions()}addAllocation(){const e=this.serverGroupsWithoutAllocation();e.length?(this.loadBalancer.splitDescription.allocationDescriptions.push({serverGroupName:e[0],allocation:0,locatorType:"fromExisting"}),this.loadBalancer.splitDescription.allocationDescriptions.length>1&&!this.loadBalancer.splitDescription.shardBy&&(this.loadBalancer.splitDescription.shardBy="IP"),this.updateServerGroupOptions()):this.forPipelineConfig&&this.loadBalancer.splitDescription.allocationDescriptions.push({allocation:0,locatorType:"text",serverGroupName:""})}removeAllocation(e){this.loadBalancer.splitDescription.allocationDescriptions.splice(e,1),this.updateServerGroupOptions()}allocationIsInvalid(){return 100!==this.loadBalancer.splitDescription.allocationDescriptions.reduce(((e,n)=>e+n.allocation),0)}updateServerGroupOptions(){this.serverGroupOptions=this.serverGroupsWithoutAllocation()}showAddButton(){return!!this.forPipelineConfig||this.serverGroupsWithoutAllocation().length>0}showShardByOptions(){return this.loadBalancer.splitDescription.allocationDescriptions.length>1||this.loadBalancer.migrateTraffic}initializeAsTextInput(e){return!!this.forPipelineConfig&&!this.loadBalancer.serverGroups.map((e=>e.name)).includes(e)}serverGroupsWithoutAllocation(){const e=this.loadBalancer.splitDescription.allocationDescriptions.map((e=>e.serverGroupName)),n=this.loadBalancer.serverGroups.map((e=>e.name));return M(n,e)}},templateUrl:"appengine/src/loadBalancer/configure/wizard/basicSettings.component.html"},ve="spinnaker.appengine.loadBalancerSettings.component";e(ve,[]).component("appengineLoadBalancerBasicSettings",he),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/basicSettings.component.html",'<ng-form name="basicSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Allocations\n <help-field key="appengine.loadBalancer.allocations"></help-field>\n </div>\n <div class="col-md-9">\n <div ng-if="!$ctrl.forPipelineConfig">\n <appengine-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-allocation-configuration-row>\n </div>\n <div ng-if="$ctrl.forPipelineConfig">\n <appengine-stage-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n application="$ctrl.application"\n region="{{ $ctrl.loadBalancer.region }}"\n account="{{ $ctrl.loadBalancer.account || $ctrl.loadBalancer.credentials }}"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-stage-allocation-configuration-row>\n </div>\n <button class="add-new col-md-11" ng-if="$ctrl.showAddButton()" ng-click="$ctrl.addAllocation()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add allocation\n </button>\n </div>\n </div>\n <div class="form-group" ng-if="$ctrl.allocationIsInvalid()">\n <div class="col-md-12 text-center">\n <p class="alert alert-warning">Allocations must sum to 100%.</p>\n </div>\n </div>\n </div>\n\n <div class="row" ng-if="$ctrl.showShardByOptions()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Shard By</div>\n <div class="col-md-9">\n <form class="form-inline">\n <div class="form-group">\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="IP" />\n IP\n <help-field key="appengine.loadBalancer.shardBy.ip"></help-field>\n </label>\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="COOKIE" />\n Cookie\n <help-field key="appengine.loadBalancer.shardBy.cookie"></help-field>\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n</ng-form>\n')}]);class fe{static mapTargetCoordinateToLabel(e){const n=s.TARGET_LIST.find((n=>n.val===e));return n?n.label:null}$doCheck(){this.setInputViewValue()}setInputViewValue(){switch(this.allocationDescription.locatorType){case"text":case"fromExisting":this.inputViewValue=this.allocationDescription.serverGroupName;break;case"targetCoordinate":if(this.allocationDescription.cluster&&this.allocationDescription.target){const e=fe.mapTargetCoordinateToLabel(this.allocationDescription.target);this.inputViewValue=`${e} (${this.allocationDescription.cluster})`}else this.inputViewValue=null;break;default:this.inputViewValue=null}}}const be={bindings:{allocationDescription:"<"},controller:fe,template:'<input ng-model="$ctrl.inputViewValue" type="text" class="form-control input-sm" readonly/>'};const ye={bindings:{application:"<",region:"@",account:"@",allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},controller:class{constructor(){this.targets=s.TARGET_LIST}$onInit(){const e=c.clusterFilterForCredentialsAndRegion(this.account,this.region);this.clusterList=c.getClusters([this.application],e)}getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}onLocatorTypeChange(){this.serverGroupOptions.includes(this.allocationDescription.serverGroupName)||delete this.allocationDescription.serverGroupName,this.onAllocationChange()}},templateUrl:"appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html"},Ce="spinnaker.appengine.stageAllocationConfigurationRow.component";e(Ce,[]).component("appengineStageAllocationConfigurationRow",ye).component("appengineStageAllocationLabel",be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html",'<div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <appengine-stage-allocation-label allocation-description="$ctrl.allocationDescription">\n </appengine-stage-allocation-label>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input\n type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n</div>\n<div class="form-group">\n <div class="well col-md-11" style="padding-top: 5px; padding-bottom: 10px">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Locator</div>\n <div class="col-md-7">\n <form>\n <div class="form-group">\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="fromExisting"\n />\n Existing server group\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="targetCoordinate"\n />\n Coordinates\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="text"\n />\n Text input\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="form-group" ng-switch on="$ctrl.allocationDescription.locatorType">\n <div ng-switch-when="fromExisting">\n <div class="col-md-3 sm-label-right" style="padding-left: 0px">Server group</div>\n <div class="col-md-7">\n <ui-select\n ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices\n repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search"\n >\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <div ng-switch-when="targetCoordinate">\n <div class="col-md-3 sm-label-right">Cluster</div>\n <div class="col-md-7">\n <cluster-selector\n class="small"\n clusters="$ctrl.clusterList"\n model="$ctrl.allocationDescription.cluster"\n ></cluster-selector>\n </div>\n <div class="col-md-3 sm-label-right">Target</div>\n <div class="col-md-7">\n <target-select model="$ctrl.allocationDescription" options="$ctrl.targets"></target-select>\n </div>\n </div>\n <div ng-switch-when="text">\n <div class="col-md-3 sm-label-right">\n Text\n <help-field key="appengine.loadBalancer.textLocator"></help-field>\n </div>\n <div class="col-md-7">\n <input class="form-control input-sm" type="text" ng-model="$ctrl.allocationDescription.serverGroupName" />\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n')}]);class Se{static convertTrafficSplitToTrafficSplitDescription(e){const n=W(e.allocations,((e,n,t)=>e.concat({serverGroupName:t,allocation:n,locatorType:"fromExisting"})),[]);return{shardBy:e.shardBy,allocationDescriptions:n}}constructor(e){this.credentials=e.account||e.credentials,this.account=this.credentials,this.cloudProvider=e.cloudProvider,this.loadBalancerName=e.name,this.name=e.name,this.region=e.region,this.migrateTraffic=e.migrateTraffic||!1,this.serverGroups=e.serverGroups}mapAllocationsToDecimals(){this.splitDescription.allocationDescriptions.forEach((e=>{e.allocation=e.allocation/100}))}mapAllocationsToPercentages(){this.splitDescription.allocationDescriptions.forEach((e=>{e.allocation=Math.round(1e3*e.allocation)/10}))}}class we{constructor(e){this.$q=e}normalizeLoadBalancer(e){e.provider=e.type,e.instanceCounts=this.buildInstanceCounts(e.serverGroups),e.instances=[],e.serverGroups.forEach((n=>{n.account=e.account,n.region=e.region,n.detachedInstances&&(n.detachedInstances=n.detachedInstances.map((e=>({id:e})))),n.instances=n.instances.concat(n.detachedInstances||[]).map((n=>this.transformInstance(n,e)))}));const n=U(e.serverGroups,{isDisabled:!1});return e.instances=H(n).map("instances").flatten().value(),this.$q.resolve(e)}convertLoadBalancerForEditing(e,n){return n.getDataSource("loadBalancers").ready().then((()=>{const t=n.getDataSource("loadBalancers").data.find((n=>n.name===e.name&&(n.account===e.account||n.account===e.credentials)));return t&&(e.serverGroups=N(t.serverGroups)),e}))}convertLoadBalancerToUpsertDescription(e){return new Se(e)}buildInstanceCounts(e){const n=H(e).map("instances").flatten().reduce(((e,n)=>(q(n,"health.state")&&e[V(n.health.state)]++,e)),{up:0,down:0,outOfService:0,succeeded:0,failed:0,starting:0,unknown:0}).value();return n.outOfService+=H(e).map("detachedInstances").flatten().value().length,n}transformInstance(e,n){e.provider=n.type,e.account=n.account,e.region=n.region,e.loadBalancers=[n.name];const t=e.health||{};return e.healthState=j(e,"health.state")||"OutOfService",e.health=[t],e}}we.$inject=["$q"];const Ge="spinnaker.appengine.loadBalancer.transformer.service";function Ae(e,n){void 0===n&&(n={});var t=n.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css","top"===t&&a.firstChild?a.insertBefore(i,a.firstChild):a.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}e(Ge,[]).service("appengineLoadBalancerTransformer",we);Ae("appengine-load-balancer-basic-settings a.btn.btn-link {\n padding: 0;\n}\nappengine-load-balancer-basic-settings .form-group {\n margin-top: 0.4rem;\n}\nappengine-load-balancer-advanced-settings .checkbox input[type='checkbox'] {\n margin: 0 0 0.1rem 0;\n}\n");class ke{constructor(e,n,t,a,i,r,o,l,s){this.$scope=e,this.$state=n,this.$uibModalInstance=t,this.application=a,this.isNew=r,this.forPipelineConfig=o,this.appengineLoadBalancerTransformer=l,this.wizardSubFormValidation=s,this.state={loading:!0},this.submitButtonLabel=this.forPipelineConfig?"Done":"Update",this.isNew?this.heading="Create New Load Balancer":(this.heading=`Edit ${[i.name,i.region,i.account||i.credentials].join(":")}`,this.appengineLoadBalancerTransformer.convertLoadBalancerForEditing(i,a).then((e=>{this.loadBalancer=this.appengineLoadBalancerTransformer.convertLoadBalancerToUpsertDescription(e),i.split&&!this.loadBalancer.splitDescription?this.loadBalancer.splitDescription=Se.convertTrafficSplitToTrafficSplitDescription(i.split):this.loadBalancer.splitDescription=i.splitDescription,this.loadBalancer.mapAllocationsToPercentages(),this.setTaskMonitor(),this.initializeFormValidation(),this.state.loading=!1})))}submit(){const e=N(this.loadBalancer);return e.mapAllocationsToDecimals(),delete e.serverGroups,this.forPipelineConfig?this.$uibModalInstance.close(e):this.taskMonitor.submit((()=>p.upsertLoadBalancer(e,this.application,"Update")))}cancel(){this.$uibModalInstance.dismiss()}showSubmitButton(){return this.wizardSubFormValidation.subFormsAreValid()}setTaskMonitor(){this.taskMonitor=new d({application:this.application,title:"Updating your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete()})}initializeFormValidation(){this.wizardSubFormValidation.config({form:"form",scope:this.$scope}).register({page:"basic-settings",subForm:"basicSettingsForm",validators:[{watchString:"ctrl.loadBalancer.splitDescription",validator:e=>100===e.allocationDescriptions.reduce(((e,n)=>e+n.allocation),0),watchDeep:!0}]}).register({page:"advanced-settings",subForm:"advancedSettingsForm"})}onTaskComplete(){this.application.getDataSource("loadBalancers").refresh(),this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope,(()=>this.onApplicationRefresh()))}onApplicationRefresh(){if(this.$scope.$$destroyed)return;this.$uibModalInstance.dismiss();const e={name:this.loadBalancer.name,accountId:this.loadBalancer.credentials,region:this.loadBalancer.region,provider:"appengine"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",e):this.$state.go(".loadBalancerDetails",e)}}ke.$inject=["$scope","$state","$uibModalInstance","application","loadBalancer","isNew","forPipelineConfig","appengineLoadBalancerTransformer","wizardSubFormValidation"];const $e="spinnaker.appengine.loadBalancer.wizard.controller";e($e,[]).controller("appengineLoadBalancerWizardCtrl",ke);class Be{constructor(e,n,t,a,i){this.$uibModal=e,this.$state=n,this.$scope=t,this.app=i,this.state={loading:!0},this.dispatchRules=[],this.loadBalancerFromParams=a,this.app.getDataSource("loadBalancers").ready().then((()=>this.extractLoadBalancer()))}editLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",controller:"appengineLoadBalancerWizardCtrl as ctrl",size:"lg",resolve:{application:()=>this.app,loadBalancer:()=>N(this.loadBalancer),isNew:()=>!1,forPipelineConfig:()=>!1}})}deleteLoadBalancer(){const e={application:this.app,title:"Deleting "+this.loadBalancer.name};i.confirm({header:"Really delete "+this.loadBalancer.name+"?",buttonText:"Delete "+this.loadBalancer.name,body:this.getConfirmationModalBodyHtml(),account:this.loadBalancer.account,taskMonitorConfig:e,submitMethod:()=>{const e={cloudProvider:this.loadBalancer.cloudProvider,loadBalancerName:this.loadBalancer.name,credentials:this.loadBalancer.account};return p.deleteLoadBalancer(e,this.app)}})}canDeleteLoadBalancer(){return"default"!==this.loadBalancer.name}extractLoadBalancer(){this.loadBalancer=this.app.getDataSource("loadBalancers").data.find((e=>e.name===this.loadBalancerFromParams.name&&e.account===this.loadBalancerFromParams.accountId)),this.loadBalancer?(this.state.loading=!1,this.buildDispatchRules(),this.app.getDataSource("loadBalancers").onRefresh(this.$scope,(()=>this.extractLoadBalancer()))):this.autoClose()}buildDispatchRules(){this.dispatchRules=[],this.loadBalancer&&this.loadBalancer.dispatchRules&&this.loadBalancer.dispatchRules.forEach((e=>{e.service===this.loadBalancer.name&&this.dispatchRules.push(e.domain+e.path)}))}getConfirmationModalBodyHtml(){const e=this.loadBalancer.serverGroups.map((e=>e.name)),n=!!e&&e.length>0,t=!!e&&e.length>1;if(n){if(t){const n=e.map((e=>`<li>${e}</li>`)).join("");return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy the following server groups:\n <ul>\n ${n}\n </ul>\n </p>\n </div>\n `}return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy <b>${e[0]}</b>.\n </p>\n </div>\n `}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}}Be.$inject=["$uibModal","$state","$scope","loadBalancer","app"];const xe="spinnaker.appengine.loadBalancerDetails.controller";e(xe,[]).controller("appengineLoadBalancerDetailsCtrl",Be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]);const Te="spinnaker.appengine.loadBalancer.module";e(Te,[me,xe,ge,ve,Ge,$e,Ce]);class De{}De.PLATFORM="App Engine Service";class Ee{constructor(e){this.$scope=e,e.platformHealth=De.PLATFORM}setStageRegion(){const e=this.$scope.accounts.find((e=>e.name===this.$scope.stage.credentials));e&&e.name&&g.getAccountDetails(e.name).then((e=>{this.$scope.stage.region=e.region}))}setStageCloudProvider(){this.$scope.stage.cloudProvider="appengine"}setAccounts(){return g.listAccounts("appengine").then((e=>{this.$scope.accounts=e}))}setTargets(){this.$scope.targets=s.TARGET_LIST,this.$scope.stage.target||(this.$scope.stage.target=this.$scope.targets[0].val)}setStageCredentials(){!this.$scope.stage.credentials&&this.$scope.application.defaultCredentials.appengine&&(this.$scope.stage.credentials=this.$scope.application.defaultCredentials.appengine)}}Ee.$inject=["$scope"];class Fe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials()}}Fe.$inject=["$scope"];const Le="spinnaker.appengine.pipeline.stage.destroyAsgStage";e(Le,[]).config((()=>{u.pipeline.registerStage({provides:"destroyServerGroup",key:"destroyServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to destroy a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineDestroyAsgStageCtrl",Fe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",'<div ng-controller="appengineDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n single-region="true"\n disable-region-select="true"\n on-account-update="destroyAsgStageCtrl.setStageRegion()"\n component="stage"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",'<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Pe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Pe.$inject=["$scope"];const Ie="spinnaker.appengine.pipeline.stage.disableAsgStage";e(Ie,[]).config((()=>{u.pipeline.registerStage({provides:"disableServerGroup",key:"disableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to disable a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineDisableAsgStageCtrl",Pe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",'<div ng-controller="appengineDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="disableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",'<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Oe{constructor(e,n,t){this.$uibModal=e,this.$uibModalInstance=n,this.application=t,this.state={loading:!0},this.initialize()}submit(){const e=m.getValue("appengine","loadBalancer"),n=this.$uibModal.open({templateUrl:e.createLoadBalancerTemplateUrl,controller:`${e.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.application,loadBalancer:()=>N(this.selectedLoadBalancer),isNew:()=>!1,forPipelineConfig:()=>!0}}).result;this.$uibModalInstance.close(n)}cancel(){this.$uibModalInstance.dismiss()}initialize(){this.application.getDataSource("loadBalancers").ready().then((()=>{this.loadBalancers=this.application.loadBalancers.data.filter((e=>"appengine"===e.cloudProvider)),this.loadBalancers.length&&(this.selectedLoadBalancer=this.loadBalancers[0]),this.state.loading=!1}))}}Oe.$inject=["$uibModal","$uibModalInstance","application"];const Ne="spinnaker.appengine.loadBalancerChoiceModal.controller";e(Ne,[]).controller("appengineLoadBalancerChoiceModelCtrl",Oe);class ze{constructor(e,n){this.$scope=e,this.$uibModal=n,e.stage.loadBalancers=e.stage.loadBalancers||[],e.stage.cloudProvider="appengine"}addLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",controller:"appengineLoadBalancerChoiceModelCtrl as ctrl",resolve:{application:()=>this.$scope.application}}).result.then((e=>{this.$scope.stage.loadBalancers.push(e)})).catch((()=>{}))}editLoadBalancer(e){const n=m.getValue("appengine","loadBalancer");this.$uibModal.open({templateUrl:n.createLoadBalancerTemplateUrl,controller:`${n.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.$scope.application,loadBalancer:()=>N(this.$scope.stage.loadBalancers[e]),isNew:()=>!1,forPipelineConfig:()=>!0}}).result.then((n=>{this.$scope.stage.loadBalancers[e]=n})).catch((()=>{}))}removeLoadBalancer(e){this.$scope.stage.loadBalancers.splice(e,1)}}ze.$inject=["$scope","$uibModal"];const Re="spinnaker.appengine.pipeline.stage.editLoadBalancerStage";e(Re,[Ne]).config((()=>{u.pipeline.registerStage({label:"Edit Load Balancer",description:"Edits a load balancer",key:"upsertAppEngineLoadBalancers",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",executionConfigSections:["editLoadBalancerConfig","taskStatus"],controller:"appengineEditLoadBalancerStageCtrl",controllerAs:"editLoadBalancerStageCtrl",validators:[]})})).controller("appengineEditLoadBalancerStageCtrl",ze),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Select Load Balancer</h4>\n </div>\n <div class="modal-body" ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="modal-body" ng-if="!ctrl.state.loading">\n <div class="alert alert-warning" ng-if="ctrl.loadBalancers.length === 0">\n <p>This application has no App Engine load balancers.</p>\n </div>\n <form\n role="form"\n name="form"\n class="form-horizontal"\n ng-submit="ctrl.submit()"\n ng-if="ctrl.loadBalancers.length > 0"\n >\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Load Balancer</b>\n </div>\n <div class="col-md-7">\n <ui-select class="form-control input-sm" ng-model="ctrl.selectedLoadBalancer">\n <ui-select-match>\n <account-tag account="$select.selected.account"></account-tag>\n <span style="margin-left: 5px">{{$select.selected.name}}</span>\n </ui-select-match>\n <ui-select-choices repeat="loadBalancer in ctrl.loadBalancers | filter: $select.search">\n <account-tag account="loadBalancer.account"></account-tag>\n <span style="margin-left: 5px" ng-bind-html="loadBalancer.name"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </form>\n </div>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <button class="btn btn-primary" ng-if="ctrl.loadBalancers.length > 0" ng-click="ctrl.submit()">\n <span class="far fa-check-circle"></span> Edit\n </button>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",'<div class="well well-sm clearfix" ng-if="!pipeline.strategy">\n <div class="row">\n <div class="col-md-12">\n <h4 class="text-left">Load Balancers</h4>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n <td class="condensed-actions">\n <a class="btn btn-sm btn-link" href ng-click="editLoadBalancerStageCtrl.editLoadBalancer($index)">\n <span class="glyphicon glyphicon-edit" uib-tooltip="Edit"></span\n ></a>\n <a\n class="btn btn-sm btn-link pad-left"\n href\n ng-click="editLoadBalancerStageCtrl.removeLoadBalancer($index)"\n >\n <span class="glyphicon glyphicon-trash" uib-tooltip="Remove"></span>\n </a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="8">\n <button class="btn btn-block btn-sm add-new" ng-click="editLoadBalancerStageCtrl.addLoadBalancer()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add load balancer\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'editLoadBalancerConfig\'">\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.context.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]);class Me extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Me.$inject=["$scope"];const Ue="spinnaker.appengine.pipeline.stage.enableAsgStage";e(Ue,[]).config((()=>{u.pipeline.registerStage({provides:"enableServerGroup",key:"enableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineEnableAsgStageCtrl",Me),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",'<div ng-controller="appengineEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="enableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",'<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class He extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setStageCredentials();const n=e.stage;void 0===n.shrinkToSize&&(n.shrinkToSize=1),void 0===n.allowDeleteActive&&(n.allowDeleteActive=!1),void 0===n.retainLargerOverNewer&&(n.retainLargerOverNewer="false"),n.retainLargerOverNewer=n.retainLargerOverNewer.toString()}pluralize(e,n){return 1===n?e:e+"s"}}He.$inject=["$scope"];const qe="spinnaker.appengine.pipeline.stage.shrinkClusterStage";e(qe,[]).config((function(){u.pipeline.registerStage({provides:"shrinkCluster",key:"shrinkCluster",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"shrinkToSize",fieldLabel:"shrink to [X] Server Groups"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("appengineShrinkClusterStageCtrl",He),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",'<div ng-controller="appengineShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="shrinkClusterStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Shrink Options">\n <div class="form-inline">\n Shrink to\n <input\n type="number"\n min="0"\n required\n ng-model="stage.shrinkToSize"\n class="form-control input-sm"\n style="width: 50px"\n />\n {{shrinkClusterStageCtrl.pluralize(\'server group\', stage.shrinkToSize)}}, keeping the\n <select class="form-control input-sm" ng-model="stage.retainLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n </div>\n </stage-config-field>\n <div class="form-group">\n <div class="col-md-offset-3 col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="stage.allowDeleteActive" />\n Allow deletion of active server groups\n </label>\n </div>\n </div>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]);class Ve extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Ve.$inject=["$scope"];const je="spinnaker.appengine.pipeline.stage.startServerGroupStage";e(je,[]).config((()=>{u.pipeline.registerStage({label:"Start Server Group",description:"Starts a server group.",key:"startAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",executionConfigSections:["startServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",controller:"appengineStartServerGroupStageCtrl",controllerAs:"startServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})})).controller("appengineStartServerGroupStageCtrl",Ve),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",'<div ng-controller="appengineStartServerGroupStageCtrl as startServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="startServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'startServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",'<span class="task-label"> Start Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class We extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then((()=>{super.setStageRegion()})),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}We.$inject=["$scope"];const _e="spinnaker.appengine.pipeline.stage.stopServerGroupStage";e(_e,[]).config((()=>{u.pipeline.registerStage({label:"Stop Server Group",description:"Stops a server group.",key:"stopAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",executionConfigSections:["stopServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",controller:"appengineStopServerGroupStageCtrl",controllerAs:"stopServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})})).controller("appengineStopServerGroupStageCtrl",We),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",'<div ng-controller="appengineStopServerGroupStageCtrl as stopServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="stopServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",'<div ng-controller="appengineStopServerGroupExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'stopServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",'<span class="task-label"> Stop Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Je="spinnaker.appengine.pipeline.module";e(Je,[Le,Ie,Re,Ue,qe,je,_e]);class Ke extends Q.Component{constructor(e){super(e),this.destroy$=new X,this.setRegionList=e=>{const{application:n}=this.props,t=n=>!n||n.account===e;Z(n.ready()).pipe(ee(this.destroy$)).subscribe((()=>{const e=c.getRegions([n],t);e.sort(),this.setState({availableRegions:e})}))},this.accountChanged=e=>{this.setRegionList(e)};const n=e.credentialsField||"credentials";this.state={availableRegions:[],cloudProvider:e.cloudProvider,componentName:e.componentName||"",credentialsField:n}}componentDidMount(){const{componentName:e,formik:n}=this.props,{credentialsField:t}=this.state,a=j(n.values,e?`${e}.${t}`:`${t}`,void 0);this.setRegionList(a)}componentWillUnmount(){this.destroy$.next()}render(){const{accounts:e}=this.props,{credentialsField:n,availableRegions:t,componentName:a}=this.state;return Q.createElement("div",{className:"col-md-9"},Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.${n}`:`${n}`,label:"Account",input:n=>Q.createElement(v,{...n,stringOptions:e&&e.map((e=>e.name)),clearable:!1}),onChange:this.accountChanged,required:!0})),Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.region`:"region",label:"Region",input:e=>Q.createElement(v,{...e,stringOptions:t,clearable:!1}),required:!0})))}}const Ye=class extends Q.Component{constructor(){super(...arguments),this.destroy$=new X,this.state={accounts:[]},this.onTemplateArtifactEdited=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,null),this.props.formik.setFieldValue(`${n}.artifact`,e),this.props.formik.setFieldValue(`${n}.account`,e.artifactAccount)},this.onTemplateArtifactSelected=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,e),this.props.formik.setFieldValue(`${n}.artifact`,null)},this.removeInputArtifact=e=>{this.props.formik.setFieldValue(e,null)},this.getInputArtifact=(e,n)=>e[n]?e[n]:{account:"",id:""}}componentDidMount(){Z(g.listAccounts("appengine")).pipe(ee(this.destroy$)).subscribe((e=>this.setState({accounts:e})))}render(){const e=this.props.formik.values,n=this.state.accounts;return Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Basic Settings")),Q.createElement("div",null,Q.createElement(Ke,{componentName:"",accounts:n,application:this.props.application,cloudProvider:"appengine",credentialsField:"account",formik:this.props.formik})),Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Configuration Settings")),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"cronArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"cronArtifact").id,label:"Cron Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"cronArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"cronArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("cronArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"dispatchArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"dispatchArtifact").id,label:"Dispatch Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"dispatchArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"dispatchArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("dispatchArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"indexArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"indexArtifact").id,label:"Index Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"indexArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"indexArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("indexArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(e,"queueArtifact").artifact,excludedArtifactTypePatterns:Ye.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(e,"queueArtifact").id,label:"Queue Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"queueArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"queueArtifact"),pipeline:this.props.pipeline,stage:e})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("queueArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))))}};let Qe=Ye;function Xe(e){const n=new S(e);return n.field("account").required(),n.field("region").required(),n.validateForm()}Qe.excludedArtifactTypes=b(y.BITBUCKET_FILE,y.CUSTOM_OBJECT,y.EMBEDDED_BASE64,y.GCS_OBJECT,y.GITHUB_FILE,y.GITLAB_FILE,y.S3_OBJECT,y.HTTP_FILE,y.ORACLE_OBJECT);u.pipeline.registerStage({label:"Deploy App Engine Configuration",description:"Deploy index, dispatch, cron, and queue configuration to App Engine.",key:"deployAppEngineConfiguration",component:function({application:e,pipeline:n,stage:t,updateStage:a}){const i=Q.useMemo((()=>({...N(t)})),[]);return Q.createElement(C,{application:e,onChange:a,pipeline:n,stage:i,validate:Xe,render:e=>Q.createElement(Qe,{...e})})},producesArtifacts:!1,cloudProvider:"appengine",executionDetailsSections:[w,G],validateFn:Xe});const Ze=A.providers.appengine||{defaults:{}};var en,nn;Ze&&(Ze.resetToOriginal=A.resetProvider("appengine")),(nn=en||(en={})).GCS="gcs",nn.GIT="git",nn.ARTIFACT="artifact",nn.CONTAINER_IMAGE="containerImage";const tn=class{constructor(e){this.$q=e}static getTriggerOptions(e){return(e.triggers||[]).filter((e=>"git"===e.type||"jenkins"===e.type||"travis"===e.type)).map((e=>"git"===e.type?{source:e.source,project:e.project,slug:e.slug,branch:e.branch,type:"git"}:{master:e.master,job:e.job,type:e.type}))}static getExpectedArtifacts(e){return e.expectedArtifacts||[]}buildNewServerGroupCommand(e,n,t="create"){null==n&&(n="appengine");const a={accounts:g.getAllAccountDetailsForProvider("appengine"),storageAccounts:k.getStorageAccounts()},i={mode:t,submitButtonLabel:this.getSubmitButtonLabel(t),disableStrategySelection:"create"===t};return this.$q.all(a).then((t=>{const a=this.getCredentials(t.accounts),r=this.getRegion(t.accounts,a);return{application:e.name,backingData:t,viewState:i,fromArtifact:!1,credentials:a,region:r,selectedProvider:n,interestingHealthProviderNames:[],sourceType:en.GIT}}))}buildServerGroupCommandFromExisting(e,n){return this.buildNewServerGroupCommand(e,"appengine","clone").then((e=>(e.stack=n.stack,e.freeFormDetails=n.detail,e)))}buildNewServerGroupCommandForPipeline(e,n){return this.$q.when({viewState:{pipeline:n,stage:e},backingData:{triggerOptions:tn.getTriggerOptions(n),expectedArtifacts:tn.getExpectedArtifacts(n)}})}buildServerGroupCommandFromPipeline(e,n,t,a){return this.buildNewServerGroupCommand(e,"appengine","editPipeline").then((e=>e={...e,...n,backingData:{...e.backingData,triggerOptions:tn.getTriggerOptions(a),expectedArtifacts:tn.getExpectedArtifacts(a)},credentials:n.account||e.credentials,viewState:{...e.viewState,stage:t,pipeline:a}}))}getCredentials(e){const n=(e||[]).map((e=>e.name)),t=Ze.defaults.account;return n.includes(t)?t:n[0]}getRegion(e,n){const t=e.find((e=>e.name===n));return t?t.region:null}getSubmitButtonLabel(e){switch(e){case"createPipeline":return"Add";case"editPipeline":return"Done";case"clone":return"Clone";default:return"Create"}}};let an=tn;an.$inject=["$q"];const rn="spinnaker.appengine.serverGroupCommandBuilder.service";e(rn,[]).service("appengineServerGroupCommandBuilder",an);class on{constructor(e,t,a,i){this.$scope=e,this.excludedGcsArtifactTypes=b(y.GCS_OBJECT),this.excludedContainerArtifactTypes=b(y.DOCKER_IMAGE),this.onExpectedArtifactEdited=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.expectedArtifactId=null,this.$scope.command.expectedArtifact=e}))},this.onExpectedArtifactSelected=e=>{this.onChangeExpectedArtifactId(e.id)},this.onChangeExpectedArtifactId=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.expectedArtifactId=e,this.$scope.command.expectedArtifact=null}))},this.onExpectedArtifactAccountSelected=e=>{this.$scope.$applyAsync((()=>{this.$scope.command.storageAccountName=e}))},n(this,a("BasicSettingsMixin",{$scope:e,imageReader:null,$uibModalStack:i,$state:t})),this.$scope.command.gitCredentialType||this.onAccountChange(),this.$scope.containerArtifactDelegate=new $(e,[y.DOCKER_IMAGE]),this.$scope.containerArtifactController=new B(this.$scope.containerArtifactDelegate),this.$scope.gcsArtifactDelegate=new $(e,[y.GCS_OBJECT]),this.$scope.gcsArtifactController=new B(this.$scope.gcsArtifactDelegate)}isGitSource(){return this.$scope.command.sourceType===en.GIT}isGcsSource(){return this.$scope.command.sourceType===en.GCS}isContainerImageSource(){return this.$scope.command.sourceType===en.CONTAINER_IMAGE}toggleResolveViaTrigger(){this.$scope.command.fromTrigger=!this.$scope.command.fromTrigger,delete this.$scope.command.trigger,delete this.$scope.command.branch}onTriggerChange(){_(this,"$scope.command.trigger.matchBranchOnRegex",void 0)}onAccountChange(){const e=this.findAccountInBackingData();e?(this.$scope.command.gitCredentialType=this.getSupportedGitCredentialTypes()[0],this.$scope.command.region=e.region):(this.$scope.command.gitCredentialType="NONE",delete this.$scope.command.region)}getSupportedGitCredentialTypes(){const e=this.findAccountInBackingData();return e&&e.supportedGitCredentialTypes?e.supportedGitCredentialTypes:["NONE"]}humanReadableGitCredentialType(e){switch(e){case"HTTPS_USERNAME_PASSWORD":return"HTTPS with username and password";case"HTTPS_GITHUB_OAUTH_TOKEN":return"HTTPS with Github OAuth token";case"SSH":return"SSH";default:return"No credentials"}}findAccountInBackingData(){return this.$scope.command.backingData.accounts.find((e=>this.$scope.command.credentials===e.name))}}on.$inject=["$scope","$state","$controller","$uibModalStack"];const ln="spinnaker.appengine.basicSettings.controller";e(ln,[]).controller("appengineServerGroupBasicSettingsCtrl",on);Ae(".appengine-server-group-wizard input[type='checkbox'] {\n margin-top: 0.75rem;\n}\n.appengine-server-group-wizard help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.appengine-server-group-wizard .artifact-configuration-section {\n border-bottom: 1px solid #eee;\n padding: 0 0 4px 0;\n margin: 0 0 5px 0;\n}\n.appengine-server-group-wizard .artifact-configuration-section.last-entry {\n border-bottom: none;\n}\n.appengine-server-group-wizard .artifact-configuration-section .Select,\n.appengine-server-group-wizard .artifact-configuration-section input {\n margin: 0 0 1px;\n}\n");class sn{constructor(e,n={id:"",account:""}){var t;const a={configurable:!1,enumerable:!1,writable:!1};this.id=null==n?void 0:n.id,this.account=n.account||(null==(t=null==n?void 0:n.artifact)?void 0:t.artifactAccount),this.artifact=null==n?void 0:n.artifact,Object.defineProperty(this,"$scope",{...a,value:e});const i=new x(this),r=new B(i);Object.defineProperty(this,"delegate",{...a,value:i}),Object.defineProperty(this,"controller",{...a,value:r})}}class cn{constructor(e){this.$scope=e,this.artifactAccounts=[],this.updateConfigArtifacts=e=>{this.$scope.$applyAsync((()=>{this.command.configArtifacts=e}))}}$onInit(){this.command.configFiles||(this.command.configFiles=[]),this.command.configArtifacts||(this.command.configArtifacts=[]),this.$scope.command||(this.$scope.command=this.command),this.command.configArtifacts=this.command.configArtifacts.map((e=>new sn(this.$scope,e))),g.getArtifactAccounts().then((e=>{this.artifactAccounts=e,this.command.configArtifacts.forEach((n=>{n.delegate.setAccounts(e),n.controller.updateAccounts(n.delegate.getSelectedExpectedArtifact())}))}))}addConfigFile(){this.command.configFiles.push("")}addConfigArtifact(){const e=new sn(this.$scope,{id:"",account:""});e.delegate.setAccounts(this.artifactAccounts),e.controller.updateAccounts(e.delegate.getSelectedExpectedArtifact()),this.command.configArtifacts.push(e)}deleteConfigFile(e){this.command.configFiles.splice(e,1)}deleteConfigArtifact(e){this.command.configArtifacts.splice(e,1)}mapTabToSpaces(e){if(9===e.which){e.preventDefault();const n=e.target.selectionStart,t=e.target.value;e.target.value=`${t.substring(0,n)} ${t.substring(n)}`,e.target.selectionStart+=2}}isContainerImageSource(){return this.command.sourceType===en.CONTAINER_IMAGE}}cn.$inject=["$scope"];const pn={bindings:{command:"="},controller:cn,templateUrl:"appengine/src/serverGroup/configure/wizard/configFiles.component.html"},dn="spinnaker.appengine.configFileConfigurer.component";e(dn,[]).component("appengineConfigFileConfigurer",pn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/configFiles.component.html",'<div class="form-horizontal container-fluid">\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Application Root\n <help-field class="help-field-absolute" key="appengine.serverGroup.applicationDirectoryRoot"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm"\n name="applicationDirectoryRoot"\n ng-model="$ctrl.command.applicationDirectoryRoot"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Config Filepaths <help-field key="appengine.serverGroup.configFilepaths"></help-field>\n </div>\n <div class="col-md-7">\n <ui-select\n multiple\n tagging\n tagging-label=""\n style="width: 380px"\n name="configFilepaths"\n ng-model="$ctrl.command.configFilepaths"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="filepath in []">\n <span ng-bind-html="filepath"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Config Files\n <span ng-if="!$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFiles"></help-field>\n </span>\n <span ng-if="$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFilesRequired"></help-field>\n </span>\n </div>\n <div ng-repeat="configFile in $ctrl.command.configFiles track by $index">\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $index > 0 }" style="margin-top: 5px">\n <textarea\n cols="46"\n rows="10"\n class="small"\n spellcheck="false"\n ng-keydown="$ctrl.mapTabToSpaces($event)"\n style="font-family: Menlo, Monaco, Consolas, \'Courier New\', monospace"\n ng-model="$ctrl.command.configFiles[$index]"\n ></textarea>\n </div>\n <div class="col-md-1" style="margin-top: 5px">\n <button type="button" class="btn btn-sm btn-default" ng-click="$ctrl.deleteConfigFile($index)">\n <span class="glyphicon glyphicon-trash"></span> Delete\n </button>\n </div>\n </div>\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $ctrl.command.configFiles.length > 0 }">\n <button class="btn btn-block btn-add-trigger add-new" ng-click="$ctrl.addConfigFile()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Config File\n </button>\n </div>\n <config-file-artifact-list\n ng-if="$ctrl.command.viewState.pipeline"\n config-artifacts="$ctrl.command.configArtifacts"\n pipeline="$ctrl.command.viewState.pipeline"\n stage="$ctrl.command.viewState.stage"\n update-config-artifacts="$ctrl.updateConfigArtifacts"\n >\n </config-file-artifact-list>\n </div>\n</div>\n')}]);const gn={bindings:{trigger:"<"},template:'\n <span ng-if="$ctrl.trigger.type === \'git\'">\n Resolved at runtime by <b>{{$ctrl.trigger.source}}</b> trigger: {{$ctrl.trigger.project}}/{{$ctrl.trigger.slug}}<span ng-if="$ctrl.trigger.branch">:{{$ctrl.trigger.branch}}</span>\n </span>\n <span ng-if="$ctrl.trigger.type === \'jenkins\'">\n Resolved at runtime by <b>Jenkins</b> trigger: {{$ctrl.trigger.master}}/{{$ctrl.trigger.job}}\n </span>\n '},un="spinnaker.appengine.dynamicBranchLabel.component";e(un,[]).component("appengineDynamicBranchLabel",gn);class mn{constructor(e,n,t,a,i,r){this.$scope=e,this.$uibModalInstance=n,this.serverGroupCommand=t,this.application=a,this.serverGroupWriter=i,this.pages={basicSettings:"appengine/src/serverGroup/configure/wizard/basicSettings.html",advancedSettings:"appengine/src/serverGroup/configure/wizard/advancedSettings.html"},this.state={loading:!0},["create","clone","editPipeline"].includes(j(t,"viewState.mode"))?(this.$scope.command=t,this.state.loading=!1,this.initialize()):r.buildNewServerGroupCommand(a,"appengine","createPipeline").then((e=>{this.$scope.command=J(e,t),this.$scope.command.viewState.pipeline=t.viewState.pipeline,this.$scope.command.viewState.stage=t.viewState.stage,this.state.loading=!1,this.initialize()}))}cancel(){this.$uibModalInstance.dismiss()}submit(){const e=this.$scope.command.viewState.mode;if(["editPipeline","createPipeline"].includes(e))return this.$uibModalInstance.close(this.$scope.command);{const e=t(this.$scope.command);e.viewState.mode="create";const n=()=>this.serverGroupWriter.cloneServerGroup(e,this.$scope.application);return this.taskMonitor.submit(n),null}}initialize(){this.$scope.application=this.application,this.taskMonitor=new d({application:this.application,title:"Creating your server group",modalInstance:this.$uibModalInstance}),this.$scope.showPlatformHealthOnlyOverride=this.application.attributes.platformHealthOnlyShowOverride,this.$scope.platformHealth=De.PLATFORM,this.application.attributes.platformHealthOnly&&(this.$scope.command.interestingHealthProviderNames=[De.PLATFORM])}}mn.$inject=["$scope","$uibModalInstance","serverGroupCommand","application","serverGroupWriter","appengineServerGroupCommandBuilder"];const hn="spinnaker.appengine.cloneServerGroup.controller";e(hn,[T,un,dn]).controller("appengineCloneServerGroupCtrl",mn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/basicSettings.html",'<div class="container-fluid form-horizontal" ng-controller="appengineServerGroupBasicSettingsCtrl as basicSettingsCtrl">\n <ng-form name="basicSettings">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Account</div>\n <div class="col-md-7">\n <account-select-field\n read-only="command.viewState.readOnlyFields.credentials"\n component="command"\n field="credentials"\n on-change="basicSettingsCtrl.onAccountChange()"\n accounts="command.backingData.accounts"\n provider="\'appengine\'"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Region</div>\n <div class="col-md-7">\n <input type="text" readonly class="form-control input-sm" name="region" ng-model="command.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Stack\n <help-field key="aws.serverGroup.stack"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.stackPattern"\n name="stack"\n ng-model="command.stack"\n />\n </div>\n </div>\n <div class="form-group row slide-in" ng-if="basicSettings.stack.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.) and underscore(_) special characters are allowed in the Stack field.</span>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Detail\n <help-field key="aws.serverGroup.detail"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.detailPattern"\n name="details"\n ng-model="command.freeFormDetails"\n />\n </div>\n </div>\n\n <div class="form-group row slide-in" ng-if="basicSettings.details.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.), underscore(_), and dash(-) special characters are allowed in the Detail field.</span>\n </div>\n </div>\n\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Source Type</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="git" /> Git </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="gcs" /> GCS </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="containerImage" /> Container Image </label>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGcsSource()">\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedGcsArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n GCS URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.gcs.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gcsUrl" ng-model="command.repositoryUrl" />\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGitSource()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Repository URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.git.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gitRepo" ng-model="command.repositoryUrl" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Credential Type\n <help-field class="help-field-absolute" key="appengine.serverGroup.gitCredentialType"></help-field>\n </div>\n <div class="col-md-7">\n <select\n class="form-control input-sm"\n ng-options="basicSettingsCtrl.humanReadableGitCredentialType(type) for type in basicSettingsCtrl.getSupportedGitCredentialTypes()"\n ng-model="command.gitCredentialType"\n ></select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Branch <help-field key="appengine.serverGroup.branch"></help-field></div>\n <div class="col-md-7">\n <input\n ng-if="!command.fromTrigger"\n type="text"\n required\n class="form-control input-sm"\n name="branch"\n ng-model="command.branch"\n />\n\n <ui-select\n ng-if="command.fromTrigger"\n ng-model="command.trigger"\n class="form-control input-sm"\n on-select="basicSettingsCtrl.onTriggerChange()"\n required\n >\n <ui-select-match allow-clear>\n <appengine-dynamic-branch-label trigger="command.trigger"></appengine-dynamic-branch-label>\n </ui-select-match>\n <ui-select-choices repeat="trigger in command.backingData.triggerOptions">\n <appengine-dynamic-branch-label trigger="trigger"></appengine-dynamic-branch-label>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div\n class="col-md-7 col-md-offset-3"\n ng-if="command.viewState.mode === \'createPipeline\' || command.viewState.mode === \'editPipeline\'"\n >\n <span class="pull-right small" ng-if="!command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Resolve via trigger</a>\n </span>\n <span class="pull-right small" ng-if="command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Click for text input</a>\n </span>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isContainerImageSource()">\n <div class="form-group">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedContainerArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n Image URL\n <help-field key="appengine.serverGroup.containerImageUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n required\n class="form-control input-sm"\n name="containerImageUrl"\n ng-model="command.containerImageUrl"\n />\n </div>\n </div>\n </div>\n\n <div ng-if="command.trigger.type === \'jenkins\'" class="form-group">\n <div class="col-md-7 col-md-offset-3">\n <div class="form-inline">\n <small>Match branch from trigger on regex</small>\n <help-field key="appengine.serverGroup.matchBranchOnRegex"></help-field>\n <input\n type="text"\n style="width: 140px"\n class="form-control input-sm pull-right"\n name="matchOnRegex"\n ng-model="command.trigger.matchBranchOnRegex"\n />\n </div>\n </div>\n </div>\n\n <deployment-strategy-selector\n field-columns="7"\n ng-if="!command.viewState.disableStrategySelection"\n command="command"\n ></deployment-strategy-selector>\n\n <div class="form-group" ng-if="!command.viewState.hideClusterNamePreview">\n <div class="col-md-12">\n <div class="well-compact" ng-class="basicSettingsCtrl.showPreviewAsWarning() ? \'alert alert-warning\' : \'well\'">\n <h5 class="text-center">\n <p>Your server group will be in the cluster:</p>\n <p>\n <strong>\n {{basicSettingsCtrl.getNamePreview()}}\n <span ng-if="basicSettingsCtrl.createsNewCluster()"> (new cluster)</span>\n </strong>\n </p>\n <div\n class="text-left"\n ng-if="!basicSettingsCtrl.createsNewCluster() && command.viewState.mode === \'create\' && latestServerGroup"\n >\n <p>There is already a server group in this cluster. Do you want to clone it?</p>\n <p>\n Cloning copies the entire configuration from the selected server group, allowing you to modify whichever\n fields (e.g. image) you need to change in the new server group.\n </p>\n <p>\n To clone a server group, select "Clone" from the "Server Group Actions" menu in the details view of the\n server group.\n </p>\n <p>\n <a href ng-click="basicSettingsCtrl.navigateToLatestServerGroup()">\n Go to details for {{latestServerGroup.name}}\n </a>\n </p>\n </div>\n </h5>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/advancedSettings.html",'<div class="form-horizontal container-fluid">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Promote <help-field key="appengine.serverGroup.promote"></help-field></div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.promote" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Stop Previous Version <help-field key="appengine.serverGroup.stopPreviousVersion"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.stopPreviousVersion" />\n </div>\n </div>\n <div class="form-group" ng-if="showPlatformHealthOnlyOverride">\n <div class="col-md-4 sm-label-right">Task Completion</div>\n <div class="col-md-7">\n <platform-health-override command="command" platform-health-type="platformHealth"> </platform-health-override>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Suppress Version String\n <help-field key="appengine.serverGroup.suppress-version-string"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.suppressVersionString" />\n </div>\n </div>\n</div>\n')}]);const vn="spinnaker.appengine.configFileArtifactList.component";e(vn,[]).component("configFileArtifactList",ne(E((e=>{const n=(n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:n,artifact:null}),e.updateConfigArtifacts(a)};return Q.createElement(Q.Fragment,null,e.configArtifacts.map(((t,a)=>Q.createElement("div",{key:t.id,className:te("artifact-configuration-section col-md-12",{"last-entry":e.configArtifacts.length-1===a})},Q.createElement("div",{className:"col-md-9"},Q.createElement(D,{artifact:t.artifact,excludedArtifactTypePatterns:[],expectedArtifactId:null==t.artifact?t.id:null,onArtifactEdited:n=>{((n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:null,artifact:n}),e.updateConfigArtifacts(a)})(n,a)},onExpectedArtifactSelected:e=>{((e,t)=>{n(e.id,t)})(e,a)},pipeline:e.pipeline,stage:e.stage})),Q.createElement("div",{className:"col-md-1"},Q.createElement("button",{type:"button",className:"btn btn-sm btn-default",onClick:()=>(n=>{const t=[...e.configArtifacts];t.splice(n,1),e.updateConfigArtifacts(t)})(a)},Q.createElement("span",{className:"glyphicon glyphicon-trash"})," Delete"))))),Q.createElement("div",{className:"col-md-7 col-md-offset-3"},Q.createElement("button",{className:"btn btn-block btn-add-trigger add-new",onClick:()=>{e.updateConfigArtifacts(e.configArtifacts.concat([{id:"",account:""}]))}},Q.createElement("span",{className:"glyphicon glyphicon-plus-sign"})," Add Config Artifact")))}),"configFileArtifactList"),["configArtifacts","pipeline","stage","updateConfigArtifacts"]));const fn="spinnaker.appengine.serverGroup.write.service";e(fn,[]).service("appengineServerGroupWriter",class{startServerGroup(e,n){const t={job:[this.buildJob(e,n,"startAppEngineServerGroup")],application:n,description:`Start Server Group: ${e.name}`};return F.executeTask(t)}stopServerGroup(e,n){const t={job:[this.buildJob(e,n,"stopAppEngineServerGroup")],application:n,description:`Stop Server Group: ${e.name}`};return F.executeTask(t)}buildJob(e,n,t){return{type:t,region:e.region,serverGroupName:e.name,credentials:e.account,cloudProvider:"appengine",application:n.name}}});const bn=class{constructor(e,n,t,a,i,r,o,l){this.$state=e,this.$scope=n,this.$uibModal=t,this.app=i,this.serverGroupWriter=r,this.appengineServerGroupWriter=o,this.appengineServerGroupCommandBuilder=l,this.state={loading:!0},this.app.ready().then((()=>this.extractServerGroup(a))).then((()=>{this.$scope.$$destroyed||this.app.getDataSource("serverGroups").onRefresh(this.$scope,(()=>this.extractServerGroup(a)))})).catch((()=>this.autoClose()))}static buildExpectedAllocationsTable(e){return`\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Server Group</th>\n <th>Allocation</th>\n </tr>\n </thead>\n <tbody>\n ${K(e,((e,n)=>`\n <tr>\n <td>${n}</td>\n <td>${100*e}%</td>\n </tr>`)).join("")}\n </tbody>\n </table>`}canDisableServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!1;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}canDestroyServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!0;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}destroyServerGroup(){const e={name:this.serverGroup.name,accountId:this.serverGroup.account,region:this.serverGroup.region},n={application:this.app,title:"Destroying "+this.serverGroup.name,onTaskComplete:()=>{this.$state.includes("**.serverGroup",e)&&this.$state.go("^")}},t={header:"Really destroy "+this.serverGroup.name+"?",buttonText:"Destroy "+this.serverGroup.name,account:this.serverGroup.account,taskMonitorConfig:n,submitMethod:e=>this.serverGroupWriter.destroyServerGroup(this.serverGroup,this.app,e),askForReason:!0,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,body:this.getBodyTemplate(this.serverGroup,this.app),interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}enableServerGroup(){const e={application:this.app,title:"Enabling "+this.serverGroup.name},n=`<div class="well well-sm">\n <p>\n Enabling <b>${this.serverGroup.name}</b> will set its traffic allocation for\n <b>${this.serverGroup.loadBalancers[0]}</b> to 100%.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n </div>\n `,t={header:"Really enable "+this.serverGroup.name+"?",buttonText:"Enable "+this.serverGroup.name,body:n,account:this.serverGroup.account,taskMonitorConfig:e,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.enableServerGroup(this.serverGroup,this.app,{...e}),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}disableServerGroup(){const e={application:this.app,title:"Disabling "+this.serverGroup.name},n=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app),t=`<div class="well well-sm">\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${bn.buildExpectedAllocationsTable(n)}\n </div>\n </div>\n </div>\n `,a={header:"Really disable "+this.serverGroup.name+"?",buttonText:"Disable "+this.serverGroup.name,body:t,account:this.serverGroup.account,taskMonitorConfig:e,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.disableServerGroup(this.serverGroup,this.app.name,e),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(a.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(a)}stopServerGroup(){const e={application:this.app,title:"Stopping "+this.serverGroup.name};let n;this.serverGroup.disabled||(n=`<div class="alert alert-danger">\n <p>Stopping this server group will scale it down to zero instances.</p>\n <p>\n This server group is currently serving traffic from <b>${this.serverGroup.loadBalancers[0]}</b>.\n Traffic directed to this server group after it has been stopped will not be handled.\n </p>\n </div>`);const t={header:"Really stop "+this.serverGroup.name+"?",buttonText:"Stop "+this.serverGroup.name,account:this.serverGroup.account,body:n,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.stopServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(t)}startServerGroup(){const e={application:this.app,title:"Starting "+this.serverGroup.name},n={header:"Really start "+this.serverGroup.name+"?",buttonText:"Start "+this.serverGroup.name,account:this.serverGroup.account,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.startServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(n)}cloneServerGroup(){this.$uibModal.open({templateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",controller:"appengineCloneServerGroupCtrl as ctrl",size:"lg",resolve:{title:()=>"Clone "+this.serverGroup.name,application:()=>this.app,serverGroup:()=>this.serverGroup,serverGroupCommand:()=>this.appengineServerGroupCommandBuilder.buildServerGroupCommandFromExisting(this.app,this.serverGroup)}})}canStartServerGroup(){return!!this.canStartOrStopServerGroup()&&"STOPPED"===this.serverGroup.servingStatus}canStopServerGroup(){return!!this.canStartOrStopServerGroup()&&"SERVING"===this.serverGroup.servingStatus}canStartOrStopServerGroup(){var e;return"FLEXIBLE"===this.serverGroup.env||["MANUAL","BASIC"].includes(null==(e=this.serverGroup.scalingPolicy)?void 0:e.type)}getBodyTemplate(e,n){let t="";const a={};if(L.addDestroyWarningMessage(n,e,a),a.body&&(t+=a.body),!e.disabled){const a=this.expectedAllocationsAfterDisableOperation(e,n);t+=`\n <div class="well well-sm">\n <p>\n A destroy operation will first disable this server group.\n </p>\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${e.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${bn.buildExpectedAllocationsTable(a)}\n </div>\n </div>\n </div>\n `}return t}expectedAllocationsAfterDisableOperation(e,n){const t=n.getDataSource("loadBalancers").data.find((n=>{var t,a;const i=null!=(a=null==(t=n.split)?void 0:t.allocations)?a:{};return Object.keys(i).includes(e.name)}));if(t){let n=N(t.split.allocations);delete n[e.name];const a=W(n,((e,n)=>e+n),0),i="COOKIE"===t.split.shardBy?1e3:100;return n=Y(n,(e=>Math.round(e/a*i)/i)),n}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}extractServerGroup(e){return P.getServerGroup(this.app.name,e.accountId,e.region,e.name).then((n=>{let t=this.app.getDataSource("serverGroups").data.find((n=>n.name===e.name&&n.account===e.accountId&&n.region===e.region));t||this.app.getDataSource("loadBalancers").data.some((n=>n.account===e.accountId&&n.serverGroups.some((n=>{let a=!1;return n.name===e.name&&(t=n,a=!0),a})))),this.serverGroup={...n,...t},this.state.loading=!1}))}};let yn=bn;yn.$inject=["$state","$scope","$uibModal","serverGroup","app","serverGroupWriter","appengineServerGroupWriter","appengineServerGroupCommandBuilder"];const Cn="spinnaker.appengine.serverGroup.details.controller";e(Cn,[fn,T]).controller("appengineServerGroupDetailsCtrl",yn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]);class Sn{constructor(e){this.cloudProvider="appengine",this.provider="appengine",this.credentials=e.credentials,this.account=e.credentials,this.application=e.application,this.stack=e.stack,this.freeFormDetails=e.freeFormDetails,this.repositoryUrl=e.repositoryUrl,this.branch=e.branch,this.configFilepaths=e.configFilepaths,this.promote=e.promote,this.stopPreviousVersion=e.stopPreviousVersion,this.type=e.type,this.region=e.region,this.strategy=e.strategy,this.strategyApplication=e.strategyApplication,this.strategyPipeline=e.strategyPipeline,this.fromTrigger=e.fromTrigger,this.trigger=e.trigger,this.gitCredentialType=e.gitCredentialType,this.configFiles=e.configFiles,this.configArtifacts=e.configArtifacts.filter((e=>!!e.id||!!e.artifact)),this.applicationDirectoryRoot=e.applicationDirectoryRoot,this.interestingHealthProviderNames=e.interestingHealthProviderNames||[],this.expectedArtifactId=e.expectedArtifactId,this.expectedArtifact=e.expectedArtifact,this.fromArtifact=e.fromArtifact,this.sourceType=e.sourceType,this.storageAccountName=e.storageAccountName,this.containerImageUrl=e.containerImageUrl,this.suppressVersionString=e.suppressVersionString}}class wn{constructor(e){this.$q=e}normalizeServerGroup(e){return this.$q.resolve(e)}convertServerGroupCommandToDeployConfiguration(e){return new Sn(e)}}wn.$inject=["$q"];const Gn="spinnaker.appengine.serverGroup.transformer.service";e(Gn,[]).service("appengineServerGroupTransformer",wn);I.registerValidator("appengine",new class{validate(e=""){const n=[],t=[];return e.length&&(this.validateSpecialCharacters(e,t),this.validateLength(e,n,t)),{warnings:n,errors:t}}validateSpecialCharacters(e,n){/^[a-z0-9]*$/g.test(e)||n.push("Only numbers and lowercase letters are allowed.")}validateLength(e,n,t){if(e.length>58)t.push("The maximum length for an App Engine application name is 63 characters.");else if(e.length>48)if(e.length>=56)n.push("You will not be able to include a stack or detail field for clusters.");else{const t=56-e.length;n.push(`If you plan to include a stack or detail field for clusters, you will only have\n ${t} character${t>1?"s":""} to do so.`)}}});Ae('.cloud-provider-logo .icon-appengine {\n -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n background-color: #4285f4;\n}\n');const An="spinnaker.appengine";e(An,[hn,ie,le,pe,se,Te,Je,ln,rn,Cn,Gn,fn,vn]).config((()=>{m.registerProvider("appengine",{name:"App Engine",instance:{detailsTemplateUrl:"appengine/src/instance/details/details.html",detailsController:"appengineInstanceDetailsCtrl"},serverGroup:{transformer:"appengineServerGroupTransformer",detailsController:"appengineServerGroupDetailsCtrl",detailsTemplateUrl:"appengine/src/serverGroup/details/details.html",commandBuilder:"appengineServerGroupCommandBuilder",cloneServerGroupController:"appengineCloneServerGroupCtrl",cloneServerGroupTemplateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",skipUpstreamStageCheck:!0},loadBalancer:{transformer:"appengineLoadBalancerTransformer",createLoadBalancerTemplateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",createLoadBalancerController:"appengineLoadBalancerWizardCtrl",detailsTemplateUrl:"appengine/src/loadBalancer/details/details.html",detailsController:"appengineLoadBalancerDetailsCtrl"},logo:{path:"appengine.logoc2c312af6aa99037.png"}})})),O.registerProvider("appengine",["custom"]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/instance/details/details.html",'<div class="details-panel">\n <div class="header">\n <instance-details-header\n health-state="ctrl.instance.healthState"\n instance-id="ctrl.instance ? ctrl.instance.name : ctrl.instanceIdNotFound"\n loading="ctrl.state.loading"\n standalone="false"\n ></instance-details-header>\n <div ng-if="!ctrl.state.loading">\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Instance Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && ctrl.instance">\n <collapsible-section heading="Instance Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Launched</dt>\n <dd ng-if="ctrl.instance.launchTime">{{ctrl.instance.launchTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.instance.account" pad="right"></account-tag>{{}}</dd>\n <dt ng-if="ctrl.instance.serverGroup">Server Group</dt>\n <dd ng-if="ctrl.instance.serverGroup">\n <a\n ui-sref="^.serverGroup({region: ctrl.instance.region,\n accountId: ctrl.instance.account,\n serverGroup: ctrl.instance.serverGroup,\n provider: \'appengine\'})"\n >{{ctrl.instance.serverGroup}}</a\n >\n </dd>\n <dt>Region</dt>\n <dd>{{ctrl.instance.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmZoneName"\n label="Zone"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <dl>\n <dt>Load Balancer</dt>\n <dd>\n <span\n class="pull-left"\n uib-tooltip="{{ctrl.instance.healthState === \'Up\' ? ctrl.upToolTip : ctrl.outOfServiceToolTip}}"\n tooltip-placement="right"\n >\n <span class="glyphicon glyphicon-{{ctrl.instance.healthState}}-triangle"></span>\n {{ctrl.instance.loadBalancers[0]}}\n </span>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Metrics" expanded="true">\n <dl>\n <appengine-conditional-dt-dd component="ctrl.instance" key="instanceStatus" label="Availability">\n <key-label><help-field key="appengine.instance.availability"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="averageLatency">\n <key-label><help-field key="appengine.instance.averageLatency"></help-field></key-label>\n <value-label>ms</value-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="errors">\n <key-label><help-field key="appengine.instance.errors"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="qps" label="QPS">\n <key-label><help-field key="appengine.instance.qps"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="requests">\n <key-label><help-field key="appengine.instance.requests"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="vmStatus" label="VM Status">\n <key-label><help-field key="appengine.instance.vmStatus"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmDebugEnabled"\n label="Debug Enabled"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && !ctrl.instance">\n <div class="content-section">\n <div class="content-body text-center">\n <h3>Instance not found.</h3>\n </div>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/details/details.html",'<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled}">\n <div class="header" ng-if="ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div class="header" ng-if="!ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <cloud-provider-logo provider="ctrl.serverGroup.type" height="\'36px\'" width="\'36px\'"></cloud-provider-logo>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.serverGroup.name}}</h3>\n </div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Server Group Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-if="ctrl.canStopServerGroup()">\n <a href ng-click="ctrl.stopServerGroup()"> Stop </a>\n </li>\n <li ng-if="ctrl.canStartServerGroup()">\n <a href ng-click="ctrl.startServerGroup()"> Start </a>\n </li>\n <li ng-if="ctrl.serverGroup.disabled">\n <a href ng-click="ctrl.enableServerGroup()"> Enable </a>\n </li>\n <li ng-if="!ctrl.serverGroup.disabled && ctrl.canDisableServerGroup()">\n <a href ng-click="ctrl.disableServerGroup()"> Disable </a>\n </li>\n <li\n ng-if="!ctrl.serverGroup.disabled && !ctrl.canDisableServerGroup()"\n uib-tooltip="You cannot disable a server group if it is the\n only server group receiving traffic from a load balancer."\n class="disabled"\n >\n <a href> Disable </a>\n </li>\n <li ng-if="ctrl.canDestroyServerGroup()">\n <a href ng-click="ctrl.destroyServerGroup()">Destroy</a>\n </li>\n <li\n ng-if="!ctrl.canDestroyServerGroup()"\n uib-tooltip="You cannot destroy a server group if it is the only server group\n receiving traffic from a load balancer. You may be able to delete\n this server group\'s load balancer."\n class="disabled"\n >\n <a href>Destroy</a>\n </li>\n <li\n uib-tooltip="It is not possible to clone an App Engine server group\'s full\n launch configuration. However, clicking this button will allow\n you to deploy into this server group\'s cluster."\n >\n <a href ng-click="ctrl.cloneServerGroup()">Clone</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n <div class="content" ng-if="!ctrl.state.loading">\n <div class="band band-info" ng-if="ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled">Disabled</div>\n <server-group-running-tasks-details\n server-group="ctrl.serverGroup"\n application="ctrl.app"\n ></server-group-running-tasks-details>\n <collapsible-section heading="Server Group Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Created</dt>\n <dd>{{ctrl.serverGroup.createdTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.serverGroup.account"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.serverGroup.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup"\n key="env"\n label="Environment"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.serverGroup" key="instanceClass"></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Size" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min === ctrl.serverGroup.capacity.max">\n <dt>Min/Max</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min !== ctrl.serverGroup.capacity.max">\n <dt>Min</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Max</dt>\n <dd>{{ctrl.serverGroup.capacity.max}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Health" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup">\n <dt>Instances</dt>\n <dd>\n <health-counts container="ctrl.serverGroup.instanceCounts" class="pull-left"></health-counts>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.serverGroup"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Scaling Policy" expanded="true" ng-if="ctrl.serverGroup.scalingPolicy">\n <dl>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="type"\n ></appengine-conditional-dt-dd>\n \x3c!--MANUAL SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="instances"\n ></appengine-conditional-dt-dd>\n \x3c!--BASIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="idleTimeout"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxInstances"\n ></appengine-conditional-dt-dd>\n \x3c!--AUTOMATIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="coolDownPeriod"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="aggregationWindowLength"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="targetUtilization"\n label="Target CPU Utilization"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetRequestCountPerSecond"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/details/details.html",'<div class="details-panel">\n <div ng-if="ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <i class="fa icon-sitemap"></i>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.loadBalancer.name}}</h3>\n </div>\n <div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Load Balancer Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.editLoadBalancer()">Edit Load Balancer</a></li>\n <li ng-if="ctrl.canDeleteLoadBalancer()">\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <li\n ng-if="!ctrl.canDeleteLoadBalancer()"\n uib-tooltip="You cannot delete a default service."\n class="disabled"\n >\n <a href>Delete Load Balancer</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="content">\n <collapsible-section heading="Load Balancer Details" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>In</dt>\n <dd><account-tag account="ctrl.loadBalancer.account" pad="right"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.loadBalancer.region}}</dd>\n <dt ng-if="ctrl.loadBalancer.serverGroups.length">Server Groups</dt>\n <dd ng-if="ctrl.loadBalancer.serverGroups.length">\n <ul>\n <li ng-repeat="serverGroup in ctrl.loadBalancer.serverGroups | orderBy: [\'isDisabled\', \'-name\']">\n <a\n ui-sref="^.serverGroup({region: serverGroup.region,\n accountId: serverGroup.account,\n serverGroup: serverGroup.name,\n provider: \'appengine\'})"\n >\n {{serverGroup.name}}\n </a>\n </li>\n </ul>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <health-counts class="pull-left" container="ctrl.loadBalancer.instanceCounts"></health-counts>\n </collapsible-section>\n <collapsible-section heading="Traffic Split" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt ng-if="ctrl.loadBalancer.split.shardBy">Shard By</dt>\n <dd ng-if="ctrl.loadBalancer.split.shardBy">\n {{ctrl.loadBalancer.split.shardBy}}\n <help-field\n key="appengine.loadBalancer.shardBy.{{ctrl.loadBalancer.split.shardBy.toLowerCase()}}"\n ></help-field>\n </dd>\n <hr ng-if="ctrl.loadBalancer.split.shardBy" />\n <ul>\n <li ng-repeat="(serverGroup, percent) in ctrl.loadBalancer.split.allocations">\n {{serverGroup}}:<span class="pull-right">{{percent | decimalToPercent}}</span>\n </li>\n </ul>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.loadBalancer"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Dispatch Rules" expanded="true" ng-if="ctrl.dispatchRules.length > 0">\n <dl class="dl-horizontal dl-narrow">\n <span ng-repeat-start="rule in ctrl.dispatchRules">{{rule}}</span><br ng-repeat-end />\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]);export{An as APPENGINE_MODULE};
|
|
1
|
+
import{module as e,extend as n,copy as t}from"angular";import{HelpContentsRegistry as a,ConfirmationModalService as i,RecentHistoryService as r,InstanceReader as o,InstanceWriter as l,StageConstants as s,AppListExtractor as c,LoadBalancerWriter as p,TaskMonitor as d,AccountService as g,Registry as u,CloudProviderRegistry as m,FormikFormField as h,ReactSelectInput as v,StageArtifactSelectorDelegate as f,excludeAllTypesExcept as b,ArtifactTypePatterns as y,FormValidator as C,FormikStageConfig as S,ExecutionDetailsTasks as w,ExecutionArtifactTab as G,SETTINGS as A,StorageAccountReader as k,NgAppEngineDeployArtifactDelegate as $,ExpectedArtifactSelectorViewController as B,NgAppengineConfigArtifactDelegate as x,SERVER_GROUP_WRITER as T,StageArtifactSelector as D,withErrorBoundary as E,TaskExecutor as F,ServerGroupWarningMessageService as L,ServerGroupReader as P,ApplicationNameValidator as I,DeploymentStrategyRegistry as O}from"@spinnaker/core";import{cloneDeep as N,flattenDeep as z,uniq as R,difference as M,filter as U,chain as H,has as q,camelCase as V,get as j,reduce as W,set as _,merge as J,map as K,mapValues as Y}from"lodash";import Q from"react";import{Subject as X,from as Z}from"rxjs";import{takeUntil as ee}from"rxjs/operators";import{react2angular as ne}from"react2angular";import te from"classnames";const ae={bindings:{component:"<"},template:'\n <dt>HTTPS</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpsUrl}}" target="_blank">{{$ctrl.component.httpsUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpsUrl"></copy-to-clipboard>\n </dl>\n <dt>HTTP</dt>\n <dl class="small">\n <a href="{{$ctrl.component.httpUrl}}" target="_blank">{{$ctrl.component.httpUrl}}</a>\n <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"\n tool-tip="\'Copy URL to clipboard\'"\n text="$ctrl.component.httpUrl"></copy-to-clipboard>\n </dl>\n '},ie="spinnaker.appengine.componentUrlDetails.component";e(ie,[]).component("appengineComponentUrlDetails",ae);class re{constructor(e){this.$filter=e}$onInit(){this.label||(this.label=this.$filter("robotToHuman")(this.key))}}re.$inject=["$filter"];const oe={bindings:{label:"@",key:"@",component:"<"},transclude:{keyLabel:"?keyText",valueLabel:"?valueLabel"},template:'\n <dt ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.label}}<span ng-transclude="keyLabel"></span></dt>\n <dd ng-if="$ctrl.component[$ctrl.key]">{{$ctrl.component[$ctrl.key]}}<span ng-transclude="valueLabel"></span></dd>\n ',controller:re},le="spinnaker.appengine.conditionalDescriptionListItem";e(le,[]).component("appengineConditionalDtDd",oe);const se="spinnaker.appengine.loadBalancer.createMessage.component";e(se,[]).component("appengineLoadBalancerMessage",{bindings:{showCreateMessage:"<",columnOffset:"@",columns:"@"},templateUrl:"appengine/src/common/loadBalancerMessage.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/common/loadBalancerMessage.component.html",'<div class="row">\n <div class="col-md-offset-{{ $ctrl.columnOffset || 0 }} col-md-{{ $ctrl.columns || 12 }}">\n <div class="well">\n <p>\n <span ng-if="$ctrl.showCreateMessage">Spinnaker cannot create a load balancer for App Engine.</span>\n A Spinnaker load balancer maps to an App Engine service, which is specified in a version\'s\n <code>app.yaml</code>.\n </p>\n <p>For example, the following <code>app.yaml</code></p>\n <pre class="text-left" style="white-space: pre-line">\n \t\t\truntime: python27\n \t\t\tapi_version: 1\n …\n \t\t\tservice: mobile\n …\n \t\t</pre\n >\n <p>\n deploys to the <code>mobile</code> service. If you do not specify a service, your version will be deployed to\n the <code>default</code> service.\n </p>\n <p>\n If a service does not exist when a version is deployed, it will be created. It will then be editable as a load\n balancer within Spinnaker.\n </p>\n <p>\n <a href="https://cloud.google.com/appengine/docs/python/config/appref" target="_blank"\n >App Engine Documentation</a\n >\n </p>\n </div>\n </div>\n</div>\n')}]);[{key:"appengine.serverGroup.gcs.repositoryUrl",value:"The full URL to the GCS bucket or TAR file containing the source files for this deployment,\n including 'gs://'. For example, <b>gs://my-bucket/my-app</b> or <b>gs://my-bucket/app.tar</b>."},{key:"appengine.serverGroup.git.repositoryUrl",value:"The full URL to the git repository containing the source files for this deployment,\n including protocol. For example, <b>https://github.com/spinnaker/deck.git</b>"},{key:"appengine.serverGroup.gitCredentialType",value:"The credential type that will be used to access the git repository for this deployment.\n You can configure these credentials alongside your App Engine credentials."},{key:"appengine.serverGroup.branch",value:"The name of the branch in the above git repository to be used for this deployment."},{key:"appengine.serverGroup.applicationDirectoryRoot",value:"(Optional) Path to the directory root of the application to be deployed,\n starting from the root of the git repository. This is the directory from which\n <code>gcloud app deploy</code> will be run."},{key:"appengine.serverGroup.configFilepaths",value:"Paths to App Engine application configuration files, starting from the application directory root,\n specified above. In most cases, this will be <code>app.yaml</code> or <code>cron.yaml</code>,\n but could be <code>path/to/app.yaml</code> or <code>path/to/cron.yaml</code>."},{key:"appengine.serverGroup.configFiles",value:"<p>(Optional) The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file). These files should not conflict with the config filepaths above:\n if you include, for example, the contents of an <code>app.yaml</code>\n file here, you should <b>not</b> specify the file path to an <code>app.yaml</code> above.<br></p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.configFilesRequired",value:"<p>The contents of an App Engine config file (e.g., an <code>app.yaml</code> or\n <code>cron.yaml</code> file).</p>\n <p>An <code>app.yaml</code> file is required and must have <code>runtime: custom</code> in\n order to deploy successfully.</p>\n <p>If this is a pipeline stage, you can use Spinnaker Pipeline Expressions here.</p>"},{key:"appengine.serverGroup.matchBranchOnRegex",value:"(Optional) A Jenkins trigger may produce details from multiple repositories and branches.\n Spinnaker will use the regex specified here to help resolve a branch for the deployment.\n If Spinnaker cannot resolve exactly one branch from the trigger, this pipeline will fail."},{key:"appengine.serverGroup.promote",value:"If selected, the newly deployed server group will receive all traffic."},{key:"appengine.serverGroup.stopPreviousVersion",value:"If selected, the previously running server group in this server group's <b>service</b>\n (Spinnaker load balancer) will be stopped. This option will be respected only if this server group will\n be receiving all traffic and the previous server group is using manual scaling."},{key:"appengine.serverGroup.containerImageUrl",value:"The full URL to the container image to use for deployment. The URL must be one of the valid GCR hostnames,\n for example <b>gcr.io/my-project/image:tag</b>"},{key:"appengine.serverGroup.suppress-version-string",value:'Spinnaker automatically versions your server groups. This means deployments through Spinnaker receive a\n short version string at the end of their name, like "v001". In most cases you will want to keep this\n version as part of the name. Preventing this string from being added to your server group will stop\n it from being considered when rolling back, promoting new versions or executing deployment strategies.\n If you are certain that you do not want the version string applied to this server group then check\n this box.'},{key:"appengine.loadBalancer.shardBy.cookie",value:'Diversion based on a specially named cookie, "GOOGAPPUID." The cookie must be set by the application itself or no diversion will occur.'},{key:"appengine.loadBalancer.shardBy.ip",value:"Diversion based on applying the modulus operation to a fingerprint of the IP address."},{key:"appengine.loadBalancer.migrateTraffic",value:"If selected, traffic will be gradually shifted to a single version. For gradual traffic migration,\n the target version must be located within instances that are configured for\n both warmup requests and automatic scaling.\n Gradual traffic migration is not supported in the App Engine flexible environment."},{key:"appengine.loadBalancer.allocations",value:"An allocation is the percent of traffic directed to a server group."},{key:"appengine.loadBalancer.textLocator",value:"Either the name of a server group, or a Spinnaker Pipeline Expression\n that resolves to the name of a server group."},{key:"appengine.instance.availability",value:"\n An instance's <b>availability</b> is determined by its version (Spinnaker server group).\n <ul>\n <li>Manual scaling versions use resident instances</li>\n <li>Basic scaling versions use dynamic instances</li>\n <li>Auto scaling versions use dynamic instances - but if you specify a number, N,\n of minimum idle instances, the first N instances will be resident,\n and additional dynamic instances will be created as necessary.\n </li>\n </ul>"},{key:"appengine.instance.averageLatency",value:"Average latency over the last minute in milliseconds."},{key:"appengine.instance.vmStatus",value:"Status of the virtual machine where this instance lives."},{key:"appengine.instance.qps",value:"Average queries per second over the last minute."},{key:"appengine.instance.errors",value:"Number of errors since this instance was started."},{key:"appengine.instance.requests",value:"Number of requests since this instance was started."}].forEach(e=>a.register(e.key,e.value));class ce{constructor(e,n,t){this.$q=e,this.app=n,this.state={loading:!0},this.upToolTip="An App Engine instance is 'Up' if a load balancer is directing traffic to its server group.",this.outOfServiceToolTip="\n An App Engine instance is 'Out Of Service' if no load balancers are directing traffic to its server group.",this.app.ready().then(()=>this.retrieveInstance(t)).then(e=>{this.instance=e,this.state.loading=!1}).catch(()=>{this.instanceIdNotFound=t.instanceId,this.state.loading=!1})}terminateInstance(){const e=N(this.instance),n=`${this.instance.name.substring(0,10)}...`;e.placement={},e.instanceId=e.name;const t={application:this.app,title:"Terminating "+n,onTaskComplete(){this.$state.includes("**.instanceDetails",{instanceId:e.name})&&this.$state.go("^")}};i.confirm({header:"Really terminate "+n+"?",buttonText:"Terminate "+n,account:e.account,taskMonitorConfig:t,submitMethod:()=>l.terminateInstance(e,this.app,{cloudProvider:"appengine"})})}retrieveInstance(e){const n=z([this.app.getDataSource("serverGroups").data,this.app.getDataSource("loadBalancers").data,this.app.getDataSource("loadBalancers").data.map(e=>e.serverGroups)]).find(n=>n.instances.some(n=>n.id===e.instanceId));if(n){const t={region:n.region,account:n.account};return"serverGroup"===n.category&&(t.serverGroup=n.name),r.addExtraDataToLatest("instances",t),o.getInstanceDetails(n.account,n.region,e.instanceId).then(e=>(e.account=n.account,e.region=n.region,e))}return this.$q.reject()}}ce.$inject=["$q","app","instance"];const pe="spinnaker.appengine.instanceDetails.controller";e(pe,[]).controller("appengineInstanceDetailsCtrl",ce);const de={bindings:{loadBalancer:"=",application:"<"},template:'\n <ng-form name="advancedSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Migrate Traffic <help-field key="appengine.loadBalancer.migrateTraffic"></help-field>\n </div>\n <div class="col-md-9">\n <div class="checkbox">\n <input type="checkbox" ng-disabled="$ctrl.disableMigrateTraffic() && !($ctrl.loadBalancer.migrateTraffic = false)" ng-model="$ctrl.loadBalancer.migrateTraffic">\n </div>\n </div>\n </div>\n </div>\n </ng-form>\n ',controller:class{constructor(){this.state={error:!1}}disableMigrateTraffic(){if(1!==this.loadBalancer.splitDescription.allocationDescriptions.length)return!0;{const e=this.loadBalancer.splitDescription.allocationDescriptions[0].serverGroupName,n=this.loadBalancer.serverGroups.find(n=>n.name===e);return!!n&&!n.allowsGradualTrafficMigration}}}},ge="spinnaker.appengine.loadBalancer.advancedSettings.component";e(ge,[]).component("appengineLoadBalancerAdvancedSettings",de);const ue={bindings:{allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},template:'\n <div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <ui-select ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n {{$select.selected}}\n </ui-select-match>\n <ui-select-choices repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search">\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"/>\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n </div>\n ',controller:class{getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}}},me="spinnaker.appengine.allocationConfigurationRow.component";e(me,[]).component("appengineAllocationConfigurationRow",ue);const he={bindings:{loadBalancer:"=",forPipelineConfig:"<",application:"<"},controller:class{$onInit(){this.updateServerGroupOptions()}addAllocation(){const e=this.serverGroupsWithoutAllocation();e.length?(this.loadBalancer.splitDescription.allocationDescriptions.push({serverGroupName:e[0],allocation:0,locatorType:"fromExisting"}),this.loadBalancer.splitDescription.allocationDescriptions.length>1&&!this.loadBalancer.splitDescription.shardBy&&(this.loadBalancer.splitDescription.shardBy="IP"),this.updateServerGroupOptions()):this.forPipelineConfig&&this.loadBalancer.splitDescription.allocationDescriptions.push({allocation:0,locatorType:"text",serverGroupName:""})}removeAllocation(e){this.loadBalancer.splitDescription.allocationDescriptions.splice(e,1),this.updateServerGroupOptions()}allocationIsInvalid(){return 100!==this.loadBalancer.splitDescription.allocationDescriptions.reduce((e,n)=>e+n.allocation,0)}updateServerGroupOptions(){this.serverGroupOptions=this.serverGroupsWithoutAllocation()}showAddButton(){return!!this.forPipelineConfig||this.serverGroupsWithoutAllocation().length>0}showShardByOptions(){return this.loadBalancer.splitDescription.allocationDescriptions.length>1||this.loadBalancer.migrateTraffic}initializeAsTextInput(e){return!!this.forPipelineConfig&&!this.loadBalancer.serverGroups.map(e=>e.name).includes(e)}serverGroupsWithoutAllocation(){const e=this.loadBalancer.splitDescription.allocationDescriptions.map(e=>e.serverGroupName),n=this.loadBalancer.serverGroups.map(e=>e.name);return M(n,e)}},templateUrl:"appengine/src/loadBalancer/configure/wizard/basicSettings.component.html"},ve="spinnaker.appengine.loadBalancerSettings.component";e(ve,[]).component("appengineLoadBalancerBasicSettings",he),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/basicSettings.component.html",'<ng-form name="basicSettingsForm">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Allocations\n <help-field key="appengine.loadBalancer.allocations"></help-field>\n </div>\n <div class="col-md-9">\n <div ng-if="!$ctrl.forPipelineConfig">\n <appengine-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-allocation-configuration-row>\n </div>\n <div ng-if="$ctrl.forPipelineConfig">\n <appengine-stage-allocation-configuration-row\n ng-repeat="description in $ctrl.loadBalancer.splitDescription.allocationDescriptions"\n allocation-description="description"\n application="$ctrl.application"\n region="{{ $ctrl.loadBalancer.region }}"\n account="{{ $ctrl.loadBalancer.account || $ctrl.loadBalancer.credentials }}"\n server-group-options="$ctrl.serverGroupOptions"\n on-allocation-change="$ctrl.updateServerGroupOptions()"\n remove-allocation="$ctrl.removeAllocation($index)"\n >\n </appengine-stage-allocation-configuration-row>\n </div>\n <button class="add-new col-md-11" ng-if="$ctrl.showAddButton()" ng-click="$ctrl.addAllocation()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add allocation\n </button>\n </div>\n </div>\n <div class="form-group" ng-if="$ctrl.allocationIsInvalid()">\n <div class="col-md-12 text-center">\n <p class="alert alert-warning">Allocations must sum to 100%.</p>\n </div>\n </div>\n </div>\n\n <div class="row" ng-if="$ctrl.showShardByOptions()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Shard By</div>\n <div class="col-md-9">\n <form class="form-inline">\n <div class="form-group">\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="IP" />\n IP\n <help-field key="appengine.loadBalancer.shardBy.ip"></help-field>\n </label>\n <label class="radio-inline">\n <input type="radio" ng-model="$ctrl.loadBalancer.splitDescription.shardBy" value="COOKIE" />\n Cookie\n <help-field key="appengine.loadBalancer.shardBy.cookie"></help-field>\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n</ng-form>\n')}]);class fe{static mapTargetCoordinateToLabel(e){const n=s.TARGET_LIST.find(n=>n.val===e);return n?n.label:null}$doCheck(){this.setInputViewValue()}setInputViewValue(){switch(this.allocationDescription.locatorType){case"text":case"fromExisting":this.inputViewValue=this.allocationDescription.serverGroupName;break;case"targetCoordinate":if(this.allocationDescription.cluster&&this.allocationDescription.target){const e=fe.mapTargetCoordinateToLabel(this.allocationDescription.target);this.inputViewValue=`${e} (${this.allocationDescription.cluster})`}else this.inputViewValue=null;break;default:this.inputViewValue=null}}}const be={bindings:{allocationDescription:"<"},controller:fe,template:'<input ng-model="$ctrl.inputViewValue" type="text" class="form-control input-sm" readonly/>'};const ye={bindings:{application:"<",region:"@",account:"@",allocationDescription:"<",removeAllocation:"&",serverGroupOptions:"<",onAllocationChange:"&"},controller:class{constructor(){this.targets=s.TARGET_LIST}$onInit(){const e=c.clusterFilterForCredentialsAndRegion(this.account,this.region);this.clusterList=c.getClusters([this.application],e)}getServerGroupOptions(){return this.allocationDescription.serverGroupName?R(this.serverGroupOptions.concat(this.allocationDescription.serverGroupName)):this.serverGroupOptions}onLocatorTypeChange(){this.serverGroupOptions.includes(this.allocationDescription.serverGroupName)||delete this.allocationDescription.serverGroupName,this.onAllocationChange()}},templateUrl:"appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html"},Ce="spinnaker.appengine.stageAllocationConfigurationRow.component";e(Ce,[]).component("appengineStageAllocationConfigurationRow",ye).component("appengineStageAllocationLabel",be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html",'<div class="form-group">\n <div class="row">\n <div class="col-md-7">\n <appengine-stage-allocation-label allocation-description="$ctrl.allocationDescription">\n </appengine-stage-allocation-label>\n </div>\n <div class="col-md-3">\n <div class="input-group input-group-sm">\n <input\n type="number"\n ng-model="$ctrl.allocationDescription.allocation"\n required\n class="form-control input-sm"\n min="0"\n ng-change="$ctrl.onAllocationChange()"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-2">\n <a class="btn btn-link sm-label" ng-click="$ctrl.removeAllocation()">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </div>\n </div>\n</div>\n<div class="form-group">\n <div class="well col-md-11" style="padding-top: 5px; padding-bottom: 10px">\n <div class="row">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Locator</div>\n <div class="col-md-7">\n <form>\n <div class="form-group">\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="fromExisting"\n />\n Existing server group\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="targetCoordinate"\n />\n Coordinates\n </label>\n <br />\n <label class="small" style="font-weight: normal">\n <input\n type="radio"\n ng-change="$ctrl.onLocatorTypeChange()"\n ng-model="$ctrl.allocationDescription.locatorType"\n value="text"\n />\n Text input\n </label>\n </div>\n </form>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="form-group" ng-switch on="$ctrl.allocationDescription.locatorType">\n <div ng-switch-when="fromExisting">\n <div class="col-md-3 sm-label-right" style="padding-left: 0px">Server group</div>\n <div class="col-md-7">\n <ui-select\n ng-model="$ctrl.allocationDescription.serverGroupName"\n on-select="$ctrl.onAllocationChange()"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices\n repeat="serverGroup as serverGroup in $ctrl.getServerGroupOptions() | filter: $select.search"\n >\n <div ng-bind-html="serverGroup | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <div ng-switch-when="targetCoordinate">\n <div class="col-md-3 sm-label-right">Cluster</div>\n <div class="col-md-7">\n <cluster-selector\n class="small"\n clusters="$ctrl.clusterList"\n model="$ctrl.allocationDescription.cluster"\n ></cluster-selector>\n </div>\n <div class="col-md-3 sm-label-right">Target</div>\n <div class="col-md-7">\n <target-select model="$ctrl.allocationDescription" options="$ctrl.targets"></target-select>\n </div>\n </div>\n <div ng-switch-when="text">\n <div class="col-md-3 sm-label-right">\n Text\n <help-field key="appengine.loadBalancer.textLocator"></help-field>\n </div>\n <div class="col-md-7">\n <input class="form-control input-sm" type="text" ng-model="$ctrl.allocationDescription.serverGroupName" />\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n')}]);class Se{static convertTrafficSplitToTrafficSplitDescription(e){const n=W(e.allocations,(e,n,t)=>e.concat({serverGroupName:t,allocation:n,locatorType:"fromExisting"}),[]);return{shardBy:e.shardBy,allocationDescriptions:n}}constructor(e){this.credentials=e.account||e.credentials,this.account=this.credentials,this.cloudProvider=e.cloudProvider,this.loadBalancerName=e.name,this.name=e.name,this.region=e.region,this.migrateTraffic=e.migrateTraffic||!1,this.serverGroups=e.serverGroups}mapAllocationsToDecimals(){this.splitDescription.allocationDescriptions.forEach(e=>{e.allocation=e.allocation/100})}mapAllocationsToPercentages(){this.splitDescription.allocationDescriptions.forEach(e=>{e.allocation=Math.round(1e3*e.allocation)/10})}}class we{constructor(e){this.$q=e}normalizeLoadBalancer(e){e.provider=e.type,e.instanceCounts=this.buildInstanceCounts(e.serverGroups),e.instances=[],e.serverGroups.forEach(n=>{n.account=e.account,n.region=e.region,n.detachedInstances&&(n.detachedInstances=n.detachedInstances.map(e=>({id:e}))),n.instances=n.instances.concat(n.detachedInstances||[]).map(n=>this.transformInstance(n,e))});const n=U(e.serverGroups,{isDisabled:!1});return e.instances=H(n).map("instances").flatten().value(),this.$q.resolve(e)}convertLoadBalancerForEditing(e,n){return n.getDataSource("loadBalancers").ready().then(()=>{const t=n.getDataSource("loadBalancers").data.find(n=>n.name===e.name&&(n.account===e.account||n.account===e.credentials));return t&&(e.serverGroups=N(t.serverGroups)),e})}convertLoadBalancerToUpsertDescription(e){return new Se(e)}buildInstanceCounts(e){const n=H(e).map("instances").flatten().reduce((e,n)=>(q(n,"health.state")&&e[V(n.health.state)]++,e),{up:0,down:0,outOfService:0,succeeded:0,failed:0,starting:0,unknown:0}).value();return n.outOfService+=H(e).map("detachedInstances").flatten().value().length,n}transformInstance(e,n){e.provider=n.type,e.account=n.account,e.region=n.region,e.loadBalancers=[n.name];const t=e.health||{};return e.healthState=j(e,"health.state")||"OutOfService",e.health=[t],e}}we.$inject=["$q"];const Ge="spinnaker.appengine.loadBalancer.transformer.service";function Ae(e,n){void 0===n&&(n={});var t=n.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css","top"===t&&a.firstChild?a.insertBefore(i,a.firstChild):a.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}e(Ge,[]).service("appengineLoadBalancerTransformer",we);Ae("appengine-load-balancer-basic-settings a.btn.btn-link {\n padding: 0;\n}\nappengine-load-balancer-basic-settings .form-group {\n margin-top: 0.4rem;\n}\nappengine-load-balancer-advanced-settings .checkbox input[type='checkbox'] {\n margin: 0 0 0.1rem 0;\n}\n");class ke{constructor(e,n,t,a,i,r,o,l,s){this.$scope=e,this.$state=n,this.$uibModalInstance=t,this.application=a,this.isNew=r,this.forPipelineConfig=o,this.appengineLoadBalancerTransformer=l,this.wizardSubFormValidation=s,this.state={loading:!0},this.submitButtonLabel=this.forPipelineConfig?"Done":"Update",this.isNew?this.heading="Create New Load Balancer":(this.heading=`Edit ${[i.name,i.region,i.account||i.credentials].join(":")}`,this.appengineLoadBalancerTransformer.convertLoadBalancerForEditing(i,a).then(e=>{this.loadBalancer=this.appengineLoadBalancerTransformer.convertLoadBalancerToUpsertDescription(e),i.split&&!this.loadBalancer.splitDescription?this.loadBalancer.splitDescription=Se.convertTrafficSplitToTrafficSplitDescription(i.split):this.loadBalancer.splitDescription=i.splitDescription,this.loadBalancer.mapAllocationsToPercentages(),this.setTaskMonitor(),this.initializeFormValidation(),this.state.loading=!1}))}submit(){const e=N(this.loadBalancer);return e.mapAllocationsToDecimals(),delete e.serverGroups,this.forPipelineConfig?this.$uibModalInstance.close(e):this.taskMonitor.submit(()=>p.upsertLoadBalancer(e,this.application,"Update"))}cancel(){this.$uibModalInstance.dismiss()}showSubmitButton(){return this.wizardSubFormValidation.subFormsAreValid()}setTaskMonitor(){this.taskMonitor=new d({application:this.application,title:"Updating your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete()})}initializeFormValidation(){this.wizardSubFormValidation.config({form:"form",scope:this.$scope}).register({page:"basic-settings",subForm:"basicSettingsForm",validators:[{watchString:"ctrl.loadBalancer.splitDescription",validator:e=>100===e.allocationDescriptions.reduce((e,n)=>e+n.allocation,0),watchDeep:!0}]}).register({page:"advanced-settings",subForm:"advancedSettingsForm"})}onTaskComplete(){this.application.getDataSource("loadBalancers").refresh(),this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope,()=>this.onApplicationRefresh())}onApplicationRefresh(){if(this.$scope.$$destroyed)return;this.$uibModalInstance.dismiss();const e={name:this.loadBalancer.name,accountId:this.loadBalancer.credentials,region:this.loadBalancer.region,provider:"appengine"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",e):this.$state.go(".loadBalancerDetails",e)}}ke.$inject=["$scope","$state","$uibModalInstance","application","loadBalancer","isNew","forPipelineConfig","appengineLoadBalancerTransformer","wizardSubFormValidation"];const $e="spinnaker.appengine.loadBalancer.wizard.controller";e($e,[]).controller("appengineLoadBalancerWizardCtrl",ke);class Be{constructor(e,n,t,a,i){this.$uibModal=e,this.$state=n,this.$scope=t,this.app=i,this.state={loading:!0},this.dispatchRules=[],this.loadBalancerFromParams=a,this.app.getDataSource("loadBalancers").ready().then(()=>this.extractLoadBalancer())}editLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",controller:"appengineLoadBalancerWizardCtrl as ctrl",size:"lg",resolve:{application:()=>this.app,loadBalancer:()=>N(this.loadBalancer),isNew:()=>!1,forPipelineConfig:()=>!1}})}deleteLoadBalancer(){const e={application:this.app,title:"Deleting "+this.loadBalancer.name};i.confirm({header:"Really delete "+this.loadBalancer.name+"?",buttonText:"Delete "+this.loadBalancer.name,body:this.getConfirmationModalBodyHtml(),account:this.loadBalancer.account,taskMonitorConfig:e,submitMethod:()=>{const e={cloudProvider:this.loadBalancer.cloudProvider,loadBalancerName:this.loadBalancer.name,credentials:this.loadBalancer.account};return p.deleteLoadBalancer(e,this.app)}})}canDeleteLoadBalancer(){return"default"!==this.loadBalancer.name}extractLoadBalancer(){this.loadBalancer=this.app.getDataSource("loadBalancers").data.find(e=>e.name===this.loadBalancerFromParams.name&&e.account===this.loadBalancerFromParams.accountId),this.loadBalancer?(this.state.loading=!1,this.buildDispatchRules(),this.app.getDataSource("loadBalancers").onRefresh(this.$scope,()=>this.extractLoadBalancer())):this.autoClose()}buildDispatchRules(){this.dispatchRules=[],this.loadBalancer&&this.loadBalancer.dispatchRules&&this.loadBalancer.dispatchRules.forEach(e=>{e.service===this.loadBalancer.name&&this.dispatchRules.push(e.domain+e.path)})}getConfirmationModalBodyHtml(){const e=this.loadBalancer.serverGroups.map(e=>e.name),n=!!e&&e.length>0,t=!!e&&e.length>1;if(n){if(t){const n=e.map(e=>`<li>${e}</li>`).join("");return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy the following server groups:\n <ul>\n ${n}\n </ul>\n </p>\n </div>\n `}return`<div class="alert alert-warning">\n <p>\n Deleting <b>${this.loadBalancer.name}</b> will destroy <b>${e[0]}</b>.\n </p>\n </div>\n `}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}}Be.$inject=["$uibModal","$state","$scope","loadBalancer","app"];const xe="spinnaker.appengine.loadBalancerDetails.controller";e(xe,[]).controller("appengineLoadBalancerDetailsCtrl",Be),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]);const Te="spinnaker.appengine.loadBalancer.module";e(Te,[me,xe,ge,ve,Ge,$e,Ce]);class De{}De.PLATFORM="App Engine Service";class Ee{constructor(e){this.$scope=e,e.platformHealth=De.PLATFORM}setStageRegion(){const e=this.$scope.accounts.find(e=>e.name===this.$scope.stage.credentials);e&&e.name&&g.getAccountDetails(e.name).then(e=>{this.$scope.stage.region=e.region})}setStageCloudProvider(){this.$scope.stage.cloudProvider="appengine"}setAccounts(){return g.listAccounts("appengine").then(e=>{this.$scope.accounts=e})}setTargets(){this.$scope.targets=s.TARGET_LIST,this.$scope.stage.target||(this.$scope.stage.target=this.$scope.targets[0].val)}setStageCredentials(){!this.$scope.stage.credentials&&this.$scope.application.defaultCredentials.appengine&&(this.$scope.stage.credentials=this.$scope.application.defaultCredentials.appengine)}}Ee.$inject=["$scope"];class Fe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials()}}Fe.$inject=["$scope"];const Le="spinnaker.appengine.pipeline.stage.destroyAsgStage";e(Le,[]).config(()=>{u.pipeline.registerStage({provides:"destroyServerGroup",key:"destroyServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to destroy a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("appengineDestroyAsgStageCtrl",Fe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStage.html",'<div ng-controller="appengineDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n single-region="true"\n disable-region-select="true"\n on-account-update="destroyAsgStageCtrl.setStageRegion()"\n component="stage"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",'<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Pe extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Pe.$inject=["$scope"];const Ie="spinnaker.appengine.pipeline.stage.disableAsgStage";e(Ie,[]).config(()=>{u.pipeline.registerStage({provides:"disableServerGroup",key:"disableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",validators:[{type:"targetImpedance",message:"This pipeline will attempt to disable a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("appengineDisableAsgStageCtrl",Pe),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStage.html",'<div ng-controller="appengineDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="disableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",'<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class Oe{constructor(e,n,t){this.$uibModal=e,this.$uibModalInstance=n,this.application=t,this.state={loading:!0},this.initialize()}submit(){const e=m.getValue("appengine","loadBalancer"),n=this.$uibModal.open({templateUrl:e.createLoadBalancerTemplateUrl,controller:`${e.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.application,loadBalancer:()=>N(this.selectedLoadBalancer),isNew:()=>!1,forPipelineConfig:()=>!0}}).result;this.$uibModalInstance.close(n)}cancel(){this.$uibModalInstance.dismiss()}initialize(){this.application.getDataSource("loadBalancers").ready().then(()=>{this.loadBalancers=this.application.loadBalancers.data.filter(e=>"appengine"===e.cloudProvider),this.loadBalancers.length&&(this.selectedLoadBalancer=this.loadBalancers[0]),this.state.loading=!1})}}Oe.$inject=["$uibModal","$uibModalInstance","application"];const Ne="spinnaker.appengine.loadBalancerChoiceModal.controller";e(Ne,[]).controller("appengineLoadBalancerChoiceModelCtrl",Oe);class ze{constructor(e,n){this.$scope=e,this.$uibModal=n,e.stage.loadBalancers=e.stage.loadBalancers||[],e.stage.cloudProvider="appengine"}addLoadBalancer(){this.$uibModal.open({templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",controller:"appengineLoadBalancerChoiceModelCtrl as ctrl",resolve:{application:()=>this.$scope.application}}).result.then(e=>{this.$scope.stage.loadBalancers.push(e)}).catch(()=>{})}editLoadBalancer(e){const n=m.getValue("appengine","loadBalancer");this.$uibModal.open({templateUrl:n.createLoadBalancerTemplateUrl,controller:`${n.createLoadBalancerController} as ctrl`,size:"lg",resolve:{application:()=>this.$scope.application,loadBalancer:()=>N(this.$scope.stage.loadBalancers[e]),isNew:()=>!1,forPipelineConfig:()=>!0}}).result.then(n=>{this.$scope.stage.loadBalancers[e]=n}).catch(()=>{})}removeLoadBalancer(e){this.$scope.stage.loadBalancers.splice(e,1)}}ze.$inject=["$scope","$uibModal"];const Re="spinnaker.appengine.pipeline.stage.editLoadBalancerStage";e(Re,[Ne]).config(()=>{u.pipeline.registerStage({label:"Edit Load Balancer",description:"Edits a load balancer",key:"upsertAppEngineLoadBalancers",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",executionConfigSections:["editLoadBalancerConfig","taskStatus"],controller:"appengineEditLoadBalancerStageCtrl",controllerAs:"editLoadBalancerStageCtrl",validators:[]})}).controller("appengineEditLoadBalancerStageCtrl",ze),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Select Load Balancer</h4>\n </div>\n <div class="modal-body" ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="modal-body" ng-if="!ctrl.state.loading">\n <div class="alert alert-warning" ng-if="ctrl.loadBalancers.length === 0">\n <p>This application has no App Engine load balancers.</p>\n </div>\n <form\n role="form"\n name="form"\n class="form-horizontal"\n ng-submit="ctrl.submit()"\n ng-if="ctrl.loadBalancers.length > 0"\n >\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Load Balancer</b>\n </div>\n <div class="col-md-7">\n <ui-select class="form-control input-sm" ng-model="ctrl.selectedLoadBalancer">\n <ui-select-match>\n <account-tag account="$select.selected.account"></account-tag>\n <span style="margin-left: 5px">{{$select.selected.name}}</span>\n </ui-select-match>\n <ui-select-choices repeat="loadBalancer in ctrl.loadBalancers | filter: $select.search">\n <account-tag account="loadBalancer.account"></account-tag>\n <span style="margin-left: 5px" ng-bind-html="loadBalancer.name"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </form>\n </div>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <button class="btn btn-primary" ng-if="ctrl.loadBalancers.length > 0" ng-click="ctrl.submit()">\n <span class="far fa-check-circle"></span> Edit\n </button>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html",'<div class="well well-sm clearfix" ng-if="!pipeline.strategy">\n <div class="row">\n <div class="col-md-12">\n <h4 class="text-left">Load Balancers</h4>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n <td class="condensed-actions">\n <a class="btn btn-sm btn-link" href ng-click="editLoadBalancerStageCtrl.editLoadBalancer($index)">\n <span class="glyphicon glyphicon-edit" uib-tooltip="Edit"></span\n ></a>\n <a\n class="btn btn-sm btn-link pad-left"\n href\n ng-click="editLoadBalancerStageCtrl.removeLoadBalancer($index)"\n >\n <span class="glyphicon glyphicon-trash" uib-tooltip="Remove"></span>\n </a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="8">\n <button class="btn btn-block btn-sm add-new" ng-click="editLoadBalancerStageCtrl.addLoadBalancer()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add load balancer\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'editLoadBalancerConfig\'">\n <div class="row">\n <div class="col-md-12">\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Account</th>\n <th>Name</th>\n <th>Region</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="loadBalancer in stage.context.loadBalancers">\n <td>\n <account-tag account="loadBalancer.credentials"></account-tag>\n </td>\n <td>{{ loadBalancer.name }}</td>\n <td>{{ loadBalancer.region }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]);class Me extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Me.$inject=["$scope"];const Ue="spinnaker.appengine.pipeline.stage.enableAsgStage";e(Ue,[]).config(()=>{u.pipeline.registerStage({provides:"enableServerGroup",key:"enableServerGroup",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",executionStepLabelUrl:"appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("appengineEnableAsgStageCtrl",Me),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStage.html",'<div ng-controller="appengineEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="enableAsgStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",'<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class He extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setStageCredentials();const n=e.stage;void 0===n.shrinkToSize&&(n.shrinkToSize=1),void 0===n.allowDeleteActive&&(n.allowDeleteActive=!1),void 0===n.retainLargerOverNewer&&(n.retainLargerOverNewer="false"),n.retainLargerOverNewer=n.retainLargerOverNewer.toString()}pluralize(e,n){return 1===n?e:e+"s"}}He.$inject=["$scope"];const qe="spinnaker.appengine.pipeline.stage.shrinkClusterStage";e(qe,[]).config(function(){u.pipeline.registerStage({provides:"shrinkCluster",key:"shrinkCluster",cloudProvider:"appengine",templateUrl:"appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"shrinkToSize",fieldLabel:"shrink to [X] Server Groups"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("appengineShrinkClusterStageCtrl",He),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",'<div ng-controller="appengineShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="shrinkClusterStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Shrink Options">\n <div class="form-inline">\n Shrink to\n <input\n type="number"\n min="0"\n required\n ng-model="stage.shrinkToSize"\n class="form-control input-sm"\n style="width: 50px"\n />\n {{shrinkClusterStageCtrl.pluralize(\'server group\', stage.shrinkToSize)}}, keeping the\n <select class="form-control input-sm" ng-model="stage.retainLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n </div>\n </stage-config-field>\n <div class="form-group">\n <div class="col-md-offset-3 col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="stage.allowDeleteActive" />\n Allow deletion of active server groups\n </label>\n </div>\n </div>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]);class Ve extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}Ve.$inject=["$scope"];const je="spinnaker.appengine.pipeline.stage.startServerGroupStage";e(je,[]).config(()=>{u.pipeline.registerStage({label:"Start Server Group",description:"Starts a server group.",key:"startAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",executionConfigSections:["startServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",controller:"appengineStartServerGroupStageCtrl",controllerAs:"startServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})}).controller("appengineStartServerGroupStageCtrl",Ve),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStage.html",'<div ng-controller="appengineStartServerGroupStageCtrl as startServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="startServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupExecutionDetails.html",'<div ng-controller="BaseExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'startServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/startServerGroup/startServerGroupStepLabel.html",'<span class="task-label"> Start Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);class We extends Ee{constructor(e){super(e),this.$scope=e,super.setAccounts().then(()=>{super.setStageRegion()}),super.setStageCloudProvider(),super.setTargets(),super.setStageCredentials(),e.stage.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(e.stage.interestingHealthProviderNames=[De.PLATFORM])}}We.$inject=["$scope"];const _e="spinnaker.appengine.pipeline.stage.stopServerGroupStage";e(_e,[]).config(()=>{u.pipeline.registerStage({label:"Stop Server Group",description:"Stops a server group.",key:"stopAppEngineServerGroup",templateUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",executionDetailsUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",executionConfigSections:["stopServerGroupConfig","taskStatus"],executionStepLabelUrl:"appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",controller:"appengineStopServerGroupStageCtrl",controllerAs:"stopServerGroupStageCtrl",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],cloudProvider:"appengine"})}).controller("appengineStopServerGroupStageCtrl",We),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStage.html",'<div ng-controller="appengineStopServerGroupStageCtrl as stopServerGroupStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector\n application="application"\n component="stage"\n single-region="true"\n disable-region-select="true"\n on-account-update="stopServerGroupStageCtrl.setStageRegion()"\n accounts="accounts"\n >\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="targets"></target-select>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="platformHealth">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupExecutionDetails.html",'<div ng-controller="appengineStopServerGroupExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'stopServerGroupConfig\'">\n <div class="row">\n <div class="col-md-9">\n <dl class="dl-narrow dl-horizontal">\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Server Group</dt>\n <dd>{{stage.context.serverGroupName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n </div>\n\n <div class="step-section-details" ng-if="detailsSection === \'taskStatus\'">\n <div class="row">\n <execution-step-details item="stage"></execution-step-details>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/pipeline/stages/stopServerGroup/stopServerGroupStepLabel.html",'<span class="task-label"> Stop Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Je="spinnaker.appengine.pipeline.module";e(Je,[Le,Ie,Re,Ue,qe,je,_e]);class Ke extends Q.Component{constructor(e){super(e),this.destroy$=new X,this.setRegionList=e=>{const{application:n}=this.props,t=n=>!n||n.account===e;Z(n.ready()).pipe(ee(this.destroy$)).subscribe(()=>{const e=c.getRegions([n],t);e.sort(),this.setState({availableRegions:e})})},this.accountChanged=e=>{this.setRegionList(e)};const n=e.credentialsField||"credentials";this.state={availableRegions:[],cloudProvider:e.cloudProvider,componentName:e.componentName||"",credentialsField:n}}componentDidMount(){const{componentName:e,formik:n}=this.props,{credentialsField:t}=this.state,a=j(n.values,e?`${e}.${t}`:`${t}`,void 0);this.setRegionList(a)}componentWillUnmount(){this.destroy$.next()}render(){const{accounts:e}=this.props,{credentialsField:n,availableRegions:t,componentName:a}=this.state;return Q.createElement("div",{className:"col-md-9"},Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.${n}`:`${n}`,label:"Account",input:n=>Q.createElement(v,{...n,stringOptions:e&&e.map(e=>e.name),clearable:!1}),onChange:this.accountChanged,required:!0})),Q.createElement("div",{className:"sp-margin-m-bottom"},Q.createElement(h,{name:a?`${a}.region`:"region",label:"Region",input:e=>Q.createElement(v,{...e,stringOptions:t,clearable:!1}),required:!0})))}}const Ye=class e extends Q.Component{constructor(){super(...arguments),this.destroy$=new X,this.state={accounts:[]},this.onTemplateArtifactEdited=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,null),this.props.formik.setFieldValue(`${n}.artifact`,e),this.props.formik.setFieldValue(`${n}.account`,e.artifactAccount)},this.onTemplateArtifactSelected=(e,n)=>{this.props.formik.setFieldValue(`${n}.id`,e),this.props.formik.setFieldValue(`${n}.artifact`,null)},this.removeInputArtifact=e=>{this.props.formik.setFieldValue(e,null)},this.getInputArtifact=(e,n)=>e[n]?e[n]:{account:"",id:""}}componentDidMount(){Z(g.listAccounts("appengine")).pipe(ee(this.destroy$)).subscribe(e=>this.setState({accounts:e}))}render(){const n=this.props.formik.values,t=this.state.accounts;return Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Basic Settings")),Q.createElement("div",null,Q.createElement(Ke,{componentName:"",accounts:t,application:this.props.application,cloudProvider:"appengine",credentialsField:"account",formik:this.props.formik})),Q.createElement("div",{className:"col-md-offset-0 col-md-9"},Q.createElement("h4",null,"Configuration Settings")),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(n,"cronArtifact").artifact,excludedArtifactTypePatterns:e.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(n,"cronArtifact").id,label:"Cron Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"cronArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"cronArtifact"),pipeline:this.props.pipeline,stage:n})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("cronArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(n,"dispatchArtifact").artifact,excludedArtifactTypePatterns:e.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(n,"dispatchArtifact").id,label:"Dispatch Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"dispatchArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"dispatchArtifact"),pipeline:this.props.pipeline,stage:n})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("dispatchArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(n,"indexArtifact").artifact,excludedArtifactTypePatterns:e.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(n,"indexArtifact").id,label:"Index Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"indexArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"indexArtifact"),pipeline:this.props.pipeline,stage:n})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("indexArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))),Q.createElement("div",null,Q.createElement("div",{className:"col-md-offset-1 col-md-9"},Q.createElement(f,{artifact:this.getInputArtifact(n,"queueArtifact").artifact,excludedArtifactTypePatterns:e.excludedArtifactTypes,expectedArtifactId:this.getInputArtifact(n,"queueArtifact").id,label:"Queue Artifact",onArtifactEdited:e=>{this.onTemplateArtifactEdited(e,"queueArtifact")},helpKey:"",onExpectedArtifactSelected:e=>this.onTemplateArtifactSelected(e.id,"queueArtifact"),pipeline:this.props.pipeline,stage:n})),Q.createElement("div",{className:"col-md-1"},Q.createElement("div",{className:"form-control-static"},Q.createElement("button",{onClick:()=>this.removeInputArtifact("queueArtifact")},Q.createElement("span",{className:"glyphicon glyphicon-trash"}),Q.createElement("span",{className:"sr-only"},"Remove field"))))))}};Ye.excludedArtifactTypes=b(y.BITBUCKET_FILE,y.CUSTOM_OBJECT,y.EMBEDDED_BASE64,y.GCS_OBJECT,y.GITHUB_FILE,y.GITLAB_FILE,y.S3_OBJECT,y.HTTP_FILE,y.ORACLE_OBJECT);let Qe=Ye;function Xe(e){const n=new C(e);return n.field("account").required(),n.field("region").required(),n.validateForm()}u.pipeline.registerStage({label:"Deploy App Engine Configuration",description:"Deploy index, dispatch, cron, and queue configuration to App Engine.",key:"deployAppEngineConfiguration",component:function({application:e,pipeline:n,stage:t,updateStage:a}){const i=Q.useMemo(()=>({...N(t)}),[]);return Q.createElement(S,{application:e,onChange:a,pipeline:n,stage:i,validate:Xe,render:e=>Q.createElement(Qe,{...e})})},producesArtifacts:!1,cloudProvider:"appengine",executionDetailsSections:[w,G],validateFn:Xe});const Ze=A.providers.appengine||{defaults:{}};Ze&&(Ze.resetToOriginal=A.resetProvider("appengine"));var en=(e=>(e.GCS="gcs",e.GIT="git",e.ARTIFACT="artifact",e.CONTAINER_IMAGE="containerImage",e))(en||{});const nn=class e{constructor(e){this.$q=e}static getTriggerOptions(e){return(e.triggers||[]).filter(e=>"git"===e.type||"jenkins"===e.type||"travis"===e.type).map(e=>"git"===e.type?{source:e.source,project:e.project,slug:e.slug,branch:e.branch,type:"git"}:{master:e.master,job:e.job,type:e.type})}static getExpectedArtifacts(e){return e.expectedArtifacts||[]}buildNewServerGroupCommand(e,n,t="create"){null==n&&(n="appengine");const a={accounts:g.getAllAccountDetailsForProvider("appengine"),storageAccounts:k.getStorageAccounts()},i={mode:t,submitButtonLabel:this.getSubmitButtonLabel(t),disableStrategySelection:"create"===t};return this.$q.all(a).then(t=>{const a=this.getCredentials(t.accounts),r=this.getRegion(t.accounts,a);return{application:e.name,backingData:t,viewState:i,fromArtifact:!1,credentials:a,region:r,selectedProvider:n,interestingHealthProviderNames:[],sourceType:"git"}})}buildServerGroupCommandFromExisting(e,n){return this.buildNewServerGroupCommand(e,"appengine","clone").then(e=>(e.stack=n.stack,e.freeFormDetails=n.detail,e))}buildNewServerGroupCommandForPipeline(n,t){return this.$q.when({viewState:{pipeline:t,stage:n},backingData:{triggerOptions:e.getTriggerOptions(t),expectedArtifacts:e.getExpectedArtifacts(t)}})}buildServerGroupCommandFromPipeline(n,t,a,i){return this.buildNewServerGroupCommand(n,"appengine","editPipeline").then(n=>n={...n,...t,backingData:{...n.backingData,triggerOptions:e.getTriggerOptions(i),expectedArtifacts:e.getExpectedArtifacts(i)},credentials:t.account||n.credentials,viewState:{...n.viewState,stage:a,pipeline:i}})}getCredentials(e){const n=(e||[]).map(e=>e.name),t=Ze.defaults.account;return n.includes(t)?t:n[0]}getRegion(e,n){const t=e.find(e=>e.name===n);return t?t.region:null}getSubmitButtonLabel(e){switch(e){case"createPipeline":return"Add";case"editPipeline":return"Done";case"clone":return"Clone";default:return"Create"}}};nn.$inject=["$q"];let tn=nn;const an="spinnaker.appengine.serverGroupCommandBuilder.service";e(an,[]).service("appengineServerGroupCommandBuilder",tn);class rn{constructor(e,t,a,i){this.$scope=e,this.excludedGcsArtifactTypes=b(y.GCS_OBJECT),this.excludedContainerArtifactTypes=b(y.DOCKER_IMAGE),this.onExpectedArtifactEdited=e=>{this.$scope.$applyAsync(()=>{this.$scope.command.expectedArtifactId=null,this.$scope.command.expectedArtifact=e})},this.onExpectedArtifactSelected=e=>{this.onChangeExpectedArtifactId(e.id)},this.onChangeExpectedArtifactId=e=>{this.$scope.$applyAsync(()=>{this.$scope.command.expectedArtifactId=e,this.$scope.command.expectedArtifact=null})},this.onExpectedArtifactAccountSelected=e=>{this.$scope.$applyAsync(()=>{this.$scope.command.storageAccountName=e})},n(this,a("BasicSettingsMixin",{$scope:e,imageReader:null,$uibModalStack:i,$state:t})),this.$scope.command.gitCredentialType||this.onAccountChange(),this.$scope.containerArtifactDelegate=new $(e,[y.DOCKER_IMAGE]),this.$scope.containerArtifactController=new B(this.$scope.containerArtifactDelegate),this.$scope.gcsArtifactDelegate=new $(e,[y.GCS_OBJECT]),this.$scope.gcsArtifactController=new B(this.$scope.gcsArtifactDelegate)}isGitSource(){return this.$scope.command.sourceType===en.GIT}isGcsSource(){return this.$scope.command.sourceType===en.GCS}isContainerImageSource(){return this.$scope.command.sourceType===en.CONTAINER_IMAGE}toggleResolveViaTrigger(){this.$scope.command.fromTrigger=!this.$scope.command.fromTrigger,delete this.$scope.command.trigger,delete this.$scope.command.branch}onTriggerChange(){_(this,"$scope.command.trigger.matchBranchOnRegex",void 0)}onAccountChange(){const e=this.findAccountInBackingData();e?(this.$scope.command.gitCredentialType=this.getSupportedGitCredentialTypes()[0],this.$scope.command.region=e.region):(this.$scope.command.gitCredentialType="NONE",delete this.$scope.command.region)}getSupportedGitCredentialTypes(){const e=this.findAccountInBackingData();return e&&e.supportedGitCredentialTypes?e.supportedGitCredentialTypes:["NONE"]}humanReadableGitCredentialType(e){switch(e){case"HTTPS_USERNAME_PASSWORD":return"HTTPS with username and password";case"HTTPS_GITHUB_OAUTH_TOKEN":return"HTTPS with Github OAuth token";case"SSH":return"SSH";default:return"No credentials"}}findAccountInBackingData(){return this.$scope.command.backingData.accounts.find(e=>this.$scope.command.credentials===e.name)}}rn.$inject=["$scope","$state","$controller","$uibModalStack"];const on="spinnaker.appengine.basicSettings.controller";e(on,[]).controller("appengineServerGroupBasicSettingsCtrl",rn);Ae(".appengine-server-group-wizard input[type='checkbox'] {\n margin-top: 0.75rem;\n}\n.appengine-server-group-wizard help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.appengine-server-group-wizard .artifact-configuration-section {\n border-bottom: 1px solid #eee;\n padding: 0 0 4px 0;\n margin: 0 0 5px 0;\n}\n.appengine-server-group-wizard .artifact-configuration-section.last-entry {\n border-bottom: none;\n}\n.appengine-server-group-wizard .artifact-configuration-section .Select,\n.appengine-server-group-wizard .artifact-configuration-section input {\n margin: 0 0 1px;\n}\n");class ln{constructor(e,n={id:"",account:""}){var t;const a={configurable:!1,enumerable:!1,writable:!1};this.id=null==n?void 0:n.id,this.account=n.account||(null==(t=null==n?void 0:n.artifact)?void 0:t.artifactAccount),this.artifact=null==n?void 0:n.artifact,Object.defineProperty(this,"$scope",{...a,value:e});const i=new x(this),r=new B(i);Object.defineProperty(this,"delegate",{...a,value:i}),Object.defineProperty(this,"controller",{...a,value:r})}}class sn{constructor(e){this.$scope=e,this.artifactAccounts=[],this.updateConfigArtifacts=e=>{this.$scope.$applyAsync(()=>{this.command.configArtifacts=e})}}$onInit(){this.command.configFiles||(this.command.configFiles=[]),this.command.configArtifacts||(this.command.configArtifacts=[]),this.$scope.command||(this.$scope.command=this.command),this.command.configArtifacts=this.command.configArtifacts.map(e=>new ln(this.$scope,e)),g.getArtifactAccounts().then(e=>{this.artifactAccounts=e,this.command.configArtifacts.forEach(n=>{n.delegate.setAccounts(e),n.controller.updateAccounts(n.delegate.getSelectedExpectedArtifact())})})}addConfigFile(){this.command.configFiles.push("")}addConfigArtifact(){const e=new ln(this.$scope,{id:"",account:""});e.delegate.setAccounts(this.artifactAccounts),e.controller.updateAccounts(e.delegate.getSelectedExpectedArtifact()),this.command.configArtifacts.push(e)}deleteConfigFile(e){this.command.configFiles.splice(e,1)}deleteConfigArtifact(e){this.command.configArtifacts.splice(e,1)}mapTabToSpaces(e){if(9===e.which){e.preventDefault();const n=e.target.selectionStart,t=e.target.value;e.target.value=`${t.substring(0,n)} ${t.substring(n)}`,e.target.selectionStart+=2}}isContainerImageSource(){return this.command.sourceType===en.CONTAINER_IMAGE}}sn.$inject=["$scope"];const cn={bindings:{command:"="},controller:sn,templateUrl:"appengine/src/serverGroup/configure/wizard/configFiles.component.html"},pn="spinnaker.appengine.configFileConfigurer.component";e(pn,[]).component("appengineConfigFileConfigurer",cn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/configFiles.component.html",'<div class="form-horizontal container-fluid">\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Application Root\n <help-field class="help-field-absolute" key="appengine.serverGroup.applicationDirectoryRoot"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm"\n name="applicationDirectoryRoot"\n ng-model="$ctrl.command.applicationDirectoryRoot"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="!$ctrl.isContainerImageSource()">\n <div class="col-md-3 sm-label-right">\n Config Filepaths <help-field key="appengine.serverGroup.configFilepaths"></help-field>\n </div>\n <div class="col-md-7">\n <ui-select\n multiple\n tagging\n tagging-label=""\n style="width: 380px"\n name="configFilepaths"\n ng-model="$ctrl.command.configFilepaths"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="filepath in []">\n <span ng-bind-html="filepath"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Config Files\n <span ng-if="!$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFiles"></help-field>\n </span>\n <span ng-if="$ctrl.isContainerImageSource()">\n <help-field key="appengine.serverGroup.configFilesRequired"></help-field>\n </span>\n </div>\n <div ng-repeat="configFile in $ctrl.command.configFiles track by $index">\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $index > 0 }" style="margin-top: 5px">\n <textarea\n cols="46"\n rows="10"\n class="small"\n spellcheck="false"\n ng-keydown="$ctrl.mapTabToSpaces($event)"\n style="font-family: Menlo, Monaco, Consolas, \'Courier New\', monospace"\n ng-model="$ctrl.command.configFiles[$index]"\n ></textarea>\n </div>\n <div class="col-md-1" style="margin-top: 5px">\n <button type="button" class="btn btn-sm btn-default" ng-click="$ctrl.deleteConfigFile($index)">\n <span class="glyphicon glyphicon-trash"></span> Delete\n </button>\n </div>\n </div>\n <div class="col-md-7" ng-class="{ \'col-md-offset-3\': $ctrl.command.configFiles.length > 0 }">\n <button class="btn btn-block btn-add-trigger add-new" ng-click="$ctrl.addConfigFile()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Config File\n </button>\n </div>\n <config-file-artifact-list\n ng-if="$ctrl.command.viewState.pipeline"\n config-artifacts="$ctrl.command.configArtifacts"\n pipeline="$ctrl.command.viewState.pipeline"\n stage="$ctrl.command.viewState.stage"\n update-config-artifacts="$ctrl.updateConfigArtifacts"\n >\n </config-file-artifact-list>\n </div>\n</div>\n')}]);const dn={bindings:{trigger:"<"},template:'\n <span ng-if="$ctrl.trigger.type === \'git\'">\n Resolved at runtime by <b>{{$ctrl.trigger.source}}</b> trigger: {{$ctrl.trigger.project}}/{{$ctrl.trigger.slug}}<span ng-if="$ctrl.trigger.branch">:{{$ctrl.trigger.branch}}</span>\n </span>\n <span ng-if="$ctrl.trigger.type === \'jenkins\'">\n Resolved at runtime by <b>Jenkins</b> trigger: {{$ctrl.trigger.master}}/{{$ctrl.trigger.job}}\n </span>\n '},gn="spinnaker.appengine.dynamicBranchLabel.component";e(gn,[]).component("appengineDynamicBranchLabel",dn);class un{constructor(e,n,t,a,i,r){this.$scope=e,this.$uibModalInstance=n,this.serverGroupCommand=t,this.application=a,this.serverGroupWriter=i,this.pages={basicSettings:"appengine/src/serverGroup/configure/wizard/basicSettings.html",advancedSettings:"appengine/src/serverGroup/configure/wizard/advancedSettings.html"},this.state={loading:!0},["create","clone","editPipeline"].includes(j(t,"viewState.mode"))?(this.$scope.command=t,this.state.loading=!1,this.initialize()):r.buildNewServerGroupCommand(a,"appengine","createPipeline").then(e=>{this.$scope.command=J(e,t),this.$scope.command.viewState.pipeline=t.viewState.pipeline,this.$scope.command.viewState.stage=t.viewState.stage,this.state.loading=!1,this.initialize()})}cancel(){this.$uibModalInstance.dismiss()}submit(){const e=this.$scope.command.viewState.mode;if(["editPipeline","createPipeline"].includes(e))return this.$uibModalInstance.close(this.$scope.command);{const e=t(this.$scope.command);e.viewState.mode="create";const n=()=>this.serverGroupWriter.cloneServerGroup(e,this.$scope.application);return this.taskMonitor.submit(n),null}}initialize(){this.$scope.application=this.application,this.taskMonitor=new d({application:this.application,title:"Creating your server group",modalInstance:this.$uibModalInstance}),this.$scope.showPlatformHealthOnlyOverride=this.application.attributes.platformHealthOnlyShowOverride,this.$scope.platformHealth=De.PLATFORM,this.application.attributes.platformHealthOnly&&(this.$scope.command.interestingHealthProviderNames=[De.PLATFORM])}}un.$inject=["$scope","$uibModalInstance","serverGroupCommand","application","serverGroupWriter","appengineServerGroupCommandBuilder"];const mn="spinnaker.appengine.cloneServerGroup.controller";e(mn,[T,gn,pn]).controller("appengineCloneServerGroupCtrl",un),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/basicSettings.html",'<div class="container-fluid form-horizontal" ng-controller="appengineServerGroupBasicSettingsCtrl as basicSettingsCtrl">\n <ng-form name="basicSettings">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Account</div>\n <div class="col-md-7">\n <account-select-field\n read-only="command.viewState.readOnlyFields.credentials"\n component="command"\n field="credentials"\n on-change="basicSettingsCtrl.onAccountChange()"\n accounts="command.backingData.accounts"\n provider="\'appengine\'"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Region</div>\n <div class="col-md-7">\n <input type="text" readonly class="form-control input-sm" name="region" ng-model="command.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Stack\n <help-field key="aws.serverGroup.stack"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.stackPattern"\n name="stack"\n ng-model="command.stack"\n />\n </div>\n </div>\n <div class="form-group row slide-in" ng-if="basicSettings.stack.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.) and underscore(_) special characters are allowed in the Stack field.</span>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Detail\n <help-field key="aws.serverGroup.detail"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n class="form-control input-sm no-spel"\n ng-pattern="basicSettingsCtrl.detailPattern"\n name="details"\n ng-model="command.freeFormDetails"\n />\n </div>\n </div>\n\n <div class="form-group row slide-in" ng-if="basicSettings.details.$error.pattern">\n <div class="col-sm-9 col-sm-offset-2 error-message">\n <span>Only dot(.), underscore(_), and dash(-) special characters are allowed in the Detail field.</span>\n </div>\n </div>\n\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Source Type</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="git" /> Git </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="gcs" /> GCS </label>\n </div>\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.sourceType" value="containerImage" /> Container Image </label>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGcsSource()">\n <div class="form-group row">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedGcsArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n GCS URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.gcs.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gcsUrl" ng-model="command.repositoryUrl" />\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isGitSource()">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Repository URL\n <help-field class="help-field-absolute" key="appengine.serverGroup.git.repositoryUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input type="text" required class="form-control input-sm" name="gitRepo" ng-model="command.repositoryUrl" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n Git Credential Type\n <help-field class="help-field-absolute" key="appengine.serverGroup.gitCredentialType"></help-field>\n </div>\n <div class="col-md-7">\n <select\n class="form-control input-sm"\n ng-options="basicSettingsCtrl.humanReadableGitCredentialType(type) for type in basicSettingsCtrl.getSupportedGitCredentialTypes()"\n ng-model="command.gitCredentialType"\n ></select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Branch <help-field key="appengine.serverGroup.branch"></help-field></div>\n <div class="col-md-7">\n <input\n ng-if="!command.fromTrigger"\n type="text"\n required\n class="form-control input-sm"\n name="branch"\n ng-model="command.branch"\n />\n\n <ui-select\n ng-if="command.fromTrigger"\n ng-model="command.trigger"\n class="form-control input-sm"\n on-select="basicSettingsCtrl.onTriggerChange()"\n required\n >\n <ui-select-match allow-clear>\n <appengine-dynamic-branch-label trigger="command.trigger"></appengine-dynamic-branch-label>\n </ui-select-match>\n <ui-select-choices repeat="trigger in command.backingData.triggerOptions">\n <appengine-dynamic-branch-label trigger="trigger"></appengine-dynamic-branch-label>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div\n class="col-md-7 col-md-offset-3"\n ng-if="command.viewState.mode === \'createPipeline\' || command.viewState.mode === \'editPipeline\'"\n >\n <span class="pull-right small" ng-if="!command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Resolve via trigger</a>\n </span>\n <span class="pull-right small" ng-if="command.fromTrigger">\n <a href ng-click="basicSettingsCtrl.toggleResolveViaTrigger()">Click for text input</a>\n </span>\n </div>\n </div>\n </div>\n\n <div ng-if="basicSettingsCtrl.isContainerImageSource()">\n <div class="form-group">\n <label class="col-md-3 sm-label-right">Resolve URL</label>\n <div class="col-md-7">\n <div class="radio radio-inline">\n <label> <input type="radio" ng-model="command.fromArtifact" ng-value="false" /> via text input </label>\n </div>\n <div class="radio radio-inline" ng-if="command.viewState.pipeline">\n <label>\n <input type="radio" ng-model="command.fromArtifact" ng-value="true" /> via pipeline artifact\n </label>\n </div>\n </div>\n </div>\n <stage-artifact-selector-delegate\n ng-if="command.fromArtifact"\n artifact="command.expectedArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedContainerArtifactTypes"\n expected-artifact-id="command.expectedArtifactId"\n field-columns="7"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onExpectedArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onExpectedArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n <div class="form-group" ng-if="!command.fromArtifact">\n <div class="col-md-3 sm-label-right">\n Image URL\n <help-field key="appengine.serverGroup.containerImageUrl"></help-field>\n </div>\n <div class="col-md-7">\n <input\n type="text"\n required\n class="form-control input-sm"\n name="containerImageUrl"\n ng-model="command.containerImageUrl"\n />\n </div>\n </div>\n </div>\n\n <div ng-if="command.trigger.type === \'jenkins\'" class="form-group">\n <div class="col-md-7 col-md-offset-3">\n <div class="form-inline">\n <small>Match branch from trigger on regex</small>\n <help-field key="appengine.serverGroup.matchBranchOnRegex"></help-field>\n <input\n type="text"\n style="width: 140px"\n class="form-control input-sm pull-right"\n name="matchOnRegex"\n ng-model="command.trigger.matchBranchOnRegex"\n />\n </div>\n </div>\n </div>\n\n <deployment-strategy-selector\n field-columns="7"\n ng-if="!command.viewState.disableStrategySelection"\n command="command"\n ></deployment-strategy-selector>\n\n <div class="form-group" ng-if="!command.viewState.hideClusterNamePreview">\n <div class="col-md-12">\n <div class="well-compact" ng-class="basicSettingsCtrl.showPreviewAsWarning() ? \'alert alert-warning\' : \'well\'">\n <h5 class="text-center">\n <p>Your server group will be in the cluster:</p>\n <p>\n <strong>\n {{basicSettingsCtrl.getNamePreview()}}\n <span ng-if="basicSettingsCtrl.createsNewCluster()"> (new cluster)</span>\n </strong>\n </p>\n <div\n class="text-left"\n ng-if="!basicSettingsCtrl.createsNewCluster() && command.viewState.mode === \'create\' && latestServerGroup"\n >\n <p>There is already a server group in this cluster. Do you want to clone it?</p>\n <p>\n Cloning copies the entire configuration from the selected server group, allowing you to modify whichever\n fields (e.g. image) you need to change in the new server group.\n </p>\n <p>\n To clone a server group, select "Clone" from the "Server Group Actions" menu in the details view of the\n server group.\n </p>\n <p>\n <a href ng-click="basicSettingsCtrl.navigateToLatestServerGroup()">\n Go to details for {{latestServerGroup.name}}\n </a>\n </p>\n </div>\n </h5>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/advancedSettings.html",'<div class="form-horizontal container-fluid">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Promote <help-field key="appengine.serverGroup.promote"></help-field></div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.promote" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Stop Previous Version <help-field key="appengine.serverGroup.stopPreviousVersion"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.stopPreviousVersion" />\n </div>\n </div>\n <div class="form-group" ng-if="showPlatformHealthOnlyOverride">\n <div class="col-md-4 sm-label-right">Task Completion</div>\n <div class="col-md-7">\n <platform-health-override command="command" platform-health-type="platformHealth"> </platform-health-override>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Suppress Version String\n <help-field key="appengine.serverGroup.suppress-version-string"></help-field>\n </div>\n <div class="col-md-7">\n <input type="checkbox" ng-model="command.suppressVersionString" />\n </div>\n </div>\n</div>\n')}]);const hn="spinnaker.appengine.configFileArtifactList.component";e(hn,[]).component("configFileArtifactList",ne(E(e=>{const n=(n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:n,artifact:null}),e.updateConfigArtifacts(a)};return Q.createElement(Q.Fragment,null,e.configArtifacts.map((t,a)=>Q.createElement("div",{key:t.id,className:te("artifact-configuration-section col-md-12",{"last-entry":e.configArtifacts.length-1===a})},Q.createElement("div",{className:"col-md-9"},Q.createElement(D,{artifact:t.artifact,excludedArtifactTypePatterns:[],expectedArtifactId:null==t.artifact?t.id:null,onArtifactEdited:n=>{((n,t)=>{const a=[...e.configArtifacts];a.splice(t,1,{...a[t],id:null,artifact:n}),e.updateConfigArtifacts(a)})(n,a)},onExpectedArtifactSelected:e=>{((e,t)=>{n(e.id,t)})(e,a)},pipeline:e.pipeline,stage:e.stage})),Q.createElement("div",{className:"col-md-1"},Q.createElement("button",{type:"button",className:"btn btn-sm btn-default",onClick:()=>(n=>{const t=[...e.configArtifacts];t.splice(n,1),e.updateConfigArtifacts(t)})(a)},Q.createElement("span",{className:"glyphicon glyphicon-trash"})," Delete")))),Q.createElement("div",{className:"col-md-7 col-md-offset-3"},Q.createElement("button",{className:"btn btn-block btn-add-trigger add-new",onClick:()=>{e.updateConfigArtifacts(e.configArtifacts.concat([{id:"",account:""}]))}},Q.createElement("span",{className:"glyphicon glyphicon-plus-sign"})," Add Config Artifact")))},"configFileArtifactList"),["configArtifacts","pipeline","stage","updateConfigArtifacts"]));const vn="spinnaker.appengine.serverGroup.write.service";e(vn,[]).service("appengineServerGroupWriter",class{startServerGroup(e,n){const t={job:[this.buildJob(e,n,"startAppEngineServerGroup")],application:n,description:`Start Server Group: ${e.name}`};return F.executeTask(t)}stopServerGroup(e,n){const t={job:[this.buildJob(e,n,"stopAppEngineServerGroup")],application:n,description:`Stop Server Group: ${e.name}`};return F.executeTask(t)}buildJob(e,n,t){return{type:t,region:e.region,serverGroupName:e.name,credentials:e.account,cloudProvider:"appengine",application:n.name}}});const fn=class e{constructor(e,n,t,a,i,r,o,l){this.$state=e,this.$scope=n,this.$uibModal=t,this.app=i,this.serverGroupWriter=r,this.appengineServerGroupWriter=o,this.appengineServerGroupCommandBuilder=l,this.state={loading:!0},this.app.ready().then(()=>this.extractServerGroup(a)).then(()=>{this.$scope.$$destroyed||this.app.getDataSource("serverGroups").onRefresh(this.$scope,()=>this.extractServerGroup(a))}).catch(()=>this.autoClose())}static buildExpectedAllocationsTable(e){return`\n <table class="table table-condensed">\n <thead>\n <tr>\n <th>Server Group</th>\n <th>Allocation</th>\n </tr>\n </thead>\n <tbody>\n ${K(e,(e,n)=>`\n <tr>\n <td>${n}</td>\n <td>${100*e}%</td>\n </tr>`).join("")}\n </tbody>\n </table>`}canDisableServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!1;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}canDestroyServerGroup(){if(this.serverGroup){if(this.serverGroup.disabled)return!0;const e=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app);return!!e&&Object.keys(e).length>0}return!1}destroyServerGroup(){const e={name:this.serverGroup.name,accountId:this.serverGroup.account,region:this.serverGroup.region},n={application:this.app,title:"Destroying "+this.serverGroup.name,onTaskComplete:()=>{this.$state.includes("**.serverGroup",e)&&this.$state.go("^")}},t={header:"Really destroy "+this.serverGroup.name+"?",buttonText:"Destroy "+this.serverGroup.name,account:this.serverGroup.account,taskMonitorConfig:n,submitMethod:e=>this.serverGroupWriter.destroyServerGroup(this.serverGroup,this.app,e),askForReason:!0,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,body:this.getBodyTemplate(this.serverGroup,this.app),interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}enableServerGroup(){const e={application:this.app,title:"Enabling "+this.serverGroup.name},n=`<div class="well well-sm">\n <p>\n Enabling <b>${this.serverGroup.name}</b> will set its traffic allocation for\n <b>${this.serverGroup.loadBalancers[0]}</b> to 100%.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n </div>\n `,t={header:"Really enable "+this.serverGroup.name+"?",buttonText:"Enable "+this.serverGroup.name,body:n,account:this.serverGroup.account,taskMonitorConfig:e,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.enableServerGroup(this.serverGroup,this.app,{...e}),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(t)}disableServerGroup(){const n={application:this.app,title:"Disabling "+this.serverGroup.name},t=this.expectedAllocationsAfterDisableOperation(this.serverGroup,this.app),a=`<div class="well well-sm">\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${this.serverGroup.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${e.buildExpectedAllocationsTable(t)}\n </div>\n </div>\n </div>\n `,r={header:"Really disable "+this.serverGroup.name+"?",buttonText:"Disable "+this.serverGroup.name,body:a,account:this.serverGroup.account,taskMonitorConfig:n,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,submitMethod:e=>this.serverGroupWriter.disableServerGroup(this.serverGroup,this.app.name,e),askForReason:!0,interestingHealthProviderNames:[]};this.app.attributes.platformHealthOnlyShowOverride&&this.app.attributes.platformHealthOnly&&(r.interestingHealthProviderNames=[De.PLATFORM]),i.confirm(r)}stopServerGroup(){const e={application:this.app,title:"Stopping "+this.serverGroup.name};let n;this.serverGroup.disabled||(n=`<div class="alert alert-danger">\n <p>Stopping this server group will scale it down to zero instances.</p>\n <p>\n This server group is currently serving traffic from <b>${this.serverGroup.loadBalancers[0]}</b>.\n Traffic directed to this server group after it has been stopped will not be handled.\n </p>\n </div>`);const t={header:"Really stop "+this.serverGroup.name+"?",buttonText:"Stop "+this.serverGroup.name,account:this.serverGroup.account,body:n,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.stopServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(t)}startServerGroup(){const e={application:this.app,title:"Starting "+this.serverGroup.name},n={header:"Really start "+this.serverGroup.name+"?",buttonText:"Start "+this.serverGroup.name,account:this.serverGroup.account,platformHealthOnlyShowOverride:this.app.attributes.platformHealthOnlyShowOverride,platformHealthType:De.PLATFORM,taskMonitorConfig:e,submitMethod:()=>this.appengineServerGroupWriter.startServerGroup(this.serverGroup,this.app),askForReason:!0};i.confirm(n)}cloneServerGroup(){this.$uibModal.open({templateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",controller:"appengineCloneServerGroupCtrl as ctrl",size:"lg",resolve:{title:()=>"Clone "+this.serverGroup.name,application:()=>this.app,serverGroup:()=>this.serverGroup,serverGroupCommand:()=>this.appengineServerGroupCommandBuilder.buildServerGroupCommandFromExisting(this.app,this.serverGroup)}})}canStartServerGroup(){return!!this.canStartOrStopServerGroup()&&"STOPPED"===this.serverGroup.servingStatus}canStopServerGroup(){return!!this.canStartOrStopServerGroup()&&"SERVING"===this.serverGroup.servingStatus}canStartOrStopServerGroup(){var e;return"FLEXIBLE"===this.serverGroup.env||["MANUAL","BASIC"].includes(null==(e=this.serverGroup.scalingPolicy)?void 0:e.type)}getBodyTemplate(n,t){let a="";const i={};if(L.addDestroyWarningMessage(t,n,i),i.body&&(a+=i.body),!n.disabled){const i=this.expectedAllocationsAfterDisableOperation(n,t);a+=`\n <div class="well well-sm">\n <p>\n A destroy operation will first disable this server group.\n </p>\n <p>\n For App Engine, a disable operation sets this server group's allocation\n to 0% and sets the other enabled server groups' allocations to their relative proportions\n before the disable operation. The approximate allocations that will result from this operation are shown below.\n </p>\n <p>\n If you would like more fine-grained control over your server groups' allocations,\n edit <b>${n.loadBalancers[0]}</b> under the <b>Load Balancers</b> tab.\n </p>\n <div class="row">\n <div class="col-md-12">\n ${e.buildExpectedAllocationsTable(i)}\n </div>\n </div>\n </div>\n `}return a}expectedAllocationsAfterDisableOperation(e,n){const t=n.getDataSource("loadBalancers").data.find(n=>{var t,a;const i=null!=(a=null==(t=n.split)?void 0:t.allocations)?a:{};return Object.keys(i).includes(e.name)});if(t){let n=N(t.split.allocations);delete n[e.name];const a=W(n,(e,n)=>e+n,0),i="COOKIE"===t.split.shardBy?1e3:100;return n=Y(n,e=>Math.round(e/a*i)/i),n}return null}autoClose(){this.$scope.$$destroyed||(this.$state.params.allowModalToStayOpen=!0,this.$state.go("^",null,{location:"replace"}))}extractServerGroup(e){return P.getServerGroup(this.app.name,e.accountId,e.region,e.name).then(n=>{let t=this.app.getDataSource("serverGroups").data.find(n=>n.name===e.name&&n.account===e.accountId&&n.region===e.region);t||this.app.getDataSource("loadBalancers").data.some(n=>n.account===e.accountId&&n.serverGroups.some(n=>{let a=!1;return n.name===e.name&&(t=n,a=!0),a})),this.serverGroup={...n,...t},this.state.loading=!1})}};fn.$inject=["$state","$scope","$uibModal","serverGroup","app","serverGroupWriter","appengineServerGroupWriter","appengineServerGroupCommandBuilder"];let bn=fn;const yn="spinnaker.appengine.serverGroup.details.controller";e(yn,[vn,T]).controller("appengineServerGroupDetailsCtrl",bn),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]);class Cn{constructor(e){this.cloudProvider="appengine",this.provider="appengine",this.credentials=e.credentials,this.account=e.credentials,this.application=e.application,this.stack=e.stack,this.freeFormDetails=e.freeFormDetails,this.repositoryUrl=e.repositoryUrl,this.branch=e.branch,this.configFilepaths=e.configFilepaths,this.promote=e.promote,this.stopPreviousVersion=e.stopPreviousVersion,this.type=e.type,this.region=e.region,this.strategy=e.strategy,this.strategyApplication=e.strategyApplication,this.strategyPipeline=e.strategyPipeline,this.fromTrigger=e.fromTrigger,this.trigger=e.trigger,this.gitCredentialType=e.gitCredentialType,this.configFiles=e.configFiles,this.configArtifacts=e.configArtifacts.filter(e=>!!e.id||!!e.artifact),this.applicationDirectoryRoot=e.applicationDirectoryRoot,this.interestingHealthProviderNames=e.interestingHealthProviderNames||[],this.expectedArtifactId=e.expectedArtifactId,this.expectedArtifact=e.expectedArtifact,this.fromArtifact=e.fromArtifact,this.sourceType=e.sourceType,this.storageAccountName=e.storageAccountName,this.containerImageUrl=e.containerImageUrl,this.suppressVersionString=e.suppressVersionString}}class Sn{constructor(e){this.$q=e}normalizeServerGroup(e){return this.$q.resolve(e)}convertServerGroupCommandToDeployConfiguration(e){return new Cn(e)}}Sn.$inject=["$q"];const wn="spinnaker.appengine.serverGroup.transformer.service";e(wn,[]).service("appengineServerGroupTransformer",Sn);I.registerValidator("appengine",new class{validate(e=""){const n=[],t=[];return e.length&&(this.validateSpecialCharacters(e,t),this.validateLength(e,n,t)),{warnings:n,errors:t}}validateSpecialCharacters(e,n){/^[a-z0-9]*$/g.test(e)||n.push("Only numbers and lowercase letters are allowed.")}validateLength(e,n,t){if(e.length>58)t.push("The maximum length for an App Engine application name is 63 characters.");else if(e.length>48)if(e.length>=56)n.push("You will not be able to include a stack or detail field for clusters.");else{const t=56-e.length;n.push(`If you plan to include a stack or detail field for clusters, you will only have\n ${t} character${t>1?"s":""} to do so.`)}}});Ae('.cloud-provider-logo .icon-appengine {\n -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22utf-8%22%3F%3E%3C!-- Generator%3A Adobe Illustrator 18.1.1%2C SVG Export Plug-In . SVG Version%3A 6.00 Build 0) --%3E%3Csvg version%3D%221.1%22 xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22 x%3D%220px%22 y%3D%220px%22 width%3D%22128px%22%09 height%3D%22128px%22 viewBox%3D%2215 15 90 90%22 xml%3Aspace%3D%22preserve%22%3E%3Cfilter id%3D%22invert%22%3E %3CfeColorMatrix in%3D%22SourceGraphic%22 type%3D%22matrix%22 values%3D%22-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0%22%2F%3E%3C%2Ffilter%3E%3Cg id%3D%22art%22 filter%3D%22url(%23invert)%22%3E%09%3Cg%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9988%2C40.9577c-12.7278%2C0-23.0452%2C10.3167-23.0452%2C23.0464%09%09%09c0%2C12.726%2C10.3174%2C23.0463%2C23.0452%2C23.0463c12.7273%2C0%2C23.0446-10.3203%2C23.0446-23.0463%09%09%09C87.0434%2C51.2744%2C76.7261%2C40.9577%2C63.9988%2C40.9577 M63.9988%2C81.4887c-9.6584%2C0-17.4876-7.8286-17.4876-17.4846%09%09%09c0-9.6584%2C7.8291-17.4877%2C17.4876-17.4877c9.6578%2C0%2C17.4858%2C7.8292%2C17.4858%2C17.4877%09%09%09C81.4846%2C73.6601%2C73.6566%2C81.4887%2C63.9988%2C81.4887%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M100.1431%2C61.274l-9.6079-3.0195c0.3997%2C1.8539%2C0.615%2C3.7759%2C0.615%2C5.7478%09%09%09c0%2C1.3791-0.1047%2C2.7337-0.3021%2C4.0569h9.295c0.8587-0.2515%2C1.4327-0.7846%2C1.4327-1.5765v-3.6345%09%09%09C101.5759%2C62.0575%2C101.0018%2C61.5094%2C100.1431%2C61.274%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M63.9923%2C36.8497c1.9303%2C0%2C3.8119%2C0.2046%2C5.6285%2C0.5884l-3.4443-9.5729%09%09%09c-0.2515-0.8606-0.7852-1.434-1.5765-1.434h-1.4392c-0.7913%2C0-1.3407%2C0.5734-1.5761%2C1.434l-2.9924%2C9.5236%09%09%09C60.3372%2C37.0368%2C62.1425%2C36.8497%2C63.9923%2C36.8497%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M36.8468%2C64.0023c0-1.9719%2C0.2154-3.8932%2C0.6145-5.7478l-9.6073%2C3.0195%09%09%09c-0.8587%2C0.2354-1.4322%2C0.7835-1.4322%2C1.5754v3.6344c0%2C0.7907%2C0.5735%2C1.3238%2C1.4322%2C1.5753h9.2955%09%09%09C36.9515%2C66.7366%2C36.8468%2C65.3814%2C36.8468%2C64.0023%22%2F%3E%09%09%3Cpath style%3D%22fill%3A%23FFFFFF%3B%22 d%3D%22M72.2276%2C56.4819l-2.1802%2C2.1794v-0.0048c-1.5651-1.5639-3.6549-2.4274-5.8687-2.4274%09%09%09c-2.2192%2C0-4.303%2C0.8659-5.8704%2C2.4322c-3.2362%2C3.2355-3.2362%2C8.5037%2C0%2C11.7399l-2.1802%2C2.1783%09%09%09c2.0627%2C2.0609%2C4.9096%2C3.3383%2C8.0483%2C3.3383c6.2803%2C0%2C11.3896-5.1081%2C11.3896-11.3895%09%09%09C75.566%2C61.3883%2C74.289%2C58.5421%2C72.2276%2C56.4819 M67.7284%2C67.7342c-1.0278%2C1.0302-2.3799%2C1.5459-3.7284%2C1.5459%09%09%09c-1.3526%2C0-2.7012-0.5156-3.7313-1.5459c-2.0616-2.0615-2.0616-5.4018%2C0-7.4639c1.0301-1.0314%2C2.3787-1.544%2C3.7313-1.544%09%09%09c1.3485%2C0%2C2.7006%2C0.5127%2C3.7284%2C1.544C69.79%2C62.3312%2C69.79%2C65.6727%2C67.7284%2C67.7342%22%2F%3E%09%3C%2Fg%3E%3C%2Fg%3E%3Cg id%3D%22Guides%22 style%3D%22display%3Anone%3B%22%3E%3C%2Fg%3E%3C%2Fsvg%3E");\n background-color: #4285f4;\n}\n');const Gn="spinnaker.appengine";e(Gn,[mn,ie,le,pe,se,Te,Je,on,an,yn,wn,vn,hn]).config(()=>{m.registerProvider("appengine",{name:"App Engine",instance:{detailsTemplateUrl:"appengine/src/instance/details/details.html",detailsController:"appengineInstanceDetailsCtrl"},serverGroup:{transformer:"appengineServerGroupTransformer",detailsController:"appengineServerGroupDetailsCtrl",detailsTemplateUrl:"appengine/src/serverGroup/details/details.html",commandBuilder:"appengineServerGroupCommandBuilder",cloneServerGroupController:"appengineCloneServerGroupCtrl",cloneServerGroupTemplateUrl:"appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",skipUpstreamStageCheck:!0},loadBalancer:{transformer:"appengineLoadBalancerTransformer",createLoadBalancerTemplateUrl:"appengine/src/loadBalancer/configure/wizard/wizard.html",createLoadBalancerController:"appengineLoadBalancerWizardCtrl",detailsTemplateUrl:"appengine/src/loadBalancer/details/details.html",detailsController:"appengineLoadBalancerDetailsCtrl"},logo:{path:"appengine.logoc2c312af6aa99037.png"}})}),O.registerProvider("appengine",["custom"]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/instance/details/details.html",'<div class="details-panel">\n <div class="header">\n <instance-details-header\n health-state="ctrl.instance.healthState"\n instance-id="ctrl.instance ? ctrl.instance.name : ctrl.instanceIdNotFound"\n loading="ctrl.state.loading"\n standalone="false"\n ></instance-details-header>\n <div ng-if="!ctrl.state.loading">\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Instance Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && ctrl.instance">\n <collapsible-section heading="Instance Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Launched</dt>\n <dd ng-if="ctrl.instance.launchTime">{{ctrl.instance.launchTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.instance.account" pad="right"></account-tag>{{}}</dd>\n <dt ng-if="ctrl.instance.serverGroup">Server Group</dt>\n <dd ng-if="ctrl.instance.serverGroup">\n <a\n ui-sref="^.serverGroup({region: ctrl.instance.region,\n accountId: ctrl.instance.account,\n serverGroup: ctrl.instance.serverGroup,\n provider: \'appengine\'})"\n >{{ctrl.instance.serverGroup}}</a\n >\n </dd>\n <dt>Region</dt>\n <dd>{{ctrl.instance.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmZoneName"\n label="Zone"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <dl>\n <dt>Load Balancer</dt>\n <dd>\n <span\n class="pull-left"\n uib-tooltip="{{ctrl.instance.healthState === \'Up\' ? ctrl.upToolTip : ctrl.outOfServiceToolTip}}"\n tooltip-placement="right"\n >\n <span class="glyphicon glyphicon-{{ctrl.instance.healthState}}-triangle"></span>\n {{ctrl.instance.loadBalancers[0]}}\n </span>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Metrics" expanded="true">\n <dl>\n <appengine-conditional-dt-dd component="ctrl.instance" key="instanceStatus" label="Availability">\n <key-label><help-field key="appengine.instance.availability"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="averageLatency">\n <key-label><help-field key="appengine.instance.averageLatency"></help-field></key-label>\n <value-label>ms</value-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="errors">\n <key-label><help-field key="appengine.instance.errors"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="qps" label="QPS">\n <key-label><help-field key="appengine.instance.qps"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="requests">\n <key-label><help-field key="appengine.instance.requests"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.instance" key="vmStatus" label="VM Status">\n <key-label><help-field key="appengine.instance.vmStatus"></help-field></key-label>\n </appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.instance"\n key="vmDebugEnabled"\n label="Debug Enabled"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n <div class="content" ng-if="!ctrl.state.loading && !ctrl.instance">\n <div class="content-section">\n <div class="content-body text-center">\n <h3>Instance not found.</h3>\n </div>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/details/details.html",'<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled}">\n <div class="header" ng-if="ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div class="header" ng-if="!ctrl.state.loading">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <cloud-provider-logo provider="ctrl.serverGroup.type" height="\'36px\'" width="\'36px\'"></cloud-provider-logo>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.serverGroup.name}}</h3>\n </div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Server Group Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-if="ctrl.canStopServerGroup()">\n <a href ng-click="ctrl.stopServerGroup()"> Stop </a>\n </li>\n <li ng-if="ctrl.canStartServerGroup()">\n <a href ng-click="ctrl.startServerGroup()"> Start </a>\n </li>\n <li ng-if="ctrl.serverGroup.disabled">\n <a href ng-click="ctrl.enableServerGroup()"> Enable </a>\n </li>\n <li ng-if="!ctrl.serverGroup.disabled && ctrl.canDisableServerGroup()">\n <a href ng-click="ctrl.disableServerGroup()"> Disable </a>\n </li>\n <li\n ng-if="!ctrl.serverGroup.disabled && !ctrl.canDisableServerGroup()"\n uib-tooltip="You cannot disable a server group if it is the\n only server group receiving traffic from a load balancer."\n class="disabled"\n >\n <a href> Disable </a>\n </li>\n <li ng-if="ctrl.canDestroyServerGroup()">\n <a href ng-click="ctrl.destroyServerGroup()">Destroy</a>\n </li>\n <li\n ng-if="!ctrl.canDestroyServerGroup()"\n uib-tooltip="You cannot destroy a server group if it is the only server group\n receiving traffic from a load balancer. You may be able to delete\n this server group\'s load balancer."\n class="disabled"\n >\n <a href>Destroy</a>\n </li>\n <li\n uib-tooltip="It is not possible to clone an App Engine server group\'s full\n launch configuration. However, clicking this button will allow\n you to deploy into this server group\'s cluster."\n >\n <a href ng-click="ctrl.cloneServerGroup()">Clone</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n <div class="content" ng-if="!ctrl.state.loading">\n <div class="band band-info" ng-if="ctrl.serverGroup.isDisabled || ctrl.serverGroup.disabled">Disabled</div>\n <server-group-running-tasks-details\n server-group="ctrl.serverGroup"\n application="ctrl.app"\n ></server-group-running-tasks-details>\n <collapsible-section heading="Server Group Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Created</dt>\n <dd>{{ctrl.serverGroup.createdTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="ctrl.serverGroup.account"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.serverGroup.region}}</dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup"\n key="env"\n label="Environment"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd component="ctrl.serverGroup" key="instanceClass"></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Size" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min === ctrl.serverGroup.capacity.max">\n <dt>Min/Max</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.capacity.min !== ctrl.serverGroup.capacity.max">\n <dt>Min</dt>\n <dd>{{ctrl.serverGroup.capacity.min}}</dd>\n <dt>Max</dt>\n <dd>{{ctrl.serverGroup.capacity.max}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Health" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup">\n <dt>Instances</dt>\n <dd>\n <health-counts container="ctrl.serverGroup.instanceCounts" class="pull-left"></health-counts>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.serverGroup"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Scaling Policy" expanded="true" ng-if="ctrl.serverGroup.scalingPolicy">\n <dl>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="type"\n ></appengine-conditional-dt-dd>\n \x3c!--MANUAL SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="instances"\n ></appengine-conditional-dt-dd>\n \x3c!--BASIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="idleTimeout"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxInstances"\n ></appengine-conditional-dt-dd>\n \x3c!--AUTOMATIC SCALING PROPERTIES--\x3e\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="coolDownPeriod"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minTotalInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minIdleInstances"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="maxPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy"\n key="minPendingLatency"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="aggregationWindowLength"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.cpuUtilization"\n key="targetUtilization"\n label="Target CPU Utilization"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetReadOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.diskUtilization"\n key="targetWriteOpsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetReceivedPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentBytesPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.networkUtilization"\n key="targetSentPacketsPerSecond"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetConcurrentRequests"\n ></appengine-conditional-dt-dd>\n <appengine-conditional-dt-dd\n component="ctrl.serverGroup.scalingPolicy.requestUtilization"\n key="targetRequestCountPerSecond"\n ></appengine-conditional-dt-dd>\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal appengine-server-group-wizard" novalidate>\n <div ng-if="ctrl.state.loading" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div>\n <v2-modal-wizard\n ng-if="!ctrl.state.loading"\n heading="Create Server Group"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Config Files" mark-complete-on-view="false">\n <appengine-config-file-configurer command="command"></appengine-config-file-configurer>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancer" label="Load Balancer" done="true">\n <appengine-load-balancer-message show-create-message="false"></appengine-load-balancer-message>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" done="true">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="!state.loading">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="form.$valid"\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/configure/wizard/wizard.html",'<form name="form">\n <div ng-if="ctrl.state.loading && !ctrl.isNew" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{::ctrl.heading}}"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n ng-if="!ctrl.state.loading || ctrl.isNew"\n >\n <div ng-if="!ctrl.isNew">\n <v2-wizard-page key="basic-settings" label="Basic Settings" mark-complete-on-view="false">\n <appengine-load-balancer-basic-settings\n load-balancer="ctrl.loadBalancer"\n application="ctrl.application"\n for-pipeline-config="ctrl.forPipelineConfig"\n ></appengine-load-balancer-basic-settings>\n </v2-wizard-page>\n <v2-wizard-page key="advanced-settings" label="Advanced Settings" mark-complete-on-view="false">\n <appengine-load-balancer-advanced-settings\n load-balancer="ctrl.loadBalancer"\n ></appengine-load-balancer-advanced-settings>\n </v2-wizard-page>\n </div>\n </v2-modal-wizard>\n <appengine-load-balancer-message\n ng-if="ctrl.isNew"\n column-offset="1"\n columns="10"\n show-create-message="true"\n ></appengine-load-balancer-message>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n ng-if="!ctrl.isNew && ctrl.showSubmitButton()"\n label="ctrl.submitButtonLabel"\n is-disabled="appengineLoadBalancerForm.$invalid || ctrl.taskMonitor.submitting || ctrl.state.loading"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n >\n </submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("appengine/src/loadBalancer/details/details.html",'<div class="details-panel">\n <div ng-if="ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="header">\n <div class="close-button">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div class="header-text horizontal middle">\n <i class="fa icon-sitemap"></i>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>{{ctrl.loadBalancer.name}}</h3>\n </div>\n <div>\n <div class="actions">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n Load Balancer Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.editLoadBalancer()">Edit Load Balancer</a></li>\n <li ng-if="ctrl.canDeleteLoadBalancer()">\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <li\n ng-if="!ctrl.canDeleteLoadBalancer()"\n uib-tooltip="You cannot delete a default service."\n class="disabled"\n >\n <a href>Delete Load Balancer</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n <div ng-if="!ctrl.state.loading" class="content">\n <collapsible-section heading="Load Balancer Details" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>In</dt>\n <dd><account-tag account="ctrl.loadBalancer.account" pad="right"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{ctrl.loadBalancer.region}}</dd>\n <dt ng-if="ctrl.loadBalancer.serverGroups.length">Server Groups</dt>\n <dd ng-if="ctrl.loadBalancer.serverGroups.length">\n <ul>\n <li ng-repeat="serverGroup in ctrl.loadBalancer.serverGroups | orderBy: [\'isDisabled\', \'-name\']">\n <a\n ui-sref="^.serverGroup({region: serverGroup.region,\n accountId: serverGroup.account,\n serverGroup: serverGroup.name,\n provider: \'appengine\'})"\n >\n {{serverGroup.name}}\n </a>\n </li>\n </ul>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <health-counts class="pull-left" container="ctrl.loadBalancer.instanceCounts"></health-counts>\n </collapsible-section>\n <collapsible-section heading="Traffic Split" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt ng-if="ctrl.loadBalancer.split.shardBy">Shard By</dt>\n <dd ng-if="ctrl.loadBalancer.split.shardBy">\n {{ctrl.loadBalancer.split.shardBy}}\n <help-field\n key="appengine.loadBalancer.shardBy.{{ctrl.loadBalancer.split.shardBy.toLowerCase()}}"\n ></help-field>\n </dd>\n <hr ng-if="ctrl.loadBalancer.split.shardBy" />\n <ul>\n <li ng-repeat="(serverGroup, percent) in ctrl.loadBalancer.split.allocations">\n {{serverGroup}}:<span class="pull-right">{{percent | decimalToPercent}}</span>\n </li>\n </ul>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS" expanded="true">\n <dl class="dl-narrow">\n <appengine-component-url-details component="ctrl.loadBalancer"></appengine-component-url-details>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Dispatch Rules" expanded="true" ng-if="ctrl.dispatchRules.length > 0">\n <dl class="dl-horizontal dl-narrow">\n <span ng-repeat-start="rule in ctrl.dispatchRules">{{rule}}</span><br ng-repeat-end />\n </dl>\n </collapsible-section>\n </div>\n</div>\n')}]);export{Gn as APPENGINE_MODULE};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|