@spinnaker/google 2026.0.1 → 2026.1.0
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/domain/loadBalancer.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loadBalancer/configure/choice/gceLoadBalancerPipelineModal.d.ts +9 -0
- package/dist/loadBalancer/configure/http/templates.d.ts +3 -0
- package/package.json +3 -3
- package/src/domain/loadBalancer.ts +4 -2
- package/src/gce.module.ts +2 -0
- package/src/help/gce.help.ts +2 -0
- package/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.spec.js +66 -0
- package/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.ts +20 -5
- package/src/loadBalancer/configure/choice/gceLoadBalancerPipelineModal.ts +47 -0
- package/src/loadBalancer/configure/gceL4LoadBalancerPipeline.spec.js +143 -0
- package/src/loadBalancer/configure/http/createHttpLoadBalancer.controller.js +22 -0
- package/src/loadBalancer/configure/http/createHttpLoadBalancer.controller.spec.js +96 -0
- package/src/loadBalancer/configure/http/listeners/listener.component.html +79 -24
- package/src/loadBalancer/configure/http/listeners/listener.component.js +82 -0
- package/src/loadBalancer/configure/http/listeners/listener.component.spec.js +125 -0
- package/src/loadBalancer/configure/http/templates.ts +3 -0
- package/src/loadBalancer/configure/http/transformer.service.js +16 -1
- package/src/loadBalancer/configure/http/transformer.service.spec.js +182 -0
- package/src/loadBalancer/configure/internal/gceCreateInternalLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/configure/internalhttp/createInternalHttpLoadBalancer.controller.spec.js +89 -0
- package/src/loadBalancer/configure/internalhttp/createInternalHttpLoadBalancer.controller.ts +23 -0
- package/src/loadBalancer/configure/network/createLoadBalancer.controller.js +48 -32
- package/src/loadBalancer/configure/network/createLoadBalancer.controller.spec.js +36 -0
- package/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/details/loadBalancerType/loadBalancerType.component.js +3 -1
- package/src/loadBalancer/details/loadBalancerType/loadBalancerType.component.spec.js +85 -0
- package/src/loadBalancer/loadBalancer.setTransformer.ts +1 -0
- package/src/serverGroup/configure/serverGroupConfiguration.service.js +0 -1
- package/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.js +1 -4
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import*as e from"angular";import{module as n,noop as t}from"angular";import{SETTINGS as a,withErrorBoundary as i,LayoutProvider as l,FormField as c,HelpField as r,CheckboxInput as o,SearchService as s,InfrastructureCaches as d,REST as u,HelpContentsRegistry as g,CloudProviderRegistry as p,FirewallLabels as m,ConfirmationModalService as h,RecentHistoryService as v,InstanceReader as f,InstanceWriter as b,AccountService as y,CACHE_INITIALIZER_SERVICE as k,NameUtils as S,LOAD_BALANCER_READ_SERVICE as w,SubnetReader as C,NetworkReader as B,TaskExecutor as T,TaskMonitor as G,LoadBalancerWriter as A,ModalWizard as P,Registry as $,BakeExecutionLabel as x,ExpectedArtifactService as I,ArtifactReferenceService as z,AuthenticationService as N,PipelineTemplates as D,BakeryReader as M,StageConstants as E,PipelineConfigService as R,SecurityGroupWriter as L,SECURITY_GROUP_READER as H,IMAGE_READER as F,excludeAllTypesExcept as U,ArtifactTypePatterns as O,NgGCEImageArtifactDelegate as q,ExpectedArtifactSelectorViewController as V,INSTANCE_TYPE_SERVICE as j,WizardModal as K,WizardPage as W,FormikFormField as Z,ReactModal as X,NumberInput as Y,ReactSelectInput as J,SERVER_GROUP_WRITER as Q,ServerGroupWarningMessageService as ee,ServerGroupTemplates as ne,ServerGroupReader as te,ClusterTargetBuilder as ae,ApplicationNameValidator as ie,DeploymentStrategyRegistry as le}from"@spinnaker/core";import ce from"react";import{react2angular as re}from"react2angular";import oe,{isEmpty as se,defaults as de,uniqWith as ue,chain as ge,uniq as pe,map as me,find as he,trimEnd as ve,get as fe,partition as be,groupBy as ye,cloneDeep as ke,has as Se,intersectionBy as we,isFinite as Ce,without as Be,set as Te,intersection as Ge,last as Ae}from"lodash";import Pe,{Async as $e}from"react-select";import xe from"@uirouter/angularjs";import Ie from"angular-ui-bootstrap";import{$http as ze,$q as Ne}from"ngimport";import De from"ui-select";import{Subject as Me,from as Ee}from"rxjs";import{switchMap as Re}from"rxjs/operators";function Le(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))}}var He,Fe;function Ue(e){const{label:n,help:t,input:a,actions:i,validation:l,required:c}=e,r=!se(n)||!se(t),{hidden:o,messageNode:s}=l;return ce.createElement("div",{className:"gce-autoscaling-layout"},r&&ce.createElement("label",{className:"col-md-3 sm-label-right"},n,c&&ce.createElement("span",null,"*")," ",t),ce.createElement("div",{className:"col-md-2 content-fields"},ce.createElement("div",null,a," ",i),!o&&ce.createElement("div",{className:"message"},s)))}Le(".gce-autoscaling-layout .checkbox {\n margin-left: 5px;\n margin-top: 7px;\n}\n"),(Fe=He||(He={})).NONE="NONE",Fe.STANDARD="OPTIMIZE_AVAILABILITY";const Oe=de(a.providers.gce||{},{defaults:{},feature:{},resetToOriginal:a.resetProvider("gce")});const qe="spinnaker.gce.predictiveAutoscaling";n(qe,[]).component("gcePredictiveAutoscaling",re(i((function({policy:e,updatePolicy:n}){var t;return Oe.feature.predictiveAutoscaling?ce.createElement(l,{value:Ue},ce.createElement("div",{className:"row"},ce.createElement(c,{help:ce.createElement(r,{id:"gce.serverGroup.scalingPolicy.predictiveAutoscaling"}),input:e=>ce.createElement(o,{...e}),label:"Enable predictive autoscaling",onChange:t=>{var a;a=t.target.checked?He.STANDARD:He.NONE,n({...e,cpuUtilization:{...e.cpuUtilization,predictiveMethod:a}})},value:(null==(t=null==e?void 0:e.cpuUtilization)?void 0:t.predictiveMethod)===He.STANDARD}))):null}),"gcePredictiveAutoscaling"),["policy","updatePolicy"]));const _e="spinnaker.gce.addressReader.service";n(_e,[]).service("gceAddressReader",class{listAddresses(e){return e?this.listAddresses(null).then((n=>n.filter((n=>n.region===e)))):s.search({q:"",type:"addresses",allowShortQuery:"true"},d.get("addresses")).then((e=>e&&e.results?e.results.filter((e=>"gce"===e.provider)).map((e=>{const n=JSON.parse(e.address);return n.account=e.account,n.region=e.region,n})):[])).catch((()=>[]))}});const Ve="spinnaker.deck.gce.backendService.reader.service";n(Ve,[]).factory("gceBackendServiceReader",(function(){return{listBackendServices:function e(n){return n?e().then((([e])=>{if(e){return(e.results||[]).filter((e=>e.kind===n))}return[]})):u("/search").useCache(d.get("backendServices")).query({q:"",type:"backendServices",allowShortQuery:"true"}).get()}}}));const je="spinnaker.gce.healthCheck.reader";n(je,[]).service("gceHealthCheckReader",class{listHealthChecks(e){return e?this.listHealthChecks().then((n=>n.filter((n=>n.healthCheckType===e)))):s.search({q:"",type:"healthChecks",allowShortQuery:"true"},d.get("healthChecks")).then((e=>{if(e&&e.results){const n=e.results.filter((e=>"gce"===e.provider)).map((e=>{const n=JSON.parse(e.healthCheck);return n.account=e.account,n}));return ue(n,((e,n)=>e.name===n.name&&e.healthCheckType===n.healthCheckType&&e.account===n.account))}return[]})).catch((()=>[]))}});const Ke="spinnaker.gce.cache.initializer";n(Ke,[Ve,_e,je]).factory("gceCacheConfigurer",["gceAddressReader","gceBackendServiceReader","gceCertificateReader","gceHealthCheckReader",function(e,n,t,a){const i=Object.create(null);return i.addresses={initializers:[()=>e.listAddresses()]},i.backendServices={initializers:[()=>n.listBackendServices()]},i.certificates={initializers:[()=>t.listCertificates()]},i.healthChecks={initializers:[()=>a.listHealthChecks()]},i}]);const We="spinnaker.gce.common.xpnNaming.service";n(We,[]).factory("gceXpnNamingService",(function(){return{deriveProjectId:e=>{const n=e.selfLink.split("/");return n[n.indexOf("projects")+1]},decorateXpnResourceIfNecessary:(e,n)=>{if(!n)return null;const t=n.split("/"),a=t[t.indexOf("projects")+1],i=oe.last(t);return a!==e?a+"/"+i:i}}}));const Ze={"gce.httpLoadBalancer.certificate":"The name of an SSL certificate. If specified, Spinnaker will create an HTTPS load balancer.","gce.httpLoadBalancer.defaultService":"A default service handles any requests that do not match a specified host rule or path matching rule.","gce.httpLoadBalancer.externalIP":"The IP address for this listener. If you do not specify an IP, your listener will be assigned an ephemeral IP.","gce.httpLoadBalancer.hostRule.hostPattern":"For example, <b>example.com</b>.","gce.httpLoadBalancer.namedPort":"\n For HTTP(S) and SSL/TCP load balancers,\n incoming traffic is directed through a named port (for Spinnaker, the named port is <b>http</b>).\n The mapping from named port to port number is specified per server group\n and can be configured within the server group creation dialogue under <b>Port Name Mapping</b>.","gce.httpLoadBalancer.pathRule.paths":"For example, <b>/path</b> in <b>example.com/path</b>","gce.httpLoadBalancer.port":"HTTP requests can be load balanced based on port 80 or port 8080. HTTPS requests can be load balanced on port 443.","gce.image.artifact":"The artifact that is to be deployed to this cluster. The artifact should represent a deployable image.","gce.image.source":"\n <p>Where the image to deploy is read from.</p>\n <p>\n <b>Artifact:</b> Deploy an artifact that was supplied/created upstream. The expected artifact must be referenced here, and will be bound at runtime.\n </p>\n <p>\n <b>Prior Stage:</b> Deploy the result of the most recent Bake or Find Image stage.\n </p>\n ","gce.instance.customInstance.cores":"<ul><li>Above 1, vCPU count must be even.</li><li>Zones that support Haswell and Ivy Bridge processors can support custom machine types up to 32 vCPUs.</li><li>Zones that support Sandy Bridge processors can support up to 16 vCPUs.</li></ul>","gce.instance.customInstance.memory":"<ul><li>Memory per vCPU must be between .9 GB and 6.5 GB.</li><li>Total memory must be a multiple of 256 MB.</li><li>Memory per vCPU must be between .9 GB and 800 GB if selected extended memory feature.</li></ul>","gce.instance.customInstance.extendedmemory":'Add more memory per vCPU to your VM instance. <a href="https://cloud.google.com/compute/docs/instances/creating-instance-with-custom-machine-type#extendedmemory">Click here </a>for more information regarding extended memory',"gce.instance.customMetadata.instance-template":"The instance template used to configure this instance.","gce.instance.customMetadata.load-balancer-names":'This field is used to "remember" what load balancers this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.global-load-balancer-names":'This field is used to "remember" what global load balancers this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.backend-service-names":'This field is used to "remember" what backend services this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.load-balancing-policy":'This field is used to "remember" the load balancing policy this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.startup-script":"This script will run automatically on every boot.","gce.instance.labels.spinnaker-region":"This label can be used to group instances when querying for metrics.","gce.instance.labels.spinnaker-server-group":"This label can be used to group instances when querying for metrics.","gce.instance.storage":"<p>Storage options can be fully-configured on the <b>Advanced Settings</b> tab. These are just default settings. Please be aware of your Local SSD quotas as you provision VMs.</p>","gce.instance.storage.localSSD":"<p>Local SSD disks are always 375GB. All non shared-core instance types support attaching up to 8 Local SSD disks. Please be aware of your Local SSD quotas as you provision VMs.</p>","gce.instance.serviceAccount":'<p>Service accounts authenticate applications running on your virtual machine instances to other Google Cloud Platform services. Valid values are either "default" or the full email address of a custom service account.</p>',"gce.instance.authScopes":"<p>Service account scopes specify which Google Cloud Platform APIs your instances can authenticate with, and define the level of access that your instances have with those services. You can enter custom auth scopes by typing into the blank field.</p>","gce.instance.authScopes.cloud-platform":"<p>The instances in this server group have full API access to all Google Cloud services.</p>","gce.instanceType.32core":"<p>32-core machine types are in Beta and are available only in Ivy Bridge and Haswell zones.</p>","gce.internalLoadBalancer.ports":"Use a comma to separate up to five TCP ports.","gce.internalHttpLoadBalancer.network":"Network must have a subnet whose 'purpose' is 'INTERNAL_HTTPS_LOAD_BALANCER'","gce.loadBalancer.connectionDraining":"(Optional) If set, enables connection draining for this backend service. This field defines the number of seconds to wait before instances that belong to this backend service are terminated in order to drain in-flight connections.","gce.loadBalancer.detail":"<p>(Optional) <b>Detail</b> is a string of free-form alphanumeric characters and hyphens to describe any other variables.</p>","gce.loadBalancer.advancedSettings.healthInterval":"<p>Configures the interval, in seconds, between load balancer health checks.</p><p>Default: <b>10</b></p>","gce.loadBalancer.advancedSettings.healthyThreshold":"<p>Configures the number of healthy observations before reinstituting an instance into the load balancer’s traffic rotation.</p><p>Default: <b>10</b></p>","gce.loadBalancer.advancedSettings.unhealthyThreshold":"<p>Configures the number of unhealthy observations before deservicing an instance from the load balancer.</p><p>Default: <b>2</b></p>","gce.loadBalancer.healthCheck":"(Optional) <b>Health Checks</b> use HTTP requests to determine if a VM instance is healthy.","gce.loadBalancer.portName":"(Required) The <b>Port Name</b> this backend service will forward traffic to. Load balancers in GCP specify a <b>Port Name</b>, and each server group added to a load balancer needs to specify a mapping from that <b>Port Name</b> to a port to actually receive traffic.","gce.loadBalancer.portRange":"(Optional) Only packets addressed to ports in the specified <b>Port Range</b> will be forwarded. If left empty, all ports are forwarded. Must be a single port number or two port numbers separated by a dash. Each port number must be between 1 and 65535, inclusive. For example: 5000-5999.","gce.securityGroup.sourceCIDRs":"Traffic is only allowed from sources that match one of these CIDR ranges, or one of the source tags above.","gce.securityGroup.sourceTags":"Traffic is only allowed from sources that match one of these tags, or one of the source CIDR ranges below.","gce.securityGroup.targetTags":"Traffic is only allowed if the target instance has one of these tags.","gce.securityGroup.targetServiceAccounts":"Traffic is allowed if the target instance has one of these service accounts.","gce.securityGroup.sourceServiceAccounts":"Traffic is allowed if the source instance has one of these service accounts.","gce.serverGroup.associatePublicIpAddress.providerField":"Check if new GCE server groups in this application should be assigned a public IP address by default.","gce.serverGroup.resizeWithAutoscalingPolicy":"\n Setting the desired instance count for a server group with an autoscaler is not supported by Spinnaker;\n if the desired instance count differs from the instance count that the autoscaler wants to maintain for its configured metrics,\n it will quickly override the desired instance count.","gce.serverGroup.scalingPolicy.coolDownPeriodSec":"How long to wait before collecting information from a new instance. This should be at least the time it takes to initialize the instance.","gce.serverGroup.scalingPolicy.cpuUtilization":"Autoscaler adds or removes instances to maintain this CPU usage on each instance.","gce.serverGroup.scalingPolicy.predictiveAutoscaling":"Autoscaler adds or removes instances based on forecasted load. You must set a CPU utilization target to enable predictive autoscaling.","gce.serverGroup.scalingPolicy.loadBalancingUtilization":"Autoscaler adds or removes instances to maintain this usage of load-balancing capacity.","gce.serverGroup.scalingPolicy.customMetricUtilizations":"Autoscaler adds or removes instances to maintain this usage for custom metric.","gce.serverGroup.scalingPolicy.cronExpression":"\n Start time and recurrence using a cron expression. Examples: <br/>\n Every day at 3 PM:<br/>\n 0 15 * * *<br/>\n Every week Monday to Friday at 8:30 AM: <br/>\n 30 8 * * Mon-Fri<br/>\n Once on 30 January 2030 at midnight: <br/>\n 0 0 30 1 * 2030","gce.serverGroup.imageName":"(Required) <b>Image</b> is the Google Compute Engine image. Images are restricted to the account selected.","gce.serverGroup.capacity":"The number of instances that the instance group manager will attempt to maintain. Deleting or abandoning instances will affect this number, as will resizing the group.","gce.serverGroup.minCpuPlatform":'The baseline minimum CPU platform to use for your instances, whenever available. Select "Automatic" unless you have a specific need.',"gce.serverGroup.customMetadata":"<b>Custom Metadata</b> will be propagated to the instances in this server group. This is useful for passing in arbitrary values that can be queried by your code on the instance.","gce.serverGroup.userData":"<p>Custom user data will be propagated to the instances in this server group. Key/value pairs can either be newline or comma delimited.</p><strong>Example:</strong><br/> <pre>key=value<br>key2=value2</pre>","gce.serverGroup.customMetadata.load-balancer-names":'This field is used to "remember" what load balancers this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.global-load-balancer-names":'This field is used to "remember" what global load balancers this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.backend-service-names":'This field is used to "remember" what backend services this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.load-balancing-policy":'This field is used to "remember" the load balancing policy this server group is configured with, even if the server group is deregistered from the load balancer. This allows us to re-enable the server group with the same load balancing policy specified on creation.',"gce.serverGroup.customMetadata.select-zones":"This regional server group will be deployed only to specific zones within the region.","gce.serverGroup.customMetadata.startup-script":"This script will run automatically on every boot.","gce.serverGroup.labels.spinnaker-region":"This label can be used to group instances when querying for metrics.","gce.serverGroup.labels.spinnaker-server-group":"This label can be used to group instances when querying for metrics.","gce.serverGroup.shieldedVmConfig":"Shielded VM features include trusted UEFI firmware and come with options for Secure Boot, Virtual Trusted Platform Module (vTPM), and Integrity Monitoring.","gce.serverGroup.shieldedVmSecureBoot":"Secure boot helps protect your VM instances against boot-level and kernel-level malware and rootkits.","gce.serverGroup.shieldedVmVtpm":"Virtual Trusted Platform Module (vTPM) validates your guest VM pre-boot and boot integrity, and offers key generation and protection.","gce.serverGroup.shieldedVmIntegrityMonitoring":"Integrity monitoring lets you monitor and verify the runtime boot integrity of your shielded VM instances using Stackdriver reports. Note: requires vTPM to be enabled.","gce.serverGroup.preemptibility":"A preemptible VM costs much less, but lasts only 24 hours. It can be terminated sooner due to system demands.","gce.serverGroup.automaticRestart":"Compute Engine can automatically restart VM instances if they are terminated for non-user-initiated reasons (maintenance event, hardware failure, software failure, etc.).","gce.serverGroup.onHostMaintenance":"When Compute Engine performs periodic infrastructure maintenance it can migrate your VM instances to other hardware without downtime.","gce.serverGroup.canIpForward":'Forwarding allows the instance to help route packets. See <a target="_blank" href="https://cloud.google.com/compute/docs/networking?hl=en_US#canipforward">here</a> for more information.',"gce.serverGroup.securityGroups.implicit":"Firewall rules with no target tags defined will permit incoming connections that match the ingress rules to all instances in the network.","gce.serverGroup.securityGroups.targetTags":"This {{firewall}} rule will be associated with this server group only if a target tag is selected.","gce.serverGroup.autoscaling.targetCPUUsage":"Autoscaling adds or removes VMs in the group to maintain this level of CPU usage on each VM.","gce.serverGroup.autoscaling.targetHTTPLoadBalancingUsage":"Autoscaling adds or removes VMs in the group to maintain this usage of load-balancing capacity. This value is a percentage of the 'Maximum rate' setting of the load balancer this group is used by.","gce.serverGroup.autoscaling.targetMetric":"Autoscaling adds or removes VMs in the group to maintain these target levels.","gce.serverGroup.autoscaling.minVMs":"The least number of VM instances the group will contain, even if the target is not met.","gce.serverGroup.autoscaling.maxVMs":"The largest number of VM instances allowed, even if the target is exceeded.","gce.serverGroup.autoscaling.cooldown":"How long to wait before collecting information from a new instance. This should be at least the time it takes to initialize the instance. To find the minimum, create an instance from the same image and note how long it takes to start.","gce.serverGroup.autoscaling.mode":"Mode of operation of the autoscaling policy. This guides the autoscaler by defining the types of scaling operations it can perform. Options are ON, ONLY_SCALE_OUT, and OFF.","gce.serverGroup.autoHealing":"VMs in the group are recreated as needed. You can use a health check to recreate a VM if the health check finds the VM unresponsive. If you do not select a health check, VMs are recreated only when stopped.","gce.serverGroup.initialDelaySec":"The time to allow an instance to boot and applications to fully start before the first health check.","gce.serverGroup.maxUnavailable":"\n Maximum number of instances that can be unavailable when auto-healing. The instance is considered available if all of the following conditions are satisfied:\n <ul>\n <li>1. Instance's status is RUNNING.</li>\n <li>2. Instance's liveness health check result was observed to be HEALTHY at least once.</li>\n </ul>","gce.serverGroup.subnet":"\n Subnetworks allow you to regionally segment the network IP space into prefixes (subnets) and control which prefix a VM instance's internal IP address is allocated from. There are several types of GCE networks:\n <ul>\n <li><b>Legacy (non-subnet) Network</b>: IP address allocation occurs at the global network level. This means the network address space spans across all regions.</li>\n <li><b>Auto Subnet Network</b>: Server groups will be automatically assigned to the specified region's subnet.</li>\n <li><b>Custom Subnet Network</b>: A subnet must be selected for the server group. If no subnets have been created for the specified region, you will not be able to provision the server group.</li>\n </ul>","gce.serverGroup.loadBalancingPolicy.balancingMode":"Tells the load balancer when the backend is at capacity. If a backend is at capacity, new requests are routed to the nearest region that can handle requests. The balancing mode can be based on CPU utilization or requests per second (RPS).","gce.serverGroup.loadBalancingPolicy.maxRatePerInstance":"The maximum number of requests per second that can be sent to the backend instance group. Input must be a number greater than zero.","gce.serverGroup.loadBalancingPolicy.maxUtilization":"The maximum CPU utilization allowed for the backend. CPU utilization is calculated by averaging CPU use across all instances in the backend instance group. Input must be a number between 0 and 100.","gce.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance":"The target connections per second for individual instances. When this number is exceeded, requests are directed to another backend.","gce.serverGroup.loadBalancingPolicy.capacityScaler":"\n An additional control to manage your maximum CPU utilization or RPS.\n If you want your instances to operate at a max 80% CPU utilization, set your balancing mode to 80% max CPU utilization and your capacity to 100%.\n If you want to cut instance utilization by half, set your balancing mode to 80% max CPU utilization and your capacity to 50%. Input must be a number between 0 and 100.","gce.serverGroup.loadBalancingPolicy.portName":"A load balancer sends traffic to an instance group through a named port. Input must be a port name.","gce.serverGroup.loadBalancingPolicy.listeningPort":"A load balancer sends traffic to an instance group through a named port. Input must be a port number (i.e., between 1 and 65535).","gce.serverGroup.traffic":"Registers the server group with any associated load balancers. These registrations are used by Spinnaker to determine if the server group is enabled.","gce.serverGroup.accelerator":"Attaches GPUs to instances in this server group. The set of available GPUs is dictated by the selected zone. You cannot attach GPUs to instances with shared-core machine types.","gce.tagImage.consideredStages":"Limit which previous stages will be considered when locating images to tag. If left unchecked, images generated by any upstream stage will be tagged.","pipeline.config.gce.bake.accountName":"<p>(Optional) The name of a Google account configured within Rosco. If left blank, the first configured account will be used.</p>","pipeline.config.gce.bake.baseImage":"<p>(Optional) A GCE image name. For example: ubuntu-1204-precise-v20150910.</p>","gce.loadBalancerType.Network":"\n <p>Use Network Load Balancing to balance the load on your systems based on incoming IP protocol data, such as address, port, and protocol type.</p>\n <p>Network Load Balancing is a regional, non-proxied load balancer. You can use it to load balance UDP traffic, and TCP and SSL traffic on ports that are not supported by the SSL proxy and TCP proxy load balancers.</p>","gce.loadBalancerType.HTTP(S)":"<p>Google Cloud Platform (GCP) HTTP(S) Load Balancing provides global load balancing for HTTP(S) requests destined for your instances.</p>","gce.loadBalancerType.Internal":"<p>Internal TCP/UDP Load Balancing is a regional load balancer that enables you to run and scale your services behind a private load balancing IP address that is accessible only to your internal virtual machine instances.</p>","gce.loadBalancerType.SSL":"<p>Google Cloud SSL Proxy Load Balancing terminates user SSL (TLS) connections at the load balancing layer, then balances the connections across your instances using the SSL or TCP protocols. This supports both IPv4 and IPv6 addresses for client traffic.</p>","gce.loadBalancerType.TCP":"<p>Google Cloud Platform (GCP) TCP Proxy Load Balancing allows you to use a single IP address for all users around the world. GCP TCP proxy load balancing automatically routes traffic to the instances that are closest to the user.</p>"};Object.keys(Ze).forEach((e=>g.register(e,Ze[e])));class Xe{static findImages(e){return u("/images/find").query(e).get().catch((()=>[]))}static getImage(){return null}}class Ye extends ce.Component{constructor(){super(...arguments),this.loadOptions=e=>new Promise((n=>{if(!e||e.length<3)n({options:[],complete:!1});else{n({options:ge(this.props.availableImages).filter((n=>n.imageName.toLowerCase().includes(e))).take(20).map((e=>({value:e.imageName,label:e.imageName}))).value(),complete:!1})}}))}render(){return ce.createElement($e,{cache:null,clearable:!1,ignoreAccents:!1,loadOptions:this.loadOptions,onChange:e=>this.props.selectImage(e.value,this.props.target),placeholder:"Type at least 3 characters to search for an image...",searchPromptText:"Type at least 3 characters to search for an image...",value:{value:this.props.selectedImage,label:this.props.selectedImage}})}}const Je="spinnaker.gce.imageSelect";n(Je,[]).component("gceImageSelect",re(i(Ye,"gceImageSelect"),["availableImages","selectedImage","selectImage","target"]));const Qe="spinnaker.serverGroup.customInstanceBuilder.gce.service";n(Qe,[]).factory("gceCustomInstanceBuilderService",(function(){function e(e,n,t,a){let i=0;if("E2"===e)i=32;else i=a[t].vCpuMax;return n<=i}function n(e,n){if(1===n)return!0;switch(e){case"N2":case"N2D":return n%4==0;default:return n%2==0}}function t(e,n){switch(e){case"E2":case"N2":case"N2D":return Math.ceil(.5*n*4)/4;default:return Math.ceil(.9*n*4)/4}}function a(e,n){switch(e){case"E2":return 8*n>128?128:8*n;case"N2":case"N2D":return 8*n;default:return 6.5*n}}function i(e,n,i,l){const c=t(e,i),r=l?800:a(e,i);return oe.inRange(n,c,r)||n===r}return{generateValidVCpuListForLocation:function(e,n){const t=n[e].vCpuMax;return[1,...oe.range(2,t,2),t]},generateValidMemoryListForVCpuCount:function(e,n){const i=t(e,n),l=a(e,n);return[...oe.range(i,l,.25),l]},generateValidInstanceFamilyList:function(){return["n1","e2","n2","n2d"].map((e=>e.toUpperCase()))},generateInstanceTypeString:function(e,n,t,a){const i=a?"-ext":"",l=1024*Number(t);return"n1"===(e=e.toLowerCase())?`custom-${n}-${l}${i}`:`${e}-custom-${n}-${l}${i}`},customInstanceChoicesAreValid:function(t,a,l,c,r,o){return oe.every([n(t,a),i(t,l,a,o),e(t,a,c,r)])},memoryIsValid:i,vCpuCountForLocationIsValid:e,parseInstanceTypeString:function(e){const[n,t]=oe.chain(e.split("-")).takeRight(2).map((e=>Number(e))).value();let a=e.split("-")[0].toUpperCase();return"CUSTOM"===a&&(a="N1"),{instanceFamily:a,vCpuCount:n,memory:t/1024}}}}));const en="spinnaker.gce.customInstance.filter";n(en,[Qe]).filter("customInstanceFilter",["gceCustomInstanceBuilderService",function(e){return function(n){if(oe.includes(n,"custom-")){const{instanceFamily:t,vCpuCount:a,memory:i}=e.parseInstanceTypeString(n);return`${t} ${a} vCPU / ${i} GB RAM`}return n}}]);class nn{isHttpLoadBalancer(e){return!("gce"!==e.provider&&"gce"!==e.type||"HTTP"!==e.loadBalancerType&&"INTERNAL_MANAGED"!==e.loadBalancerType)}normalizeLoadBalancerNamesForAccount(e,n,t){const a=[];return e.forEach((e=>{const i=t.find((t=>n===t.account&&this.isHttpLoadBalancer(t)&&t.listeners.map((e=>e.name)).includes(e)));i?a.push(i.name):a.push(e)})),pe(a)}}nn.REGION="global";const tn="spinnaker.gce.httpLoadBalancerUtils.service";n(tn,[]).service("gceHttpLoadBalancerUtils",nn);const an="spinnaker.instance.detail.gce.controller";n(an,[xe,Ie,We,tn]).controller("gceInstanceDetailsCtrl",["$scope","$state","$uibModal","instance","app","moniker","environment","$q","gceHttpLoadBalancerUtils","gceXpnNamingService",function(e,n,t,a,i,l,c,r,o,s){function d(){const n={};let t,l,c,d,g;return i.serverGroups?(i.serverGroups.data.some((function(e){return e.instances.some((function(i){if(i.id===a.instanceId)return t=i,l=e.loadBalancers,c=e.account,d=e.region,n.serverGroup=e.name,!0}))})),t||(i.loadBalancers.data.some((function(e){return e.instances.some((function(n){if(n.id===a.instanceId)return t=n,l=[e.name],c=e.account,d=e.region,g=e.vpcId,!0}))})),t||i.loadBalancers.data.some((function(e){return e.serverGroups.some((function(n){return!!n.isDisabled&&n.instances.some((function(n){if(n.id===a.instanceId)return t=n,l=[e.name],c=e.account,d=e.region,g=e.vpcId,!0}))}))})))):(t={},l=[],c=a.account,d=a.region),t&&c&&d?(n.account=c,n.region=d,v.addExtraDataToLatest("instances",n),f.getInstanceDetails(c,d,a.instanceId).then((function(n){e.state.loading=!1,function(n,t){i.isStandalone&&(n.health=t.health),n.health=n.health||[];const a=n.health.filter((function(e){return"Google"!==e.type||"Unknown"!==e.state}));t.health&&a.forEach((function(e){const n=t.health.filter((function(n){return n.type===e.type}));n.length&&oe.defaults(e,n[0])})),e.healthMetrics=a}(t,n),e.instance=oe.defaults(n,t),e.instance.account=c,e.instance.region=d,e.instance.vpcId=g,i.getDataSource("loadBalancers")&&(e.instance.loadBalancers=o.normalizeLoadBalancerNamesForAccount(l,c,i.getDataSource("loadBalancers").data)),e.instance.internalDnsName=e.instance.instanceId,e.instance.internalIpAddress=e.instance.networkInterfaces[0].networkIP,e.instance.networkInterfaces[0].accessConfigs&&(e.instance.externalIpAddress=e.instance.networkInterfaces[0].accessConfigs[0].natIP),e.baseIpAddress=e.instance.externalIpAddress||e.instance.internalIpAddress;const r=s.deriveProjectId(e.instance);e.instance.logsLink="https://console.developers.google.com/project/"+r+"/logs?service=gce_instance&minLogLevel=0&filters=text:"+e.instance.instanceId,e.instance.network=function(n){const t=oe.get(e.instance,"networkInterfaces[0].network");return s.decorateXpnResourceIfNecessary(n,t)}(r),e.instance.subnet=function(n){const t=oe.get(e.instance,"networkInterfaces[0].subnetwork");return s.decorateXpnResourceIfNecessary(n,t)}(r),e.instance.sshLink=e.instance.selfLink.replace(/www.googleapis.com\/compute\/(alpha|beta|v1)/,"cloudssh.developers.google.com")+"?authuser=0&hl=en_US",e.instance.gcloudSSHCommand="gcloud compute ssh --project "+r+" --zone "+e.instance.placement.availabilityZone+" "+a.instanceId,function(){if(oe.has(e,"instance.tags.items")&&oe.has(e,"instance.securityGroups")){const n=oe.chain(e.instance.securityGroups).map((n=>oe.find(i.securityGroups.data,{accountName:e.instance.account,region:"global",id:n.groupId}))).compact().value(),t={};e.instance.tags.items.forEach((e=>{const a=oe.filter(n,(n=>oe.includes(n.targetTags,e))),i=oe.map(a,"name");if(!oe.isEmpty(i)){const n=i.length>1?"groups":"group";t[e]="This tag associates this instance with security "+n+" <em>"+i.join(", ")+"</em>."}})),e.instance.tags.helpMap=t}}()}),u)):(t||(e.instanceIdNotFound=a.instanceId,e.state.loading=!1),r.when(null))}function u(){e.$$destroyed||n.go("^",{allowModalToStayOpen:!0},{location:"replace"})}e.detailsTemplateUrl=p.getValue("gce","instance.detailsTemplateUrl"),e.firewallsLabel=m.get("Firewalls"),e.state={loading:!0,standalone:i.isStandalone},e.application=i,e.moniker=l,e.environment=c,this.canRegisterWithLoadBalancer=function(){const n=e.instance,t=!i.loadBalancers||oe.chain(i.loadBalancers.data).filter((e=>"NETWORK"!==e.loadBalancerType&&e.account===n.account)).map("name").intersection(n.loadBalancers||[]).value().length;if(!n.loadBalancers||!n.loadBalancers.length||t)return!1;const a=n.health.some((function(e){return"LoadBalancer"===e.type&&"OutOfService"===e.state})),l=n.health.some((function(e){return"LoadBalancer"===e.type}));return a||!l},this.canDeregisterFromLoadBalancer=function(){const n=e.instance,t=!i.loadBalancers||oe.chain(i.loadBalancers.data).filter((e=>"NETWORK"!==e.loadBalancerType&&e.account===n.account)).map("name").intersection(n.loadBalancers||[]).value().length;if(!n.loadBalancers||!n.loadBalancers.length||t)return!1;return n.health.some((function(e){return"LoadBalancer"===e.type}))},this.canRegisterWithDiscovery=function(){const n=e.instance.health.filter((function(e){return"Discovery"===e.type}));return!!n.length&&"OutOfService"===n[0].state},this.showInstanceActionsDivider=function(){return this.canRegisterWithDiscovery()||this.hasHealthState("Discovery","Up")||this.canRegisterWithLoadBalancer()||this.canDeregisterFromLoadBalancer()},this.terminateInstance=function(){const t=e.instance,a={application:i,title:"Terminating "+t.instanceId,onTaskComplete:function(){n.includes("**.instanceDetails",{instanceId:t.instanceId})&&n.go("^")}};h.confirm({header:"Really terminate "+t.instanceId+"?",buttonText:"Terminate "+t.instanceId,account:t.account,taskMonitorConfig:a,submitMethod:function(){const e={cloudProvider:"gce"};return t.serverGroup&&(e.managedInstanceGroupName=t.serverGroup),b.terminateInstance(t,i,e)}})},this.terminateInstanceAndShrinkServerGroup=function(){const t=e.instance,a={application:i,title:"Terminating "+t.instanceId+" and shrinking server group",onTaskComplete:function(){n.includes("**.instanceDetails",{instanceId:t.instanceId})&&n.go("^")}};h.confirm({header:"Really terminate "+t.instanceId+" and shrink "+t.serverGroup+"?",buttonText:"Terminate "+t.instanceId+" and shrink "+t.serverGroup,account:t.account,taskMonitorConfig:a,submitMethod:function(){return b.terminateInstanceAndShrinkServerGroup(t,i,{serverGroupName:t.serverGroup,instanceIds:[t.instanceId],zone:t.placement.availabilityZone})}})},this.rebootInstance=function(){const n=e.instance,t={application:i,title:"Rebooting "+n.instanceId};h.confirm({header:"Really reboot "+n.instanceId+"?",buttonText:"Reboot "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return b.rebootInstance(n,i,{interestingHealthProviderNames:[]})}})},this.registerInstanceWithLoadBalancer=function(){const n=e.instance,t=n.loadBalancers.join(" and "),a={application:i,title:"Registering "+n.instanceId+" with "+t};h.confirm({header:"Really register "+n.instanceId+" with "+t+"?",buttonText:"Register "+n.instanceId,account:n.account,taskMonitorConfig:a,submitMethod:function(){return b.registerInstanceWithLoadBalancer(n,i)}})},this.deregisterInstanceFromLoadBalancer=function(){const n=e.instance,t=n.loadBalancers.join(" and "),a={application:i,title:"Deregistering "+n.instanceId+" from "+t};h.confirm({header:"Really deregister "+n.instanceId+" from "+t+"?",buttonText:"Deregister "+n.instanceId,account:n.account,taskMonitorConfig:a,submitMethod:function(){return b.deregisterInstanceFromLoadBalancer(n,i)}})},this.enableInstanceInDiscovery=function(){const n=e.instance,t={application:i,title:"Enabling "+n.instanceId+" in discovery"};h.confirm({header:"Really enable "+n.instanceId+" in discovery?",buttonText:"Enable "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return b.enableInstanceInDiscovery(n,i)}})},this.disableInstanceInDiscovery=function(){const n=e.instance,t={application:i,title:"Disabling "+n.instanceId+" in discovery"};h.confirm({header:"Really disable "+n.instanceId+" in discovery?",buttonText:"Disable "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return b.disableInstanceInDiscovery(n,i)}})},this.hasHealthState=function(n,t){return e.instance.health.some((function(e){return e.type===n&&e.state===t}))};(i.isStandalone?d():r.all([i.serverGroups.ready(),i.loadBalancers.ready()]).then(d)).then((()=>{e.$$destroyed||i.isStandalone||i.serverGroups.onRefresh(e,d)})),e.account=a.account}]);const ln=Object.freeze([{instanceType:"n1-standard-1",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"f1-micro",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"g1-small",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-small",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-micro",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-medium",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"n1-highmem-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"default",disks:[{type:"pd-ssd",sizeGb:10}]}]),cn="spinnaker.gce.instanceType.service";n(cn,[]).factory("gceInstanceTypeService",["$q","$log",function(e,n){const t=[{type:"general",label:"General Purpose",families:[{type:"n1-standard",description:"This family provides a balance of compute, memory, and network resources, and it is a good choice for general purpose applications.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-standard-1",label:"Small",cpu:1,memory:3.75,costFactor:1},{name:"n1-standard-2",label:"Medium",cpu:2,memory:7.5,costFactor:2},{name:"n1-standard-4",label:"Large",cpu:4,memory:15,costFactor:2},{name:"n1-standard-8",label:"XLarge",cpu:8,memory:30,costFactor:3},{name:"n1-standard-16",label:"2XLarge",cpu:16,memory:60,costFactor:3},{name:"n1-standard-32",label:"4XLarge",cpu:32,memory:120,costFactor:4},{name:"n1-standard-64",label:"8XLarge",cpu:64,memory:240,costFactor:4},{name:"n1-standard-96",label:"12XLarge",cpu:96,memory:360,costFactor:4}]}],icon:"hdd"},{type:"memory",label:"High Memory",families:[{type:"n1-highmem",description:"High memory machine types are ideal for tasks that require more memory relative to virtual cores. High memory machine types have 6.50GB of RAM per virtual core.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-highmem-2",label:"Medium",cpu:2,memory:13,costFactor:2},{name:"n1-highmem-4",label:"Large",cpu:4,memory:26,costFactor:2},{name:"n1-highmem-8",label:"XLarge",cpu:8,memory:52,costFactor:3},{name:"n1-highmem-16",label:"2XLarge",cpu:16,memory:104,costFactor:3},{name:"n1-highmem-32",label:"4XLarge",cpu:32,memory:208,costFactor:4},{name:"n1-highmem-64",label:"8XLarge",cpu:64,memory:416,costFactor:4},{name:"n1-highmem-96",label:"12XLarge",cpu:96,memory:624,costFactor:4}]}],icon:"hdd"},{type:"cpu",label:"High CPU",families:[{type:"n1-highcpu",description:"High CPU machine types are ideal for tasks that require more virtual cores relative to memory. High CPU machine types have one virtual core for every 0.90GB of RAM.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-highcpu-2",label:"Medium",cpu:2,memory:1.8,costFactor:1},{name:"n1-highcpu-4",label:"Large",cpu:4,memory:3.6,costFactor:2},{name:"n1-highcpu-8",label:"XLarge",cpu:8,memory:7.2,costFactor:2},{name:"n1-highcpu-16",label:"2XLarge",cpu:16,memory:14.4,costFactor:3},{name:"n1-highcpu-32",label:"4XLarge",cpu:32,memory:28.8,costFactor:4},{name:"n1-highcpu-64",label:"8XLarge",cpu:64,memory:57.6,costFactor:4},{name:"n1-highcpu-96",label:"12XLarge",cpu:96,memory:86.4,costFactor:4}]}],icon:"hdd"},{type:"micro",label:"Micro Utility",families:[{type:"f1-micro bursting",description:"This family of machine types is a good choice for small, non-resource intensive workloads that don’t use the full CPU often or consistently, but occasionally need to burst (e.g. web servers, developer environments and small databases).",storageType:"Std",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"f1-micro",label:"Micro",cpu:1,memory:.6,costFactor:1},{name:"g1-small",label:"Small",cpu:1,memory:1.7,costFactor:1},{name:"e2-micro",label:"E2 Micro",cpu:2,memory:1,costFactor:1},{name:"e2-small",label:"E2 Small",cpu:2,memory:2,costFactor:2},{name:"e2-medium",label:"E2 Medium",cpu:2,memory:4,costFactor:2}]}],icon:"hdd"},{type:"custom",label:"Custom Type",families:[],icon:"asterisk"},{type:"buildCustom",label:"Build Custom",families:[{type:"buildCustom",instanceTypes:[{name:"n1buildCustom",nameRegex:/custom-\d{1,2}-\d{4,6}/,storage:{localSSDSupported:!0}},{name:"e2buildCustom",nameRegex:/e2-custom-(\d{1,2})-(\d{3,6})/,storage:{localSSDSupported:!1}},{name:"otherbuildCustom",nameRegex:/(.*)-?custom-(\d{1,2})-(\d{3,6})/,storage:{localSSDSupported:!0}}]}],icon:"wrench"}],i=oe.memoize((()=>{const e=oe.cloneDeep(t);return y.getAllAccountDetailsForProvider("gce").then((t=>{let a=oe.get(t,"[0].instanceTypeDisks");if(oe.isEmpty(a)&&(a=ln),a){oe.flatten(e.map((e=>e.families))).forEach((e=>{e.instanceTypes.forEach((e=>{const t=a.find((n=>n.instanceType===e.name));if(t){const a=t.disks.map((e=>{switch(e.type){case"PD_SSD":return{type:"pd-ssd",sizeGb:e.sizeGb};case"PD_STANDARD":return{type:"pd-standard",sizeGb:e.sizeGb};case"LOCAL_SSD":return{type:"local-ssd",sizeGb:375};default:return n.warn(`Disk type '${e.type}' not supported.`),null}})).filter((e=>!!e));let i=0,l=0;if(t.supportsLocalSSD)l=a.filter((e=>"local-ssd"===e.type)).length,i=375;else{const e=a.filter((e=>e.type.startsWith("pd-")));e.length&&(i=e.reduce(((e,n)=>Math.max(e,n.sizeGb)),0),l=e.filter((e=>e.sizeGb===i)).length)}e.storage={localSSDSupported:t.supportsLocalSSD,size:i,count:l,defaultSettings:{disks:a}}}}))}))}return e}))}));function l(e,n){if(!e||!n||0===n.length)return console.error("Invalid input parameters"),[];return n.filter((n=>e[n])).flatMap((n=>e[n].instanceTypes||[]))}return{getCategories:i,getAvailableTypesForRegions:l,getAllTypesByRegion:function(){return i().then((e=>oe.chain(e).map("families").flatten().map("instanceTypes").flatten().map("name").filter((e=>"n1buildCustom"!==e&&"e2buildCustom"!==e&&"otherbuildCustom"!==e)).value()))},getAvailableTypesForLocations:l,resolveInstanceTypeDetails:e=>({name:e,storage:Object.assign({isDefault:!0},a.providers.gce.defaults.instanceTypeStorage)})}}]);const rn="spinnaker.google.instance.multiInstance.task.transformer";n(rn,[]).factory("gceMultiInstanceTaskTransformer",(function(){const e={rebootInstances:e=>{e.interestingHealthProviderNames=[]}};return{transform:(n,t)=>{((e,n)=>{n.zone=e.instances[0].availabilityZone})(n,t),e[t.type]&&e[t.type](t)}}}));const on="spinnaker.gce.iap.interceptor";n(on,[]).service("iapInterceptor",class{constructor(){this.responseError=e=>{const{config:n,status:t}=e;return 401===t&&a.feature.iapRefresherEnabled?ze.get("/_gcp_iap/do_session_refresh").then((()=>ze.get(n.url,n).then((e=>e))),(()=>Ne.reject(e))):Ne.reject(e)}}}).config(["$httpProvider",e=>{e.interceptors.push("iapInterceptor")}]);const sn="spinnaker.gce.loadBalancerTypeToWizard.constant";n(sn,[]).constant("loadBalancerTypeToWizardMap",{NETWORK:{label:"Network",createTemplateUrl:"google/src/loadBalancer/configure/network/createLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/network/editLoadBalancer.html",controller:"gceCreateLoadBalancerCtrl"},HTTP:{label:"HTTP(S)",createTemplateUrl:"google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",controller:"gceCreateHttpLoadBalancerCtrl"},INTERNAL_MANAGED:{label:"Internal HTTP(S)",createTemplateUrl:"google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",controller:"gceCreateInternalHttpLoadBalancerCtrl"},INTERNAL:{label:"Internal",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceInternalLoadBalancerCtrl"},SSL:{label:"SSL",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceSslLoadBalancerCtrl"},TCP:{label:"TCP",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceTcpLoadBalancerCtrl"}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/createLoadBalancer.html",'<form name="form" class="form-horizontal">\n <v2-modal-wizard heading="Create New Load Balancer" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Listener" label="Listener" done="true">\n <ng-include src="pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Health Check" label="Health Check" done="true">\n <ng-include src="pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Advanced Settings" label="Advanced Settings" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/editLoadBalancer.html",'<v2-modal-wizard\n heading="Edit {{loadBalancer.name}}: {{loadBalancer.region}}: {{loadBalancer.credentials}}"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n>\n <v2-wizard-page key="Listener" label="Listener" done="true">\n <ng-include src="pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Health Check" label="Health Check" done="true" render="!!loadBalancer.listeners[0].healthCheck">\n <ng-include src="pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="Advanced Settings"\n label="Advanced Settings"\n done="true"\n render="!!loadBalancer.listeners[0].healthCheck"\n >\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n</v2-modal-wizard>\n<div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="isNew"\n ></submit-button>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listeners" label="Listeners" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listeners" label="Listeners" done="true">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listeners" label="Listeners" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listeners" label="Listeners" done="true">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]);class dn{constructor(e,n,t,a){this.$uibModal=e,this.$uibModalInstance=n,this.application=t,this.loadBalancerTypeToWizardMap=a,this.choice="Network"}$onInit(){this.choices=me(this.loadBalancerTypeToWizardMap,(e=>e.label))}choose(e){const n=he(this.loadBalancerTypeToWizardMap,(n=>n.label===e));this.$uibModalInstance.dismiss(),this.$uibModal.open({templateUrl:n.createTemplateUrl,controller:`${n.controller} as ctrl`,size:"lg",resolve:{application:()=>this.application,loadBalancer:()=>null,isNew:()=>!0,forPipelineConfig:()=>!1}})}}dn.$inject=["$uibModal","$uibModalInstance","application","loadBalancerTypeToWizardMap"];const un="spinnaker.gce.loadBalancerChoice.modal.controller";n(un,[sn]).controller("gceLoadBalancerChoiceCtrl",dn);class gn{constructor(e){this.credentials=e,this.provider="gce",this.stack="",this.detail="",this.region="global",this.loadBalancerType="HTTP",this.certificate="",this.hostRules=[],this.listeners=[]}}class pn{constructor(){this.backends=[],this.sessionAffinity="NONE",this.affinityCookieTtlSec=null}}class mn{constructor(){this.requestPath="/",this.port=80,this.checkIntervalSec=10,this.timeoutSec=5,this.healthyThreshold=10,this.unhealthyThreshold=2}}class hn{constructor(){this.pathRules=[]}}class vn{constructor(){this.pathMatcher=new hn}}class fn{}class bn{constructor(){this.certificate=null}}const yn="spinnaker.deck.gce.httpLoadBalancer.backendService.component";n(yn,[]).component("gceHttpLoadBalancerBackendServiceSelector",{bindings:{deleteService:"&",backendService:"=",command:"=",index:"="},templateUrl:"google/src/loadBalancer/configure/http/backendService/backendService.component.html",controller:function(){this.$onInit=()=>{this.backingData=this.command.backingData,this.loadBalancer=this.command.loadBalancer;const e=this.backingData.backendServicesKeyedByName;this.onBackendServiceSelect=e=>{n(e),this.command.onHealthCheckSelected(e.healthCheck,this.command)},this.toggleEditExisting=()=>{if(this.editExisting=!this.editExisting,this.editExisting)delete this.backendService.name;else{const e=new pn;n(e)}},this.getAllHealthChecks=()=>{const e=this.loadBalancer.healthChecks.concat(this.backingData.healthChecks);return oe.chain(e).filter((e=>e.account===this.loadBalancer.credentials||!e.account)).map((e=>e.name)).uniq().value()},this.getSessionAffinitySuggestions=()=>"HTTP"===this.loadBalancer.loadBalancerType?["None","Client IP","Generated Cookie"]:["None","Client IP","Generated Cookie","Header Field","HTTP Cookie"],this.getAllServiceNames=()=>this.command.backingData.backendServices.filter((e=>e.account===this.loadBalancer.credentials)).map((e=>e.name)),this.maxCookieTtl=86400;e[(()=>oe.get(this,"backendService.name"))()]&&(this.editExisting=!0);const n=e=>{this.loadBalancer.backendServices[this.index]=this.backendService=e}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendService.component.html",'<div class="container-fluid form-horizontal">\n <hr ng-if="$ctrl.index > 0" />\n <ng-form name="backendService">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="backendService.serviceName.$error.validateUnique">\n <validation-error message="There is already a backend service with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="backendService.serviceName.$error.pattern || backendService.serviceName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters, numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.backendService"\n on-select="$ctrl.onBackendServiceSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="backendService in $ctrl.backingData.backendServices | filter: {name: $select.search, account: $ctrl.loadBalancer.credentials}"\n >\n <div ng-bind-html="backendService.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n required\n type="text"\n name="serviceName"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n max="63"\n validate-unique="$ctrl.getAllServiceNames()"\n ng-model-options="{ debounce: 250 }"\n ng-model="$ctrl.backendService.name"\n />\n </div>\n\n <div class="col-md-2">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteService()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing backend services</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Health Check</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.backendService.healthCheck"\n required\n on-select="$ctrl.command.onHealthCheckSelected($item, $ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices repeat="healthCheck in $ctrl.getAllHealthChecks() | filter: $select.search">\n <div ng-bind-html="healthCheck | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Session Affinity</b>\n </div>\n <div class="col-md-4">\n <ui-select ng-model="$ctrl.backendService.sessionAffinity" required class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices repeat="sessionAffinity in $ctrl.getSessionAffinitySuggestions() | filter: $select.search">\n <div ng-bind-html="sessionAffinity | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Port Name</b>\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input required type="string" class="form-control input-sm" ng-model="$ctrl.backendService.portName" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n\n <div ng-if="$ctrl.loadBalancer.loadBalancerType === \'HTTP\'" class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Enable CDN</b>\n </div>\n <div class="col-md-4">\n <input type="checkbox" ng-model="$ctrl.backendService.enableCDN" />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.backendService.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="$ctrl.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ $ctrl.maxCookieTtl }}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);const kn="spinnaker.deck.gce.httpLoadBalancer.basicSettings.component";n(kn,[]).component("gceHttpLoadBalancerBasicSettings",{bindings:{command:"=",application:"="},templateUrl:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.component.html",controller:function(){this.$onInit=()=>{const e=this.command;this.loadBalancer=e.loadBalancer,this.accounts=e.backingData.accounts;const n=e.backingData.loadBalancerMap;this.getName=(e,n)=>{const t=[n,e.stack||"",e.detail||""].join("-");return oe.trimEnd(t,"-")},this.updateName=(e,n)=>{e.urlMapName=this.getName(e,n)},this.accountChanged=t=>{this.existingLoadBalancerNames=oe.get(n,[t,"urlMapNames"])||[],e.onAccountChange(e)},this.loadBalancer.name||this.updateName(this.loadBalancer,this.application.name),this.existingLoadBalancerNames=oe.get(n,[this.loadBalancer.credentials,"urlMapNames"])||[]}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.component.html",'<div class="container-fluid form-horizontal">\n <ng-form name="basicSettings">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\n \'alert-danger\': basicSettings.urlMapName.$error.validateUnique,\n \'alert-info\': !basicSettings.urlMapName.$error.validateUnique\n }"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ $ctrl.getName($ctrl.loadBalancer, $ctrl.application.name) }}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.urlMapName"\n validate-unique="$ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="urlMapName"\n />\n <validation-error\n ng-if="basicSettings.urlMapName.$error.validateUnique"\n message="There is already a load balancer in {{ $ctrl.loadBalancer.credentials }} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="$ctrl.loadBalancer"\n field="credentials"\n accounts="$ctrl.accounts"\n provider="\'gce\'"\n on-change="$ctrl.accountChanged($ctrl.loadBalancer.credentials)"\n >\n </account-select-field>\n </div>\n </div>\n <gce-region-select-field\n ng-if="$ctrl.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'"\n label-columns="2"\n field-columns="8"\n component="$ctrl.loadBalancer"\n field="region"\n account="$ctrl.loadBalancer.credentials"\n on-change="$ctrl.command.onRegionSelected($ctrl.command)"\n regions="$ctrl.command.backingData.regions"\n ></gce-region-select-field>\n <gce-network-select-field\n ng-if="$ctrl.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'"\n label-columns="2"\n field-columns="8"\n component="$ctrl.loadBalancer"\n field="network"\n helpKey="gce.internalHttpLoadBalancer.network"\n account="$ctrl.loadBalancer.credentials"\n networks="$ctrl.command.backingData.internalHttpLbNetworks"\n ></gce-network-select-field>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="$ctrl.updateName($ctrl.loadBalancer, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="$ctrl.updateName($ctrl.loadBalancer, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="basicSettings.stackName.$error.pattern">\n <validation-error message="Stack can only contain lowercase letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="basicSettings.detailName.$error.pattern">\n <validation-error\n message="Detail can only contain lowercase letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="basicSettings.urlMapName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]);class Sn{constructor(e){this.cacheInitializer=e,this.refreshing=!1,this.tooltipTemplate="google/src/cache/cacheRefreshTooltip.html"}$onInit(){const e=this.cacheKeyAlias||this.cacheKey;this.capitalizedKey=e[0].toUpperCase()+e.substring(1),this.depluralizedKey=e.substring(0,e.length-1)}getRefreshTime(){return d.get(this.cacheKey).getStats().ageMax}refresh(){this.refreshing=!0,this.cacheInitializer.refreshCache(this.cacheKey).then((()=>this.onRefresh())).then((()=>{this.refreshing=!1}))}}Sn.$inject=["cacheInitializer"];const wn={bindings:{onRefresh:"&",cacheKey:"@",cacheKeyAlias:"@",renderCompact:"<"},controller:Sn,templateUrl:"google/src/cache/cacheRefresh.component.html"},Cn="spinnaker.gce.cacheRefresh.component";n(Cn,[k]).component("gceCacheRefresh",wn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/cache/cacheRefreshTooltip.html",'<p ng-if="$ctrl.refreshing">{{$ctrl.capitalizedKey}} data is <strong>refreshing</strong></p>\n<p ng-if="!$ctrl.refreshing">(click <span class="fa fa-sync-alt"></span> to refresh)</p>\n<p>Last refresh: {{$ctrl.getRefreshTime() | timestamp }}</p>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/cache/cacheRefresh.component.html",'<div ng-if="!$ctrl.renderCompact">\n <p>\n <span ng-if="$ctrl.refreshing"><span class="fa fa-sync-alt fa-spin"></span></span>\n {{::$ctrl.capitalizedKey}}\n <span ng-if="!$ctrl.refreshing">last refreshed {{ $ctrl.getRefreshTime() | timestamp }}</span>\n <span ng-if="$ctrl.refreshing"> refreshing...</span>\n </p>\n <p>\n If you\'re not finding a {{::$ctrl.depluralizedKey}} that was recently added,\n <a href ng-click="$ctrl.refresh()">click here</a> to refresh the list.\n </p>\n</div>\n<div ng-if="$ctrl.renderCompact">\n <a href uib-tooltip-template="$ctrl.tooltipTemplate">\n <span class="fa fa-sync-alt" ng-click="$ctrl.refresh()" ng-class="{ \'fa-spin\': $ctrl.refreshing }"></span>\n </a>\n</div>\n')}]);const Bn="spinnaker.gce.certificateReader.service";n(Bn,[]).service("gceCertificateReader",class{listCertificates(){return s.search({q:"",type:"sslCertificates",allowShortQuery:"true"},d.get("certificates")).then((e=>e&&e.results?e.results.filter((e=>"gce"===e.provider)):[])).catch((()=>[]))}});const Tn={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE","Client IP and protocol":"CLIENT_IP_PROTO","Client IP, port and protocol":"CLIENT_IP_PORT_PROTO","Header Field":"HEADER_FIELD","HTTP Cookie":"HTTP_COOKIE","Strong Cookie":"STRONG_COOKIE_AFFINITY"},Gn=oe.invert(Tn),An="spinnaker.gce.deck.httpLoadBalancer.transformer";n(An,[]).factory("gceHttpLoadBalancerTransformer",(function(){const e=["backendServices","healthChecks","listeners","stack","detail"];return{serialize:function(n,t){const a=oe.cloneDeep(n),{loadBalancer:i,backingData:l}=a;!function(e,n){const t=oe.assign(n.healthChecksKeyedByName,oe.keyBy(e.healthChecks,"name")),a=oe.assign(n.backendServicesKeyedByName,oe.keyBy(e.backendServices,"name"));oe.forEach(a,(e=>{e.healthCheck=t[e.healthCheck],e.sessionAffinity=Tn[e.sessionAffinity]||e.sessionAffinity})),e.defaultService=a[e.defaultService],i=e.hostRules,l=a,i.forEach((e=>{const n=e.pathMatcher;n.defaultService=l[n.defaultService],n.pathRules.forEach((e=>{e.backendService=l[e.backendService]}))}));var i,l}(i,l),i.hostRules=i.hostRules.reduce(((e,n)=>e.concat(n.hostPatterns.map((e=>({hostPatterns:[e],pathMatcher:oe.cloneDeep(n.pathMatcher)}))))),[]);const c=function(n){return n.listeners.map((t=>{let a=oe.cloneDeep(n);return a=oe.omit(a,e),a.name=t.name,a.portRange=t.port,a.certificate=t.certificate||null,a.ipAddress=t.ipAddress,a.subnet=t.subnet,a}))}(i);return t&&(c[0].listenersToDelete=oe.chain(t.listeners).map("name").difference(oe.map(n.loadBalancer.listeners,"name")).value()),c},deserialize:function(e){const{backendServices:n,healthChecks:t,defaultService:a}=function(e){const n=e.defaultService;let t=[e.defaultService];e.hostRules&&(t=e.hostRules.reduce(((e,n)=>(e=e.concat(n.pathMatcher.defaultService),n.pathMatcher.pathRules.reduce(((e,n)=>e.concat(n.backendService)),e))),t));const a=oe.chain(t).map("healthCheck").uniqBy("name").cloneDeep().value();return a.forEach((n=>{n.account=e.account||e.credentials})),t=oe.uniqBy(t,"name"),t.forEach((e=>{e.healthCheck=e.healthCheck.name,e.sessionAffinity=Gn[e.sessionAffinity]||e.sessionAffinity})),{backendServices:t,healthChecks:a,defaultService:n}}(e),i=function(e){return function(e){return e.forEach((e=>{const n=e.pathMatcher;n.defaultService=n.defaultService.name,n.pathRules.forEach((e=>{e.backendService=e.backendService.name}))})),e}(oe.cloneDeep(e.hostRules))}(e),l=function(e){return e.listeners.forEach((e=>{const{stack:n,freeFormDetails:t}=S.parseLoadBalancerName(e.name);e.stack=n,e.detail=t,e.created=!0})),e.listeners}(e);return{defaultService:a.name,backendServices:n,healthChecks:t,hostRules:i,listeners:l,network:e.network,region:e.region,urlMapName:e.urlMapName,credentials:e.credentials||e.account}}}}));const Pn="spinnaker.deck.gce.httpLoadBalancer.backing.service";n(Pn,[Ve,Bn,w,tn,_e,je,An,We]).factory("gceHttpLoadBalancerCommandBuilder",["$q","gceHttpLoadBalancerUtils","gceBackendServiceReader","gceCertificateReader","gceHealthCheckReader","gceHttpLoadBalancerTransformer","loadBalancerReader","gceAddressReader","gceXpnNamingService",function(e,n,t,a,i,l,c,r,o){function s(n){const t={backendServices:g(n),healthChecks:u(n),certificates:p(),loadBalancerMap:m(),networks:B.listNetworksByProvider("gce").then((e=>oe.chain(e).map("name").compact().uniq().value())),subnets:C.listSubnetsByProvider("gce"),addresses:r.listAddresses(n),accounts:y.listAccounts("gce")};return e.all(t)}function d(e,n){!function(e,n){const t=oe.map(e,"name"),a=oe.get(n,"credentials.name")||n.credentials;t.includes(a)||(n.credentials=oe.first(t))}(e.accounts,n),function(e,n){const t=e.accounts.map((e=>e.name));t.forEach((t=>{oe.has(e,["loadBalancerMap",t,"listeners"])&&(e.loadBalancerMap[t].listeners=oe.without(e.loadBalancerMap[t].listeners,...n.map((e=>e.name))))}))}(e,n.listeners);const t=oe.keyBy(n.healthChecks,"name"),a=oe.keyBy(e.healthChecks,"name");e.healthChecksKeyedByName=oe.assign(a,oe.cloneDeep(t)),e.healthChecksKeyedByNameCopy=oe.cloneDeep(a),e.healthChecks=oe.map(a,oe.identity);const i=oe.keyBy(n.backendServices,"name"),l=oe.keyBy(e.backendServices,"name");e.backendServicesKeyedByName=oe.assign(l,oe.cloneDeep(i)),e.backendServicesKeyedByNameCopy=oe.cloneDeep(l),e.backendServices=oe.map(l,oe.identity),e.regions=e.accounts.find((e=>e.name===n.credentials)).regions.map((e=>e.name)),e.subnetMap=oe.groupBy(e.subnets,"network"),e.internalHttpLbNetworks=e.networks.filter((n=>e.subnetMap[n].find((e=>"INTERNAL_HTTPS_LOAD_BALANCER"===e.purpose))))}function u(e){return i.listHealthChecks().then((n=>n.filter((n=>!e||n.region===e))))}function g(e){const n=e?"regionBackendService":"globalBackendService";return t.listBackendServices(n).then((n=>((n=n.filter((n=>!e||n.region===e))).forEach((e=>{e.healthCheck=e.healthCheckLink.split("/").pop();const n="string"==typeof e.affinityCookieTtlSec;e.affinityCookieTtlSec=n?Number(e.affinityCookieTtlSec):null,e.sessionAffinity=Gn[e.sessionAffinity]||e.sessionAffinity})),n)))}function p(){return a.listCertificates()}function m(e){return c.listLoadBalancers("gce").then((e=>oe.chain(e).map((e=>e.accounts)).flatten().groupBy("name").mapValues((e=>{const t=oe.chain(e).map((e=>e.regions)).flatten().filter((e=>e.name===(e||n.REGION))).map((e=>e.loadBalancers)).flatten().value();return{urlMapNames:oe.chain(t).map("urlMapName").uniq().value(),listeners:oe.chain(t).map("name").uniq().value()}})).valueOf()))}function h(e){u(e.loadBalancer.region).then((n=>{e.backingData.healthChecks=n,e.backingData.healthChecksKeyedByName=oe.keyBy(n,"name"),e.backingData.healthChecksKeyedByNameCopy=oe.cloneDeep(e.backingData.healthChecksKeyedByName),e.loadBalancer.healthChecks=e.loadBalancer.healthChecks.map((n=>{const t=e.backingData.healthChecksKeyedByName[oe.get(n,"name")];return t?oe.cloneDeep(t):n}))}))}function v(e){p().then((n=>{e.backingData.certificates=n}))}function f(e){g(e.loadBalancer.region).then((n=>{e.backingData.backendServices=n,e.backingData.backendServicesKeyedByName=oe.keyBy(n,"name"),e.backingData.backendServicesKeyedByNameCopy=oe.cloneDeep(e.backingData.backendServicesKeyedByName),e.loadBalancer.backendServices=e.loadBalancer.backendServices.map((n=>{const t=e.backingData.backendServicesKeyedByName[oe.get(n,"name")];return t?oe.cloneDeep(t):n}))}))}function b(e){s(e.loadBalancer.region).then((n=>{Object.assign(e.backingData,n),d(e.backingData,e.loadBalancer)}))}function k(e,n){if(!n.loadBalancer.healthChecks.find((n=>oe.get(n,"name")===e))){const t=n.backingData.healthChecksKeyedByName[e];t&&n.loadBalancer.healthChecks.push(t)}}function S(e,n){if(!n.loadBalancer.backendServices.find((n=>n.name===e))){const t=n.backingData.backendServicesKeyedByName[e];n.loadBalancer.backendServices.push(t),t.healthCheck&&k(t.healthCheck,n)}}function w(e){const n=e.loadBalancer.backendServices.concat(e.backingData.backendServices);return oe.chain(n).filter((n=>n.account===e.loadBalancer.credentials||n.account===e.loadBalancer.account)).map("name").compact().uniq().value()}function T(e){return oe.chain(e.loadBalancer.healthChecks).map("name").difference(oe.map(e.loadBalancer.backendServices,"healthCheck")).compact().uniq().value()}function G(e){const n=e.loadBalancer.defaultService,t=oe.map(e.loadBalancer.hostRules,"pathMatcher.defaultService"),a=oe.chain(e.loadBalancer.hostRules).map("pathMatcher.pathRules").flatten().map("backendService").value(),i=oe.chain([n,...t,...a]).compact().uniq().value();return oe.chain(e.loadBalancer.backendServices).map("name").difference(i).compact().uniq().value()}function A(e){const n=G(e);e.loadBalancer.backendServices=e.loadBalancer.backendServices.filter((e=>!n.includes(e.name)))}function P(e){const n=T(e);e.loadBalancer.healthChecks=e.loadBalancer.healthChecks.filter((e=>!n.includes(e.name)))}function $(e){e.loadBalancer.backendServices=[],e.loadBalancer.healthChecks=[],e.loadBalancer.hostRules=[],e.loadBalancer.listeners=[new bn],e.loadBalancer.defaultService=null,e.backingData.regions=e.backingData.accounts.find((n=>n.name===e.loadBalancer.credentials)).regions.map((e=>e.name))}function x(e){r.listAddresses("global").then((n=>{e.backingData.addresses=n}))}return{buildCommand:function({originalLoadBalancer:e,isNew:n,isInternal:t}){return function(e,n,t){let a=null;t&&(a=e?e.region:Oe.defaults.region);return s(a).then((a=>{const i=function(e,n,t){const a=new gn(Oe.defaults.account||null);t?(a.loadBalancerType="INTERNAL_MANAGED",a.isInternal=!0,n&&(n.network=o.decorateXpnResourceIfNecessary(n.project,n.network),n.listeners.forEach((e=>{e.subnet=o.decorateXpnResourceIfNecessary(n.project,e.subnet)}))),a.region=Oe.defaults.region,a.network="default"):(a.loadBalancerType="HTTP",a.isInternal=!1);let i;i=e?{backendServices:[],listeners:[new bn],healthChecks:[]}:l.deserialize(n);return oe.assign(a,i)}(n,e,t);return d(a,i),{backingData:a,loadBalancer:i}}))}(e=oe.cloneDeep(e),n,t).then((({backingData:e,loadBalancer:t})=>({backingData:e,getAllBackendServices:w,isNew:n,loadBalancer:t,onAccountChange:$,onBackendServiceRefresh:f,onBackendServiceSelected:S,onRegionSelected:b,onCertificateRefresh:v,onHealthCheckRefresh:h,onHealthCheckSelected:k,getUnusedBackendServices:G,removeUnusedBackendServices:A,getUnusedHealthChecks:T,removeUnusedHealthChecks:P,onAddressRefresh:x})))}}}]);const $n="spinnaker.deck.gce.loadBalancer.hostAndPathRules.service";n($n,[]).factory("hostAndPathRulesService",(function(){function e(e,n,t){return{hostPattern:e,path:n,backend:t}}return{buildTable:function(n,t){const a=e("Any unmatched (default)","Any unmatched (default)",t.name);return 0===n.length?[a]:n.reduce(((n,t)=>{const[a]=t.hostPatterns,{defaultService:i,pathRules:l}=t.pathMatcher;return n.push(e(a,"/*",i.name)),n.concat(l.reduce(((n,t)=>{const{backendService:i,paths:l}=t;return n.concat(l.map((n=>e(a,n,i.name))))}),[]))}),[a])}}}));const xn="spinnaker.deck.gce.loadBalancer.hostAndPathRules.controller";n(xn,[$n]).controller("gceHostAndPathRulesCtrl",["hostRules","defaultService","loadBalancerName","$uibModalInstance","hostAndPathRulesService",function(e,n,t,a,i){this.table=i.buildTable(e,n),this.loadBalancerName=t,this.close=a.dismiss}]);const In="spinnaker.deck.gce.loadBalancer.hostAndPathRulesButton.component";n(In,[Ie,xn]).component("gceHostAndPathRulesButton",{bindings:{hostRules:"=",defaultService:"=",loadBalancerName:"="},template:'<a href ng-click="$ctrl.viewHostAndPathRules()">View Host and Path Rules</a>',controller:["$uibModal",function(e){this.viewHostAndPathRules=()=>{e.open({templateUrl:"google/src/loadBalancer/details/hostAndPathRules/hostAndPathRules.modal.html",controller:"gceHostAndPathRulesCtrl",controllerAs:"ctrl",size:"lg",resolve:{hostRules:()=>this.hostRules,defaultService:()=>this.defaultService,loadBalancerName:()=>this.loadBalancerName}})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/hostAndPathRules/hostAndPathRules.modal.html",'<div modal-page class="form-inline">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Host And Path Rules for {{ ctrl.loadBalancerName }}</h4>\n </div>\n <div class="modal-body clearfix">\n <div class="section-body">\n <div class="well well-sm">\n <div class="row">\n <div class="col-md-12 content-fields">\n <input placeholder="Filter" ng-model="searchText" class="form-control input-sm" />\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>Host</th>\n <th>Path</th>\n <th>Backend Service</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in ctrl.table | filter:searchText">\n <td>{{ row.hostPattern }}</td>\n <td>{{ row.path }}</td>\n <td>{{ row.backend }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.close()">Close</button>\n </div>\n</div>\n')}]);const zn="spinnaker.deck.httpLoadBalancer.healthCheck.component";n(zn,[]).component("gceHttpLoadBalancerHealthCheck",{bindings:{command:"=",deleteHealthCheck:"&",healthCheck:"=",index:"="},templateUrl:"google/src/loadBalancer/configure/http/healthCheck/healthCheck.component.html",controller:function(){this.$onInit=()=>{this.max=Number.MAX_SAFE_INTEGER,this.backingData=this.command.backingData,this.loadBalancer=this.command.loadBalancer;const e=this.backingData.healthChecksKeyedByName;this.onHealthCheckSelect=e=>{n(e)},this.getAllHealthCheckNames=()=>this.command.backingData.healthChecks.filter((e=>e.account===this.loadBalancer.credentials)).map((e=>e.name)),this.toggleEditExisting=()=>{this.editExisting=!this.editExisting,this.editExisting?delete this.healthCheck.name:n(new mn)};const n=e=>{this.loadBalancer.healthChecks[this.index]=this.healthCheck=e};this.onProtocolChange=()=>{this.healthCheck.healthCheckType!==this.healthCheckType&&n(Object.assign({},new mn,{healthCheckType:this.healthCheckType}))},e[(()=>oe.get(this,"healthCheck.name"))()]&&(this.editExisting=!0),this.healthCheckType=this.healthCheck.healthCheckType}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthCheck.component.html",'<hr ng-if="$ctrl.index > 0" />\n<ng-form name="healthCheck">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="healthCheck.healthCheckName.$error.validateUnique">\n <validation-error message="There is already a health check with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="healthCheck.healthCheckName.$error.pattern || healthCheck.healthCheckName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters, numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Protocol</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-change="$ctrl.onProtocolChange()"\n ng-model="$ctrl.healthCheckType"\n ng-options="protocol as protocol for protocol in [\'HTTP\', \'HTTPS\', \'TCP\', \'SSL\', \'HTTP2\', \'GRPC\']"\n ></select>\n </div>\n <div class="col-md-2">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteHealthCheck()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.healthCheck"\n on-select="$ctrl.onHealthCheckSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="healthCheck in $ctrl.backingData.healthChecks | filter: {name: $select.search, account: $ctrl.loadBalancer.credentials, healthCheckType: $ctrl.healthCheckType}"\n >\n <div ng-bind-html="healthCheck.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n type="text"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n name="healthCheckName"\n max="63"\n required\n validate-unique="$ctrl.getAllHealthCheckNames()"\n ng-model-options="{ debounce: 250 }"\n ng-model="$ctrl.healthCheck.name"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing health checks</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div\n class="form-group"\n ng-if="\n $ctrl.healthCheck.healthCheckType === \'HTTP\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTPS\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTP2\'\n "\n >\n <div class="col-md-4 sm-label-right">Request Path</div>\n <div class="col-md-4">\n <input class="form-control input-sm" required ng-model="$ctrl.healthCheck.requestPath" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n required\n min="1"\n max="65535"\n ng-model="$ctrl.healthCheck.port"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.healthCheck.healthCheckType === \'GRPC\'">\n <div class="col-md-4 sm-label-right">Service Name</div>\n <div class="col-md-4">\n <input class="form-control input-sm" ng-model="$ctrl.healthCheck.grpcServiceName" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.timeoutSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.checkIntervalSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.healthyThreshold"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.unhealthyThreshold"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const Nn="spinnaker.deck.gce.httpLoadBalancer.pathRule.component";n(Nn,[]).component("gcePathRule",{bindings:{pathRule:"=",command:"=",index:"=",deletePathRule:"&"},templateUrl:"google/src/loadBalancer/configure/http/pathRule/pathRule.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/pathRule/pathRule.component.html",'<hr />\n<div class="form-group">\n <div class="col-md-4 sm-label-right">\n Paths\n <help-field key="gce.httpLoadBalancer.pathRule.paths"> </help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n multiple\n tagging\n tagging-label=""\n ng-model="$ctrl.pathRule.paths"\n sortable="true"\n class="form-control input-sm"\n required\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="path in []">\n {{ path }}\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deletePathRule()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n</div>\n\n<div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Backend Service</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.pathRule.backendService"\n on-select="$ctrl.command.onBackendServiceSelected($item, $ctrl.command)"\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="backendService in $ctrl.command.getAllBackendServices($ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Dn="spinnaker.deck.gce.httpLoadBalancer.hostRule.component";n(Dn,[Nn]).component("gceHostRule",{bindings:{hostRule:"=",index:"=",command:"=",deleteHostRule:"&"},templateUrl:"google/src/loadBalancer/configure/http/hostRule/hostRule.component.html",controller:function(){this.$onInit=()=>{this.loadBalancer=this.command.loadBalancer;const e=this.hostRule.pathMatcher.pathRules;this.addPathRule=()=>{e.push(new fn)},this.deletePathRule=n=>{e.splice(n,1)}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRule.component.html",'<hr class="host-rule" ng-if="$ctrl.index > 0" />\n<div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Host Patterns\n <help-field key="gce.httpLoadBalancer.hostRule.hostPattern"> </help-field>\n </div>\n <div class="col-md-4">\n <ui-select multiple tagging tagging-label="" ng-model="$ctrl.hostRule.hostPatterns" class="form-control input-sm">\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="hostPattern in []">\n {{ hostPattern }}\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteHostRule()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.hostRule.pathMatcher.defaultService"\n on-select="$ctrl.command.onBackendServiceSelected($item, $ctrl.command)"\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="backendService in $ctrl.command.getAllBackendServices($ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <gce-path-rule\n path-rule="pathRule"\n index="$index"\n command="$ctrl.command"\n delete-path-rule="$ctrl.deletePathRule($index)"\n ng-repeat="pathRule in $ctrl.hostRule.pathMatcher.pathRules"\n >\n </gce-path-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="$ctrl.addPathRule()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add path rule\n </button>\n </div>\n </div>\n</div>\n')}]);const Mn="spinnaker.deck.gce.httpLoadBalancer.write.service";e.module(Mn,[]).factory("gceHttpLoadBalancerWriter",(function(){return{upsertLoadBalancers:function(n,t,a){return n.forEach((n=>{e.extend(n,{type:"upsertLoadBalancer",cloudProvider:"gce",loadBalancerName:n.name})})),d.clearCache("backendServices"),d.clearCache("healthChecks"),T.executeTask({job:n,application:t,description:`${a} Load Balancer: ${n[0].urlMapName}`})},deleteLoadBalancers:function(n,t,a={}){const i={type:"deleteLoadBalancer",loadBalancerName:n.listeners[0].name,regions:[n.region||"global"],region:n.region||"global",loadBalancerType:n.loadBalancerType,cloudProvider:n.provider,credentials:n.account};return e.extend(i,a),d.clearCache("backendServices"),d.clearCache("healthChecks"),T.executeTask({job:[i],application:t,description:`Delete load balancer: ${n.urlMapName} in ${n.account}:global`})}}}));const En={bindings:{initialIpAddress:"<",addressList:"<",readOnly:"<",onAddressSelect:"&",account:"<"},template:'\n <ui-select on-select="$ctrl.onAddressSelect({address: $ctrl.selectedAddress})"\n ng-disabled="$ctrl.readOnly"\n ng-model="$ctrl.selectedAddress"\n class="form-control input-sm">\n <ui-select-match allow-clear>\n {{$ctrl.selectedAddress.address}} <span ng-if="$ctrl.selectedAddress.name">({{$ctrl.selectedAddress.name}})</span>\n </ui-select-match>\n <ui-select-choices repeat="address in $ctrl.addressList | filter: {name: $select.search, account: $ctrl.account}">\n <span>\n {{address.address}} <span ng-if="address.name">({{address.name}})</span> <br>\n </span>\n </ui-select-choices>\n </ui-select>',controller:class{$onInit(){this.selectedAddress=this.addressList.find((e=>e.address===this.initialIpAddress)),this.selectedAddress||(this.selectedAddress={address:this.initialIpAddress,account:this.account})}}},Rn="spinnaker.gce.addressSelector.component";n(Rn,[]).component("gceAddressSelector",En);const Ln="spinnaker.deck.gce.httpLoadBalancer.listener.component";n(Ln,[Rn]).component("gceListener",{bindings:{command:"=",listener:"=",deleteListener:"&",index:"=",application:"="},templateUrl:"google/src/loadBalancer/configure/http/listeners/listener.component.html",controller:function(){this.$onInit=()=>{this.certificates=this.command.backingData.certificates;const e=this.command.backingData.loadBalancerMap;this.getName=(e,n)=>{const t=[n,e.stack||"",e.detail||""].join("-");return oe.trimEnd(t,"-")},this.getCertificates=()=>this.command.backingData.certificates.filter((e=>e.account===this.command.loadBalancer.credentials)).map((e=>e.name)),this.getSubnets=()=>{const e=this.command.backingData.subnetMap[this.command.loadBalancer.network].filter((e=>e.region===this.command.loadBalancer.region)).map((e=>e.name));return oe.uniq(e)},this.getInternalAddresses=()=>this.command.backingData.addresses.filter((e=>"INTERNAL"===e.addressType&&e.subnetwork.split("/").pop()===this.listener.subnet)),this.updateName=(e,n)=>{e.name=this.getName(e,n)},this.localListenerHasSameName=()=>this.command.loadBalancer.listeners.filter((e=>e.name===this.listener.name)).length>1,this.existingListenerNames=()=>oe.get(e,[this.command.loadBalancer.credentials,"listeners"]),this.isHttps=e=>443===e||"443"===e,this.listener.name||this.updateName(this.listener,this.application.name),this.onAddressSelect=e=>{this.listener.ipAddress=e?e.address:null}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listener.component.html",'<hr ng-if="$ctrl.index > 0" />\n<div class="container-fluid form-horizontal">\n <ng-form name="listener">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-show="listener.stackName.$dirty || listener.detailName.$dirty || !$ctrl.listener.created"\n ng-class="{\n \'alert-danger\': listener.listenerName.$error.validateUnique,\n \'alert-info\': !listener.listenerName.$error.validateUnique\n }"\n >\n <strong>Your listener will be named:</strong>\n <span>{{ $ctrl.getName($ctrl.listener, $ctrl.application.name) }}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="$ctrl.listener.name"\n ng-readonly="$ctrl.listener.created"\n validate-unique="$ctrl.existingListenerNames()"\n validate-ignore-case="true"\n name="listenerName"\n />\n <validation-error\n ng-if="listener.listenerName.$error.validateUnique || $ctrl.localListenerHasSameName()"\n message="There is already a listener in {{ $ctrl.command.loadBalancer.credentials }} with that name."\n >\n </validation-error>\n </div>\n\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.listener.stack"\n ng-readonly="$ctrl.listener.created"\n name="stackName"\n ng-change="$ctrl.updateName($ctrl.listener, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail <help-field key="gce.loadBalancer.detail"></help-field></div>\n\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.listener.detail"\n ng-readonly="$ctrl.listener.created"\n name="detailName"\n ng-change="$ctrl.updateName($ctrl.listener, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteListener()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'">\n <div class="col-md-2 sm-label-right">Subnet</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="$ctrl.listener.subnet"\n ng-disabled="$ctrl.listener.created"\n ng-required="true"\n >\n <option\n ng-repeat="subnet in $ctrl.getSubnets()"\n value="{{ subnet }}"\n ng-selected="$ctrl.listener.subnet === subnet"\n >\n {{ subnet }}\n </option>\n </select>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'">\n <div class="col-md-2 sm-label-right">Internal IP</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="$ctrl.listener.ipAddress"\n ng-disabled="$ctrl.listener.created"\n ng-required="true"\n >\n <option\n ng-repeat="address in $ctrl.getInternalAddresses()"\n value="{{ address.address }}"\n ng-selected="$ctrl.listener.ipAddress === address.address"\n >\n {{ address.name }}\n </option>\n </select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Port<help-field key="gce.httpLoadBalancer.port"></help-field></div>\n <div class="col-md-3">\n <ui-select\n ng-model="$ctrl.listener.port"\n ng-disabled="$ctrl.listener.created"\n required\n class="form-control input-sm"\n >\n <ui-select-match>\n <span>{{ $select.selected }}</span>\n </ui-select-match>\n <ui-select-choices repeat="choice as choice in [8080, 80, 443]">\n <span>{{ choice }}</span>\n </ui-select-choices>\n </ui-select>\n </div>\n <div ng-if="$ctrl.isHttps($ctrl.listener.port)">\n <div class="col-md-2 sm-label-right">\n Certificate\n <help-field key="gce.httpLoadBalancer.certificate" class="help-field-absolute"></help-field>\n </div>\n <div class="col-md-3">\n <ui-select\n ng-model="$ctrl.listener.certificate"\n ng-disabled="$ctrl.listener.created && !$ctrl.listener.certificate"\n required\n class="form-control input-sm"\n >\n <ui-select-match allow-clear placeholder="Select...">{{ $select.selected }}</ui-select-match>\n <ui-select-choices repeat="certificate in $ctrl.getCertificates() | filter: $select.search">\n <span ng-bind-html="certificate | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-1" style="padding-left: 0; margin-top: 4px">\n <gce-cache-refresh\n cache-key="certificates"\n render-compact="true"\n on-refresh="$ctrl.command.onCertificateRefresh($ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'HTTP\'">\n <div class="col-md-2 sm-label-right">\n External IP\n <help-field key="gce.httpLoadBalancer.externalIP" class="help-field-absolute"></help-field>\n </div>\n <div class="col-md-5">\n <gce-address-selector\n address-list="$ctrl.command.backingData.addresses"\n read-only="$ctrl.listener.created"\n account="$ctrl.command.loadBalancer.credentials"\n initial-ip-address="$ctrl.listener.ipAddress"\n on-address-select="$ctrl.onAddressSelect(address)"\n ></gce-address-selector>\n </div>\n <div class="col-md-1" style="padding-left: 0; margin-top: 4px">\n <gce-cache-refresh\n ng-if="!$ctrl.listener.created"\n cache-key="addresses"\n render-compact="true"\n on-refresh="$ctrl.command.onAddressRefresh($ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-7 col-md-offset-2" ng-if="listener.stackName.$error.pattern">\n <validation-error message="Stack can only contain lowercase letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-2" ng-if="listener.detailName.$error.pattern">\n <validation-error\n message="Detail can only contain lowercase letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]);Le(".gce-http-lb input[type='checkbox'] {\n margin: 10px 0 0 5px;\n position: absolute;\n}\n.gce-http-lb help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.gce-http-lb .ui-select-multiple[tagging] {\n width: 220px;\n}\n.gce-http-lb gce-host-rule hr.host-rule {\n border-top: 1px solid var(--color-concrete);\n}\n");const Hn="spinnaker.deck.gce.loadBalancer.createHttp.controller";n(Hn,[Ie,xe,yn,kn,Cn,Pn,In,zn,Dn,Mn,Ln,An]).controller("gceCreateHttpLoadBalancerCtrl",["$scope","$uibModal","$uibModalInstance","application","loadBalancer","isNew","gceHttpLoadBalancerWriter","$state","wizardSubFormValidation","gceHttpLoadBalancerCommandBuilder","gceHttpLoadBalancerTransformer",function(e,n,t,a,i,l,c,r,o,s,d){this.application=a,this.isNew=l,this.modalDescriptor=this.isNew?"Create HTTP(S) load balancer":`Edit ${i.name}:global:${i.account}`,this.pages={location:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",listeners:"google/src/loadBalancer/configure/http/listeners/listeners.html",defaultService:"google/src/loadBalancer/configure/http/defaultService/defaultService.html",backendServices:"google/src/loadBalancer/configure/http/backendService/backendServices.html",healthChecks:"google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",hostRules:"google/src/loadBalancer/configure/http/hostRule/hostRules.html"};const u={backendServices:pn,healthChecks:mn,hostRules:vn,listeners:bn};this.add=e=>{this.command.loadBalancer[e].push(new u[e])},this.remove=(e,n)=>{this.command.loadBalancer[e].splice(n,1)};const g=()=>{if(e.$$destroyed)return;t.close();const n=this.command.loadBalancer,a={name:n.urlMapName,accountId:n.credentials,region:n.region,provider:"gce"};r.includes("**.loadBalancerDetails")?r.go("^.loadBalancerDetails",a):r.go(".loadBalancerDetails",a)};e.taskMonitor=this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:t,onTaskComplete:()=>{a.loadBalancers.refresh(),a.loadBalancers.onNextRefresh(e,g)}}),this.submit=()=>{const e=d.serialize(this.command,i),n=this.isNew?"Create":"Update";this.taskMonitor.submit((()=>c.upsertLoadBalancers(e,a,n)))},s.buildCommand({isNew:l,originalLoadBalancer:i}).then((n=>{this.command=n,o.config({scope:e,form:"form"}).register({page:"location",subForm:"location"}).register({page:"listeners",subForm:"listeners",validators:[{watchString:"ctrl.command.loadBalancer.listeners",validator:e=>e.length>0,collection:!0}]}).register({page:"default-service",subForm:"defaultService"}).register({page:"health-checks",subForm:"healthChecks"}).register({page:"backend-services",subForm:"backendServices"}).register({page:"host-rules",subForm:"hostRules"})})),this.cancel=t.dismiss}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",'<ng-form name="location">\n <gce-http-load-balancer-basic-settings\n application="ctrl.application"\n command="ctrl.command"\n ></gce-http-load-balancer-basic-settings>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listeners.html",'<ng-form name="listeners">\n <gce-listener\n command="ctrl.command"\n delete-listener="ctrl.remove(\'listeners\', $index)"\n index="$index"\n application="ctrl.application"\n listener="listener"\n ng-repeat="listener in ctrl.command.loadBalancer.listeners"\n ></gce-listener>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'listeners\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add listener\n </button>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/defaultService/defaultService.html",'<ng-form name="defaultService">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b><help-field key="gce.httpLoadBalancer.defaultService"></help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="ctrl.command.loadBalancer.defaultService"\n required\n on-select="ctrl.command.onBackendServiceSelected($item, ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select..."> {{$select.selected}} </ui-select-match>\n <ui-select-choices\n repeat="backendService in ctrl.command.getAllBackendServices(ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendServices.html",'<ng-form name="backendServices">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedBackendServices(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following backend services have not been associated with a host or path rule, or set as this load balancer\'s\n default service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedBackendServices(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedBackendServices(ctrl.command)"\n >Remove backend services</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.backendServices.length && !ctrl.command.backingData.backendServices.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any backend services.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-backend-service-selector\n ng-repeat="backendService in ctrl.command.loadBalancer.backendServices"\n backend-service="backendService"\n index="$index"\n command="ctrl.command"\n delete-service="ctrl.remove(\'backendServices\', $index)"\n >\n </gce-http-load-balancer-backend-service-selector>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'backendServices\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add backend service\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="backendServices"\n cache-key-alias="backend services"\n on-refresh="ctrl.command.onBackendServiceRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",'<ng-form name="healthChecks">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedHealthChecks(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following health checks have not been associated with a backend service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedHealthChecks(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedHealthChecks(ctrl.command)"\n >Remove health checks</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.healthChecks.length && !ctrl.command.backingData.healthChecks.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any health checks.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-health-check\n ng-repeat="healthCheck in ctrl.command.loadBalancer.healthChecks"\n index="$index"\n command="ctrl.command"\n delete-health-check="ctrl.remove(\'healthChecks\', $index)"\n health-check="healthCheck"\n ></gce-http-load-balancer-health-check>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'healthChecks\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add health check\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.command.onHealthCheckRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRules.html",'<ng-form name="hostRules">\n <gce-host-rule\n ng-repeat="hostRule in ctrl.command.loadBalancer.hostRules"\n host-rule="hostRule"\n index="$index"\n command="ctrl.command"\n delete-host-rule="ctrl.remove(\'hostRules\', $index)"\n ></gce-host-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'hostRules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add host rule\n </button>\n </div>\n </div>\n</ng-form>\n')}]);class Fn{constructor(e,n,t,a){this.$scope=e,this.application=n,this.$uibModalInstance=t,this.$state=a}onTaskComplete(e){d.clearCache("healthChecks"),this.application.getDataSource("loadBalancers").refresh(),this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope,(()=>this.onApplicationRefresh(e)))}cancel(){this.$uibModalInstance.dismiss()}getName(e,n){const t=[n.name,e.stack||"",e.detail||""].join("-");return ve(t,"-")}onApplicationRefresh(e){if(this.$scope.$$destroyed)return;this.$uibModalInstance.close();const n={name:e.loadBalancerName,accountId:e.credentials,region:e.region,provider:"gce"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",n):this.$state.go(".loadBalancerDetails",n)}}Fn.$inject=["$scope","application","$uibModalInstance","$state"];class Un{constructor(e,n,t,a){this.$q=e,this.loadBalancerReader=n,this.gceHealthCheckReader=t,this.gceCertificateReader=a,this.dataFetchers={existingLoadBalancerNamesByAccount:()=>this.loadBalancerReader.listLoadBalancers("gce").then((e=>oe.chain(e).map("accounts").flatten().groupBy("name").mapValues((e=>oe.chain(e).map("regions").flatten().map("loadBalancers").flatten().map("name").value())).value())),accounts:()=>y.listAccounts("gce"),networks:()=>B.listNetworksByProvider("gce"),subnets:()=>C.listSubnetsByProvider("gce"),healthChecks:()=>this.gceHealthCheckReader.listHealthChecks(),certificates:()=>this.gceCertificateReader.listCertificates()}}getBackingData(e){const n=e.reduce(((e,n)=>(this.dataFetchers[n]&&(e[n]=this.dataFetchers[n]()),e)),{});return this.$q.all(n)}groupHealthChecksByAccountAndType(e){return oe.chain(e).groupBy("account").mapValues((e=>oe.groupBy(e,"healthCheckType"))).value()}groupHealthCheckNamesByAccount(e,n){return oe.chain(e).groupBy("account").mapValues((e=>oe.chain(e).map("name").difference(n).value())).value()}}Un.$inject=["$q","loadBalancerReader","gceHealthCheckReader","gceCertificateReader"];const On="spinnaker.gce.commonLoadBalancerCommandBuilder.service";n(On,[w,Bn,je]).service("gceCommonLoadBalancerCommandBuilder",Un);class qn{constructor(e){this.$scope=e,this.editExisting=!1,this.max=Number.MAX_SAFE_INTEGER}$onInit(){this.healthCheck.name?(this.healthCheckPlaceholder=this.healthCheck,this.existingHealthChecksForProtocol=this.healthChecksByAccountAndType[this.credentials][this.healthCheck.healthCheckType],this.editExisting=!0):this.existingHealthChecksForProtocol=this.healthChecksByAccountAndType[this.credentials].TCP,this.$scope.$watch("$ctrl.credentials",(()=>this.setExistingHealthChecksForProtocol()))}onProtocolChange(){delete this.healthCheck.name,delete this.healthCheckPlaceholder,this.setExistingHealthChecksForProtocol()}setExistingHealthChecksForProtocol(){this.existingHealthChecksForProtocol=fe(this,["healthChecksByAccountAndType",this.credentials,this.healthCheck.healthCheckType])||[],this.existingHealthChecksForProtocol.find((e=>e.name===this.healthCheck.name))||(delete this.healthCheck.name,delete this.healthCheckPlaceholder)}toggleEditExisting(){this.editExisting=!this.editExisting,delete this.healthCheck.name,delete this.healthCheckPlaceholder}onHealthCheckSelect(e){Object.assign(this.healthCheck,e)}onHealthCheckNameChange(e){this.healthCheck.name=e}}qn.$inject=["$scope"];const _n={bindings:{healthCheck:"=",credentials:"<",healthChecksByAccountAndType:"<",existingHealthCheckNames:"<"},templateUrl:"google/src/loadBalancer/configure/common/healthCheck.component.html",controller:qn},Vn="spinnaker.gce.healthCheckSelector.component";n(Vn,[]).component("gceHealthCheckSelector",_n),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/healthCheck.component.html",'<ng-form name="healthCheckForm" novalidate>\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="healthCheckForm.healthCheckName.$error.validateUnique">\n <validation-error message="There is already a health check with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="healthCheckForm.healthCheckName.$error.pattern || healthCheckForm.healthCheckName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters,\n numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Protocol</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-change="$ctrl.onProtocolChange()"\n ng-options="protocol as protocol for protocol in [\'HTTP\', \'HTTPS\', \'TCP\', \'SSL\', \'HTTP2\', \'GRPC\']"\n ng-model="$ctrl.healthCheck.healthCheckType"\n ></select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.healthCheckPlaceholder"\n on-select="$ctrl.onHealthCheckSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="healthCheck in $ctrl.existingHealthChecksForProtocol | filter: {name: $select.search}"\n >\n <div ng-bind-html="healthCheck.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n novalidate\n type="text"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n name="healthCheckName"\n max="63"\n required\n validate-unique="$ctrl.existingHealthCheckNames"\n ng-model-options="{ debounce: 250 }"\n ng-change="$ctrl.onHealthCheckNameChange($ctrl.healthCheckPlaceholder.name)"\n ng-model="$ctrl.healthCheckPlaceholder.name"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing health checks</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div\n class="form-group"\n ng-if="\n $ctrl.healthCheck.healthCheckType === \'HTTP\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTPS\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTP2\'\n "\n >\n <div class="col-md-4 sm-label-right">Request Path</div>\n <div class="col-md-4">\n <input class="form-control input-sm" required ng-model="$ctrl.healthCheck.requestPath" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n required\n min="1"\n max="65535"\n ng-model="$ctrl.healthCheck.port"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.healthCheck.healthCheckType === \'GRPC\'">\n <div class="col-md-4 sm-label-right">Service Name</div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n ng-model="$ctrl.healthCheck.grpcServiceName"\n placeholder="com.example.HealthService"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.timeoutSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.checkIntervalSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.healthyThreshold"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.unhealthyThreshold"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);class jn{constructor(e){this.sessionAffinity=e}}class Kn{constructor(e){this.region=e,this.ipProtocol="TCP",this.loadBalancerType="INTERNAL",this.network="default",this.cloudProvider="gce",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class Wn extends Fn{constructor(e,n,t,a,i,l,c,r,o){super(e,n,t,o),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.wizardSubFormValidation=c,this.gceXpnNamingService=r,this.pages={location:"google/src/loadBalancer/configure/internal/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/internal/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Client IP and protocol":"CLIENT_IP_PROTO","Client IP, port and protocol":"CLIENT_IP_PORT_PROTO"},this.viewState=new jn("None"),this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","networks","subnets","healthChecks"]).then((e=>{this.isNew?this.loadBalancer=new Kn(Oe?Oe.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map((e=>e.name));n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.networks=e.networks,this.subnets=e.subnets,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})}))}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then((e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)}))}networkUpdated(){this.subnetOptions=this.subnets.filter((e=>e.region===this.loadBalancer.region&&(e.account===this.loadBalancer.credentials||e.account===this.loadBalancer.account)&&e.network===this.loadBalancer.network)).map((e=>e.id)),this.subnetOptions.includes(this.loadBalancer.subnet)||(this.loadBalancer.subnet=this.subnetOptions[0])}protocolUpdated(){"UDP"===this.loadBalancer.ipProtocol&&(this.viewState=new jn("None"),this.loadBalancer.backendService.sessionAffinity="NONE")}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],this.networkOptions=this.networks.filter((e=>e.account===this.loadBalancer.credentials)).map((e=>e.id)),y.getRegionsForAccount(this.loadBalancer.credentials).then((e=>{this.regions=e.map((e=>e.name)),this.networkUpdated()}))}regionUpdated(){this.networkUpdated()}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);n.ports=n.ports.split(",").map((e=>e.trim())),n.cloudProvider="gce",n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,delete n.instances,this.taskMonitor.submit((()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}})))}initializeEditMode(){this.loadBalancer.ports=this.loadBalancer.ports.join(", "),this.loadBalancer.subnet=this.gceXpnNamingService.decorateXpnResourceIfNecessary(this.loadBalancer.project,this.loadBalancer.subnet),this.loadBalancer.network=this.gceXpnNamingService.decorateXpnResourceIfNecessary(this.loadBalancer.project,this.loadBalancer.network),this.viewState=new jn(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}Wn.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","wizardSubFormValidation","gceXpnNamingService","$state"];const Zn="spinnaker.gce.internalLoadBalancer.controller";n(Zn,[Vn,On,We]).controller("gceInternalLoadBalancerCtrl",Wn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/internal/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="2"\n field-columns="8"\n component="ctrl.loadBalancer"\n field="region"\n account="ctrl.loadBalancer.credentials"\n on-change="ctrl.regionUpdated()"\n regions="ctrl.regions"\n ></gce-region-select-field>\n\n <gce-network-select-field\n networks="ctrl.networkOptions"\n component="ctrl.loadBalancer"\n on-change="ctrl.networkUpdated()"\n field="network"\n account="ctrl.loadBalancer.credentials"\n field-columns="8"\n label-columns="2"\n ></gce-network-select-field>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/internal/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Subnet</th>\n <th>Ports<help-field key="gce.internalLoadBalancer.ports"></help-field></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <select\n ng-disabled="!ctrl.isNew"\n class="form-control input-sm"\n ng-change="ctrl.protocolUpdated()"\n ng-model="ctrl.loadBalancer.ipProtocol"\n ng-options="protocol for protocol in [\'TCP\', \'UDP\']"\n ></select>\n </td>\n\n <td>\n <select\n class="form-control input-sm"\n ng-options="subnet for subnet in ctrl.subnetOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.subnet"\n ></select>\n </td>\n\n <td><input class="form-control input-sm" required ng-model="ctrl.loadBalancer.ports" /></td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const Xn="spinnaker.deck.gce.loadBalancer.createInternalHttp.controller";class Yn{constructor(e,n,t,a,i,l,c,r,o,s){this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceHttpLoadBalancerCommandBuilder=i,this.isNew=l,this.wizardSubFormValidation=c,this.gceHttpLoadBalancerTransformer=r,this.gceHttpLoadBalancerWriter=o,this.$state=s,this.pages={location:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",listeners:"google/src/loadBalancer/configure/http/listeners/listeners.html",defaultService:"google/src/loadBalancer/configure/http/defaultService/defaultService.html",backendServices:"google/src/loadBalancer/configure/http/backendService/backendServices.html",healthChecks:"google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",hostRules:"google/src/loadBalancer/configure/http/hostRule/hostRules.html"},this.keyToTemplateMap={backendServices:pn,healthChecks:mn,hostRules:vn,listeners:bn},this.modalDescriptor=this.isNew?"Create Internal HTTP(S) load balancer":`Edit ${this.loadBalancer.name}:${this.loadBalancer.region}:${this.loadBalancer.account}`;e.taskMonitor=this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>{n.loadBalancers.refresh(),n.loadBalancers.onNextRefresh(e,this.onApplicationRefresh.bind(this))}})}$onInit(){this.gceHttpLoadBalancerCommandBuilder.buildCommand({isNew:this.isNew,originalLoadBalancer:this.loadBalancer,isInternal:!0}).then((e=>{this.command=e,this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"location"}).register({page:"listeners",subForm:"listeners",validators:[{watchString:"ctrl.command.loadBalancer.listeners",validator:e=>e.length>0,collection:!0}]}).register({page:"default-service",subForm:"defaultService"}).register({page:"health-checks",subForm:"healthChecks"}).register({page:"backend-services",subForm:"backendServices"}).register({page:"host-rules",subForm:"hostRules"})}))}add(e){this.command.loadBalancer[e].push(new this.keyToTemplateMap[e])}remove(e,n){this.command.loadBalancer[e].splice(n,1)}cancel(){this.$uibModalInstance.dismiss()}submit(){const e=this.gceHttpLoadBalancerTransformer.serialize(this.command,this.loadBalancer),n=this.isNew?"Create":"Update";this.taskMonitor.submit((()=>this.gceHttpLoadBalancerWriter.upsertLoadBalancers(e,this.application,n)))}onApplicationRefresh(){if(this.$scope.$$destroyed)return;this.$uibModalInstance.close();const e=this.command.loadBalancer,n={name:e.urlMapName,accountId:e.credentials,region:e.region,provider:"gce"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",n):this.$state.go(".loadBalancerDetails",n)}}Yn.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceHttpLoadBalancerCommandBuilder","isNew","wizardSubFormValidation","gceHttpLoadBalancerTransformer","gceHttpLoadBalancerWriter","$state"],n(Xn,["ui.bootstrap",xe,yn,kn,Cn,Pn,In,zn,Dn,Mn,Ln,An]).controller("gceCreateInternalHttpLoadBalancerCtrl",Yn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",'<ng-form name="location">\n <gce-http-load-balancer-basic-settings\n application="ctrl.application"\n command="ctrl.command"\n ></gce-http-load-balancer-basic-settings>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listeners.html",'<ng-form name="listeners">\n <gce-listener\n command="ctrl.command"\n delete-listener="ctrl.remove(\'listeners\', $index)"\n index="$index"\n application="ctrl.application"\n listener="listener"\n ng-repeat="listener in ctrl.command.loadBalancer.listeners"\n ></gce-listener>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'listeners\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add listener\n </button>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/defaultService/defaultService.html",'<ng-form name="defaultService">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b><help-field key="gce.httpLoadBalancer.defaultService"></help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="ctrl.command.loadBalancer.defaultService"\n required\n on-select="ctrl.command.onBackendServiceSelected($item, ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select..."> {{$select.selected}} </ui-select-match>\n <ui-select-choices\n repeat="backendService in ctrl.command.getAllBackendServices(ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendServices.html",'<ng-form name="backendServices">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedBackendServices(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following backend services have not been associated with a host or path rule, or set as this load balancer\'s\n default service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedBackendServices(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedBackendServices(ctrl.command)"\n >Remove backend services</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.backendServices.length && !ctrl.command.backingData.backendServices.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any backend services.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-backend-service-selector\n ng-repeat="backendService in ctrl.command.loadBalancer.backendServices"\n backend-service="backendService"\n index="$index"\n command="ctrl.command"\n delete-service="ctrl.remove(\'backendServices\', $index)"\n >\n </gce-http-load-balancer-backend-service-selector>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'backendServices\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add backend service\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="backendServices"\n cache-key-alias="backend services"\n on-refresh="ctrl.command.onBackendServiceRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",'<ng-form name="healthChecks">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedHealthChecks(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following health checks have not been associated with a backend service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedHealthChecks(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedHealthChecks(ctrl.command)"\n >Remove health checks</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.healthChecks.length && !ctrl.command.backingData.healthChecks.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any health checks.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-health-check\n ng-repeat="healthCheck in ctrl.command.loadBalancer.healthChecks"\n index="$index"\n command="ctrl.command"\n delete-health-check="ctrl.remove(\'healthChecks\', $index)"\n health-check="healthCheck"\n ></gce-http-load-balancer-health-check>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'healthChecks\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add health check\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.command.onHealthCheckRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRules.html",'<ng-form name="hostRules">\n <gce-host-rule\n ng-repeat="hostRule in ctrl.command.loadBalancer.hostRules"\n host-rule="hostRule"\n index="$index"\n command="ctrl.command"\n delete-host-rule="ctrl.remove(\'hostRules\', $index)"\n ></gce-host-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'hostRules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add host rule\n </button>\n </div>\n </div>\n</ng-form>\n')}]);const Jn="spinnaker.google.regionSelectField.directive";n(Jn,[]).directive("gceRegionSelectField",(function(){return{restrict:"E",templateUrl:"google/src/regionSelectField.directive.html",scope:{regions:"=",component:"=",field:"@",account:"=",onChange:"&",labelColumns:"@",readOnly:"=",fieldColumns:"@"}}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/regionSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">Region</div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!account">(Select an account)</div>\n <div class="col-md-{{fieldColumns || 7}}">\n <select\n class="form-control input-sm"\n ng-if="account && !readOnly"\n ng-model="component.region"\n ng-change="onChange()"\n required\n >\n <option ng-repeat="region in regions" value="{{region}}" ng-selected="component[field] === region">\n {{region}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const Qn="spinnaker.gce.loadBalancer.transformer";n(Qn,[]).factory("gceLoadBalancerTransformer",["$q",function(e){function n(e){const n=e.instances,t=e.serverGroups||[e];e.instanceCounts={up:n.filter((e=>"InService"===e.health[0].state)).length,down:n.filter((e=>"OutOfService"===e.health[0].state)).length,outOfService:t.reduce(((e,n)=>n.instances.filter((e=>"OutOfService"===e.healthState)).length+e),0)}}return{normalizeLoadBalancer:function(t){t.serverGroups.forEach((function(e){e.account=t.account,e.detachedInstances?(e.detachedInstances=e.detachedInstances.map((function(e){return{id:e}})),e.instances=e.instances.concat(e.detachedInstances)):e.detachedInstances=[],e.instances.forEach((function(e){!function(e,n){e.health=e.health||{},e.provider=n.type,e.account=n.account,e.region=n.region,e.health.type="LoadBalancer",e.healthState=e.health.state?"InService"===e.health.state?"Up":"Down":"OutOfService",e.health=[e.health],e.loadBalancers=[n.name]}(e,t)})),n(e)}));const a=oe.filter(t.serverGroups,{isDisabled:!1});return t.provider=t.type,t.instances=oe.chain(a).map("instances").flatten().value(),t.detachedInstances=oe.chain(a).map("detachedInstances").flatten().value(),oe.get(t,"backendService.healthCheck")&&(t.backendService.healthCheck.timeout=t.backendService.healthCheck.timeoutSec,t.backendService.healthCheck.interval=t.backendService.healthCheck.checkIntervalSec),n(t),e.when(t)},convertLoadBalancerForEditing:function(e){const n={provider:"gce",region:e.region,credentials:e.account,listeners:[],name:e.name,regionZones:e.availabilityZones};if(e.elb){const t=e.elb;if(n.vpcId=t.vpcid,t.listenerDescriptions&&(n.listeners=t.listenerDescriptions.map((function(e){const n=e.listener;return{protocol:n.protocol,portRange:n.loadBalancerPort,healthCheck:void 0!==t.healthCheck}}))),t.healthCheck&&t.healthCheck.target){n.healthTimeout=t.healthCheck.timeout,n.healthInterval=t.healthCheck.interval,n.healthyThreshold=t.healthCheck.healthyThreshold,n.unhealthyThreshold=t.healthCheck.unhealthyThreshold;const a=e.elb.healthCheck.target,i=a.indexOf(":"),l=a.indexOf("/");-1!==i&&-1!==l&&(n.healthCheckProtocol=a.substring(0,i),n.healthCheckPort=a.substring(i+1,l),n.healthCheckPath=a.substring(l),isNaN(n.healthCheckPort)||(n.healthCheckPort=Number(n.healthCheckPort)))}else n.healthCheckProtocol="HTTP",n.healthCheckPort=80,n.healthCheckPath="/",n.healthTimeout=5,n.healthInterval=10,n.healthyThreshold=10,n.unhealthyThreshold=2;n.sessionAffinity=e.sessionAffinity||"None"}return n},constructNewLoadBalancerTemplate:function(){return{provider:"gce",stack:"",detail:"",credentials:Oe.defaults.account,region:Oe.defaults.region,healthCheckProtocol:"HTTP",healthCheckPort:80,healthCheckPath:"/",healthTimeout:5,healthInterval:10,healthyThreshold:10,unhealthyThreshold:2,sessionAffinity:"NONE",regionZones:[],listeners:[{protocol:"TCP",portRange:"8080",healthCheck:!0}]}}}}]);const et="spinnaker.loadBalancer.gce.create.controller";n(et,[xe,Qn,Jn]).controller("gceCreateLoadBalancerCtrl",["$scope","$uibModalInstance","$state","gceLoadBalancerTransformer","application","loadBalancer","isNew",function(e,n,t,a,i,l,c){const r=this;function o(){if(e.$$destroyed)return;n.close();const a={name:e.loadBalancer.name,accountId:e.loadBalancer.credentials,region:e.loadBalancer.region,provider:"gce"};t.includes("**.loadBalancerDetails")?t.go("^.loadBalancerDetails",a):t.go(".loadBalancerDetails",a)}function s(){const n=e.loadBalancer.credentials,t={};i.getDataSource("loadBalancers").refresh(!0).then((()=>{i.getDataSource("loadBalancers").data.forEach((e=>{e.account===n&&(t[e.region]=t[e.region]||[],t[e.region].push(e.name))})),e.existingLoadBalancerNames=_.flatten(_.map(t))}))}e.isNew=c,e.pages={location:"google/src/loadBalancer/configure/network/createLoadBalancerProperties.html",listeners:"google/src/loadBalancer/configure/network/listeners.html",healthCheck:"google/src/loadBalancer/configure/network/healthCheck.html",advancedSettings:"google/src/loadBalancer/configure/network/advancedSettings.html"},e.state={accountsLoaded:!1,submitting:!1},e.sessionAffinityOptions=[{value:"NONE",name:"None"},{value:"CLIENT_IP",name:"Client IP"},{value:"CLIENT_IP_PROTO",name:"Client IP and protocol"}],e.taskMonitor=new G({application:i,title:(c?"Creating ":"Updating ")+"your load balancer",modalInstance:n,onTaskComplete:function(){i.loadBalancers.refresh(),i.loadBalancers.onNextRefresh(e,o)}}),l?e.loadBalancer=a.convertLoadBalancerForEditing(l):(e.loadBalancer=a.constructNewLoadBalancerTemplate(),s(),y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accountsLoaded=!0;const t=_.map(e.accounts,"name");t.length&&!t.includes(e.loadBalancer.credentials)&&(e.loadBalancer.credentials=t[0]),r.accountUpdated()}))),this.requiresHealthCheckPath=function(){return e.loadBalancer.healthCheckProtocol&&0===e.loadBalancer.healthCheckProtocol.indexOf("HTTP")},this.prependForwardSlash=e=>e&&0!==e.indexOf("/")?`/${e}`:e,this.updateName=function(){e.loadBalancer.name=this.getName()},this.getName=function(){const n=e.loadBalancer,t=[i.name,n.stack||"",n.detail||""].join("-");return _.trimEnd(t,"-")},this.accountUpdated=function(){y.getRegionsForAccount(e.loadBalancer.credentials).then((function(n){_.isArray(n)?e.regions=_.map(n,"name"):e.regions=_.keys(n),r.regionUpdated()}))},this.regionUpdated=function(){s(),r.updateName()},this.setVisibilityHealthCheckTab=function(){const n=P;e.loadBalancer.listeners[0].healthCheck?(n.includePage("Health Check"),n.markIncomplete("Health Check"),n.includePage("Advanced Settings"),n.markIncomplete("Advanced Settings")):(n.excludePage("Health Check"),n.markComplete("Health Check"),n.excludePage("Advanced Settings"),n.markComplete("Advanced Settings"),n.markComplete("Listener"))},this.submit=function(){const n=c?"Create":"Update";e.taskMonitor.submit((function(){const t={cloudProvider:"gce",loadBalancerName:e.loadBalancer.name};if(e.loadBalancer.listeners&&e.loadBalancer.listeners.length>0){const n=e.loadBalancer.listeners[0];n.protocol&&(t.ipProtocol=n.protocol),n.portRange&&(t.portRange=n.portRange),n.healthCheck?t.healthCheck={port:e.loadBalancer.healthCheckPort,requestPath:e.loadBalancer.healthCheckPath,timeoutSec:e.loadBalancer.healthTimeout,checkIntervalSec:e.loadBalancer.healthInterval,healthyThreshold:e.loadBalancer.healthyThreshold,unhealthyThreshold:e.loadBalancer.unhealthyThreshold}:t.healthCheck=null,t.sessionAffinity=e.loadBalancer.sessionAffinity}return A.upsertLoadBalancer(e.loadBalancer,i,n,t)}))},this.cancel=function(){n.dismiss()}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <div ng-if="!state.accountsLoaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="modal-body" ng-if="state.accountsLoaded">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.loadBalancerName.$error.validateUnique, \'alert-info\': !form.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName()}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="loadBalancer.name"\n validate-unique="existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="form.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{loadBalancer.credentials}} with that name."\n ></validation-error>\n </div>\n </div>\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 component="loadBalancer"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="3"\n component="loadBalancer"\n field="region"\n account="loadBalancer.credentials"\n on-change="ctrl.regionUpdated()"\n regions="regions"\n ></gce-region-select-field>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-zA-Z0-9]*$/"\n />\n </div>\n <div class="col-md-6 form-inline">\n <label class="sm-label-right"> Detail <help-field key="gce.loadBalancer.detail"></help-field> </label>\n <input\n type="text"\n class="form-control input-sm"\n ng-model="loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-zA-Z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="form.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="form.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="form.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/listeners.html",'<div class="container-fluid form-horizontal">\n <form name="listenerForm">\n <div class="form-group">\n <div class="alert alert-warning" ng-if="listenerForm.protocolSelect.$dirty || listenerForm.portRangeInput.$dirty">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n Changing your load balancer\'s protocol or port range will cause the forwarding rule to be destroyed and\n recreated. This will most likely cause the IP address of this load balancer to change. If you want to preserve\n the IP for this load balancer, you need to reserve it and manipulate the forwarding rule in Google Cloud\n Console.\n </p>\n </div>\n <div class="col-md-3 sm-label-right table-offset">Listener</div>\n <div class="col-md-9">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>\n Port Range\n <help-field key="gce.loadBalancer.portRange"></help-field>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="listener in loadBalancer.listeners">\n <td>\n <select\n class="form-control input-sm"\n ng-model="listener.protocol"\n ng-options="protocol for protocol in [\'TCP\', \'UDP\']"\n name="protocolSelect"\n ></select>\n </td>\n <td><input class="form-control input-sm" ng-model="listener.portRange" name="portRangeInput" /></td>\n </tr>\n <tr>\n <td>\n <label\n ><input\n type="checkbox"\n ng-model="loadBalancer.listeners[0].healthCheck"\n ng-change="ctrl.setVisibilityHealthCheckTab()"\n />\n Enable health check?\n <help-field key="gce.loadBalancer.healthCheck"></help-field>\n </label>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/healthCheck.html",'<div class="container-fluid form-horizontal">\n <div class="col-md-4 sm-label-right">Ping</div>\n <div class="col-md-8">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th width="30%">Port</th>\n <th><span ng-if="ctrl.requiresHealthCheckPath()">Path</span></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n ng-model="loadBalancer.healthCheckPort"\n required\n min="0"\n />\n </td>\n <td>\n <input\n ng-if="ctrl.requiresHealthCheckPath()"\n class="form-control input-sm"\n type="text"\n ng-change="loadBalancer.healthCheckPath = ctrl.prependForwardSlash(loadBalancer.healthCheckPath)"\n ng-model="loadBalancer.healthCheckPath"\n required\n />\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/advancedSettings.html",'<div class="form-group">\n <div class="col-md-8 nest">\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthTimeout" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthInterval" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthyThreshold" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.unhealthyThreshold" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Session Affinity</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-model="loadBalancer.sessionAffinity"\n ng-options="option.value as option.name for option in sessionAffinityOptions"\n ng-disabled="!isNew"\n name="sessionAffinitySelect"\n ></select>\n </div>\n </div>\n </div>\n</div>\n')}]);class nt{constructor(e){this.sessionAffinity=e}}class tt{constructor(e="global"){this.region=e,this.portRange="443",this.ipProtocol="TCP",this.loadBalancerType="SSL",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class at extends Fn{constructor(e,n,t,a,i,l,c,r){super(e,n,t,r),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.wizardSubFormValidation=c,this.pages={location:"google/src/loadBalancer/configure/ssl/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/ssl/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",backendService:"google/src/loadBalancer/configure/ssl/backendService.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE"},this.portOptions=["25","43","110","143","195","443","465","587","700","993","995"],this.viewState=new nt("None"),this.maxCookieTtl=86400,this.hasBackendService=!0,this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","healthChecks","certificates"]).then((e=>{this.isNew?this.loadBalancer=new tt(Oe?Oe.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map((e=>e.name));n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.certificateWrappers=e.certificates,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})}))}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then((e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)}))}onCertificateRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["certificates"]).then((e=>{this.certificateWrappers=e.certificates}))}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],y.getRegionsForAccount(this.loadBalancer.credentials).then((e=>{this.regions=e.map((e=>e.name))}))}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,n.cloudProvider="gce",delete n.instances,this.taskMonitor.submit((()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}})))}initializeEditMode(){this.loadBalancer.portRange=this.loadBalancer.portRange.split("-")[0],this.loadBalancer.certificate=this.loadBalancer.certificate.split("/").pop(),this.viewState=new nt(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}at.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","wizardSubFormValidation","$state"];const it="spinnaker.gce.sslLoadBalancer.controller";n(it,[Vn,On]).controller("gceSslLoadBalancerCtrl",at),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Region</div>\n <div class="col-md-8">\n <input readonly class="form-control input-sm" ng-model="ctrl.loadBalancer.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-9 col-md-offset-2">\n <table class="table table-condensed packed" style="table-layout: fixed; width: 390px">\n <thead>\n <tr>\n <th>Certificate</th>\n <th>Port</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <ui-select ng-model="ctrl.loadBalancer.certificate" required class="form-control input-sm">\n <ui-select-match allow-clear placeholder="Select...">{{$select.selected.name}}</ui-select-match>\n <ui-select-choices\n repeat="certificateWrapper.name as certificateWrapper in ctrl.certificateWrappers | filter: {name: $select.search, account: ctrl.loadBalancer.credentials }"\n >\n <span ng-bind-html="certificateWrapper.name | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </td>\n <td>\n <select\n class="form-control input-sm"\n ng-options="port for port in ctrl.portOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.portRange"\n ></select>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </ng-form>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh cache-key="certificates" on-refresh="ctrl.onCertificateRefresh()"></gce-cache-refresh>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/backendService.html",'<div class="container-fluid form-horizontal">\n <ng-form name="portName">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Port Name\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="text"\n required\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.portName"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);class lt{constructor(e){this.sessionAffinity=e}}class ct{constructor(e="global"){this.region=e,this.portRange="443",this.ipProtocol="TCP",this.loadBalancerType="TCP",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class rt extends Fn{constructor(e,n,t,a,i,l,c,r){super(e,n,t,r),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.wizardSubFormValidation=c,this.pages={location:"google/src/loadBalancer/configure/tcp/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/tcp/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",backendService:"google/src/loadBalancer/configure/tcp/backendService.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE"},this.portOptions=["25","43","110","143","195","443","465","587","700","993","995"],this.viewState=new lt("None"),this.maxCookieTtl=86400,this.hasBackendService=!0,this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","healthChecks"]).then((e=>{this.isNew?this.loadBalancer=new ct(Oe?Oe.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map((e=>e.name));n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})}))}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then((e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)}))}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],y.getRegionsForAccount(this.loadBalancer.credentials).then((e=>{this.regions=e.map((e=>e.name))}))}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,n.cloudProvider="gce",delete n.instances,this.taskMonitor.submit((()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}})))}initializeEditMode(){this.loadBalancer.portRange=this.loadBalancer.portRange.split("-")[0],this.viewState=new lt(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}rt.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","wizardSubFormValidation","$state"];const ot="spinnaker.gce.tcpLoadBalancer.controller";n(ot,[Vn,On]).controller("gceTcpLoadBalancerCtrl",rt),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Region</div>\n <div class="col-md-8">\n <input readonly class="form-control input-sm" ng-model="ctrl.loadBalancer.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-options="port for port in ctrl.portOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.portRange"\n ></select>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/backendService.html",'<div class="container-fluid form-horizontal">\n <ng-form name="portName">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Port Name\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="text"\n required\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.portName"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);const st={bindings:{backendService:"<"},template:'\n <dt>Name</dt>\n <dd>{{$ctrl.backendService.name}}</dd>\n <dt>Health Check</dt>\n <dd>{{$ctrl.backendService.healthCheck.name}}</dd>\n <dt ng-if="$ctrl.backendService.sessionAffinity">Session Affinity</dt>\n <dd ng-if="$ctrl.backendService.sessionAffinity">{{$ctrl.backendService.sessionAffinity | gceSessionAffinityFilter}}</dd>\n <dt ng-if="$ctrl.backendService.sessionAffinity === \'GENERATED_COOKIE\'">Affinity Cookie TTL</dt>\n <dd ng-if="$ctrl.backendService.sessionAffinity === \'GENERATED_COOKIE\'">{{$ctrl.backendService.affinityCookieTtlSec}}</dd>'},dt="spinnaker.gce.loadBalancer.details.backendServiceDetails.component";n(dt,[]).component("gceBackendServiceDetails",st);const ut="spinnaker.gce.loadBalancer.details.sessionAffinity.filter";n(ut,[]).filter("gceSessionAffinityFilter",(function(){return function(e){return Gn[e]||e}}));class gt{constructor(){this.verified=!1}}class pt{constructor(){this.deleteHealthChecks=!1}}class mt{constructor(e,n,t,a,i){this.application=e,this.gceHttpLoadBalancerUtils=n,this.gceHttpLoadBalancerWriter=t,this.loadBalancer=a,this.$uibModalInstance=i,this.verification=new gt,this.params=new pt}$onInit(){const e={application:this.application,title:"Deleting "+this.loadBalancer.name,modalInstance:this.$uibModalInstance};this.taskMonitor=new G(e)}isValid(){return this.verification.verified}submit(){this.taskMonitor.submit(this.getSubmitMethod())}cancel(){this.$uibModalInstance.dismiss()}hasHealthChecks(){return!!this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(this.loadBalancer)||!!this.loadBalancer.healthCheck}getSubmitMethod(){return this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(this.loadBalancer)?()=>this.gceHttpLoadBalancerWriter.deleteLoadBalancers(this.loadBalancer,this.application,this.params):()=>{const e={cloudProvider:"gce",loadBalancerName:this.loadBalancer.name,accountName:this.loadBalancer.account,credentials:this.loadBalancer.account,region:this.loadBalancer.region,loadBalancerType:this.loadBalancer.loadBalancerType||"NETWORK",deleteHealthChecks:this.params.deleteHealthChecks};return A.deleteLoadBalancer(e,this.application)}}}mt.$inject=["application","gceHttpLoadBalancerUtils","gceHttpLoadBalancerWriter","loadBalancer","$uibModalInstance"];const ht="spinnaker.gce.loadBalancer.deleteModal.controller";n(ht,[Ie,Mn,tn]).controller("gceLoadBalancerDeleteModalCtrl",mt);const vt="spinnaker.deck.gce.loadBalancer.details.healthCheck.component";n(vt,[]).component("gceHealthCheck",{bindings:{healthCheck:"="},templateUrl:"google/src/loadBalancer/details/healthCheck/healthCheck.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/healthCheck/healthCheck.component.html",'<dt>Name</dt>\n<dd>{{ $ctrl.healthCheck.name }}</dd>\n<dt ng-if="$ctrl.healthCheck.healthCheckType">Type</dt>\n<dd ng-if="$ctrl.healthCheck.healthCheckType">{{ $ctrl.healthCheck.healthCheckType }}</dd>\n<dt ng-if="$ctrl.healthCheck.target">Target</dt>\n<dd ng-if="$ctrl.healthCheck.target">{{ $ctrl.healthCheck.target }}</dd>\n<dt>Timeout</dt>\n<dd>{{ $ctrl.healthCheck.timeout || $ctrl.healthCheck.timeoutSec }} seconds</dd>\n<dt>Interval</dt>\n<dd>{{ $ctrl.healthCheck.interval || $ctrl.healthCheck.checkIntervalSec }} seconds</dd>\n<dt>Healthy Threshold</dt>\n<dd>{{ $ctrl.healthCheck.healthyThreshold }}</dd>\n<dt>Unhealthy Threshold</dt>\n<dd>{{ $ctrl.healthCheck.unhealthyThreshold }}</dd>\n')}]);const ft="spinnaker.deck.gce.loadBalancer.loadBalancerType";n(ft,[]).component("gceLoadBalancerType",{template:"<span>{{ $ctrl.type }}</span>",bindings:{loadBalancer:"="},controller:function(){this.$onInit=()=>{var e;this.type="HTTP"===(e=this.loadBalancer).loadBalancerType?oe.isString(e.certificate)?"HTTPS":"HTTP":e.loadBalancerType}}});const bt="spinnaker.loadBalancer.gce.details.controller";e.module(bt,[xe,w,We,In,ft,tn,vt,dt,ht,ut,sn]).controller("gceLoadBalancerDetailsCtrl",["$scope","$state","$uibModal","loadBalancer","app","gceHttpLoadBalancerUtils","loadBalancerReader","$q","loadBalancerTypeToWizardMap","gceXpnNamingService",function(n,t,a,i,l,c,r,o,s,d){const u=this.application=l;function g(){return n.loadBalancer=u.loadBalancers.data.filter((function(e){const n=e.vpcId||null;return e.name===i.name&&(e.region===i.region||"global"===e.region)&&e.account===i.accountId&&n===i.vpcId}))[0],n.loadBalancer?function(){if(c.isHttpLoadBalancer(n.loadBalancer)){const e=n.loadBalancer.listeners.map((e=>r.getLoadBalancerDetails(n.loadBalancer.provider,i.accountId,n.loadBalancer.region,e.name)));return o.all(e).then((e=>{const n=(e=oe.flatten(e))[0];return n.dns=e.map((e=>{let n;return n="443"===e.listenerDescriptions[0].listener.loadBalancerPort?"https:":"http:",{dnsname:e.dnsname,protocol:n}})),n.dns=oe.uniqBy(n.dns,"dnsname"),n.listenerDescriptions=oe.flatten(e.map((e=>e.listenerDescriptions))),[n]}))}return r.getLoadBalancerDetails(n.loadBalancer.provider,i.accountId,n.loadBalancer.region,n.loadBalancer.name).then((e=>{const n=e[0];let t;return t="443"===n.listenerDescriptions[0].listener.loadBalancerPort?"https:":"http:",n.dns={dnsname:n.dnsname,protocol:t},e}))}().then((function(e){n.state.loading=!1;const t=e.filter((function(e){return e.vpcid===i.vpcId||!e.vpcid&&!i.vpcId}));t.length&&(n.loadBalancer.elb=t[0],n.loadBalancer.account=i.accountId,y.getCredentialsKeyedByAccount("gce").then((function(){c.isHttpLoadBalancer(n.loadBalancer)&&(n.loadBalancer.elb.backendServices=function(e){let n=[e.defaultService];e.hostRules.length&&(n=oe.chain(e.hostRules).reduce(((e,n)=>(e.push(n.pathMatcher.defaultService),e.concat(oe.map(n.pathMatcher.pathRules,"backendService")))),n).uniqBy("name").value());return n}(n.loadBalancer),n.loadBalancer.elb.healthChecks=oe.chain(n.loadBalancer.elb.backendServices).map("healthCheck").uniqBy("name").value())}))),y.getAccountDetails(i.accountId).then((function(e){let t;t="INTERNAL"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_backend_service"]:"NETWORK"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_target_pool","gce_health_check"]:"SSL"===n.loadBalancer.loadBalancerType||"TCP"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_backend_service"]:(n.loadBalancer.loadBalancerType,["http_load_balancer","gce_target_http_proxy","gce_url_map","gce_backend_service"]),t=oe.join(t," OR "),n.loadBalancer.project=e.project,n.loadBalancer.logsLink="https://console.developers.google.com/project/"+e.project+"/logs?advancedFilter=resource.type=("+t+')%0A"'+n.loadBalancer.name+'"'}))}),p):(n.loadBalancer||p(),o.when(null))}function p(){n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})}n.state={loading:!0},l.loadBalancers.ready().then(g).then((()=>{n.$$destroyed||l.loadBalancers.onRefresh(n,g)})),this.editLoadBalancer=function(){const t=s[n.loadBalancer.loadBalancerType];a.open({templateUrl:t.editTemplateUrl,controller:`${t.controller} as ctrl`,size:"lg",resolve:{application:function(){return u},loadBalancer:function(){return e.copy(n.loadBalancer)},isNew:function(){return!1}}})},this.deleteLoadBalancer=function(){n.loadBalancer.instances&&n.loadBalancer.instances.length||a.open({controller:"gceLoadBalancerDeleteModalCtrl as ctrl",templateUrl:"google/src/loadBalancer/details/deleteModal/deleteModal.html",resolve:{application:()=>u,loadBalancer:()=>n.loadBalancer}})},this.isHttpLoadBalancer=e=>c.isHttpLoadBalancer(e),this.getNetworkId=function(e){return d.decorateXpnResourceIfNecessary(e.project,e.network)},this.getSubnetId=function(e){return d.decorateXpnResourceIfNecessary(e.project,e.subnet)}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/deleteModal/deleteModal.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Really delete {{ctrl.loadBalancer.name}}?</h4>\n </div>\n <div class="modal-body confirmation-modal" ng-if="ctrl.hasHealthChecks()">\n <div class="row">\n <div class="col-md-offset-2 col-md-8" style="padding-left: 2px">\n <div class="checkbox">\n <label> <input ng-model="ctrl.params.deleteHealthChecks" type="checkbox" />Delete health checks? </label>\n <help-field\n expand="true"\n content="If the health checks associated with this load balancer\n are being used by other resources, they will not be deleted,\n whether this option is selected or not."\n ></help-field>\n </div>\n </div>\n </div>\n </div>\n <gce-footer\n action="ctrl.submit()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="ctrl.loadBalancer.account"\n verification="ctrl.verification"\n ></gce-footer>\n </form>\n</div>\n')}]);const yt=class{constructor(e){this.gceHttpLoadBalancerUtils=e,this.normalizeLoadBalancerSet=e=>{const[n,t]=be(e,(e=>this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(e))),a=ye(n,"urlMapName");return me(a,yt.normalizeHttpLoadBalancerGroup).concat(t)}}static normalizeHttpLoadBalancerGroup(e){const n=ke(e[0]);return n.listeners=e.map((e=>({port:e.portRange?yt.parsePortRange(e.portRange):null,name:e.name,certificate:e.certificate,ipAddress:e.ipAddress,subnet:e.subnet}))),n.name=n.urlMapName,delete n.subnet,n}static parsePortRange(e){return e.split("-")[0]}};let kt=yt;kt.$inject=["gceHttpLoadBalancerUtils"];const St="spinnaker.gce.loadBalancer.setTransformer.service";n(St,[tn]).service("gceLoadBalancerSetTransformer",kt);const wt="spinnaker.gce.pipeline.stage.bake.executionDetails.controller";n(wt,[xe]).controller("gceBakeExecutionDetailsCtrl",["$scope","$stateParams","executionDetailsSectionService","$interpolate",function(e,n,t,i){e.configSections=["bakeConfig","taskStatus","artifactStatus"];const l=()=>{e.detailsSection=n.details,e.provider=e.stage.context.cloudProviderType||"gce",e.roscoMode=a.feature.roscoMode||"function"==typeof a.feature.roscoSelector&&a.feature.roscoSelector(e.stage.context),e.bakeryDetailUrl=i(e.roscoMode&&a.roscoDetailUrl?a.roscoDetailUrl:a.bakeryDetailUrl)},c=()=>t.synchronizeSection(e.configSections,l);c(),e.$on("$stateChangeSuccess",c)}]);const Ct="spinnaker.gce.pipeline.stage..bakeStage";n(Ct,[wt]).config((function(){$.pipeline.registerStage({provides:"bake",cloudProvider:"gce",label:"Bake",description:"Bakes an image",templateUrl:"google/src/pipeline/stages/bake/bakeStage.html",executionDetailsUrl:"google/src/pipeline/stages/bake/bakeExecutionDetails.html",executionLabelComponent:x,extraLabelLines:e=>e.masterStage.context.allPreviouslyBaked||e.masterStage.context.somePreviouslyBaked?1:0,producesArtifacts:!0,supportsCustomTimeout:!0,validators:[{type:"anyFieldRequired",fields:[{fieldName:"package",fieldLabel:"Package"},{fieldName:"packageArtifactIds",fieldLabel:"Package Artifacts"}]}],restartable:!0,artifactExtractor:I.accumulateArtifacts(["packageArtifactIds"]),artifactRemover:z.removeArtifactFromFields(["packageArtifactIds"])})})).controller("gceBakeStageCtrl",["$scope","$q","$uibModal",function(e,n,t){e.stage.extendedAttributes=e.stage.extendedAttributes||{},e.stage.region="global",e.stage.cloudProvider||(e.stage.cloudProvider="gce"),e.stage.user||(e.stage.user=N.getAuthenticatedUser().name),e.viewState={loading:!0},this.addExtendedAttribute=function(){e.stage.extendedAttributes||(e.stage.extendedAttributes={}),t.open({templateUrl:D.addExtendedAttributes,controller:"bakeStageAddExtendedAttributeController",controllerAs:"addExtendedAttribute",resolve:{extendedAttribute:function(){return{key:"",value:""}}}}).result.then((function(n){e.stage.extendedAttributes[n.key]=n.value})).catch((()=>{}))},this.removeExtendedAttribute=function(n){delete e.stage.extendedAttributes[n]},this.showTemplateFileName=function(){return e.viewState.roscoMode||e.stage.templateFileName},this.showAccountName=function(){return e.viewState.roscoMode||e.stage.accountName},this.showExtendedAttributes=function(){return e.viewState.roscoMode||e.stage.extendedAttributes&&oe.size(e.stage.extendedAttributes)>0},this.showVarFileName=function(){return e.viewState.roscoMode||e.stage.varFileName},e.$watch("stage",(function(){oe.forOwn(e.stage,(function(n,t){""===n&&delete e.stage[t]})),"function"==typeof a.feature.roscoSelector&&(e.viewState.roscoMode=a.feature.roscoSelector(e.stage))}),!0),e.viewState.providerSelected=!0,n.all([M.getBaseOsOptions("gce"),M.getBaseLabelOptions(),I.getExpectedArtifactsAvailableToStage(e.stage,e.pipeline)]).then((function([n,t,i]){e.baseOsOptions=n.baseImages,e.baseLabelOptions=t,e.viewState.expectedArtifacts=i,!e.stage.baseOs&&e.baseOsOptions&&e.baseOsOptions.length&&(e.stage.baseOs=e.baseOsOptions[0].id),!e.stage.baseLabel&&e.baseLabelOptions&&e.baseLabelOptions.length&&(e.stage.baseLabel=e.baseLabelOptions[0]),e.viewState.roscoMode=a.feature.roscoMode||"function"==typeof a.feature.roscoSelector&&a.feature.roscoSelector(e.stage),e.showAdvancedOptions=function(){const n=e.stage;return!!(n.templateFileName||n.extendedAttributes&&oe.size(n.extendedAttributes)>0||n.varFileName||n.baseAmi||n.accountName)}(),e.viewState.loading=!1}))}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/bake/bakeStage.html",'<div ng-controller="gceBakeStageCtrl as bakeStageCtrl">\n <stage-config-field label="Package" help-key="pipeline.config.bake.package">\n <input type="text" class="form-control input-sm" ng-model="stage.package" />\n </stage-config-field>\n <expected-artifact-multi-selector\n command="stage"\n ids-field="packageArtifactIds"\n artifact-label="Package Artifacts"\n expected-artifacts="viewState.expectedArtifacts"\n help-field-key="pipeline.config.bake.packageArtifacts"\n show-icons="true"\n >\n </expected-artifact-multi-selector>\n\n <hr />\n\n <stage-config-field label="Base OS">\n <bake-stage-choose-os model="stage.baseOs" base-os-options="baseOsOptions"></bake-stage-choose-os>\n </stage-config-field>\n\n <stage-config-field label="Base Label">\n <label class="radio-inline" ng-repeat="baseLabel in baseLabelOptions">\n <input type="radio" ng-model="stage.baseLabel" ng-value="baseLabel" />\n {{baseLabel}}\n </label>\n </stage-config-field>\n\n <hr />\n\n \x3c!-- Even if the roscoMode flag is false, we should show the control if rebake is set. --\x3e\n <stage-config-field label="Rebake" ng-if="viewState.roscoMode || stage.rebake">\n <div class="checkbox" style="margin-bottom: 0">\n <label>\n <input type="checkbox" ng-model="stage.rebake" />\n Rebake image without regard to the status of any existing bake\n </label>\n </div>\n </stage-config-field>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-1">\n <div class="checkbox">\n <label>\n <input type="checkbox" ng-model="showAdvancedOptions" />\n <strong>Show Advanced Options</strong>\n </label>\n </div>\n </div>\n </div>\n <div ng-class="{collapse: showAdvancedOptions !== true, \'collapse.in\': showAdvancedOptions === true}">\n <stage-config-field\n label="Template File Name"\n help-key="pipeline.config.bake.templateFileName"\n ng-if="bakeStageCtrl.showTemplateFileName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.templateFileName" />\n </stage-config-field>\n <stage-config-field\n label="Account Name"\n help-key="pipeline.config.gce.bake.accountName"\n ng-if="bakeStageCtrl.showAccountName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.accountName" />\n </stage-config-field>\n <stage-config-field\n label="Extended Attributes"\n help-key="pipeline.config.bake.extendedAttributes"\n ng-if="bakeStageCtrl.showExtendedAttributes()"\n >\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th style="width: 40%">Key</th>\n <th style="width: 60%">Value</th>\n <th class="text-right">Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="(key,value) in stage.extendedAttributes">\n <td>\n <strong class="small">{{key}}</strong>\n </td>\n <td>\n <input\n type="text"\n ng-model="stage.extendedAttributes[key]"\n value="{{value}}"\n class="form-control input-sm"\n />\n </td>\n <td class="text-right">\n <a class="small" href ng-click="bakeStageCtrl.removeExtendedAttribute(key)">Remove</a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="7">\n <button class="btn btn-block btn-sm add-new" ng-click="bakeStageCtrl.addExtendedAttribute()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Extended Attribute\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </stage-config-field>\n <stage-config-field\n label="Var File Name"\n help-key="pipeline.config.bake.varFileName"\n ng-if="bakeStageCtrl.showVarFileName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.varFileName" />\n </stage-config-field>\n <stage-config-field label="Base Image" help-key="pipeline.config.gce.bake.baseImage">\n <input type="text" class="form-control input-sm" ng-model="stage.baseAmi" />\n </stage-config-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/bake/bakeExecutionDetails.html",'<div ng-controller="gceBakeExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'bakeConfig\'">\n <div class="row">\n <div class="col-md-6">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Image</dt>\n <dd>{{stage.context.ami}}</dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Package</dt>\n <dd>{{stage.context.package}}</dd>\n </dl>\n </div>\n <div class="col-md-6">\n <dl class="dl-narrow dl-horizontal">\n <dt>Base OS</dt>\n <dd>{{stage.context.baseOs}}</dd>\n <dt>Label</dt>\n <dd>{{stage.context.baseLabel}}</dd>\n <dt ng-if="roscoMode || execution.trigger.rebake || stage.context.rebake">Rebake</dt>\n <dd ng-if="roscoMode || execution.trigger.rebake || stage.context.rebake">\n {{execution.trigger.rebake || stage.context.rebake || false}}\n </dd>\n <dt ng-if="stage.context.templateFileName">Template</dt>\n <dd ng-if="stage.context.templateFileName">{{stage.context.templateFileName}}</dd>\n <dt ng-if="stage.context.accountName">Account Name</dt>\n <dd ng-if="stage.context.accountName">{{stage.context.accountName}}</dd>\n <dt ng-if="stage.context.varFileName">Var File</dt>\n <dd ng-if="stage.context.varFileName">{{stage.context.varFileName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n\n <div class="row" ng-if="stage.context.region && stage.context.status.resourceId">\n <div class="col-md-12">\n <div class="alert alert-{{stage.isFailed ? \'danger\' : \'info\'}}">\n <div ng-if="stage.context.previouslyBaked">No changes detected; reused existing bake</div>\n <div ng-if="stage.context.imageName">\n <strong>Image:</strong>\n <div select-on-dbl-click>{{stage.context.imageName}}</div>\n </div>\n <a target="_blank" href="{{ bakeryDetailUrl(stage) }}"> View Bakery Details </a>\n </div>\n </div>\n </div>\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\n <div class="step-section-details" ng-if="detailsSection === \'artifactStatus\'">\n <execution-artifact-tab stage="stage" execution="execution" config="config"></execution-artifact-tab>\n </div>\n</div>\n')}]);const Bt="spinnaker.gce.pipeline.stage..cloneServerGroupStage";n(Bt,[]).config((function(){$.pipeline.registerStage({provides:"cloneServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html",executionStepLabelUrl:"google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html",validators:[{type:"requiredField",fieldName:"targetCluster",fieldLabel:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"region"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceCloneServerGroupStageCtrl",["$scope",function(e){const n=e.stage;e.viewState={accountsLoaded:!1},y.listAccounts("gce").then((n=>{e.accounts=n,e.viewState.accountsLoaded=!0})),this.cloneTargets=E.TARGET_LIST,n.target=n.target||this.cloneTargets[0].val,n.application=e.application.name,n.cloudProvider="gce",n.cloudProviderType="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),this.targetClusterUpdated=()=>{if(n.targetCluster){const e=S.parseServerGroupName(n.targetCluster);n.stack=e.stack,n.freeFormDetails=e.freeFormDetails}else n.stack="",n.freeFormDetails=""},e.$watch("stage.targetCluster",this.targetClusterUpdated),this.removeCapacity=()=>{delete n.capacity},oe.has(n,"useSourceCapacity")||(n.useSourceCapacity=!0),this.toggleDisableTraffic=()=>{n.disableTraffic=!n.disableTraffic}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html",'<div ng-controller="gceCloneServerGroupStageCtrl as cloneServerGroupStageCtrl">\n <div ng-if="!pipeline.strategy">\n <div ng-if="viewState.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!viewState.loading">\n <account-region-cluster-selector\n application="application"\n component="stage"\n accounts="accounts"\n single-region="true"\n cluster-field="targetCluster"\n >\n </account-region-cluster-selector>\n </div>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="cloneServerGroupStageCtrl.cloneTargets"></target-select>\n </stage-config-field>\n <div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Capacity</div>\n <div class="col-md-9 radio">\n <label>\n <input\n type="radio"\n ng-model="stage.useSourceCapacity"\n ng-value="true"\n ng-click="cloneServerGroupStageCtrl.removeCapacity()"\n id="useSourceCapacityTrue"\n />\n Copy the capacity from the current server group\n <help-field key="serverGroupCapacity.useSourceCapacityTrue"></help-field>\n </label>\n </div>\n <div class="col-md-9 col-md-offset-3 radio">\n <label>\n <input type="radio" ng-model="stage.useSourceCapacity" ng-value="false" id="useSourceCapacityFalse" />\n Let me specify the capacity\n <help-field key="serverGroupCapacity.useSourceCapacityFalse"></help-field>\n </label>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3">Min</div>\n <div class="col-md-2">Max</div>\n <div class="col-md-2">Desired</div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.min"\n min="0"\n max="{{stage.capacity.max}}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.max"\n min="{{stage.capacity.min}}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.desired"\n min="{{stage.capacity.min}}"\n max="{{stage.capacity.max}}"\n required\n />\n </div>\n </div>\n </div>\n <stage-config-field label="Traffic" help-key="gce.serverGroup.traffic">\n <div class="checkbox">\n <label>\n <input\n type="checkbox"\n ng-click="cloneServerGroupStageCtrl.toggleDisableTraffic()"\n ng-checked="!stage.disableTraffic"\n />\n Send client requests to new instances\n </label>\n </div>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n <deployment-strategy-selector field-columns="6" command="stage"></deployment-strategy-selector>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html",'<span class="task-label"> Clone Server Group: {{step.context.source.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Tt="spinnaker.gce.pipeline.stage..destroyAsgStage";n(Tt,[]).config((function(){$.pipeline.registerStage({provides:"destroyServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/destroyAsg/destroyAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],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:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceDestroyAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/destroyAsg/destroyAsgStage.html",'<div ng-controller="gceDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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("google/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",'<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Gt="spinnaker.gce.pipeline.stage..disableAsgStage";n(Gt,[]).config((function(){$.pipeline.registerStage({provides:"disableServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/disableAsg/disableAsgStage.html",executionStepLabelUrl:"google/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:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceDisableAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableAsg/disableAsgStage.html",'<div ng-controller="gceDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",'<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const At="spinnaker.gce.pipeline.stage..disableClusterStage";n(At,[]).config((function(){$.pipeline.registerStage({provides:"disableCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/disableCluster/disableClusterStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"remainingEnabledServerGroups",fieldLabel:"Keep [X] enabled Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceDisableClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.remainingEnabledServerGroups&&(n.remainingEnabledServerGroups=1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.preferLargerOverNewer&&(n.preferLargerOverNewer="false"),n.preferLargerOverNewer=n.preferLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableCluster/disableClusterStage.html",'<div ng-controller="gceDisableClusterStageCtrl as disableClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Disable Options">\n <div class="form-inline">\n Keep the\n <input\n type="number"\n min="0"\n required\n ng-model="stage.remainingEnabledServerGroups"\n class="form-control input-sm"\n style="width: 50px"\n />\n <select class="form-control input-sm" ng-model="stage.preferLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n {{disableClusterStageCtrl.pluralize(\'server group\', stage.remainingEnabledServerGroups)}} enabled.\n </div>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]);const Pt="spinnaker.gce.pipeline.stage..enableAsgStage";n(Pt,[]).config((function(){$.pipeline.registerStage({provides:"enableServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/enableAsg/enableAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceEnableAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val),e.$watch("stage.credentials",e.accountUpdated)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/enableAsg/enableAsgStage.html",'<div ng-controller="gceEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",'<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const $t="spinnaker.gce.pipeline.stage..findAmiStage";e.module($t,[]).config((function(){$.pipeline.registerStage({provides:"findImage",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/findAmi/findAmiStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"selectionStrategy",fieldLabel:"Server Group Selection"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials"}]})})).controller("gceFindAmiStageCtrl",["$scope",function(n){const t=n.stage;n.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(e){n.accounts=e,n.state.accounts=!0})),n.selectionStrategies=[{label:"Largest",val:"LARGEST",description:"When multiple server groups exist, prefer the server group with the most instances"},{label:"Newest",val:"NEWEST",description:"When multiple server groups exist, prefer the newest"},{label:"Oldest",val:"OLDEST",description:"When multiple server groups exist, prefer the oldest"},{label:"Fail",val:"FAIL",description:"When multiple server groups exist, fail"}],t.regions=t.regions||[],t.cloudProvider="gce",t.selectionStrategy=t.selectionStrategy||n.selectionStrategies[0].val,e.isUndefined(t.onlyEnabled)&&(t.onlyEnabled=!0),!t.credentials&&n.application.defaultCredentials.gce&&(t.credentials=n.application.defaultCredentials.gce),!t.regions.length&&n.application.defaultRegions.gce&&t.regions.push(n.application.defaultRegions.gce),n.$watch("stage.credentials",n.accountUpdated)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findAmi/findAmiStage.html",'<div ng-controller="gceFindAmiStageCtrl as findAmiCtrl" class="form-horizontal">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n\n <stage-config-field label="Server Group Selection">\n <ui-select ng-model="stage.selectionStrategy" class="form-control input-sm">\n <ui-select-match placeholder="None">{{$select.selected.label}}</ui-select-match>\n <ui-select-choices repeat="strategy.val as strategy in selectionStrategies | filter: $select.search">\n <strong ng-bind-html="strategy.label | highlight: $select.search"></strong>\n <div ng-bind-html="strategy.description"></div>\n </ui-select-choices>\n </ui-select>\n </stage-config-field>\n <stage-config-field label="Server Group Filters">\n <label class="checkbox-inline">\n <input type="checkbox" ng-model="stage.onlyEnabled" />\n Only consider enabled Server Groups\n </label>\n </stage-config-field>\n</div>\n')}]);const xt="spinnaker.gce.pipeline.stage..findImageFromTagsStage";n(xt,[]).config((function(){$.pipeline.registerStage({provides:"findImageFromTags",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html",executionDetailsUrl:"google/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.html",executionConfigSections:["findImageConfig","taskStatus"],validators:[{type:"requiredField",fieldName:"packageName"},{type:"requiredField",fieldName:"tags"}]})})).controller("gceFindImageFromTagsStageCtrl",["$scope",function(e){e.stage.tags=e.stage.tags||{},e.stage.regions=e.stage.regions||[],e.stage.cloudProvider=e.stage.cloudProvider||"gce",M.getRegions("gce").then((function(n){e.regions=n}))}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html",'<div ng-controller="gceFindImageFromTagsStageCtrl as findImageFromTagsCtrl" class="form-horizontal">\n <stage-config-field label="Package">\n <input type="text" class="form-control input-sm" ng-model="stage.packageName" />\n </stage-config-field>\n <stage-config-field label="Tags">\n <map-editor model="stage.tags" allow-empty="true"></map-editor>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.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 === \'findImageConfig\'">\n <div class="row">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Package</dt>\n <dd>{{stage.context.packageName}}</dd>\n <dt>Tags</dt>\n <dd>\n <span ng-repeat="(key, val) in stage.context.tags"> {{key}}:{{val}}{{$last ? \'\' : \', \'}} </span>\n </dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n\n <div class="row" ng-if="stage.context.amiDetails">\n <div class="col-md-12">\n <div class="well alert alert-info">\n <h4>Results</h4>\n <dl ng-repeat="image in stage.context.amiDetails" class="dl-narrow dl-horizontal">\n <dt>Region</dt>\n <dd>{{image.region}}</dd>\n <dt>Image</dt>\n <dd>{{image.imageName}}</dd>\n </dl>\n </div>\n </div>\n </div>\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')}]);const It="spinnaker.gce.pipeline.stage..resizeAsgStage";n(It,[]).config((function(){$.pipeline.registerStage({provides:"resizeServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/resizeAsg/resizeAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],supportsCustomTimeout:!0,validators:[{type:"targetImpedance",message:"This pipeline will attempt to resize a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"action"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceResizeAsgStageCtrl",["$scope",function(e){const n=e.stage;e.viewState={accountsLoaded:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.viewState.accountsLoaded=!0})),e.resizeTargets=E.TARGET_LIST,e.scaleActions=[{label:"Scale Up",val:"scale_up"},{label:"Scale Down",val:"scale_down"},{label:"Scale to Cluster Size",val:"scale_to_cluster"},{label:"Scale to Exact Size",val:"scale_exact"}],e.resizeTypes=[{label:"Percentage",val:"pct"},{label:"Incremental",val:"incr"}],n.capacity=n.capacity||{},n.regions=n.regions||[],n.target=n.target||e.resizeTargets[0].val,n.action=n.action||e.scaleActions[0].val,n.resizeType=n.resizeType||e.resizeTypes[0].val,n.action||"exact"!==n.resizeType||(n.action="scale_exact"),n.cloudProvider="gce",n.cloudProviderType="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),this.updateResizeType=function(){"scale_exact"===n.action?(n.resizeType="exact",delete n.scalePct,delete n.scaleNum):(n.capacity={},"pct"===n.resizeType?delete n.scaleNum:(n.resizeType="incr",delete n.scalePct,n.scaleNum=n.scaleNum||0))}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/resizeAsg/resizeAsgStage.html",'<div ng-controller="gceResizeAsgStageCtrl as resizeAsgStageCtrl">\n <div ng-if="!pipeline.strategy">\n <div ng-if="viewState.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!viewState.loading">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="resizeTargets"></target-select>\n </stage-config-field>\n <stage-config-field label="Action" help-key="pipeline.config.resizeAsg.action">\n <select\n class="form-control input-sm"\n required\n ng-model="stage.action"\n ng-change="resizeAsgStageCtrl.updateResizeType()"\n ng-options="a.val as a.label for a in scaleActions"\n >\n <option>Select an action...</option>\n </select>\n </stage-config-field>\n <div ng-if="stage.action !== \'scale_exact\'">\n <stage-config-field label="{{stage.action === \'scale_to_cluster\' ? \'Additional Capacity\' : \'Type\'}}">\n <select\n class="form-control input-sm"\n required\n ng-model="stage.resizeType"\n ng-change="resizeAsgStageCtrl.updateResizeType()"\n ng-options="t.val as t.label for t in resizeTypes"\n >\n <option>Select an action...</option>\n </select>\n </stage-config-field>\n <div class="form-group" ng-if="stage.resizeType === \'pct\'">\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right" style="margin-left: 0; padding-left: 0">Resize Percentage</label>\n <div class="col-md-2">\n <input type="number" min="0" ng-model="stage.scalePct" class="form-control input-sm" />\n </div>\n </div>\n <div class="col-md-9 col-md-offset-3">\n <em class="subinput-note"\n >This is the percentage by which the target server group\'s capacity will be increased</em\n >\n </div>\n </div>\n <div class="form-group" ng-if="stage.resizeType === \'incr\'">\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right" style="margin-left: 0; padding-left: 0">Resize-by Amount</label>\n <div class="col-md-2">\n <input type="number" min="0" ng-model="stage.scaleNum" class="form-control input-sm" />\n </div>\n </div>\n\n <div class="col-md-9 col-md-offset-3">\n <em class="subinput-note"\n >This is the exact amount by which the target server group\'s capacity will be increased</em\n >\n </div>\n </div>\n </div>\n <div class="form-group" ng-if="stage.action === \'scale_exact\'">\n <div class="col-md-9 col-md-offset-3 small">\n <div class="col-md-9">\n <div class="col-md-3 col-md-offset-3">Min</div>\n <div class="col-md-3">Max</div>\n <div class="col-md-3">Desired</div>\n </div>\n </div>\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right small" style="margin-left: 0; padding-left: 0">Match Capacity</label>\n <div class="col-md-9">\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.min" class="form-control input-sm" />\n </div>\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.max" class="form-control input-sm" />\n </div>\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.desired" class="form-control input-sm" />\n </div>\n </div>\n </div>\n\n <div class="col-md-6 col-md-offset-3">\n <em class="subinput-note"\n >If the targeted server group has an Autoscaler configured, the min/max values will be used to update the\n scaling policy. If not, the desired size will be used to update the server group\'s target size.</em\n >\n </div>\n </div>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html",'<span class="task-label"> Resize Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const zt="spinnaker.gce.pipeline.stage..scaleDownClusterStage";n(zt,[]).config((function(){$.pipeline.registerStage({provides:"scaleDownCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"remainingFullSizeServerGroups",fieldLabel:"Keep [X] full size Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],strategy:!0})})).controller("gceScaleDownClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.remainingFullSizeServerGroups&&(n.remainingFullSizeServerGroups=1),void 0===n.allowScaleDownActive&&(n.allowScaleDownActive=!1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.preferLargerOverNewer&&(n.preferLargerOverNewer="false"),n.preferLargerOverNewer=n.preferLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html",'<div ng-controller="gceScaleDownClusterStageCtrl as scaleDownClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Scale Down Options">\n <div class="form-inline">\n <p>\n Keep the\n <input\n type="number"\n min="0"\n required\n ng-model="stage.remainingFullSizeServerGroups"\n class="form-control input-sm"\n style="width: 50px"\n />\n <select class="form-control input-sm" ng-model="stage.preferLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n {{scaleDownClusterStageCtrl.pluralize(\'server group\', stage.remainingFullSizeServerGroups)}} at current size.\n </p>\n <p>The remaining server groups will be scaled down to zero instances.</p>\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.allowScaleDownActive" />\n Allow scale down of active server groups\n </label>\n </div>\n </div>\n</div>\n')}]);const Nt="spinnaker.gce.pipeline.stage..shrinkClusterStage";n(Nt,[]).config((function(){$.pipeline.registerStage({provides:"shrinkCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"shrinkToSize",fieldLabel:"shrink to [X] Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})})).controller("gceShrinkClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then((function(n){e.accounts=n,e.state.accounts=!0})),n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.shrinkToSize&&(n.shrinkToSize=1),void 0===n.allowDeleteActive&&(n.allowDeleteActive=!1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.retainLargerOverNewer&&(n.retainLargerOverNewer="false"),n.retainLargerOverNewer=n.retainLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",'<div ng-controller="gceShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]);const Dt="spinnaker.gce.pipeline.stage..tagImageStage";n(Dt,[]).config((function(){$.pipeline.registerStage({provides:"upsertImageTags",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/tagImage/tagImageStage.html",executionDetailsUrl:"google/src/pipeline/stages/tagImage/tagImageExecutionDetails.html",executionConfigSections:["tagImageConfig","taskStatus"]})})).controller("gceTagImageStageCtrl",["$scope",e=>{y.listAccounts("gce").then((n=>e.accounts=n)),e.stage.tags=e.stage.tags||{},e.stage.cloudProvider=e.stage.cloudProvider||"gce";e.$watch("pipeline.stages",(()=>{const n=R.getAllUpstreamDependencies(e.pipeline,e.stage).filter((e=>E.IMAGE_PRODUCING_STAGES.includes(e.type)));e.consideredStages=new Map(n.map((e=>[e.refId,e.name])))}))}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/tagImage/tagImageStage.html",'<div ng-controller="gceTagImageStageCtrl as tagImageStageCtrl" class="form-horizontal">\n <stage-config-field label="Account">\n <account-select-field component="stage" field="credentials" accounts="accounts" provider="\'gce\'" required>\n </account-select-field>\n </stage-config-field>\n <stage-config-field label="Tags">\n <map-editor model="stage.tags" allow-empty="true"></map-editor>\n </stage-config-field>\n <stage-config-field label="Stages (optional)" help-key="gce.tagImage.consideredStages" help-key-expand="true">\n <div class="checkbox">\n <checklist model="stage.consideredStages" items="consideredStages"></checklist>\n </div>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/tagImage/tagImageExecutionDetails.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 === \'tagImageConfig\'">\n <div class="row" ng-if="stage.context.targets">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Images</dt>\n <dd ng-repeat="target in stage.context.targets">{{target.imageName}}</dd>\n </dl>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt>Tags</dt>\n <dd ng-repeat="(key, val) in stage.context.tags" ng-if="val !== null">{{key}} = {{val}}</dd>\n <dt ng-if="stage.context.consideredStages">Stages</dt>\n <dd ng-repeat="consideredStage in stage.context.consideredStages">\n <stage-name stages="execution.stages" ref-id="consideredStage"></stage-name>\n </dd>\n </dl>\n </div>\n </div>\n\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')}]);const Mt="spinnaker.gce.securityGroup.create.controller";e.module(Mt,[xe]).controller("gceCreateSecurityGroupCtrl",["$scope","$uibModalInstance","$state","$controller","application","securityGroup",function(n,t,a,i,l,c){n.pages={location:"google/src/securityGroup/configure/createSecurityGroupProperties.html",targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"};const r=this;c.backingData={},c.network="default",c.sourceRanges=[],c.sourceTags=[],c.ipIngress=[],e.extend(this,i("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:l,securityGroup:c,mode:"create"})),y.listAccounts("gce").then((function(e){n.accounts=e,r.accountUpdated()})),this.getSecurityGroupRefreshTime=function(){return d.get("securityGroups").getStats().ageMax},r.upsert=function(){r.mixinUpsert("Create")},r.initializeSecurityGroups()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupProperties.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.securityGroupName.$error.validateUnique, \'alert-info\': !form.securityGroupName.$error.validateUnique}"\n >\n <strong>Your <firewall-label label="firewall"></firewall-label> will be named:</strong>\n <span ng-bind="namePreview"></span>\n <input\n type="hidden"\n class="form-control input-sm"\n ng-model="securityGroup.name"\n validate-unique="existingSecurityGroupNames"\n validate-ignore-case="true"\n name="securityGroupName"\n ng-pattern="ctrl.namePattern"\n trigger-validation="securityGroup.subnet"\n required\n />\n <validation-error\n ng-if="form.securityGroupName.$error.validateUnique && securityGroup.credentials"\n message="There is already a {{firewallLabel}} in {{securityGroup.credentials}} with that name."\n ></validation-error>\n <validation-error\n ng-if="form.securityGroupName.$error.pattern"\n message="Name can only contain letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Detail (optional)</div>\n <div class="col-md-4">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="securityGroup.detail"\n ng-change="ctrl.updateName()"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Description</div>\n <div class="col-md-8">\n <input type="text" required class="form-control input-sm" ng-model="securityGroup.description" />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="securityGroup"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-network-select-field\n label-columns="4"\n field-columns="8"\n component="securityGroup"\n field="network"\n account="securityGroup.credentials"\n networks="securityGroup.backingData.networks"\n on-change="ctrl.registerHelpTextService()"\n ></gce-network-select-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Et="spinnaker.google.securityGroup.edit.controller";e.module(Et,[xe]).controller("gceEditSecurityGroupCtrl",["$scope","$uibModalInstance","$state","application","securityGroup","$controller",function(n,t,a,i,l,c){n.pages={targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"},n.securityGroup=l,n.state={refreshingSecurityGroups:!1},e.extend(this,c("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:i,securityGroup:l,mode:"edit"})),n.isNew=!1,n.taskMonitor=new G({application:i,title:`Updating your ${m.get("firewall")}`,modalInstance:t,onTaskComplete:()=>i.securityGroups.refresh()}),l.sourceRanges=_.map(l.sourceRanges,(function(e){return{value:e}})),l.ipIngress=_.chain(l.ipIngressRules).map((function(e){return e.portRanges&&e.portRanges.length>0?e.portRanges.map((function(n){return{type:e.protocol,startPort:n.startPort,endPort:n.endPort}})):[{type:e.protocol}]})).flatten().value(),l.sourceTags=l.sourceTags||[],this.getSecurityGroupRefreshTime=function(){return d.get("securityGroups").getStats().ageMax},this.addSourceCIDR=function(e){e.push({value:"0.0.0.0/0"})},this.removeSourceCIDR=function(e,n){e.splice(n,1)},this.addRule=function(e){e.push({type:"tcp",startPort:7001,endPort:7001})},this.removeRule=function(e,n){e.splice(n,1)},this.upsert=function(){n.taskMonitor.submit((function(){const e=_.map(n.securityGroup.ipIngress,(function(e){const n={ipProtocol:e.type};return e.startPort&&e.endPort&&(n.portRanges=[e.startPort+"-"+e.endPort]),n}));return L.upsertSecurityGroup(n.securityGroup,i,"Update",{cloudProvider:"gce",sourceRanges:_.uniq(_.map(n.securityGroup.sourceRanges,"value")),allowed:e,targetTags:n.securityGroup.targetTags||[],region:"global"})}))},this.cancel=function(){t.dismiss()}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Rt="spinnaker.gce.securityGroupHelpText.service";n(Rt,[]).service("gceSecurityGroupHelpTextService",class{register(e,n,t){return this.reset(),this.application=e,this.account=n,this.network=t,this.indexServerGroupsByTag()}getHelpTextForTag(e,n){const t=this.getServerGroupsWithTag(e);let a;switch(t.length){case 0:a=null;break;case 1:a=`This ${n} tag associates this ${m.get("firewall")} with the server group <em>${t[0]}</em>.`;break;default:a=`This ${n} tag associates this ${m.get("firewall")} with the server groups\n ${t.map((e=>`<em>${e}</em>`)).join(", ")}.`}return a}getServerGroupsWithTag(e){return this.serverGroupsIndexedByTag.get(e)?Array.from(this.serverGroupsIndexedByTag.get(e)).sort():[]}reset(){this.application=null,this.account=null,this.serverGroupsIndexedByTag=new Map}indexServerGroupsByTag(){return this.application.ready().then((()=>{this.application.getDataSource("serverGroups").data.forEach((e=>{var n;Se(e,"providerMetadata.tags.length")&&e.account===this.account&&(null==(n=e.providerMetadata)?void 0:n.networkName)===this.network&&e.providerMetadata.tags.forEach((n=>{this.serverGroupsIndexedByTag.get(n)?this.serverGroupsIndexedByTag.get(n).add(e.name):this.serverGroupsIndexedByTag.set(n,new Set([e.name]))}))}))}))}});Le(".gce-security-group-wizard .sm-label-left {\n border-bottom: 2px solid var(--color-seashell);\n}\n.gce-security-group-wizard .table {\n border-top: 2px solid var(--color-white);\n}\n.gce-security-group-wizard .table .fixed-width {\n width: 100%;\n}\n.gce-security-group-wizard .table .table-label {\n padding: 0.8rem 0 0 1rem;\n}\n.gce-security-group-wizard .table .table-remove-button {\n padding: 0 0 1rem 1rem;\n margin-right: 65%;\n}\n");const Lt="spinnaker.google.securityGroup.baseConfig.controller";n(Lt,[xe,H,Rt]).controller("gceConfigSecurityGroupMixin",["$scope","$state","$uibModalInstance","application","securityGroup","securityGroupReader","cacheInitializer","gceSecurityGroupHelpTextService","mode",function(e,n,t,a,i,l,c,r,o){const s=this;function d(){if(e.$$destroyed)return;t.close();const a={name:e.securityGroup.name,accountId:e.securityGroup.credentials||e.securityGroup.accountName,region:"global",vpcId:e.securityGroup.vpcId,provider:"gce"};n.includes("**.firewallDetails")?n.go("^.firewallDetails",a):n.go(".firewallDetails",a)}e.isNew=!0,e.state={submitting:!1,refreshingSecurityGroups:!1,removedRules:[],infiniteScroll:{numToAdd:20,currentItems:20},mode:o,target:null,targetOptions:null},e.wizard=P,s.getTagHelpText=function(e,n){return r.getHelpTextForTag(e,n)},s.addMoreItems=function(){e.state.infiniteScroll.currentItems+=e.state.infiniteScroll.numToAdd},s.registerHelpTextService=function(){r.register(a,e.securityGroup.credentials||e.securityGroup.accountName,i.network)},s.initializeTargetOptions=function(){const n=["allowAllTraffic","specifyTags"];"edit"===e.state.mode?e.state.targetOptions=n:e.state.targetOptions=["autoGenerate"].concat(n)},s.initializeTarget=function(){"create"===e.state.mode?e.state.target="autoGenerate":e.securityGroup.targetTags&&e.securityGroup.targetTags.length>0?e.state.target="specifyTags":e.state.target="allowAllTraffic"},s.getTargetLabel=function(e){switch(e){case"autoGenerate":return"Auto-generate target tag";case"allowAllTraffic":return"Allow traffic to all server groups";case"specifyTags":return"Specify target tags";default:return null}},s.onTargetChange=function(){switch(e.state.target){case"autoGenerate":e.securityGroup.targetTags=null;break;case"allowAllTraffic":e.securityGroup.targetTags=[];break;case"specifyTags":e.securityGroup.targetTags=e.securityGroup.targetTags||[]}},e.taskMonitor=new G({application:a,title:`Creating your ${m.get("firewall")}`,modalInstance:t,onTaskComplete:function(){a.securityGroups.refresh(),a.securityGroups.onNextRefresh(e,d)}}),e.securityGroup=i,s.initializeTargetOptions(),s.initializeTarget(),s.onTargetChange(),s.registerHelpTextService(),s.upsert=function(){e.taskMonitor.submit((function(){return L.upsertSecurityGroup(e.securityGroup,a,"Create")}))},s.mixinUpsert=function(n){e.taskMonitor.submit((function(){const t=oe.map(e.securityGroup.ipIngress,(function(e){const n={ipProtocol:e.type};return e.startPort&&e.endPort&&(n.portRanges=[e.startPort+"-"+e.endPort]),n}));return L.upsertSecurityGroup(e.securityGroup,a,n,{cloudProvider:"gce",sourceRanges:oe.uniq(oe.map(e.securityGroup.sourceRanges,"value")),targetTags:e.securityGroup.targetTags,sourceTags:e.securityGroup.sourceTags,allowed:t,region:"global",network:e.securityGroup.network})}))},s.accountUpdated=function(){s.initializeSecurityGroups(),s.updateNetworks(),s.updateName()},s.refreshSecurityGroups=function(){return e.state.refreshingSecurityGroups=!0,c.refreshCache("securityGroups").then((function(){return s.initializeSecurityGroups().then((function(){e.state.refreshingSecurityGroups=!1}))}))},s.initializeSecurityGroups=function(){return l.getAllSecurityGroups().then((function(n){const t=e.securityGroup.credentials||e.securityGroup.accountName;let a;a=t?n[t].gce.global:n,e.existingSecurityGroupNames=oe.map(a,"name")}))},s.cancel=function(){t.dismiss()},s.updateNetworks=function(){B.listNetworksByProvider("gce").then((function(n){const t=e.securityGroup.credentials||e.securityGroup.accountName;e.securityGroup.backingData.networks=oe(n).filter((e=>e.account===t&&!e.id.includes("/"))).map((e=>e.id)).value()}))},s.getCurrentNamePattern=function(){return/^[a-zA-Z0-9-]*$/},s.updateName=function(){const n=e.securityGroup;let t=a.name;n.detail&&(t+="-"+n.detail,t=oe.trimEnd(t,"-")),n.name=t,e.namePreview=t},s.namePattern={test:function(e){return s.getCurrentNamePattern().test(e)}},s.addSourceCIDR=function(e){e.push({value:"0.0.0.0/0"})},s.removeSourceCIDR=function(e,n){e.splice(n,1)},s.addRule=function(e){e.push({type:"tcp",startPort:7001,endPort:7001})},s.removeRule=function(e,n){e.splice(n,1)},s.dismissRemovedRules=function(){e.state.removedRules=[],P.markClean("Ingress"),P.markComplete("Ingress")},s.isValid=function(){return("specifyTags"!==e.state.target||e.securityGroup.targetTags.length>0)&&e.securityGroup.ipIngress.length>0&&(e.securityGroup.sourceTags.length>0||e.securityGroup.sourceRanges.length>0)},s.addTargetTag=function(){e.securityGroup.targetTags.push("")},s.removeTargetTag=function(n){e.securityGroup.targetTags.splice(n,1)},s.addSourceTag=function(){e.securityGroup.sourceTags.push("")},s.removeSourceTag=function(n){e.securityGroup.sourceTags.splice(n,1)}}]);const Ht="spinnaker.google.securityGroup.clone.controller";e.module(Ht,[Lt]).controller("gceCloneSecurityGroupController",["$scope","$uibModalInstance","$controller","securityGroup","application",function(n,t,a,i,l){const c=this;n.pages={location:"google/src/securityGroup/configure/createSecurityGroupProperties.html",targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"},n.firewallLabel=m.get("Firewall"),e.extend(this,a("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:l,securityGroup:i,mode:"clone"})),y.listAccounts("gce").then((function(e){n.accounts=e,c.accountUpdated()})),i.sourceRanges=oe.map(i.sourceRanges,(function(e){return{value:e}})),i.ipIngress=oe.chain(i.ipIngressRules).map((function(e){return e.portRanges&&e.portRanges.length>0?e.portRanges.map((function(n){return{type:e.protocol,startPort:n.startPort,endPort:n.endPort}})):[{type:e.protocol}]})).flatten().value(),i.backingData={},i.sourceTags=i.sourceTags||[],c.upsert=function(){c.mixinUpsert("Clone")},c.initializeSecurityGroups()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupProperties.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.securityGroupName.$error.validateUnique, \'alert-info\': !form.securityGroupName.$error.validateUnique}"\n >\n <strong>Your <firewall-label label="firewall"></firewall-label> will be named:</strong>\n <span ng-bind="namePreview"></span>\n <input\n type="hidden"\n class="form-control input-sm"\n ng-model="securityGroup.name"\n validate-unique="existingSecurityGroupNames"\n validate-ignore-case="true"\n name="securityGroupName"\n ng-pattern="ctrl.namePattern"\n trigger-validation="securityGroup.subnet"\n required\n />\n <validation-error\n ng-if="form.securityGroupName.$error.validateUnique && securityGroup.credentials"\n message="There is already a {{firewallLabel}} in {{securityGroup.credentials}} with that name."\n ></validation-error>\n <validation-error\n ng-if="form.securityGroupName.$error.pattern"\n message="Name can only contain letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Detail (optional)</div>\n <div class="col-md-4">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="securityGroup.detail"\n ng-change="ctrl.updateName()"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Description</div>\n <div class="col-md-8">\n <input type="text" required class="form-control input-sm" ng-model="securityGroup.description" />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="securityGroup"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-network-select-field\n label-columns="4"\n field-columns="8"\n component="securityGroup"\n field="network"\n account="securityGroup.credentials"\n networks="securityGroup.backingData.networks"\n on-change="ctrl.registerHelpTextService()"\n ></gce-network-select-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Ft="spinnaker.securityGroup.gce.details.controller";e.module(Ft,[xe,H,Ht,Rt]).controller("gceSecurityGroupDetailsCtrl",["$scope","$state","resolvedSecurityGroup","app","securityGroupReader","$uibModal","gceSecurityGroupHelpTextService",function(n,t,a,i,l,c,r){const o=this.application=i,s=a;function d(){return l.getSecurityGroupDetails(o,s.accountId,s.provider,s.region,s.vpcId,s.name).then((function(t){if(n.state.loading=!1,!t||oe.isEmpty(t))u();else{n.securityGroup=t;const a=l.getApplicationSecurityGroup(o,s.accountId,s.region,s.name);if(n.securityGroup=e.extend(oe.cloneDeep(a),n.securityGroup),"string"==typeof n.securityGroup.targetTags){const e=n.securityGroup.targetTags;n.securityGroup.targetTags=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.sourceTags){const e=n.securityGroup.sourceTags;n.securityGroup.sourceTags=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.targetServiceAccounts){const e=n.securityGroup.targetServiceAccounts;n.securityGroup.targetServiceAccounts=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.sourceServiceAccounts){const e=n.securityGroup.sourceServiceAccounts;n.securityGroup.sourceServiceAccounts=e.substring(1,e.length-1).split(", ")}n.securityGroup.sourceRanges=oe.chain(n.securityGroup.ipRangeRules).map((e=>e.range.ip&&e.range.cidr?e.range.ip+e.range.cidr:null)).compact().uniq().value();const i=oe.map(n.securityGroup.ipRangeRules,(function(e){return{protocol:e.protocol,portRanges:e.portRanges}}));let c={};i.forEach((function(e){oe.has(c,e.protocol)?(c[e.protocol]=c[e.protocol].concat(e.portRanges),c[e.protocol]=oe.uniqBy(c[e.protocol],(function(e){return e.startPort+"->"+e.endPort}))):c[e.protocol]=e.portRanges})),c=oe.map(c,(function(e,n){return{protocol:n,portRanges:e}})),n.securityGroup.ipIngressRules=c,n.securityGroup.protocolPortRangeCount=oe.sumBy(c,(function(e){return e.portRanges.length>1?e.portRanges.length:1})),y.getAccountDetails(s.accountId).then((function(e){n.securityGroup.logsLink="https://console.developers.google.com/project/"+e.project+"/logs?service=gce_firewall_rule&minLogLevel=0&filters=text:"+s.name})),r.register(o,n.securityGroup.accountName,n.securityGroup.network)}}),u)}function u(){n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})}n.detailsTemplateUrl=p.getValue("gce","securityGroup.detailsTemplateUrl"),n.state={loading:!0,standalone:i.isStandalone},n.firewallLabel=m.get("Firewall"),o.securityGroups.ready().then((()=>d())).then((()=>{n.$$destroyed||i.isStandalone||i.securityGroups.onRefresh(n,d)})),this.getTagHelpText=function(e,n){return r.getHelpTextForTag(e,n)},this.editInboundRules=function(){c.open({templateUrl:"google/src/securityGroup/configure/editSecurityGroup.html",controller:"gceEditSecurityGroupCtrl as ctrl",size:"lg",resolve:{securityGroup:function(){return e.copy(n.securityGroup)},application:function(){return o}}})},this.cloneSecurityGroup=function(){c.open({templateUrl:"google/src/securityGroup/clone/cloneSecurityGroup.html",controller:"gceCloneSecurityGroupController as ctrl",size:"lg",resolve:{securityGroup:function(){const t=e.copy(n.securityGroup);return t.region&&(t.regions=[t.region]),t},application:function(){return o}}})},this.deleteSecurityGroup=function(){const e={application:o,title:"Deleting "+s.name};h.confirm({header:"Really delete "+s.name+"?",buttonText:"Delete "+s.name,account:s.accountId,taskMonitorConfig:e,submitMethod:function(){return L.deleteSecurityGroup(s,o,{cloudProvider:n.securityGroup.type,securityGroupName:s.name})}})},i.isStandalone&&(i.securityGroups={refresh:d})}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/editSecurityGroup.html",'<ng-form name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard\n heading="Edit {{securityGroup.name}}: {{securityGroup.accountName}}"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/clone/cloneSecurityGroup.html",'<ng-form name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard heading="Clone {{firewallLabel}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]);const Ut="spinnaker.gce.securityGroup.reader";n(Ut,[]).factory("gceSecurityGroupReader",(function(){return{resolveIndexedSecurityGroup:function(e,n,t){return e[n.account].global[t]}}}));const Ot="spinnaker.gce.securityGroup.transformer";n(Ot,[]).factory("gceSecurityGroupTransformer",["$q",function(e){return{normalizeSecurityGroup:function(n){return e.when(n)}}}]);const qt="spinnaker.deck.gce.autoscalingPolicy.basicSettings.component";n(qt,[]).component("gceAutoscalingPolicyBasicSettings",{bindings:{policy:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/basicSettings/basicSettings.component.html",controller:function(){this.modes=["ON","OFF","ONLY_SCALE_OUT"]}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/basicSettings/basicSettings.component.html",'<ng-form name="basicSettingsForm">\n <div class="row">\n <div class="col-md-3 sm-label-right">Minimum number of instances</div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n name="minNumReplicas"\n ng-model="$ctrl.policy.minNumReplicas"\n min="0"\n max="{{ $ctrl.policy.maxNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-4 error-message" ng-if="basicSettingsForm.minNumReplicas.$error.min">\n Must be greater than zero.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Maximum number of instances</div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n min="{{ $ctrl.policy.minNumReplicas }}"\n required\n name="maxNumReplicas"\n ng-model="$ctrl.policy.maxNumReplicas"\n />\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="basicSettingsForm.maxNumReplicas.$error.min || basicSettingsForm.minNumReplicas.$error.max"\n >\n Must be greater than or equal to minimum.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Cool-down period (seconds)\n <help-field key="gce.serverGroup.scalingPolicy.coolDownPeriodSec"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n min="15"\n required\n name="coolDownPeriodSec"\n ng-model="$ctrl.policy.coolDownPeriodSec"\n />\n </div>\n <div class="col-md-4 error-message" ng-if="basicSettingsForm.coolDownPeriodSec.$error.min">\n Must be greater than or equal to 15 seconds.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Mode\n <help-field key="gce.serverGroup.autoscaling.mode"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <select class="form-control input-sm" name="mode" ng-model="$ctrl.policy.mode">\n <option ng-repeat="mode in $ctrl.modes" value="{{ mode }}" ng-selected="$ctrl.policy.mode === mode">\n {{ mode }}\n </option>\n </select>\n </div>\n </div>\n <gce-scale-in-controls policy="$ctrl.policy" update-policy="$ctrl.updatePolicy"> </gce-scale-in-controls>\n</ng-form>\n')}]);const _t="spinnaker.deck.gce.autoscalingPolicy.metricSettings.component";n(_t,[]).component("gceAutoscalingPolicyMetricSettings",{bindings:{policy:"=",showNoMetricsWarning:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/metricSettings/metricSettings.component.html",controller:function(){this.$onInit=()=>{const e={cpuUtilization:!1,loadBalancingUtilization:!1,customMetricUtilizations:!0},n=Object.keys(e);function t(e){return oe.isEqual(e,{})||oe.isUndefined(e)}this.targetTypesToDisplayMap={GAUGE:"Gauge",DELTA_PER_SECOND:"Delta / second",DELTA_PER_MINUTE:"Delta / minute"},this.metricScopeTypesToDisplayMap={TIME_SERIES_PER_INSTANCE:"Time series per instance",SINGLE_TIME_SERIES_PER_GROUP:"Single time series per group"},this.scalingpolicyTypesToDisplayMap={UTILIZATION_TARGET:"Utilization target",SINGLE_INSTANCE_ASSIGNMENT:"singleInstanceAssignment"},this.addMetric=n=>{e[n]?(this.policy[n]=this.policy[n]||[],this.policy[n].push({})):t(this.policy[n])&&(this.policy[n]={utilizationTarget:null})},this.deleteMetric=(n,t)=>{e[n]?this.policy[n].splice(t,1):this.policy[n]={}},this.showMetric=e=>!t(this.policy[e]),this.isSingleTimeSeriesPerGroup=(e,n)=>{if(this.policy.customMetricUtilizations[n].metricExportScope===e)return!0},this.isScalingPolicySingleInstanceAssignment=(e,n)=>{if(this.policy.customMetricUtilizations[n].scalingpolicy===e)return!0},this.showNoMetricsWarning=()=>oe.every(n.map((n=>oe.some([e[n]&&!oe.get(this.policy,[n,"length"]),t(this.policy[n])])))),this.setUtilizationTargetFromDisplay=(e,n)=>{this.policy[e].utilizationTarget=n/100},this.initializeTargetDisplay=e=>{this[`${e}TargetDisplay`]=function(e){if(0===e)return 0;return e?Math.round(100*e):void 0}(this.policy[e].utilizationTarget)}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/metricSettings/metricSettings.component.html",'<collapsible-section heading="CPU Utilization" expanded="true" subsection="true">\n <div class="section-body">\n <ng-form name="cpuUtilization">\n <div ng-if="$ctrl.showMetric(\'cpuUtilization\')">\n <div class="row">\n <div class="col-md-4 sm-label-right">\n Utilization Target (%)\n <help-field key="gce.serverGroup.scalingPolicy.cpuUtilization"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n min="1"\n max="99"\n name="utilizationTarget"\n class="form-control input-sm"\n required\n ng-model="$ctrl.cpuUtilizationTargetDisplay"\n ng-init="$ctrl.initializeTargetDisplay(\'cpuUtilization\')"\n ng-change="$ctrl.setUtilizationTargetFromDisplay(\'cpuUtilization\', $ctrl.cpuUtilizationTargetDisplay)"\n />\n </div>\n <div class="col-md-5 error-message">\n <span ng-if="cpuUtilization.utilizationTarget.$error.min || cpuUtilization.utilizationTarget.$error.max">\n Must be between 1 and 99.</span\n >\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'cpuUtilization\')">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <gce-predictive-autoscaling policy="$ctrl.policy" update-policy="$ctrl.updatePolicy">\n </gce-predictive-autoscaling>\n </div>\n <button\n ng-hide="$ctrl.showMetric(\'cpuUtilization\')"\n class="add-new col-md-12"\n ng-click="$ctrl.addMetric(\'cpuUtilization\')"\n >\n <span class="glyphicon glyphicon-plus-sign"></span> Add CPU Utilization metric\n </button>\n </ng-form>\n </div>\n</collapsible-section>\n<collapsible-section heading="Load Balancing Utilization" expanded="true" subsection="true">\n <div class="section-body">\n <ng-form name="loadBalancingUtilization">\n <div ng-if="$ctrl.showMetric(\'loadBalancingUtilization\')" class="row">\n <div class="col-md-3 sm-label-right">\n Utilization Target (%)\n <help-field key="gce.serverGroup.scalingPolicy.loadBalancingUtilization"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n min="1"\n max="99"\n class="form-control input-sm"\n required\n name="utilizationTarget"\n ng-model="$ctrl.loadBalancingUtilizationTargetDisplay"\n ng-init="$ctrl.initializeTargetDisplay(\'loadBalancingUtilization\')"\n ng-change="$ctrl.setUtilizationTargetFromDisplay(\'loadBalancingUtilization\', $ctrl.loadBalancingUtilizationTargetDisplay)"\n />\n </div>\n <div class="col-md-5 error-message">\n <span\n ng-if="\n loadBalancingUtilization.utilizationTarget.$error.min ||\n loadBalancingUtilization.utilizationTarget.$error.max\n "\n >\n Must be between 1 and 99.\n </span>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'loadBalancingUtilization\')">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <button\n ng-hide="$ctrl.showMetric(\'loadBalancingUtilization\')"\n class="add-new col-md-12"\n ng-click="$ctrl.addMetric(\'loadBalancingUtilization\')"\n >\n <span class="glyphicon glyphicon-plus-sign"></span> Add Load Balancing metric\n </button>\n </ng-form>\n </div>\n</collapsible-section>\n\n<collapsible-section heading="Custom Metric Utilizations" expanded="true" subsection="true">\n <div class="section-body" ng-repeat="custom in $ctrl.policy.customMetricUtilizations track by $index">\n <hr ng-if="$index > 0" />\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Metric Identifier</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="custom.metric" />\n </div>\n <div class="col-md-offset-4 col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'customMetricUtilizations\', $index)">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Additional Filter Expression</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" ng-model="custom.filter" />\n </div>\n </div>\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Metric Export Scope</div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.metricScopeTypesToDisplayMap"\n ng-model="custom.metricExportScope"\n required\n ></select>\n </div>\n </div>\n\n <div ng-if="$ctrl.isSingleTimeSeriesPerGroup(\'SINGLE_TIME_SERIES_PER_GROUP\', $index)">\n <div class="row">\n <div class="col-md-3 sm-label-right">Scaling Policy</div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.scalingpolicyTypesToDisplayMap"\n ng-model="custom.scalingpolicy"\n required\n ></select>\n </div>\n </div>\n\n <div ng-if="$ctrl.isScalingPolicySingleInstanceAssignment(\'SINGLE_INSTANCE_ASSIGNMENT\', $index)">\n <div class="row">\n <div class="col-md-3 sm-label-right">Single Instance Assignment</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="singleInstanceAssignment"\n class="form-control input-sm"\n required\n ng-model="custom.singleInstanceAssignment"\n min="0"\n />\n </div>\n </div>\n </div>\n </div>\n <div\n ng-if="\n !$ctrl.isScalingPolicySingleInstanceAssignment(\'SINGLE_INSTANCE_ASSIGNMENT\', $index) ||\n !$ctrl.isSingleTimeSeriesPerGroup(\'SINGLE_TIME_SERIES_PER_GROUP\', $index)\n "\n >\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Utilization Target\n <help-field key="gce.serverGroup.scalingPolicy.customMetricUtilizations"></help-field>\n </div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="utilizationTarget"\n class="form-control input-sm"\n required\n ng-model="custom.utilizationTarget"\n />\n </div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.targetTypesToDisplayMap"\n ng-model="custom.utilizationTargetType"\n required\n >\n <option value="">-- Target Type --</option>\n </select>\n </div>\n <div class="col-md-5 error-message" ng-if="custom.utilizationTarget <= 0">\n Utilization target must be greater than 0.0.\n </div>\n </div>\n </div>\n </div>\n <button class="add-new col-md-12" ng-click="$ctrl.addMetric(\'customMetricUtilizations\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add custom metric\n </button>\n</collapsible-section>\n<div class="row">\n <div class="col-md-12" ng-if="$ctrl.showNoMetricsWarning()">\n <div class="alert alert-warning text-center">\n <i class="fa fa-exclamation-triangle pull-left"></i>\n <span>At least one metric is required.</span>\n </div>\n </div>\n</div>\n')}]);var Vt=["Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port_of_Spain","America/Port-au-Prince","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/North","Australia/NSW","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","CET","Chile/Continental","Chile/EasterIsland","CST6CDT","Cuba","EET","Egypt","Eire","EST","EST5EDT","Etc/GMT","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/GMT0","Etc/Greenwich","Etc/UCT","Etc/Universal","Etc/UTC","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","GB","GB-Eire","GMT","GMT-0","GMT+0","GMT0","Greenwich","Hongkong","HST","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","MST","MST7MDT","Navajo","NZ","NZ-CHAT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kanton","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","Poland","Portugal","PRC","PST8PDT","ROC","ROK","Singapore","Turkey","UCT","Universal","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Samoa","UTC","W-SU","WET","Zulu"];const jt="spinnaker.deck.gce.autoscalingPolicy.scalingSchedules.component";n(jt,[]).component("gceAutoscalingPolicyScalingSchedules",{bindings:{policy:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/scalingSchedules/scalingSchedules.component.html",controller:function(){this.$onInit=()=>{const e={scalingSchedules:!0};this.timezones=Vt,this.addSchedule=n=>{var t;e[n]?(this.policy[n]=this.policy[n]||[],this.policy[n].push({})):(t=this.policy[n],(oe.isEqual(t,{})||oe.isUndefined(t))&&(this.policy[n]={}))},this.deleteSchedule=(n,t)=>{e[n]?this.policy[n].splice(t,1):this.policy[n]={}},this.selectTimezone=(e,n)=>{const{scalingSchedules:t}=this.policy,a=t[n];t[n]={...a,timezone:e},this.updatePolicy({...this.policy,scalingSchedules:[...t]})}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/scalingSchedules/scalingSchedules.component.html",'<ng-form name="scalingSchedulesForm">\n <div class="section-body" ng-repeat="schedule in $ctrl.policy.scalingSchedules track by $index">\n <ng-form name="scalingSchedule">\n <hr ng-if="$index > 0" />\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Name</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="schedule.scheduleName" />\n </div>\n <div class="col-md-offset-4 col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteSchedule(\'scalingSchedules\', $index)">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Description</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" ng-model="schedule.scheduleDescription" />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Enabled</div>\n <input type="checkbox" class="col-md-1" ng-model="schedule.enabled" value="{schedule.enabled}" />\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Minimum required instances</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="minimumRequiredInstances"\n class="form-control input-sm"\n min="0"\n required\n ng-model="schedule.minimumRequiredInstances"\n />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n CRON Expression\n <help-field key="gce.serverGroup.scalingPolicy.cronExpression"></help-field>\n </div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="schedule.scheduleCron" />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Time Zone</div>\n <div class="col-md-3">\n <gce-timezone-select\n available-timezones="$ctrl.timezones"\n selected-timezone="$ctrl.policy.scalingSchedules[$index].timezone"\n select-timezone="$ctrl.selectTimezone"\n target="$index"\n ></gce-timezone-select>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Duration (seconds)</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="duration"\n class="form-control input-sm"\n required\n ng-model="schedule.duration"\n min="301"\n />\n </div>\n <div class="col-md-4 error-message" ng-if="scalingSchedule.duration.$error.min">\n Must be greater than 300 seconds.\n </div>\n </div>\n </ng-form>\n </div>\n <div class="schedule-container">\n <button class="add-new col-md-12" ng-click="$ctrl.addSchedule(\'scalingSchedules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Scaling Schedule\n </button>\n </div>\n</ng-form>\n')}]);class Kt extends ce.Component{constructor(){super(...arguments),this.loadOptions=e=>new Promise((n=>{if(!e||e.length<3)n({options:[],complete:!1});else{n({options:ge(this.props.availableTimezones).filter((n=>n.toLowerCase().includes(e))).map((e=>({value:e,label:e}))).value(),complete:!1})}}))}render(){return ce.createElement($e,{cache:null,clearable:!1,ignoreAccents:!1,loadOptions:this.loadOptions,onChange:e=>this.props.selectTimezone(e.value,this.props.target),placeholder:"Type at least 3 characters to search for an timezone...",searchPromptText:"Type at least 3 characters to search for an timezone...",value:{value:this.props.selectedTimezone,label:this.props.selectedTimezone},required:!0})}}const Wt="spinnaker.gce.timezoneSelect";n(Wt,[]).component("gceTimezoneSelect",re(i(Kt,"gceTimezoneSelect"),["availableTimezones","selectedTimezone","selectTimezone","target"]));const Zt="spinnaker.gce.serverGroup.transformer";function Xt(e){const n=e.split("/");if(n.length<2)throw new Error(`Health check url ${e} missing expected segments.`);return{healthCheckName:n[n.length-1],healthCheckKind:n[n.length-2].slice(0,-1)}}function Yt(e){const n=function(e){const n=new Set,t=new Set;return e.forEach((e=>{n.has(e.name)?t.add(e.name):n.add(e.name)})),t}(e);return e.map((e=>({displayName:n.has(e.name)?`${e.name} (${e.kind})`:e.name,kind:e.kind,name:e.name,selfLink:e.selfLink})))}n(Zt,[tn]).factory("gceServerGroupTransformer",["gceHttpLoadBalancerUtils",function(e){return{convertServerGroupCommandToDeployConfiguration:function(e){const n=e.backingData.filtered.truncatedZones,t=de({backingData:[],viewState:[]},e);return"clone"!==e.viewState.mode&&delete t.source,t.disableTraffic=!t.enableTraffic,t.cloudProvider="gce",t.availabilityZones={},t.availabilityZones[t.region]=e.zone?[e.zone]:n,t.account=t.credentials,delete t.viewState,delete t.backingData,delete t.selectedProvider,delete t.implicitSecurityGroups,delete t.enableTraffic,delete t.providerType,delete t.enableAutoHealing,t},normalizeServerGroup:function(n,t){return t.getDataSource("loadBalancers").ready().then((()=>(n.loadBalancers&&(n.loadBalancers=e.normalizeLoadBalancerNamesForAccount(n.loadBalancers,n.account,t.getDataSource("loadBalancers").data)),n)))}}}]);class Jt{static getAvailableAccelerators({backingData:e,credentials:n,distributionPolicy:t,regional:a,selectZones:i,zone:l}){const c=this.getZoneToAcceleratorTypesMap(e,n);if(l&&!a)return this.getAcceleratorTypesForZone(c,l);if(a&&i){const e=fe(t,"zones",[]).map((e=>this.getAcceleratorTypesForZone(c,e)));return we.apply(null,[...e,"name"])}return[]}static getZoneToAcceleratorTypesMap(e,n){return fe(e,["credentialsKeyedByAccount",n,"zoneToAcceleratorTypesMap"],{})}static getAcceleratorTypesForZone(e,n){return fe(e,[n,"acceleratorTypes","acceleratorTypes"],[]).map((e=>({...e,availableCardCounts:this.getAvailableCardCounts(e)})))}static getAvailableCardCounts(e){const n=Ce(e.maximumCardsPerInstance)?e.maximumCardsPerInstance:4,t=[];for(let e=1;e<=n;e*=2)t.push(e);return t}}const Qt="spinnaker.deck.gce.tagManager.service";n(Qt,[]).factory("gceTagManager",(function(){const e=["command","securityGroups","securityGroupObjectsKeyedByTag","securityGroupObjectsKeyedById"];this.reset=()=>{e.forEach((e=>delete this[e]))},this.register=e=>{this.command=e;const{credentials:a,backingData:i}=e;if(void 0!==i.securityGroups[a]){this.securityGroupObjects=oe.cloneDeep(i.securityGroups[a].gce.global),n(this.securityGroupObjects,e.tags);const{byTag:l,bySecurityGroupId:c}=t(this.securityGroupObjects);this.securityGroupObjectsKeyedByTag=l,this.securityGroupObjectsKeyedById=c}e.securityGroups||(e.securityGroups=this.inferSecurityGroupIdsFromTags(e.tags))},this.inferSecurityGroupIdsFromTags=(e=[])=>oe.chain(e).map((e=>this.securityGroupObjectsKeyedByTag[e.value])).flatten().compact().filter((e=>e.network===this.command.network)).map("id").uniq().value();const n=(e,n=[])=>{e.forEach((e=>{const t=e.targetTags;e.tagsArray=t?t.substring(1,t.length-1).split(", "):[],e.selectedTags=n?oe.intersection(n.map((e=>e.value)),e.tagsArray):[]}))},t=e=>e.reduce(((e,n)=>{const{bySecurityGroupId:t}=e;return t[n.id]=n,n.tagsArray.reduce(((e,t)=>{const{byTag:a}=e;return a[t]||(a[t]=[]),a[t].push(n),e}),e)}),{byTag:{},bySecurityGroupId:{}});this.inferSelectedSecurityGroupFromTag=oe.debounce((e=>{let n=this.securityGroupObjectsKeyedByTag[e];const t=this.command;n&&(n=oe.filter(n,(e=>e.network===this.command.network))),n&&(i(n,e),t.securityGroups=oe.map(l(a(t.securityGroups),n),"id")),this.updateSelectedTags()}),100),this.updateSelectedTags=()=>{const e=this.command,n=e.tags.map((e=>e.value));a(e.securityGroups).forEach((t=>{t.selectedTags&&(t.selectedTags=oe.intersection(t.selectedTags,n)),oe.get(t,"selectedTags.length")||(e.securityGroups=oe.without(e.securityGroups,t.id))}))},this.addTag=e=>{const n=this.command,t=n.tags;let c=this.securityGroupObjectsKeyedByTag[e];c&&(c=oe.filter(c,(e=>e.network===this.command.network))),oe.includes(t.map((e=>e.value)),e)||t.push({value:e}),c&&(i(c,e),n.securityGroups=oe.map(l(a(n.securityGroups),c),"id"))};const a=e=>e.map((e=>this.securityGroupObjectsKeyedById[e])),i=(e,n)=>{e.forEach((e=>{e.selectedTags=oe.chain(e.selectedTags||[]).concat([n]).uniq().value()}))},l=(e,n)=>oe.chain(e).concat(n).uniq().value();return this.removeTag=e=>{const n=this.command,t=n.securityGroups||[];a(t).forEach((n=>{n.selectedTags&&(n.selectedTags=n.selectedTags.filter((n=>n!==e)))})),n.tags=n.tags.filter((n=>n.value!==e))},this.removeSecurityGroup=e=>{const n=this.securityGroupObjectsKeyedById[e],t=n.selectedTags,i=this.command;a(i.securityGroups).forEach((e=>{e.selectedTags&&(e.selectedTags=oe.difference(e.selectedTags,t))})),n.selectedTags=[],i.tags=oe.chain(i.tags).map((e=>e.value)).difference(t).map((e=>({value:e}))).value()},this.getToolTipContent=e=>{const n=oe.get(this,["securityGroupObjectsKeyedByTag",e]),t=n?n.filter((e=>e.network===this.command.network)).map((e=>e.id)):[];return`This tag associates this server group with ${t.length>1?m.get("firewalls"):m.get("firewall")}\n <em>${t.join(", ")}</em>.`},this.showToolTip=e=>!!oe.get(this,["securityGroupObjectsKeyedByTag",e]),this}));const ea="spinnaker.serverGroup.configure.gce.configuration.service";e.module(ea,[St,H,k,w,cn,Qe,tn,je,Qt]).factory("gceServerGroupConfigurationService",["securityGroupReader","gceInstanceTypeService","cacheInitializer","$q","loadBalancerReader","gceCustomInstanceBuilderService","gceHttpLoadBalancerUtils","gceHealthCheckReader","gceTagManager","gceLoadBalancerSetTransformer",function(n,t,a,i,l,c,r,o,s,d){const u=["pd-standard","pd-ssd","hyperdisk-balanced"],g=["cloud-platform","userinfo.email","compute.readonly","compute","cloud.useraccounts.readonly","cloud.useraccounts","devstorage.read_only","devstorage.write_only","devstorage.full_control","taskqueue","bigquery","sqlservice.admin","datastore","logging.write","logging.read","logging.admin","monitoring.write","monitoring.read","monitoring","bigtable.data.readonly","bigtable.data","bigtable.admin","bigtable.admin.table"];function p(e){e.backingData.credentialsKeyedByAccount[e.credentials],e.distributionPolicy.targetShape||(e.distributionPolicy.targetShape="EVEN")}function m(n){const a={dirty:{}};if(n.region){const i=[a.dirty];i.push(v(n).dirty),i.push(function(e){const n=e,a={dirty:{}},i=n.regional?n.distributionPolicy.zones:[n.zone],{credentialsKeyedByAccount:l}=n.backingData,{locationToInstanceTypesMap:c}=l[n.credentials];if(i.every((e=>!e)))return a;let r=t.getAvailableTypesForLocations(c,i);r=function(e){const n=oe.map(e,(e=>{const n=e.split("-");return{class:n[0],group:n[1],index:Number(n[2])||0}})),t=oe.sortBy(n,["class","group","index"]);return oe.map(t,(e=>e.class+"-"+e.group+(e.index?"-"+e.index:"")))}(r);const o=n.instanceType;oe.every([o,!oe.includes(o,"custom-"),!oe.includes(r,o)])&&(a.dirty.instanceType=n.instanceType,n.instanceType=null);return n.backingData.filtered.instanceTypes=r,a}(n).dirty),e.extend(...i)}else n.backingData.filtered.instanceTypes=[];return a}function h(e){const n={dirty:{}},t=e.backingData.filtered,a=e.backingData.credentialsKeyedByAccount[e.credentials].locationToCpuPlatformsMap;t.cpuPlatforms=["(Automatic)"];const i=e.regional?e.region:e.zone;return oe.has(a,i)&&(t.cpuPlatforms=oe.concat(t.cpuPlatforms,a[i])),oe.includes(t.cpuPlatforms,e.minCpuPlatform)||(delete e.minCpuPlatform,n.dirty.minCpuPlatform=!0),n}function v(e){const n=e,t={dirty:{}};let a=oe.get(n,"viewState.customInstance.vCpuCount");const i=oe.get(n,"viewState.customInstance.instanceFamily"),l=oe.get(n,"viewState.customInstance.memory"),r=oe.get(n,"viewState.customInstance.extendedMemory"),{zone:o,regional:s,region:d}=n,{locationToInstanceTypesMap:u}=n.backingData.credentialsKeyedByAccount[n.credentials],g=s?d:o;return g?((o||s)&&oe.set(n,"backingData.customInstanceTypes.vCpuList",c.generateValidVCpuListForLocation(g,u)),a&&c.vCpuCountForLocationIsValid(i,a,g,u)||(a=oe.get(n,"backingData.customInstanceTypes.vCpuList[0]"),oe.set(n,"viewState.customInstance.vCpuCount",a)),oe.set(n,"backingData.customInstanceTypes.memoryList",c.generateValidMemoryListForVCpuCount(i,a)),oe.every([l,a,!c.memoryIsValid(i,l,a,r)])&&(oe.set(n,"viewState.customInstance.memory",void 0),t.dirty.instanceType=n.instanceType,n.instanceType=null),oe.set(n,"backingData.customInstanceTypes.instanceFamilyList",c.generateValidInstanceFamilyList()),t):t}function f(e){const n={dirty:{}},t=Jt.getAvailableAccelerators(e);oe.set(e,["viewState","acceleratorTypes"],t),n.dirty.acceleratorTypes=!0;const a=oe.get(e,"acceleratorConfigs",[]);return a.length>0&&(e.acceleratorConfigs=a.filter((e=>!!t.find((n=>n.name===e.acceleratorType)))),0===e.acceleratorConfigs.length&&delete e.acceleratorConfigs),n}function b(e){e.image=e.viewState.imageId;const n={dirty:{}};return e.credentials!==e.viewState.lastImageAccount&&(e.viewState.lastImageAccount=e.credentials,oe.chain(e.backingData.allImages).find({imageName:e.image}).value()||(e.image=null,n.dirty.imageName=!0)),n}function k(e){const n={dirty:{}},t=e.backingData.filtered;if(null===e.region)return n;const a=e.backingData.credentialsKeyedByAccount[e.credentials].regions;return oe.isArray(a)?(t.zones=oe.find(a,{name:e.region}).zones,t.truncatedZones=oe.takeRight(t.zones.sort(),3)):t.zones=a[e.region],oe.chain(t.zones).includes(e.zone).value()||(delete e.zone,e.regional||(n.dirty.zone=!0)),n}function S(e){return Yt(oe.filter(e.backingData.healthChecks,{account:e.credentials}))}function w(e){const n={dirty:{}},t=e.backingData.filtered;return null===e.credentials||(t.healthChecks=S(e),oe.has(e,"autoHealingPolicy.healthCheck")&&!oe.chain(t.healthChecks).map("selfLink").includes(e.autoHealingPolicy.healthCheckUrl).value()?(delete e.autoHealingPolicy.healthCheck,n.dirty.autoHealingPolicy=!0):n.dirty.autoHealingPolicy=null),n}function T(e,n){return n.region===e.region||"global"===n.region}function G(e){const n={dirty:{}},t=e.loadBalancers,a=d.normalizeLoadBalancerSet(function(e){return oe.chain(e.backingData.loadBalancers).map("accounts").flattenDeep().filter({name:e.credentials}).map("regions").flattenDeep().map("loadBalancers").flattenDeep().filter(oe.curry(T)(e)).uniq().value()}(e));if(e.backingData.filtered.loadBalancerIndex=oe.keyBy(a,"name"),e.backingData.filtered.loadBalancers=oe.map(a,"name"),t&&e.loadBalancers){e.loadBalancers=r.normalizeLoadBalancerNamesForAccount(e.loadBalancers,e.credentials,a);const t=oe.intersection(e.backingData.filtered.loadBalancers,e.loadBalancers),i=oe.xor(t,e.loadBalancers);e.loadBalancers=t,function(e){const n=e.backendServiceMetadata,t=e.backingData.filtered.loadBalancerIndex,a=e.loadBalancers.reduce(((e,a)=>(r.isHttpLoadBalancer(t[a])&&(e[a]=oe.intersection(t[a].backendServices,n)),e)),{});Object.keys(a).length>0&&(e.backendServices=a)}(e),i.length&&(n.dirty.loadBalancers=i)}return n}function A(e,n){return a.refreshCache("healthChecks").then((function(){return o.listHealthChecks()})).then((function(t){e.backingData.healthChecks=t,n||w(e)}))}function P(e){const n={dirty:{}},t=e.backingData.filtered;return null===e.region||(t.subnets=oe.chain(e.backingData.subnets).filter({account:e.credentials,network:e.network,region:e.region}).map("id").value(),oe.chain(t.subnets).includes(e.subnet).value()||(e.subnet="",n.dirty.subnet=!0)),n}function $(e){let n=e.backingData.securityGroups[e.credentials]||{gce:{}};return n=oe.filter(n.gce.global,(function(n){return n.network===e.network})),oe.chain(n).sortBy("name").value()}function x(e){const n={dirty:{}},t=e.backingData.filtered.securityGroups,a=$(e);if(t&&e.securityGroups){const i=e.securityGroups.map((function(e){const n=oe.chain(t).find({id:e}).value();return n?n.id:e})),l=e.securityGroups.map((function(e){const n=oe.chain(t).find({id:e}).value();return n?n.id:null})).map((function(e){return oe.chain(a).find({id:e}).value()})).filter((function(e){return e}));e.securityGroups=oe.map(l,"id");const c=oe.xor(i,e.securityGroups);c.length&&(n.dirty.securityGroups=c)}e.backingData.filtered.securityGroups=oe.filter(a,(function(e){return!oe.isEmpty(e.targetTags)})),e.implicitSecurityGroups=oe.filter(a,(function(e){return oe.isEmpty(e.targetTags)}));const i=(l=e.network)&&l.includes("/")?l.split("/")[0]+"/":"";var l;const c=oe.map(e.securityGroups,(e=>e.startsWith(i)?e:i+e));return e.securityGroups=oe.difference(c,oe.map(e.implicitSecurityGroups,"id")),n}function I(e,t){return a.refreshCache("securityGroups").then((function(){return n.getAllSecurityGroups().then((function(n){e.backingData.securityGroups=n,t||x(e)}))}))}function z(e){return oe.map(oe.filter(e.backingData.networks,{account:e.credentials}),"id")}return{configureCommand:function(a,c){return i.all([y.getCredentialsKeyedByAccount("gce"),n.getAllSecurityGroups(),B.listNetworksByProvider("gce"),C.listSubnetsByProvider("gce"),l.listLoadBalancers("gce"),(r=c.credentials,Xe.findImages({account:r,provider:"gce",q:"*"})),t.getAllTypesByRegion(),i.when(e.copy(u)),i.when(e.copy(g)),o.listHealthChecks(),y.listAccounts("gce")]).then((function([n,t,a,l,r,o,d,u,g,y,C]){const T={credentialsKeyedByAccount:n,securityGroups:t,networks:a,subnets:l,loadBalancers:r,allImages:o,instanceTypes:d,persistentDiskTypes:u,authScopes:g,healthChecks:y,accounts:C};let N=i.when(null),D=i.when(null),M=i.when(null);if(T.filtered={},T.distributionPolicyTargetShapes=["ANY","EVEN"],c.backingData=T,b(c),c.securityGroups&&c.securityGroups.length){const e=oe.map($(c),"id");oe.intersection(c.securityGroups,e).length<c.securityGroups.length&&(N=I(c,!0))}if(c.network){z(c).includes(c.network)||(D=function(e){B.listNetworksByProvider("gce").then((function(n){e.backingData.networks=n}))}(c))}if(c.autoScalingPolicy&&(c.enableAutoScaling=!0),c.autoHealingPolicy&&(c.enableAutoHealing=!0),oe.has(c,"autoHealingPolicy.healthCheck")){const e=S(c);oe.chain(e).map("selfLink").includes(c.autoHealingPolicy.healthCheckUrl).value()||(M=A(c,!0))}return i.all([N,D,M]).then((()=>{var n;s.register(c),(n=c).regionalChanged=function(n){const t={dirty:{}},a=n.backingData.filtered,i=Oe.defaults;return n.regional?(n.zone=null,p(n)):n.zone||(n.region===i.region?n.zone=i.zone:n.zone=a.zones[0],e.extend(t.dirty,k(n).dirty)),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.regionChanged=function(n){const t={dirty:{}},a=n.backingData.filtered;return e.extend(t.dirty,P(n).dirty),n.region?(e.extend(t.dirty,m(n).dirty),e.extend(t.dirty,k(n).dirty),e.extend(t.dirty,h(n).dirty),e.extend(t.dirty,G(n).dirty),e.extend(t.dirty,b(n).dirty)):a.zones=null,n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.credentialsChanged=function(n){const t={dirty:{}},a=n.backingData;if(n.credentials){const i=a.credentialsKeyedByAccount[n.credentials].regions;oe.isArray(i)?a.filtered.regions=oe.map(i,"name"):a.filtered.regions=oe.keys(i),a.filtered.regions.includes(n.region)?e.extend(t.dirty,n.regionChanged(n).dirty):(n.region=null,t.dirty.region=!0),a.filtered.networks=z(n),a.filtered.networks.includes(n.network)?e.extend(t.dirty,n.networkChanged(n).dirty):(n.network=null,t.dirty.network=!0),e.extend(t.dirty,w(n).dirty),e.extend(t.dirty,m(n).dirty),p(n)}else n.region=null;return n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.networkChanged=function(n){const t={dirty:{}};return n.viewState.autoCreateSubnets=oe.chain(n.backingData.networks).filter({account:n.credentials,id:n.network}).map("autoCreateSubnets").head().value(),n.viewState.subnets=oe.chain(n.backingData.networks).filter({account:n.credentials,id:n.network}).map("subnets").head().value(),e.extend(t.dirty,P(n).dirty),e.extend(t.dirty,x(n).dirty),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.zoneChanged=function(n){const t={dirty:{}};return void 0!==n.zone||n.regional||(t.dirty.zone=!0),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),e.extend(n.viewState.dirty,m(n).dirty),e.extend(n.viewState.dirty,h(n).dirty),e.extend(n.viewState.dirty,f(n).dirty),t},n.selectZonesChanged=function(n){const t={dirty:{}};return n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),e.extend(n.viewState.dirty,f(n).dirty),t},n.customInstanceChanged=function(n){const t={dirty:{}};return n.viewState.dirty=n.viewState.dirty||{},e.extend(t,n.viewState.dirty,v(n).dirty),t}}))}));var r},configureInstanceTypes:m,configureImages:b,configureZones:k,configureSubnets:P,configureLoadBalancerOptions:G,refreshLoadBalancers:function(e,n){return l.listLoadBalancers("gce").then((function(t){e.backingData.loadBalancers=t,n||G(e)}))},refreshSecurityGroups:I,refreshInstanceTypes:function(e){return a.refreshCache("instanceTypes").then((function(){return t.getAllTypesByRegion().then((function(n){e.backingData.instanceTypes=n,m(e)}))}))},refreshHealthChecks:A}}]);const na="spinnaker.gce.accelerator.component";n(na,[]).component("gceAcceleratorConfigurer",re(i((function({acceleratorConfigs:e=[],availableAccelerators:n=[],regional:t,setAcceleratorConfigs:a,zone:i}){const l=e=>{const t=n.find((({name:n})=>n===e));return fe(t,"availableCardCounts",[])},c=e=>l(e).map((e=>({label:e.toString(),value:e}))),o=(e,n)=>{const t=l(e);return t.includes(n)?n:t.reduce(((e,t)=>t>n?e:t),1)};return ce.createElement("div",{className:"form-group"},ce.createElement("div",{className:"sm-label-left"},"Accelerators ",ce.createElement(r,{id:"gce.serverGroup.accelerator"})),ce.createElement("table",{className:"table table-condensed packed tags"},ce.createElement("thead",null,ce.createElement("tr",null,ce.createElement("th",{style:{width:"75%"}},"Type"),ce.createElement("th",{style:{width:"15%"}},"Count"),ce.createElement("th",null))),ce.createElement("tbody",null,e.map((({acceleratorType:t,acceleratorCount:i},l)=>ce.createElement("tr",{key:l},ce.createElement("td",null,ce.createElement(Pe,{clearable:!1,onChange:n=>{return t=l,i=n.value,void a(e.map(((e,n)=>t!==n?e:{acceleratorType:i,acceleratorCount:o(i,e.acceleratorCount)})));var t,i},options:n.map((({description:e,name:n})=>({label:e,value:n}))),value:t})),ce.createElement("td",null,ce.createElement(Pe,{clearable:!1,onChange:n=>{return t=l,i=n.value,void a(e.map(((e,n)=>t!==n?e:{...e,acceleratorCount:i})));var t,i},options:c(t),value:i})),ce.createElement("td",null,ce.createElement("button",{className:"btn btn-link",onClick:()=>{return n=l,void a(e.filter(((e,t)=>n!==t)));var n}},ce.createElement("span",{className:"glyphicon glyphicon-trash"})))))),!se(e)&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"Adding Accelerators places constraints on the instances that you can deploy. See",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#restrictions",target:"_blank"}," ","the complete list of these restrictions")," ","for more information.")),!se(n)&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},ce.createElement("button",{className:"btn btn-block btn-sm add-new",onClick:()=>{se(n)||a(e.concat([{acceleratorType:n[0].name,acceleratorCount:1}]))}},ce.createElement("span",{className:"glyphicon glyphicon-plus-sign"})," Add Accelerator"))),se(n)&&!t&&!i&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"A zone must be selected to configure accelerators. Please note: the set of available accelerator types are limited by zone. See"," ",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#gpus-list",target:"_blank"},"the complete list of types in each zone")," ","for more information.")),se(n)&&!t&&i&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"There are no accelerators available in the currently selected zone.")),se(n)&&t&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"There are no accelerators available in all of the currently selected zone(s). Please explicitly select only zones that each support the accelerators you would like to configure. See"," ",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#gpus-list",target:"_blank"},"the complete list of types in each zone")," ","for more information.")))))}),"gceAcceleratorConfigurer"),["acceleratorConfigs","availableAccelerators","regional","setAcceleratorConfigs","zone"]));const ta="spinnaker.google.serverGroup.configure.wizard.advancedSettings.selector.directive";n(ta,[De,Qt]).directive("gceServerGroupAdvancedSettingsSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupAdvancedSettingsSelectorCtrl"}})).controller("gceServerGroupAdvancedSettingsSelectorCtrl",["$scope","gceTagManager",function(e,n){this.addTag=()=>{this.command.tags.push({})},this.removeTag=e=>{this.command.tags.splice(e,1),n.updateSelectedTags()},this.setDisks=e=>{this.command.disks=e},this.inferSelectedSecurityGroupFromTag=n.inferSelectedSecurityGroupFromTag,this.showToolTip=n.showToolTip,this.getToolTipContent=n.getToolTipContent,this.setPreemptible=()=>{this.command.preemptible?(this.command.automaticRestart=!1,this.command.onHostMaintenance="TERMINATE"):(this.command.automaticRestart=!0,this.command.onHostMaintenance="MIGRATE")},this.setEnableVtpm=()=>{this.command.enableVtpm||(this.command.enableIntegrityMonitoring=!1)},this.setAcceleratorConfigs=n=>{e.$apply((()=>{this.command.acceleratorConfigs=n}))}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.directive.html",'<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Minimum CPU Platform</b>\n <help-field key="gce.serverGroup.minCpuPlatform"></help-field>\n </div>\n <div class="col-md-6">\n <ui-select ng-model="vm.command.minCpuPlatform" class="form-control input-sm" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices\n repeat="minCpuPlatform in vm.command.backingData.filtered.cpuPlatforms | filter: $select.search"\n >\n <span ng-bind-html="minCpuPlatform | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n\n<gce-disk-configurer\n command="vm.command"\n disks="vm.command.disks"\n update-disks="vm.setDisks(disks)"\n></gce-disk-configurer>\n\n<gce-accelerator-configurer\n accelerator-configs="vm.command.acceleratorConfigs"\n regional="vm.command.regional"\n set-accelerator-configs="vm.setAcceleratorConfigs"\n zone="vm.command.zone"\n available-accelerators="vm.command.viewState.acceleratorTypes"\n></gce-accelerator-configurer>\n\n<div class="form-group">\n <div class="sm-label-left" style="margin-bottom: 5px">\n User Data <help-field key="gce.serverGroup.userData"></help-field>\n </div>\n <div class="col-md-12">\n <textarea class="form-control" ng-model="vm.command.userData" rows="3" placeholder="Plaintext custom user data">\n </textarea>\n </div>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Custom Metadata</b>\n <help-field key="gce.serverGroup.customMetadata"></help-field>\n </div>\n <map-editor model="vm.command.instanceMetadata" add-button-label="Add New Metadata" allow-empty="true"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <table class="table table-condensed packed tags">\n <b>Tags</b>\n <tbody>\n <tr ng-repeat="tag in vm.command.tags">\n <td>\n <input\n class="form-control input-sm"\n type="text"\n ng-model="tag.value"\n ng-change="vm.inferSelectedSecurityGroupFromTag(tag.value)"\n required\n />\n </td>\n <td>\n <help-field ng-if="vm.showToolTip(tag.value)" content="{{vm.getToolTipContent(tag.value)}}"></help-field>\n <a class="btn btn-link sm-label" ng-click="vm.removeTag($index)"\n ><span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="1">\n <button class="btn btn-block btn-sm add-new" ng-click="vm.addTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Tag\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Labels</b>\n <help-field key="gce.serverGroup.labels"></help-field>\n </div>\n <map-editor model="vm.command.labels" add-button-label="Add New Label" allow-empty="true"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Resource Manager Tags</b>\n <help-field key="gce.serverGroup.resourceManagerTags"></help-field>\n </div>\n <map-editor model="vm.command.resourceManagerTags" add-button-label="Add New Tag" allow-empty="false"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Partner Metadata</b>\n <help-field key="gce.serverGroup.partnerMetadata"></help-field>\n </div>\n <map-object-editor\n model="vm.command.partnerMetadata"\n add-button-label="Add New Metadata"\n allow-empty="false"\n ></map-object-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n Shielded VMs\n <help-field key="gce.serverGroup.shieldedVmConfig"></help-field>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableSecureBoot" />\n Turn on Secure Boot\n <help-field key="gce.serverGroup.shieldedVmSecureBoot"></help-field>\n </label>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableVtpm" ng-change="vm.setEnableVtpm()" />\n Turn on vTPM\n <help-field key="gce.serverGroup.shieldedVmVtpm"></help-field>\n </label>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableIntegrityMonitoring" ng-disabled="!vm.command.enableVtpm" />\n Turn on Integrity Monitoring\n <help-field key="gce.serverGroup.shieldedVmIntegrityMonitoring"></help-field>\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Preemptibility</b>\n <help-field key="gce.serverGroup.preemptibility"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.preemptible"\n ng-value="false"\n id="preemptibleFalse"\n ng-change="vm.setPreemptible()"\n />\n Off\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.preemptible"\n ng-value="true"\n id="preemptibleTrue"\n ng-change="vm.setPreemptible()"\n />\n On\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Automatic Restart</b>\n <help-field key="gce.serverGroup.automaticRestart"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.automaticRestart" ng-value="false" id="automaticRestartFalse" />\n Off\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.automaticRestart" ng-value="true" id="automaticRestartTrue" />\n On\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>On Host Maintenance</b>\n <help-field key="gce.serverGroup.onHostMaintenance"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.onHostMaintenance" ng-value="\'MIGRATE\'" id="onHostMaintenanceMigrate" />\n Migrate\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.onHostMaintenance"\n ng-value="\'TERMINATE\'"\n id="onHostMaintenanceTerminate"\n />\n Terminate\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right"><b>Associate Public IP Address</b></div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.associatePublicIpAddress"\n ng-value="true"\n id="associatePublicIpAddressTrue"\n />\n Yes\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.associatePublicIpAddress"\n ng-value="false"\n id="associatePublicIpAddressFalse"\n />\n No\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Can IP Forward</b>\n <help-field key="gce.serverGroup.canIpForward"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.canIpForward" ng-value="true" id="canIpForwardTrue" />\n Yes\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.canIpForward" ng-value="false" id="canIpForwardFalse" />\n No\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Service Account</b>\n <help-field key="gce.instance.serviceAccount"></help-field>\n </div>\n <div class="col-md-6">\n <input type="text" class="form-control input-sm" ng-model="vm.command.serviceAccountEmail" />\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Auth Scopes</b>\n <help-field key="gce.instance.authScopes"></help-field>\n </div>\n <div class="col-md-6">\n <ui-select\n multiple\n tagging\n tagging-label="(custom auth scope)"\n ng-model="vm.command.authScopes"\n class="form-control input-sm"\n >\n <ui-select-match>{{$item}}</ui-select-match>\n <ui-select-choices repeat="authScope in vm.command.backingData.authScopes | filter: $select.search">\n <span ng-bind-html="authScope | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);class aa{constructor(e){this.$scope=e,this.handleImageChange=(e,n)=>{this.$scope.$apply((()=>{n.sourceImage=e,this.handlePersistentDiskChange()}))}}$onInit(){var e,n,t,a,i,l,c,r;this.setLocalSSDCount(),this.setPersistentDisks(),this.getLocalSSDDisks().length&&!(null==(a=null==(t=null==(n=null==(e=this.command)?void 0:e.viewState)?void 0:n.instanceTypeDetails)?void 0:t.storage)?void 0:a.localSSDSupported)&&this.updateDisks({disks:this.sortDisks(this.getPersistentDisks())}),this.isDefault=!!(null==(r=null==(c=null==(l=null==(i=this.command)?void 0:i.viewState)?void 0:l.instanceTypeDetails)?void 0:c.storage)?void 0:r.isDefault)}$onChanges(){this.$onInit()}handleLocalSSDCountChange(){if(void 0===this.localSSDCount)return;let e=this.getPersistentDisks();for(let n=0;n<this.localSSDCount;n++)e=e.concat([{type:"local-ssd",sizeGb:375}]);e=this.sortDisks(e),this.updateDisks({disks:e})}handlePersistentDiskChange(){let e=this.persistentDisks.concat(this.getLocalSSDDisks());e=this.sortDisks(e),this.updateDisks({disks:e})}addPersistentDisk(){this.persistentDisks=this.persistentDisks.concat([{type:"pd-ssd",sizeGb:10,sourceImage:null}]),this.handlePersistentDiskChange()}removePersistentDisk(e){this.persistentDisks.splice(e,1),this.handlePersistentDiskChange()}setPersistentDisks(){this.persistentDisks=this.getPersistentDisks()}setLocalSSDCount(){this.localSSDCount=this.getLocalSSDDisks().length}sortDisks(e){const n=e.find((e=>(e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-"))&&void 0===e.sourceImage));return[n].concat(Be(e,n))}getLocalSSDDisks(){return(this.command.disks||[]).filter((e=>"local-ssd"===e.type))}getPersistentDisks(){return(this.command.disks||[]).filter((e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-")))}}aa.$inject=["$scope"];const ia={controller:aa,bindings:{command:"<",disks:"<",updateDisks:"&"},template:'\n <ng-form name="diskConfigurer">\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Number of Local SSD Disks</b>\n <help-field key="gce.instance.storage.localSSD"></help-field>\n </div>\n <div class="col-md-2">\n <input type="number"\n class="form-control input-sm"\n ng-model="$ctrl.localSSDCount"\n ng-change="$ctrl.handleLocalSSDCountChange()"\n required\n min="0"\n max="{{$ctrl.command.viewState.instanceTypeDetails.storage.localSSDSupported ? 8 : 0}}"\n ng-disabled="!$ctrl.command.viewState.instanceTypeDetails.storage.localSSDSupported"/>\n </div>\n </div>\n <div class="form-group">\n <div class="sm-label-left" style="margin-bottom: 5px;">Persistent Disks\n <span class="glyphicon glyphicon-warning-sign"\n style="color: #EEBB3C;"\n ng-if="$ctrl.isDefault && !diskConfigurer.$dirty"\n uib-tooltip="This instance type does not have an explicitly configured persistent disk option and is using the\n default disk storage specified in settings.js."></span>\n </div>\n <table class="table table-condensed packed tags">\n <thead>\n <tr>\n <th style="width: 25%;">Type</th>\n <th>Size (GB)</th>\n <th style="width: 50%;">Image</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="disk in $ctrl.persistentDisks">\n <td>\n <ui-select ng-model="disk.type" class="form-control input-sm" on-select="$ctrl.handlePersistentDiskChange()" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices repeat="persistentDiskType in $ctrl.command.backingData.persistentDiskTypes | filter: $select.search">\n <span ng-bind-html="persistentDiskType | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </td>\n <td>\n <input type="number"\n class="form-control input-sm"\n ng-model="disk.sizeGb"\n ng-change="$ctrl.handlePersistentDiskChange()"\n required\n min="10"/>\n </td>\n <td>\n <div ng-if="$index === 0">\n <p class="small" style="margin: 0;" ng-if="$ctrl.command.viewState.mode === \'create\' || $ctrl.command.viewState.mode === \'clone\'">\n This disk will use the image selected at the top of this dialogue.\n </p>\n <p class="small" style="margin: 0;" ng-if="$ctrl.command.viewState.mode === \'createPipeline\' || $ctrl.command.viewState.mode === \'editPipeline\'">\n This disk will use the image inferred from this pipeline\'s execution context.\n </p>\n </div>\n <gce-image-select\n ng-if="$index > 0"\n available-images="$ctrl.command.backingData.allImages"\n selected-image="disk.sourceImage"\n select-image="$ctrl.handleImageChange"\n target="disk"\n ></gce-image-select>\n </td>\n <td ng-if="$index > 0">\n <a class="btn btn-link sm-label" style="margin-top: 0;" ng-click="$ctrl.removePersistentDisk($index)">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="4">\n <button class="btn btn-block btn-sm add-new" ng-click="$ctrl.addPersistentDisk()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Persistent Disk\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n </ng-form>'},la="spinnaker.gce.diskConfigurer.component";n(la,[]).component("gceDiskConfigurer",ia);const ca={bindings:{onHealthCheckRefresh:"&",setAutoHealingPolicy:"&",healthChecks:"<",autoHealingPolicy:"<",enabled:"<",labelColumns:"@?"},templateUrl:"google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicySelector.component.html",controller:class{$onInit(){this.autoHealingPolicy&&this.autoHealingPolicy.maxUnavailable&&("number"==typeof this.autoHealingPolicy.maxUnavailable.fixed?this.viewState={maxUnavailableMetric:"fixed"}:"number"==typeof this.autoHealingPolicy.maxUnavailable.percent&&(this.viewState={maxUnavailableMetric:"percent"})),this.autoHealingPolicy||this.setAutoHealingPolicy({autoHealingPolicy:{initialDelaySec:300}})}$onDestroy(){this.setAutoHealingPolicy({autoHealingPolicy:null})}manageMaxUnavailableMetric(e){if(e){const n="percent"===e?"fixed":"percent";Te(this.autoHealingPolicy,["maxUnavailable",n],void 0)}else this.autoHealingPolicy.maxUnavailable={}}onHealthCheckChange(e,n){if(n){const{healthCheckName:e,healthCheckKind:t}=Xt(n);this.autoHealingPolicy.healthCheck=e,this.autoHealingPolicy.healthCheckKind=t}}}},ra="spinnaker.gce.autoHealingPolicy.selector.component";n(ra,[]).component("gceAutoHealingPolicySelector",ca),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicySelector.component.html",'<div class="form-group">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Health Check</b>\n </div>\n <div class="col-md-6">\n <ui-select\n ng-model="$ctrl.autoHealingPolicy.healthCheckUrl"\n on-select="$ctrl.onHealthCheckChange($item, $model)"\n class="form-control input-sm"\n required\n >\n <ui-select-match placeholder="Select...">{{ $select.selected.displayName }}</ui-select-match>\n <ui-select-choices\n repeat="healthCheck.selfLink as healthCheck in $ctrl.healthChecks | orderBy: \'displayName\' | filter: { displayName: $select.search }"\n >\n <span ng-bind-html="healthCheck.displayName | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Initial Delay</b>\n </div>\n <div class="col-md-6">\n <div class="input-group">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.autoHealingPolicy.initialDelaySec"\n min="0"\n max="2147483647"\n required\n />\n <span class="input-group-addon">seconds</span>\n </div>\n </div>\n</div>\n<div class="form-group" ng-switch on="$ctrl.viewState.maxUnavailableMetric">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Max Unavailable <help-field key="gce.serverGroup.maxUnavailable"></help-field></b>\n </div>\n <div class="col-md-3" ng-switch-default>\n <input type="number" style="width: 130px" readonly class="form-control input-sm" />\n </div>\n <div class="col-md-3" ng-switch-when="percent">\n <div class="input-group" style="width: 130px">\n <input\n class="form-control input-sm"\n ng-model="$ctrl.autoHealingPolicy.maxUnavailable.percent"\n required\n type="number"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-3" ng-switch-when="fixed">\n <input\n class="form-control input-sm"\n style="width: 130px"\n required\n ng-model="$ctrl.autoHealingPolicy.maxUnavailable.fixed"\n type="number"\n min="0"\n max="2147483647"\n />\n </div>\n <div class="col-md-3">\n <select\n ng-model="$ctrl.viewState.maxUnavailableMetric"\n ng-change="$ctrl.manageMaxUnavailableMetric($ctrl.viewState.maxUnavailableMetric)"\n ng-options="metric for metric in [\'percent\', \'fixed\']"\n class="form-control input-sm"\n >\n <option value="">-- select metric --</option>\n </select>\n </div>\n</div>\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-7 col-md-offset-{{::$ctrl.labelColumns || 3}}">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="Health checks"\n on-refresh="$ctrl.onHealthCheckRefresh()"\n >\n </gce-cache-refresh>\n </div>\n</div>\n')}]);Le(".gce-scaling-policy-wizard .error-message {\n margin-top: 4px;\n}\n.gce-scaling-policy-wizard input.form-control,\n.gce-scaling-policy-wizard select.form-control {\n margin-bottom: 2px;\n width: 100%;\n}\n.gce-scaling-policy-wizard .schedule-container {\n overflow: hidden;\n}\n");const oa="spinnaker.gce.autoScalingPolicy.selector.component";n(oa,[qt,_t,jt]).controller("gceUpsertAutoscalingPolicyCtrl",["$scope",function(e){this.$onInit=function(){this.policy=ke({}),this.setAutoScalingPolicy({autoscalingPolicy:this.policy}),this.updatePolicy=n=>{e.$applyAsync((()=>{this.policy=n,this.setAutoScalingPolicy({autoscalingPolicy:this.policy})}))}}}]).component("gceAutoScalingPolicySelectorComponent",{bindings:{policy:"<",enabled:"<",setAutoScalingPolicy:"&",autoscalingPolicy:"<"},templateUrl:"google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.html",controller:"gceUpsertAutoscalingPolicyCtrl"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.html",'<div class="gce-scaling-policy-wizard">\n <h5 class="section-heading">Basic Settings</h5>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="$ctrl.policy" update-policy="$ctrl.updatePolicy">\n </gce-autoscaling-policy-basic-settings>\n </div>\n <h5 class="section-heading">Metric Types</h5>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="$ctrl.showNoMetricsWarning"\n policy="$ctrl.policy"\n update-policy="$ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h5 class="section-heading">Scaling Schedules</h5>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="$ctrl.policy"\n update-policy="$ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n</div>\n')}]);const sa="spinnaker.deck.gce.serverGroup.configure.advancedCapacitySelector.component";n(sa,[]).component("gceServerGroupAdvancedCapacitySelector",{bindings:{command:"="},templateUrl:"google/src/serverGroup/configure/wizard/capacity/advancedCapacitySelector.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/advancedCapacitySelector.component.html",'<div class="form-group">\n <div class="col-md-12">\n <p>Sets min, max, and desired instance counts for this server group\'s autoscaling policy.</p>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-2 col-md-offset-3">Min</div>\n <div class="col-md-2">Max</div>\n <div class="col-md-2">Desired</div>\n</div>\n<div class="form-group">\n <div class="col-md-3 sm-label-right">Capacity</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.autoscalingPolicy.minNumReplicas"\n min="0"\n max="{{ $ctrl.command.autoscalingPolicy.maxNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.autoscalingPolicy.maxNumReplicas"\n min="{{ $ctrl.command.autoscalingPolicy.minNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.capacity.desired"\n min="{{ $ctrl.command.autoscalingPolicy.minNumReplicas }}"\n max="{{ $ctrl.command.autoscalingPolicy.maxNumReplicas }}"\n required\n />\n </div>\n</div>\n')}]);const da="spinnaker.google.serverGroup.configure.wizard.simpleCapacity.selector.component";n(da,[]).component("gceServerGroupSimpleCapacitySelector",{templateUrl:"google/src/serverGroup/configure/wizard/capacity/simpleCapacitySelector.component.html",bindings:{command:"=",setSimpleCapacity:"="},controller:"gceServerGroupSimpleCapacitySelectorCtrl"}).controller("gceServerGroupSimpleCapacitySelectorCtrl",(function(){this.setMinMax=e=>{this.command.capacity.min=e,this.command.capacity.max=e}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/simpleCapacitySelector.component.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right">Number of Instances</div>\n <help-field key="gce.serverGroup.capacity"></help-field>\n <div class="col-md-2">\n <input\n type="number"\n ng-change="$ctrl.setMinMax($ctrl.command.capacity.desired)"\n class="form-control input-sm"\n ng-model="$ctrl.command.capacity.desired"\n min="0"\n required\n />\n </div>\n</div>\n')}]);Le(".gce-instance-build-custom-select {\n margin-top: 5px;\n}\n.gce-instance-build-custom-select .gce-instance-build-custom-extended-memory-checkbox {\n display: flex;\n}\n.gce-instance-build-custom-select .gce-instance-build-custom-extended-memory-checkbox-helptext {\n margin: 8px 0px 0px 3px;\n}\n");class ua extends ce.Component{constructor(){super(...arguments),this.handleVCpuChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:n,memory:this.props.selectedMemory,extendedMemory:this.props.selectedExtendedMemory})},this.handleMemoryChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:n,extendedMemory:this.props.selectedExtendedMemory})},this.handleInstanceFamilyChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:n,vCpuCount:this.props.selectedVCpuCount,memory:this.props.selectedMemory,extendedMemory:"E2"!==n&&this.props.selectedExtendedMemory})},this.handleMemoryChangeCustom=e=>{const n=+e;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:n,extendedMemory:this.props.selectedExtendedMemory})},this.handleExtendedMemory=e=>{this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:this.props.selectedMemory,extendedMemory:e})}}render(){const e=(this.props.instanceFamilyList||[]).map((e=>({label:e,value:e}))),n=(this.props.vCpuList||[]).map((e=>({label:e+"",value:e}))),t=(this.props.memoryList||[]).map((e=>({label:e+"",value:e}))),a=this.props.selectedVCpuCount?this.props.selectedVCpuCount+"":null,i=this.props.selectedMemory?this.props.selectedMemory+"":null,l=this.props.selectedInstanceFamily?this.props.selectedInstanceFamily:null;return ce.createElement("div",null,ce.createElement("div",{className:"row"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Family ")),ce.createElement("div",{className:"col-md-3"},ce.createElement(Pe,{options:e,clearable:!1,value:{label:l,value:this.props.selectedInstanceFamily},onChange:this.handleInstanceFamilyChange}))),ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Cores "),ce.createElement(r,{id:"gce.instance.customInstance.cores"})),ce.createElement("div",{className:"col-md-3"},ce.createElement(Pe,{options:n,clearable:!1,value:{label:a,value:this.props.selectedVCpuCount},onChange:this.handleVCpuChange}))),ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Memory (Gb) "),ce.createElement(r,{id:"gce.instance.customInstance.memory"})),ce.createElement("div",{className:"col-md-3"},this.props.selectedExtendedMemory?ce.createElement("input",{type:"number",name:"memory",className:"form-control",value:this.props.selectedMemory,onChange:e=>this.handleMemoryChangeCustom(e.target.value)}):ce.createElement(Pe,{options:t,clearable:!1,value:{label:i,value:this.props.selectedMemory},onChange:this.handleMemoryChange}))),"E2"!==this.props.selectedInstanceFamily?ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"}),ce.createElement("div",{className:"col-md-3"},ce.createElement("span",{className:"gce-instance-build-custom-extended-memory-checkbox"},ce.createElement(o,{name:"extendedMemory",text:"Extended Memory",checked:this.props.selectedExtendedMemory,onChange:e=>{this.handleExtendedMemory(e.target.checked)}}),ce.createElement("div",{className:"gce-instance-build-custom-extended-memory-checkbox-helptext"},ce.createElement(r,{id:"gce.instance.customInstance.extendedmemory"}))))):null)}}const ga="spinnaker.gce.customInstanceConfigurer";n(ga,[]).component("gceCustomInstanceConfigurer",re(i(ua,"gceCustomInstanceConfigurer"),["vCpuList","instanceFamilyList","memoryList","selectedVCpuCount","selectedMemory","selectedInstanceFamily","selectedExtendedMemory","onChange"]));const pa="spinnaker.deck.gce.backendServiceSelector.component";n(pa,[]).component("gceBackendServiceSelector",{bindings:{command:"=",loadBalancerName:"="},templateUrl:"google/src/serverGroup/configure/wizard/loadBalancers/elSevenOptions/backendServiceSelector.component.html",controller:["$scope",function(e){e.$on("$destroy",(()=>{this.command.backendServices&&delete this.command.backendServices[this.loadBalancerName]})),e.$on("uis:select",(function(e){e.preventDefault()}))}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/elSevenOptions/backendServiceSelector.component.html",'<div class="row">\n <div class="col-md-4 sm-label-right">\n <b style="font-family: \'Source Sans 3\', sans-serif; font-size: 14px; color: #333">Backend services</b>\n </div>\n <div class="col-md-8">\n <ui-select\n multiple\n nested\n required\n ng-model="$ctrl.command.backendServices[$ctrl.loadBalancerName]"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices\n repeat="backendService in $ctrl.command.backingData.filtered.loadBalancerIndex[$ctrl.loadBalancerName].backendServices | filter: $select.search"\n >\n <span ng-bind-html="backendService | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const ma="spinnaker.deck.gce.elSevenOptionsGenerator.component";e.module(ma,[pa]).directive("gceElSevenOptionsGenerator",["$compile",function(n){return{restrict:"E",scope:{command:"=",loadBalancerName:"@"},link:function(t,a){const i=n('<gce-backend-service-selector load-balancer-name="loadBalancerName" command="command">\n </gce-backend-service-selector>')(t),l=e.element(a).closest(".ui-select-match-item");l.after(i),t.$on("$destroy",(()=>{l.next().remove()}))}}}]);const ha="spinnaker.google.serverGroup.configure.wizard.loadBalancers.selector.directive";e.module(ha,[tn,ma,ea]).directive("gceServerGroupLoadBalancerSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/loadBalancers/loadBalancerSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupLoadBalancerSelectorCtrl"}})).controller("gceServerGroupLoadBalancerSelectorCtrl",["gceHttpLoadBalancerUtils",function(n){this.showLoadBalancingPolicy=()=>{if(oe.has(this,"command.backingData.filtered.loadBalancerIndex")){const n=this.command.backingData.filtered.loadBalancerIndex,t=this.command.loadBalancers;return e.isDefined(t)&&oe.some(t,(e=>"HTTP"===n[e].loadBalancerType||"INTERNAL_MANAGED"===n[e].loadBalancerType||"SSL"===n[e].loadBalancerType||"TCP"===n[e].loadBalancerType))}},this.isHttpLoadBalancer=e=>n.isHttpLoadBalancer(e)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/loadBalancerSelector.directive.html",'<div class="row">\n <div class="col-md-12" ng-if="vm.command.viewState.dirty.loadBalancers">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following load balancers could not be found in the selected account/region and were removed:\n </p>\n <ul>\n <li ng-repeat="loadBalancer in vm.command.viewState.dirty.loadBalancers">{{loadBalancer}}</li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="vm.command.viewState.dirty.loadBalancers = null"\n >Okay</a\n >\n </p>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Load Balancers</b></div>\n <div class="col-md-9">\n <ui-select\n ng-if="vm.command.backingData.filtered.loadBalancers.length"\n multiple\n ng-model="vm.command.loadBalancers"\n class="form-control input-sm"\n >\n <ui-select-match>\n {{$item}}\n <gce-el-seven-options-generator\n ng-if="vm.isHttpLoadBalancer(vm.command.backingData.filtered.loadBalancerIndex[$item])"\n command="vm.command"\n load-balancer-name="{{:: $item}}"\n >\n </gce-el-seven-options-generator>\n </ui-select-match>\n <ui-select-choices\n repeat="loadBalancer in vm.command.backingData.filtered.loadBalancers | filter: $select.search"\n >\n <span ng-bind-html="loadBalancer | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <gce-load-balancing-policy-selector\n ng-if="vm.showLoadBalancingPolicy()"\n command="vm.command"\n ></gce-load-balancing-policy-selector>\n</div>\n')}]);Le("gce-load-balancing-policy-selector .collapsible-subsection .collapsible-subheading {\n padding-left: 4%;\n}\ngce-load-balancing-policy-selector .collapsible-subsection .ng-invalid {\n border: 1px solid var(--color-danger);\n}\n");class va{constructor(e){this.gceBackendServiceReader=e,this.maxPort=65535}setModel(e,n){Te(this,e,n/100)}setView(e,n){this[e]=this.decimalToPercent(n)}onBalancingModeChange(e){const n=["maxUtilization","maxRatePerInstance","maxConnectionsPerInstance"];let t=[];switch(e){case"RATE":t=Be(n,"maxRatePerInstance");break;case"UTILIZATION":t=Be(n,"maxUtilization");break;case"CONNECTION":t=Be(n,"maxConnectionsPerInstance")}t.forEach((e=>delete this.command.loadBalancingPolicy[e]))}getBalancingModes(){let e=[];if(Se(this,"command.backingData.filtered.loadBalancerIndex")){const n=this.command.backingData.filtered.loadBalancerIndex,t=this.command.loadBalancers,a=t.find((e=>"SSL"===fe(n[e],"loadBalancerType"))),i=t.find((e=>"TCP"===fe(n[e],"loadBalancerType"))),l=t.find((e=>"HTTP"===fe(n[e],"loadBalancerType")))||t.find((e=>"HTTP2"===fe(n[e],"loadBalancerType")))||t.find((e=>"GRPC"===fe(n[e],"loadBalancerType")));e=(a||i)&&l?["UTILIZATION"]:a||i?["CONNECTION","UTILIZATION"]:["RATE","UTILIZATION"]}return e.includes(fe(this.command,"loadBalancingPolicy.balancingMode"))||Te(this.command,"loadBalancingPolicy.balancingMode",e[0]),e}$onInit(){this.gceBackendServiceReader.listBackendServices("globalBackendService").then((e=>{this.globalBackendServices=e}))}$onDestroy(){delete this.command.loadBalancingPolicy}addNamedPort(){fe(this.command,"loadBalancingPolicy.namedPorts")||Te(this.command,"loadBalancingPolicy.namedPorts",[]),this.command.loadBalancingPolicy.namedPorts.push({name:"",port:80})}removeNamedPort(e){this.command.loadBalancingPolicy.namedPorts.splice(e,1)}getPortNames(){const e=this.command.backingData.filtered.loadBalancerIndex,n=this.command.loadBalancers,t=this.command.loadBalancingPolicy.namedPorts.map((e=>e.name));return ge(n).flatMap((n=>((n,a)=>{switch(fe(e[a],"loadBalancerType")){case"SSL":case"TCP":case"GRPC":case"HTTP2":case"HTTP":{const i=fe(e[a],"backendServices"),l=n.filter((e=>i.includes(e.name))).map((e=>e.portName)),c=Ge(l,t);return l.filter((e=>!c.includes(e)))}default:return[]}})(this.globalBackendServices,n))).uniq().value()}decimalToPercent(e){return 0===e?0:e?Math.round(100*e):void 0}}va.$inject=["gceBackendServiceReader"];const fa={bindings:{command:"="},controller:va,templateUrl:"google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html"},ba="spinnaker.gce.loadBalancingPolicy.selector.component";n(ba,[]).component("gceLoadBalancingPolicySelector",fa),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html",'<ng-form name="loadBalancingPolicySubForm">\n <collapsible-section heading="Port Name Mapping" expanded="true" subsection="true">\n <div class="form-group">\n <div class="form-group row" ng-repeat="namedPort in $ctrl.command.loadBalancingPolicy.namedPorts">\n <div class="col-md-12">\n <div class="col-md-2 sm-label-right">\n <b>Name</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.portName"></help-field>\n </div>\n <div class="col-md-3">\n <ui-select ng-model="namedPort.name" class="form-control input-sm">\n <ui-select-match>{{ namedPort.name }}</ui-select-match>\n <ui-select-choices repeat="portName in $ctrl.getPortNames()">\n <span ng-bind-html="portName"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-2 sm-label-right">\n <b>Port</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.listeningPort"></help-field>\n </div>\n <div class="col-md-3">\n <input\n type="number"\n name="listeningPort"\n required\n class="form-control input-sm"\n ng-model="namedPort.port"\n max="{{ $ctrl.maxPort }}"\n min="1"\n />\n </div>\n <div class="col-md-2 sm-label-left">\n <span\n class="glyphicon glyphicon-trash"\n uib-tooltip="Remove Named Port"\n ng-click="$ctrl.removeNamedPort($index)"\n ></span>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.listeningPort.$error.min || loadBalancingPolicySubForm.listeningPort.$error.max\n "\n >\n Must be between 1 and {{ $ctrl.maxPort }}.\n </div>\n </div>\n </div>\n <div class="col-md-11">\n <button class="btn btn-block add-new" ng-click="$ctrl.addNamedPort()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Port Name Mapping\n </button>\n </div>\n </div>\n </collapsible-section>\n <collapsible-section heading="Capacity Metrics" expanded="true" subsection="true">\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Balancing Mode</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.balancingMode"></help-field>\n </div>\n <div class="col-md-3">\n <ui-select\n ng-model="$ctrl.command.loadBalancingPolicy.balancingMode"\n on-select="$ctrl.onBalancingModeChange($item)"\n class="form-control input-sm"\n required\n >\n <ui-select-match placeholder="Select...">{{ $select.selected }}</ui-select-match>\n <ui-select-choices repeat="balancingMode in $ctrl.getBalancingModes() | filter: $select.search">\n <span ng-bind-html="balancingMode"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <div ng-switch on="$ctrl.command.loadBalancingPolicy.balancingMode">\n <div class="form-group" ng-switch-when="UTILIZATION">\n <div class="col-md-5 sm-label-right">\n <b>Max CPU utilization</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxUtilization"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxUtilization"\n required\n class="form-control input-sm"\n ng-model="$ctrl.maxUtilizationView"\n ng-init="$ctrl.setView(\'maxUtilizationView\', $ctrl.command.loadBalancingPolicy.maxUtilization)"\n ng-change="$ctrl.setModel(\'command.loadBalancingPolicy.maxUtilization\', $ctrl.maxUtilizationView)"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.maxUtilization.$error.min || loadBalancingPolicySubForm.maxUtilization.$error.max\n "\n >\n Must be between 0 and 100%.\n </div>\n </div>\n <div class="form-group" ng-switch-when="RATE">\n <div class="col-md-5 sm-label-right">\n <b>Max RPS per instance</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxRatePerInstance"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxRatePerInstance"\n required\n class="form-control input-sm"\n ng-model="$ctrl.command.loadBalancingPolicy.maxRatePerInstance"\n min="0"\n />\n </div>\n </div>\n <div class="col-md-4 error-message" ng-if="loadBalancingPolicySubForm.maxRatePerInstance.$error.min">\n Cannot be less than zero.\n </div>\n </div>\n <div class="form-group" ng-switch-when="CONNECTION">\n <div class="col-md-5 sm-label-right">\n <b>Max connections per instance</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxConnectionsPerInstance"\n required\n class="form-control input-sm"\n ng-model="$ctrl.command.loadBalancingPolicy.maxConnectionsPerInstance"\n min="0"\n />\n </div>\n </div>\n <div class="col-md-4 error-message" ng-if="loadBalancingPolicySubForm.maxConnectionsPerInstance.$error.min">\n Cannot be less than zero.\n </div>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Capacity</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.capacityScaler"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="capacityScaler"\n required\n class="form-control input-sm"\n ng-model="$ctrl.capacityScalerView"\n ng-init="$ctrl.setView(\'capacityScalerView\', $ctrl.command.loadBalancingPolicy.capacityScaler)"\n ng-change="$ctrl.setModel(\'command.loadBalancingPolicy.capacityScaler\', $ctrl.capacityScalerView)"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.capacityScaler.$error.min || loadBalancingPolicySubForm.capacityScaler.$error.max\n "\n >\n Must be between 0 and 100%.\n </div>\n </div>\n </collapsible-section>\n</ng-form>\n')}]);const ya="spinnaker.google.networkSelectField.directive";n(ya,[]).directive("gceNetworkSelectField",(function(){return{restrict:"E",templateUrl:"google/src/networkSelectField.directive.html",scope:{networks:"=",component:"=",field:"@",account:"=",helpKey:"@",onChange:"&",labelColumns:"@",fieldColumns:"@"}}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/networkSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">\n Network <help-field ng-if="helpKey" key="{{helpKey}}"></help-field>\n </div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!account">(Select an account)</div>\n <div class="col-md-{{fieldColumns || 7}}">\n <select class="form-control input-sm" ng-if="account" ng-model="component.network" ng-change="onChange()" required>\n <option ng-repeat="network in networks" value="{{network}}" ng-selected="component[field] === network">\n {{network}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const ka="spinnaker.google.subnet.subnetSelectField.directive";n(ka,[]).directive("gceSubnetSelectField",(function(){return{restrict:"E",templateUrl:"google/src/subnet/subnetSelectField.directive.html",scope:{subnets:"=",subnetPlaceholder:"=",autoCreateSubnets:"=",component:"=",field:"@",account:"=",region:"=",onChange:"&",labelColumns:"@",helpKey:"@"}}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/subnet/subnetSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">\n Subnet\n <help-field key="{{helpKey}}"></help-field>\n </div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="subnetPlaceholder">{{subnetPlaceholder}}</div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!subnetPlaceholder">\n <select\n class="form-control input-sm"\n ng-if="account"\n ng-model="component.subnet"\n ng-change="onChange()"\n ng-required="autoCreateSubnets === false"\n >\n <option ng-repeat="subnet in subnets" value="{{subnet}}" ng-selected="component[field] === subnet">\n {{subnet}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const Sa="spinnaker.google.serverGroup.configure.wizard.basicSettings.controller";e.module(Sa,[xe,Ie,F,Jn,ya,ka]).controller("gceServerGroupBasicSettingsCtrl",["$scope","$controller","$uibModalStack","$state",function(n,t,a,i){const l=new Me;l.pipe(Re((function(){return Ee(Xe.findImages({account:n.command.credentials,provider:n.command.selectedProvider,q:"*"}))}))).subscribe((e=>{n.command.backingData.allImages=e})),this.accountUpdated=()=>{l.next()},this.selectImage=e=>{n.$apply((()=>{n.command.image=e}))},e.extend(this,t("BasicSettingsMixin",{$scope:n,imageReader:Xe,$uibModalStack:a,$state:i})),this.stackPattern={test:function(e){return(n.command.viewState.templatingEnabled?/^([a-zA-Z0-9]*(\${.+})*)*$/:/^[a-zA-Z0-9]*$/).test(e)}},this.detailPattern={test:function(e){return(n.command.viewState.templatingEnabled?/^([a-zA-Z0-9-]*(\${.+})*)*$/:/^[a-zA-Z0-9-]*$/).test(e)}},this.getSubnetPlaceholder=()=>n.command.region?n.command.viewState.autoCreateSubnets?"(Subnet will be automatically selected)":null===n.command.viewState.autoCreateSubnets?"(Subnets not supported)":null:"(Select an account)",this.imageSources=["artifact","priorStage"],this.excludedImageArtifactTypes=U(O.CUSTOM_OBJECT),this.onImageArtifactEdited=e=>{n.$applyAsync((()=>{n.command.imageArtifactId=null,n.command.imageArtifact=e}))},this.onImageArtifactSelected=e=>{this.onChangeImageArtifactId(e.id)},this.onChangeImageArtifactId=e=>{n.$applyAsync((()=>{n.command.imageArtifactId=e,n.command.imageArtifact=null}))},this.onImageArtifactAccountSelected=e=>{n.$applyAsync((()=>{n.command.imageAccountName=e}))};const c=new q(n);n.gceImageArtifact={showCreateArtifactForm:!1,delegate:c,controller:new V(c)}}]);const wa="spinnaker.deck.gce.tagSelector.component";n(wa,[Qt]).component("gceTagSelector",{bindings:{command:"=",securityGroupId:"="},templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/tagSelector.component.html",controller:["$scope","gceTagManager",function(e,n){this.securityGroup=n.securityGroupObjectsKeyedById[this.securityGroupId],this.onSelect=n.addTag,this.onRemove=n.removeTag,e.$on("uis:select",(function(e){e.preventDefault()}))}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/tagSelector.component.html",'<div class="row">\n <div class="col-md-4 sm-label-right">\n <b style="font-family: \'Source Sans 3\', sans-serif; font-size: 14px; color: #333"\n >Target Tags\n <help-field\n ng-if="!$ctrl.securityGroup.selectedTags.length"\n key="gce.serverGroup.securityGroups.targetTags"\n ></help-field>\n </b>\n </div>\n <div class="col-md-8">\n <ui-select\n multiple\n on-remove="$ctrl.onRemove($item)"\n on-select="$ctrl.onSelect($item)"\n nested\n ng-model="$ctrl.securityGroup.selectedTags"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="tag in $ctrl.securityGroup.tagsArray | filter: $select.search">\n <span ng-bind-html="tag | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Ca="spinnaker.deck.gce.tagSelectorGenerator.component";e.module(Ca,[wa,Qt]).directive("gceTagSelectorGenerator",["$compile","gceTagManager",function(n,t){return{restrict:"E",scope:{command:"=",securityGroupId:"="},link:function(a,i){const{securityGroupId:l}=a,c=t.securityGroupObjectsKeyedById[l];if(c&&c.tagsArray.length<2)return void c.tagsArray.forEach((e=>t.addTag(e)));const r=n('<gce-tag-selector security-group-id="securityGroupId" command="command">\n </gce-tag-selector>')(a);e.element(i).closest(".ui-select-match-item").after(r)}}}]);const Ba="spinnaker.google.serverGroup.configure.wizard.securityGroups.selector.directive";n(Ba,[ea,Ca,Qt]).directive("gceServerGroupSecurityGroupSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/securityGroupSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupSecurityGroupsSelectorCtrl"}})).controller("gceServerGroupSecurityGroupsSelectorCtrl",["gceServerGroupConfigurationService","gceTagManager",function(e,n){this.getSecurityGroupRefreshTime=()=>d.get("securityGroups").getStats().ageMax,this.refreshSecurityGroups=()=>{this.refreshing=!0,e.refreshSecurityGroups(this.command).then((()=>{this.refreshing=!1}))},this.onRemove=n.removeSecurityGroup}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroupSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b><firewall-label label="Firewalls"></firewall-label></b>\n </div>\n <div class="col-md-9">\n <ui-select\n ng-if="vm.command.backingData.filtered.securityGroups.length"\n multiple\n on-remove="vm.onRemove($item.id)"\n ng-model="vm.command.securityGroups"\n class="form-control input-sm"\n >\n <ui-select-match\n >{{$item.name}}\n <gce-tag-selector-generator security-group-id="$item.id" command="vm.command"> </gce-tag-selector-generator>\n </ui-select-match>\n <ui-select-choices\n repeat="securityGroup.id as securityGroup in vm.command.backingData.filtered.securityGroups | anyFieldFilter: {name: $select.search, id: $select.search}"\n >\n <span ng-bind-html="securityGroup.name | highlight: $select.search"></span>\n (<span ng-bind-html="securityGroup.id | highlight: $select.search"></span>)\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-9 col-md-offset-3 checkbox">\n <p>\n <label\n ><input type="checkbox" ng-model="vm.command.viewState.listImplicitSecurityGroups" /> Show Implicit\n <firewall-label label="Firewalls"></firewall-label>\n </label>\n <help-field key="gce.serverGroup.securityGroups.implicit"></help-field>\n </p>\n </div>\n</div>\n\n<div class="form-group" ng-if="vm.command.viewState.listImplicitSecurityGroups">\n <div class="col-md-3 sm-label-right">\n <b>Implicit <firewall-label label="Firewalls"></firewall-label></b>\n </div>\n <div class="col-md-9">\n <ul ng-if="vm.command.implicitSecurityGroups.length" style="margin-top: 10px; list-style-type: none">\n <li ng-repeat="securityGroup in vm.command.implicitSecurityGroups">{{securityGroup.name}}</li>\n </ul>\n <ul ng-if="!vm.command.implicitSecurityGroups.length" style="margin-top: 10px; list-style-type: none">\n <li>None</li>\n </ul>\n </div>\n</div>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-9 col-md-offset-3">\n <p>\n <span ng-if="refreshing"><span class="fa fa-sync-alt fa-spin"></span></span>\n <firewall-label label="Firewalls"></firewall-label>\n <span ng-if="!refreshing">last refreshed {{ vm.getSecurityGroupRefreshTime() | timestamp }}</span>\n <span ng-if="refreshing"> refreshing...</span>\n </p>\n <p>\n If you\'re not finding a <firewall-label label="firewall"></firewall-label> that was recently added,\n <a href ng-click="vm.refreshSecurityGroups()">click here</a> to refresh the list.\n </p>\n </div>\n</div>\n')}]);const Ta="spinnaker.google.serverGroup.configure.wizard.securityGroups.removed.directive";n(Ta,[]).directive("gceServerGroupSecurityGroupsRemoved",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/securityGroupsRemoved.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupSecurityGroupsRemovedCtrl"}})).controller("gceServerGroupSecurityGroupsRemovedCtrl",(function(){this.acknowledgeSecurityGroupRemoval=()=>{this.command.viewState.dirty.securityGroups=null}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroupsRemoved.directive.html",'<div class="col-md-12" ng-if="vm.command.viewState.dirty.securityGroups">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following <firewall-label label="firewalls"></firewall-label> could\n not be found in the selected account/network and were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in vm.command.viewState.dirty.securityGroups">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="vm.acknowledgeSecurityGroupRemoval()">Okay</a>\n </p>\n </div>\n</div>\n')}]);const Ga="spinnaker.google.serverGroup.configure.wizard.capacity.regional.directive";e.module(Ga,[]).directive("gceRegionalSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/regionalSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:e.noop}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/regionalSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Regional</b></div>\n <div class="col-md-7 checkbox">\n <label\n ><input type="checkbox" ng-model="vm.command.regional" />\n Distribute instances across multiple zones\n </label>\n <label ng-if="vm.command.regional"\n ><input type="checkbox" ng-model="vm.command.selectZones" />\n Select zones explicitly\n </label>\n </div>\n</div>\n')}]);const Aa="spinnaker.google.serverGroup.configure.wizard.capacity.targetShape.directive";n(Aa,[]).directive("gceTargetShapeSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/targetShapeSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:t}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/targetShapeSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Target Shape</b></div>\n <div class="col-md-3">\n <ui-select ng-model="vm.command.distributionPolicy.targetShape" class="form-control input-sm" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices\n repeat="targetShape in vm.command.backingData.distributionPolicyTargetShapes | filter: $select.search"\n >\n <span ng-bind-html="targetShape | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Pa="spinnaker.google.serverGroup.configure.wizard.capacity.zone.directive";e.module(Pa,[]).directive("gceZoneSelector",(function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:e.noop}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html",'<div class="form-group" ng-if="!vm.command.regional">\n <div class="col-md-3 sm-label-right">Zone</div>\n <div class="col-md-7" ng-if="!vm.command.region">(Select a region)</div>\n <div class="col-md-7">\n <select\n class="form-control input-sm"\n ng-model="vm.command.zone"\n ng-options="zone for zone in vm.command.backingData.filtered.zones"\n ng-required="!vm.command.regional"\n ></select>\n </div>\n</div>\n\n<div class="form-group" ng-if="vm.command.regional && vm.command.selectZones">\n <div class="col-md-3 sm-label-right">Zones</div>\n <div class="col-md-7">\n <ui-select multiple ng-model="vm.command.distributionPolicy.zones" class="form-control input-sm">\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="zone as zone in vm.command.backingData.filtered.zones">\n <span>{{ zone }}</span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const $a="spinnaker.serverGroup.configure.gce";n($a,[qt,_t,jt,ba,oa,ra,Qe,Cn,ga,la,na,Je,Wt,Zt,ea,ta,sa,da,ha,Sa,Ta,Ba,Ga,Aa,Pa]);const xa="spinnaker.deck.gce.serverGroup.hiddenMetadataKeys.value";n(xa,[]).value("gceServerGroupHiddenMetadataKeys",["load-balancer-names","global-load-balancer-names","backend-service-names","load-balancing-policy","select-zones","customUserData"]);const Ia="spinnaker.gce.serverGroupCommandBuilder.service";e.module(Ia,[j,We,Qe,xa]).factory("gceServerGroupCommandBuilder",["$q","instanceTypeService","gceCustomInstanceBuilderService","gceServerGroupHiddenMetadataKeys","gceXpnNamingService",function(n,t,a,i,l){function c(e){const n=l.deriveProjectId(e.launchConfig.instanceTemplate),t=oe.get(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].network");return l.decorateXpnResourceIfNecessary(n,t)}function r(e){const n=l.deriveProjectId(e.launchConfig.instanceTemplate),t=oe.get(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].subnetwork");return l.decorateXpnResourceIfNecessary(n,t)}function o(e){return oe.has(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].accessConfigs")}function s(e){return(e.disks||[]).filter((e=>"local-ssd"===e.type))}function d(e){return(e.disks||[]).filter((e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-")))}function u(e,n){if(e.storage.localSSDSupported)s(n).length!==e.storage.count&&(n.viewState.overriddenStorageDescription=s(n).length+"×375");else{const t=d(n);(t.some((n=>n.sizeGb!==e.storage.size))||t.length!==e.storage.count)&&(n.viewState.overriddenStorageDescription=function(e){const n=d(e),t=new Map;return n.forEach((e=>{t.has(e.sizeGb)?t.set(e.sizeGb,t.get(e.sizeGb)+1):t.set(e.sizeGb,1)})),Array.from(t).sort((([e],[n])=>n-e)).map((([e,n])=>n+"×"+e)).join(", ")}(n))}}function g(e,n){const t=e.autoHealingPolicy;if(t){const e=t.healthCheckUrl?t.healthCheckUrl:t.healthCheck;if(e){const{healthCheckName:a,healthCheckKind:i}=Xt(e);n.autoHealingPolicy={healthCheck:a,healthCheckKind:i,healthCheckUrl:e,initialDelaySec:t.initialDelaySec}}const a=t.maxUnavailable;a&&(n.autoHealingPolicy.maxUnavailable=a,n.viewState.maxUnavailableMetric="number"==typeof a.percent?"percent":"fixed")}}function p(n,t){if(n){let a="",l=[];if(e.isArray(n)){const e=n.find((e=>"customUserData"===e.key));e&&(a=e.value,l=m(a),t.userData=a),n.forEach((function(e){oe.includes(l,e.key)||oe.includes(i,e.key)||(t.instanceMetadata[e.key]=e.value)}))}else n.customUserData&&(a=n.customUserData,l=m(a),t.userData=a,n=oe.omit(n,l)),e.extend(t.instanceMetadata,oe.omit(n,i))}t.labels["spinnaker-moniker-sequence"]&&delete t.labels["spinnaker-moniker-sequence"]}function m(e){const n=[];return e.split(/\n|,/).forEach((function(e){const t=e.split("=")[0];n.push(t)})),n}function h(e,n){e&&e.items&&oe.map(e.items,(function(e){n.tags.push({value:e})}))}function v(e,n){e&&Object.assign(n.resourceManagerTags,e)}function f(e,n){e&&Object.assign(n.partnerMetadata,e)}function b(e,n){const t=(n=n||{}).account||Oe.defaults.account,a=n.region||Oe.defaults.region,i=n.zone||Oe.defaults.zone,l=!oe.has(e,"attributes.providerSettings.gce.associatePublicIpAddress")||e.attributes.providerSettings.gce.associatePublicIpAddress,c={application:e.name,credentials:t,region:a,zone:i,regional:!1,selectZones:!1,distributionPolicy:{zones:[]},network:"default",associatePublicIpAddress:l,canIpForward:!1,strategy:"",capacity:{min:0,max:0,desired:1},backendServiceMetadata:[],minCpuPlatform:"(Automatic)",disks:[{type:"pd-ssd",sizeGb:10},{type:"local-ssd",sizeGb:375}],imageSource:"priorStage",instanceMetadata:{},tags:[],labels:{},resourceManagerTags:{},partnerMetadata:{},enableSecureBoot:!1,enableVtpm:!1,enableIntegrityMonitoring:!1,preemptible:!1,automaticRestart:!0,onHostMaintenance:"MIGRATE",serviceAccountEmail:"default",authScopes:["cloud.useraccounts.readonly","devstorage.read_only","logging.write","monitoring.write"],enableTraffic:!0,cloudProvider:"gce",selectedProvider:"gce",availabilityZones:[],viewState:{instanceProfile:"custom",allImageSelection:null,useSimpleCapacity:!0,usePreferredZones:!0,listImplicitSecurityGroups:!1,mode:n.mode||"create",disableStrategySelection:!0,expectedArtifacts:[]}};return e.attributes&&e.attributes.platformHealthOnlyShowOverride&&e.attributes.platformHealthOnly&&(c.interestingHealthProviderNames=["Google"]),function(e,n,t){return y.listAccounts("gce").then((function(e){const a=oe.map(e,"name"),i=a[0],l=n&&a.includes(n);t.credentials=l?n:i||"my-account-name"}))}(0,t,c).then((()=>c))}return{buildNewServerGroupCommand:b,buildNewServerGroupCommandForPipeline:function(e,t){const a=I.getExpectedArtifactsAvailableToStage(e,t);return n.when({viewState:{pipeline:t,expectedArtifacts:a,requiresTemplateSelection:!0,stage:e}})},buildServerGroupCommandFromExisting:function(i,l,s){s=s||"clone";const d=l.moniker,m={application:i.name,autoscalingPolicy:oe.cloneDeep(l.autoscalingPolicy),strategy:"",stack:d.stack,freeFormDetails:d.detail,credentials:l.account,loadBalancers:(b=l.asg,["load-balancer-names","global-load-balancer-names"].reduce(((e,n)=>(b[n]&&(e=e.concat(b[n])),e)),[])),loadBalancingPolicy:oe.cloneDeep(l.loadBalancingPolicy),backendServiceMetadata:l.asg["backend-service-names"],securityGroups:l.securityGroups,region:l.region,capacity:{min:l.asg.minSize,max:l.asg.maxSize,desired:l.asg.desiredCapacity},regional:l.regional,network:c(l),subnet:r(l),associatePublicIpAddress:o(l),canIpForward:l.canIpForward,minCpuPlatform:l.launchConfig.minCpuPlatform||"(Automatic)",instanceMetadata:{},tags:[],labels:{},resourceManagerTags:{},partnerMetadata:{},availabilityZones:[],enableSecureBoot:l.enableSecureBoot,enableVtpm:l.enableVtpm,enableIntegrityMonitoring:l.enableIntegrityMonitoring,enableTraffic:!0,cloudProvider:"gce",selectedProvider:"gce",distributionPolicy:{zones:l.distributionPolicy?l.distributionPolicy.zones:[],targetShape:l.distributionPolicy?l.distributionPolicy.targetShape:null},selectZones:l.selectZones,source:{account:l.account,region:l.region,serverGroupName:l.name,asgName:l.name},viewState:{allImageSelection:null,useSimpleCapacity:!l.autoscalingPolicy,usePreferredZones:!1,listImplicitSecurityGroups:!1,mode:s}};var b;if(m.regional||(m.zone=l.zones[0],m.source.zone=l.zones[0]),i.attributes&&i.attributes.platformHealthOnlyShowOverride&&i.attributes.platformHealthOnly&&(m.interestingHealthProviderNames=["Google"]),g(l,m),l.launchConfig){const i=l.launchConfig.instanceType;return e.extend(m,{instanceType:i}),oe.includes(i,"custom-")&&(m.viewState.customInstance=a.parseInstanceTypeString(i),m.viewState.instanceProfile="buildCustom"),m.viewState.imageId=l.launchConfig.imageId,function(e){return t.getCategories("gce").then((function(n){n.forEach((function(n){n.families.forEach((function(t){t.instanceTypes.forEach((function(t){t.name===e.instanceType&&(e.viewState.instanceProfile=n.type)}))}))}))}))}(m).then((function(){return function(e,n){e?(n.preemptible=e.preemptible,n.automaticRestart=e.automaticRestart,n.onHostMaintenance=e.onHostMaintenance):(n.preemptible=!1,n.automaticRestart=!0,n.onHostMaintenance="MIGRATE")}(l.launchConfig.instanceTemplate.properties.scheduling,m),p(l.launchConfig.instanceTemplate.properties.metadata.items,m),h(l.launchConfig.instanceTemplate.properties.tags,m),function(e,n){e&&(Object.assign(n.labels,e),n.labels["spinnaker-region"]&&delete n.labels["spinnaker-region"],n.labels["spinnaker-server-group"]&&delete n.labels["spinnaker-server-group"],n.labels["spinnaker-moniker-sequence"]&&delete n.labels["spinnaker-moniker-sequence"])}(l.instanceTemplateLabels,m),function(e,n){e&&e.length?(n.serviceAccountEmail=e[0].email,n.authScopes=oe.map(e[0].scopes,(e=>e.replace("https://www.googleapis.com/auth/","")))):n.authScopes=[]}(l.launchConfig.instanceTemplate.properties.serviceAccounts,m),v(l.launchConfig.instanceTemplate.properties.resourceManagerTags,m),f(l.launchConfig.instanceTemplate.properties.partnerMetadata,m),function(e,a){const i=(e=e.map(((e,n)=>0===n?{type:e.initializeParams.diskType,sizeGb:e.initializeParams.diskSizeGb}:{type:e.initializeParams.diskType,sizeGb:e.initializeParams.diskSizeGb,sourceImage:oe.last(oe.get(e,"initializeParams.sourceImage","").split("/"))||null}))).filter((e=>"local-ssd"===e.type)),l=e.filter((e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-")));return l.length?(a.disks=l.concat(i),t.getInstanceTypeDetails(a.selectedProvider,oe.includes(a.instanceType,"custom-")?"buildCustom":a.instanceType).then((e=>{a.viewState.instanceTypeDetails=e,u(e,a)}))):(a.disks=[{type:"pd-ssd",sizeGb:10}].concat(i),n.when(null))}(l.launchConfig.instanceTemplate.properties.disks,m).then((function(){return m}))}))}return n.when(m)},buildServerGroupCommandFromPipeline:function(i,l,c,r){const o=oe.cloneDeep(l),m=Object.keys(o.availabilityZones)[0],y=o.zone,k=t.getCategoryForInstanceType("gce",o.instanceType),S={account:o.account,region:m,zone:y};return n.all([b(i,S),k]).then((function([i,l]){const b=I.getExpectedArtifactsAvailableToStage(c,r),y={pipeline:r,stage:c,instanceProfile:l,disableImageSelection:!0,expectedArtifacts:b,showImageSourceSelector:!0,useSimpleCapacity:!o.autoscalingPolicy,mode:"editPipeline",submitButtonLabel:"Done",customInstance:"buildCustom"===l?a.parseInstanceTypeString(o.instanceType):null,templatingEnabled:!0},k={region:m,credentials:o.account,enableTraffic:!o.disableTraffic,viewState:y};o.strategy=o.strategy||"";const S=e.extend({},i,o,k);return function(e){const a=d(e),i=s(e);return a.length?(e.disks=a.concat(i),t.getInstanceTypeDetails(e.selectedProvider,oe.includes(e.instanceType,"custom-")?"buildCustom":e.instanceType).then((n=>{e.viewState.instanceTypeDetails=n,u(n,e)}))):(e.disks=[{type:"pd-ssd",sizeGb:10}].concat(i),n.when(null))}(S).then((function(){const e=S.instanceMetadata;var n;S.loadBalancers=(n=e,["load-balancer-names","global-load-balancer-names"].reduce(((e,t)=>(n[t]&&(e=e.concat(n[t].split(","))),e)),[])),S.backendServiceMetadata=e["backend-service-names"]?e["backend-service-names"].split(","):[],S.minCpuPlatform=o.minCpuPlatform||"(Automatic)",S.instanceMetadata={},p(e,S),g(o,S),function(e,n){n.enableSecureBoot=e.enableSecureBoot,n.enableVtpm=e.enableVtpm,n.enableIntegrityMonitoring=e.enableIntegrityMonitoring}(o,S);const t={items:S.tags};S.tags=[],h(t,S);v(S.resourceManagerTags,S);return f(S.partnerMetadata,S),S}))}))}}}]);const za="spinnaker.serverGroup.configure.gce.cloneServerGroup";e.module(za,[xe,Qe,j,xa,Qt]).controller("gceCloneServerGroupCtrl",["$scope","$uibModalInstance","$q","$state","$log","serverGroupWriter","gceServerGroupConfigurationService","serverGroupCommand","application","title","gceCustomInstanceBuilderService","instanceTypeService","wizardSubFormValidation","gceServerGroupHiddenMetadataKeys","gceTagManager",function(n,t,a,i,l,c,r,o,s,d,u,g,p,h,v){function f(){if(n.$$destroyed)return;const e=n.taskMonitor.task.execution.stages.find((e=>"cloneServerGroup"===e.type));if(e&&e.context["deploy.server.groups"]){const t=e.context["deploy.server.groups"][n.command.region];if(t){const e={serverGroup:t,accountId:n.command.credentials,region:n.command.region,provider:"gce"};let a="^.^.^.clusters.serverGroup";i.includes("**.clusters.serverGroup")&&(a="^.serverGroup"),i.includes("**.clusters.cluster.serverGroup")&&(a="^.^.serverGroup"),i.includes("**.clusters")&&(a=".serverGroup"),i.go(a,e)}}}function b(){r.configureCommand(s,o).then((function(){n.state.loaded=!0,k(n.command.credentialsChanged(n.command)),k(n.command.regionalChanged(n.command)),k(n.command.regionChanged(n.command)),k(n.command.networkChanged(n.command)),k(n.command.zoneChanged(n.command)),k(n.command.customInstanceChanged(n.command)),r.configureSubnets(n.command),n.$watch("command.credentials",y(n.command.credentialsChanged)),n.$watch("command.regional",y(n.command.regionalChanged)),n.$watch("command.region",y(n.command.regionChanged)),n.$watch("command.network",y(n.command.networkChanged)),n.$watch("command.zone",y(n.command.zoneChanged)),n.$watch("command.viewState.instanceTypeDetails",(function(e){n.command.viewState.initialized?e&&e.storage&&e.storage.defaultSettings&&(n.command.disks=e.storage.defaultSettings.disks,delete n.command.viewState.overriddenStorageDescription):n.command.viewState.initialized=!0})),n.$watch("command.selectZones",y(n.command.selectZonesChanged)),n.$watch("command.distributionPolicy.zones",y(n.command.selectZonesChanged)),n.$watch("command.viewState.customInstance",(()=>{n.command.customInstanceChanged(n.command),function(){const e=n.command,t=e.regional?e.region:e.zone,{locationToInstanceTypesMap:a}=e.backingData.credentialsKeyedByAccount[e.credentials],i=oe.get(e,"viewState.customInstance.extendedMemory"),l=[oe.get(e,"viewState.customInstance.instanceFamily"),oe.get(e,"viewState.customInstance.vCpuCount"),oe.get(e,"viewState.customInstance.memory")];oe.every([...l,u.customInstanceChoicesAreValid(...l,t,a,i)])&&(e.instanceType=u.generateInstanceTypeString(...l,i),g.getInstanceTypeDetails(e.selectedProvider,"buildCustom").then((n=>{e.viewState.instanceTypeDetails=n})))}()}),!0),p.config({scope:n,form:"form"}).register({page:"location",subForm:"basicSettings"}).register({page:"capacity",subForm:"capacitySubForm"}).register({page:"zones",subForm:"zonesSubForm"}).register({page:"load-balancers",subForm:"loadBalancerSubForm"}).register({page:"autohealing-policy",subForm:"autoHealingPolicySubForm"}).register({page:"autoscaling-policy",subForm:"autoScalingPolicySubForm"})})).catch((e=>{l.error("Error generating server group command: ",e)}))}function y(e){return function(){k(e(n.command))}}function k(e){e.dirty.loadBalancers&&P.markDirty("load-balancers"),e.dirty.securityGroups&&P.markDirty("security-groups"),e.dirty.availabilityZones&&P.markDirty("capacity"),e.dirty.instanceType&&P.markDirty("instance-type")}n.pages={templateSelection:"google/src/serverGroup/configure/wizard/templateSelection/templateSelection.html",basicSettings:"google/src/serverGroup/configure/wizard/location/basicSettings.html",loadBalancers:"google/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html",securityGroups:"google/src/serverGroup/configure/wizard/securityGroups/securityGroups.html",instanceType:"google/src/serverGroup/configure/wizard/instanceType/instanceType.html",capacity:"google/src/serverGroup/configure/wizard/capacity/capacity.html",autoHealingPolicy:"google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicy.html",autoScalingPolicy:"google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicy.html",zones:"google/src/serverGroup/configure/wizard/capacity/zones.html",advancedSettings:"google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html"},n.firewallsLabel=m.get("Firewalls"),n.title=d,n.applicationName=s.name,n.application=s,n.command=o,n.state={loaded:!1,requiresTemplateSelection:!!o.viewState.requiresTemplateSelection},this.templateSelectionText={copied:["account, region, subnet, cluster name (stack, details)","load balancers",m.get("firewalls"),"instance type","all fields on the Advanced Settings page"],notCopied:[]},n.command.viewState.disableStrategySelection||this.templateSelectionText.notCopied.push("the deployment strategy (if any) used to deploy the most recent server group"),n.taskMonitor=new G({application:s,title:"Creating your server group",modalInstance:t,onTaskComplete:function(){s.serverGroups.refresh(),s.serverGroups.onNextRefresh(n,f)}}),this.isValid=function(){const e=n.command.selectZones&&oe.get(n,"command.distributionPolicy.zones.length")>=1,t=n.command.autoscalingPolicy;return n.command&&(n.command.viewState.disableImageSelection||n.command.image)&&n.command.application&&n.command.credentials&&n.command.instanceType&&n.command.region&&(n.command.regional||n.command.zone)&&null!==n.command.capacity.desired&&(!n.command.selectZones||e)&&n.form.$valid&&(!t||t&&(!se(t.cpuUtilization)||!se(t.customMetricUtilizations)||!se(t.loadBalancingUtilization)))&&P.isComplete()},this.showSubmitButton=function(){return P.allPagesVisited()},this.submit=function(){const a=function(e,n,t){let a={};oe.get(e,"length")>0&&(a=e.reduce(((e,t)=>{const a=n[t];return"HTTP"===a.loadBalancerType?e["global-load-balancer-names"]=e["global-load-balancer-names"].concat(a.listeners.map((e=>e.name))):"INTERNAL_MANAGED"===a.loadBalancerType?e["load-balancer-names"]=e["load-balancer-names"].concat(a.listeners.map((e=>e.name))):"SSL"===a.loadBalancerType||"TCP"===a.loadBalancerType?e["global-load-balancer-names"].push(t):e["load-balancer-names"].push(t),e}),{"load-balancer-names":[],"global-load-balancer-names":[]})),oe.isObject(t)&&Object.keys(t).length>0&&(a["backend-service-names"]=oe.reduce(t,((e,n)=>e.concat(n)),[]));for(const e in a)0===a[e].length?delete a[e]:a[e]=oe.uniq(a[e]).toString();return a}(n.command.loadBalancers,n.command.backingData.filtered.loadBalancerIndex,n.command.backendServices),i=n.command.loadBalancers;n.command.loadBalancers=function(e,n){let t=[];n["load-balancer-names"]&&(t=t.concat(n["load-balancer-names"].split(",")));const a=oe.chain(e).filter({loadBalancerType:"SSL"}).map("name").intersection(n["global-load-balancer-names"]?n["global-load-balancer-names"].split(","):[]).value(),i=oe.chain(e).filter({loadBalancerType:"TCP"}).map("name").intersection(n["global-load-balancer-names"]?n["global-load-balancer-names"].split(","):[]).value();return t.concat(a).concat(i)}(n.command.backingData.filtered.loadBalancerIndex,a),"(Automatic)"===n.command.minCpuPlatform&&(n.command.minCpuPlatform=""),e.extend(n.command.instanceMetadata,a);const l=n.command.tags,r=[];if(n.command.tags.forEach((function(e){r.push(e.value)})),n.command.tags=r,n.command.targetSize=n.command.capacity.desired,n.command.autoscalingPolicy?(n.command.capacity.min=n.command.autoscalingPolicy.minNumReplicas,n.command.capacity.max=n.command.autoscalingPolicy.maxNumReplicas):(n.command.capacity.min=n.command.capacity.desired,n.command.capacity.max=n.command.capacity.desired),delete n.command.securityGroups,"editPipeline"===n.command.viewState.mode||"createPipeline"===n.command.viewState.mode)return t.close(n.command);n.taskMonitor.submit((function(){const t=c.cloneServerGroup(e.copy(n.command),s);return n.command.instanceMetadata=oe.omit(n.command.instanceMetadata,h),n.command.tags=l,n.command.loadBalancers=i,n.command.securityGroups=v.inferSecurityGroupIdsFromTags(n.command.tags),t}))},this.onHealthCheckRefresh=function(){r.refreshHealthChecks(n.command)},this.onEnableAutoHealingChange=function(){n.command.overwriteAncestorAutoHealingPolicy="clone"===n.command.viewState.mode&&null!=n.command.autoHealingPolicy&&!1===n.command.enableAutoHealing},this.setAutoHealingPolicy=function(e){n.command.autoHealingPolicy=e},this.onEnableAutoScalingChange=function(){n.command.overwriteAncestorAutoScalingPolicy=null!=n.command.autoscalingPolicy&&!1===n.command.enableAutoScaling},this.setAutoScalingPolicy=function(e){n.command.autoscalingPolicy=e},this.cancel=function(){t.dismiss()},this.specialInstanceProfiles=new Set(["custom","buildCustom"]),n.command.setCustomInstanceViewState=e=>{n.$apply((()=>n.command.viewState.customInstance=e))},n.state.requiresTemplateSelection?n.state.loaded=!0:b(),this.templateSelected=()=>{n.state.requiresTemplateSelection=!1,b()},n.$on("$destroy",v.reset)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/templateSelection/templateSelection.html",'<deploy-initializer\n cloud-provider="gce"\n command="command"\n application="application"\n parent-state="state"\n dismiss="ctrl.cancel()"\n template-selection-text="ctrl.templateSelectionText"\n on-template-selected="ctrl.templateSelected()"\n></deploy-initializer>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/location/basicSettings.html",'<div class="container-fluid form-horizontal" ng-controller="gceServerGroupBasicSettingsCtrl 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 component="command"\n field="credentials"\n accounts="command.backingData.accounts"\n provider="\'gce\'"\n on-change="basicSettingsCtrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="3"\n component="command"\n field="region"\n account="command.credentials"\n regions="command.backingData.filtered.regions"\n ></gce-region-select-field>\n <gce-network-select-field\n label-columns="3"\n component="command"\n field="network"\n account="command.credentials"\n networks="command.backingData.filtered.networks"\n ></gce-network-select-field>\n <gce-subnet-select-field\n label-columns="3"\n help-key="gce.serverGroup.subnet"\n component="command"\n field="subnet"\n account="command.credentials"\n region="command.region"\n subnets="command.backingData.filtered.subnets"\n subnet-placeholder="basicSettingsCtrl.getSubnetPlaceholder()"\n auto-create-subnets="command.viewState.autoCreateSubnets"\n >\n </gce-subnet-select-field>\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"\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>Stack can only contain letters and numbers.</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"\n ng-pattern="basicSettingsCtrl.detailPattern"\n name="details"\n ng-model="command.freeFormDetails"\n />\n </div>\n </div>\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>Detail can only contain letters, numbers, and dashes(-).</span>\n </div>\n </div>\n <div ng-if="command.viewState.showImageSourceSelector">\n <image-source-selector\n command="command"\n id-field="imageSource"\n image-sources="basicSettingsCtrl.imageSources"\n image-source-text="command.viewState.imageSourceText"\n help-field-key="gce.image.source"\n >\n </image-source-selector>\n <stage-artifact-selector-delegate\n ng-if="command.imageSource === \'artifact\'"\n artifact="command.imageArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedImageArtifactTypes"\n expected-artifact-id="command.imageArtifactId"\n field-columns="7"\n help-key="\'gce.image.artifact\'"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onImageArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onImageArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n </div>\n <div class="form-group" ng-if="!command.viewState.disableImageSelection">\n <div class="col-md-3 sm-label-right">\n Image\n <help-field key="gce.serverGroup.imageName"></help-field>\n </div>\n <div class="col-md-7">\n <gce-image-select\n available-images="command.backingData.allImages"\n selected-image="command.image"\n select-image="basicSettingsCtrl.selectImage"\n ></gce-image-select>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Traffic <help-field key="gce.serverGroup.traffic"></help-field></div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableTraffic" />\n Send client requests to new instances\n </label>\n </div>\n </div>\n <deployment-strategy-selector\n ng-if="!command.viewState.disableStrategySelection && command.selectedProvider"\n command="command"\n ></deployment-strategy-selector>\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 <task-reason command="command"></task-reason>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html",'<ng-form name="loadBalancerSubForm">\n <div>\n <gce-server-group-load-balancer-selector command="command"></gce-server-group-load-balancer-selector>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroups.html",'<div class="row">\n <gce-server-group-security-groups-removed command="command"></gce-server-group-security-groups-removed>\n <gce-server-group-security-group-selector command="command"></gce-server-group-security-group-selector>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/instanceType/instanceType.html",'<div ng-show="!!((command.viewState.disableImageSelection || command.image) && (command.zone || command.regional))">\n <v2-instance-archetype-selector command="command"></v2-instance-archetype-selector>\n <div style="padding: 0 15px">\n <v2-instance-type-selector\n ng-if="command.viewState.instanceProfile && !ctrl.specialInstanceProfiles.has(command.viewState.instanceProfile)"\n command="command"\n ></v2-instance-type-selector>\n </div>\n</div>\n<h5 class="text-center" ng-if="!command.image && !command.viewState.disableImageSelection">Please select an image.</h5>\n<h5 class="text-center" ng-if="!(command.zone || command.regional)">Please select a zone.</h5>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/capacity.html",'<ng-form name="capacitySubForm">\n <div class="clearfix">\n <div class="row">\n <div class="col-md-12">\n <gce-server-group-simple-capacity-selector\n ng-if="command.viewState.useSimpleCapacity"\n command="command"\n ></gce-server-group-simple-capacity-selector>\n <gce-server-group-advanced-capacity-selector\n ng-if="!command.viewState.useSimpleCapacity"\n command="command"\n ></gce-server-group-advanced-capacity-selector>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicy.html",'<ng-form name="autoHealingPolicySubForm">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Enable Autohealing</b>\n </div>\n <div class="col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableAutoHealing" ng-change="ctrl.onEnableAutoHealingChange()" />\n </label>\n </div>\n </div>\n <gce-auto-healing-policy-selector\n ng-if="command.enableAutoHealing"\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="command.backingData.filtered.healthChecks"\n enabled="command.enableAutoHealing"\n auto-healing-policy="command.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicy.html",'<ng-form name="autoScalingPolicySubForm">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Enable Autoscaling</b>\n </div>\n <div class="col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableAutoScaling" ng-change="ctrl.onEnableAutoScalingChange()" />\n </label>\n </div>\n </div>\n <div>\n <gce-auto-scaling-policy-Selector-Component\n ng-if="command.enableAutoScaling"\n enabled="command.enableAutoScaling"\n auto-scaling-policy="command.autoscalingPolicy"\n set-auto-scaling-policy="ctrl.setAutoScalingPolicy(autoscalingPolicy)"\n ></gce-auto-scaling-policy-Selector-Component>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/zones.html",'<ng-form name="zonesSubForm">\n <div class="container-fluid form-horizontal">\n <gce-regional-selector command="command"></gce-regional-selector>\n <gce-zone-selector command="command"></gce-zone-selector>\n <gce-target-shape-selector command="command"></gce-target-shape-selector>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html",'<div class="container-fluid form-horizontal">\n <gce-server-group-advanced-settings-selector command="command"></gce-server-group-advanced-settings-selector>\n <div class="form-group" ng-if="application.attributes.platformHealthOnlyShowOverride">\n <div class="col-md-5 sm-label-right"><b>Task Completion</b></div>\n <div class="col-md-6">\n <platform-health-override command="command" platform-health-type="\'Google\'"> </platform-health-override>\n </div>\n </div>\n</div>\n')}]);class Na{static markDiskStateful(e,n,t){return T.executeTask({application:e,description:"Mark disk as stateful",job:[{cloudProvider:"gce",credentials:t.account,deviceName:n,region:t.region,serverGroupName:t.name,type:"setStatefulDisk"}]})}static statefullyUpdateBootDisk(e,n,t){return T.executeTask({application:e,description:"Statefully update boot disk image",job:[{bootImage:n,cloudProvider:"gce",credentials:t.account,region:t.region,serverGroupName:t.name,type:"statefullyUpdateBootImage"}]})}static isDiskStateful(e,n){return Se(n,["statefulPolicy","preservedState","disks",e])}static statefulMigsEnabled(){return Oe.feature.statefulMigsEnabled}}function Da({application:e,deviceName:n,serverGroup:t}){return Na.isDiskStateful(n,t)?ce.createElement("span",null," (Marked as Stateful)"):ce.createElement("button",{className:"btn-link",onClick:()=>{h.confirm({account:t.account,askForReason:!0,buttonText:"Mark as stateful",header:`Really mark disk ${n} as stateful?`,submitMethod:()=>Na.markDiskStateful(e.name,n,t),taskMonitorConfig:{application:e,title:"Marking disk as stateful"}})}},"Mark as Stateful")}function Ma(e){return ce.createElement("button",{className:"btn-link",onClick:function(){const n={...e};X.show(Ea,n,{dialogClassName:"wizard-modal modal-lg"})}},"Statefully Update")}class Ea extends ce.Component{constructor(e){super(e),this.submit=e=>{this.state.taskMonitor.submit((()=>Na.statefullyUpdateBootDisk(this.props.application.name,e.image,this.props.serverGroup)))},this.state={availableImages:[],taskMonitor:new G({application:e.application,title:"Updating Boot Image",modalInstance:G.modalInstanceEmulation((()=>this.props.dismissModal()))})}}componentDidMount(){Xe.findImages({account:this.props.serverGroup.account,provider:"gce",q:"*"}).then((e=>{this.setState({availableImages:e})}))}render(){return ce.createElement(K,{closeModal:this.submit,dismissModal:this.props.dismissModal,heading:"Update Boot Disk Image",initialValues:{image:this.props.bootImage},submitButtonLabel:"Update Image",taskMonitor:this.state.taskMonitor,render:({formik:e,nextIdx:n,wizard:t})=>ce.createElement(W,{label:"Boot Image",wizard:t,order:n(),render:()=>ce.createElement(Z,{input:n=>ce.createElement("div",{className:"full-width",style:{height:"225px"}},ce.createElement(Ye,{availableImages:this.state.availableImages,selectedImage:n.value,selectImage:n=>e.setFieldValue("image",n)})),label:"Boot image name:",name:"image",required:!0})})})}}const Ra=class extends ce.Component{render(){const{application:e,serverGroup:n}=this.props,t=fe(n,"launchConfig.instanceTemplate.properties.disks",[]),a=Na.statefulMigsEnabled(),i=a&&t.some((e=>Na.isDiskStateful(e.deviceName,n)));return t.map((t=>t.boot?ce.createElement(ce.Fragment,{key:t.deviceName},ce.createElement("dt",null,"Boot Disk",i&&ce.createElement(Ma,{application:e,bootImage:Ra.getDiskImageName(t),serverGroup:n})),ce.createElement("dd",null,Ra.getDiskTypeLabel(t)),ce.createElement("dd",null,Ra.getDiskImageLabel(t))):ce.createElement(ce.Fragment,{key:t.deviceName},ce.createElement("dt",null,"Disk",a&&ce.createElement(Da,{application:e,deviceName:t.deviceName,serverGroup:n})),ce.createElement("dd",null,Ra.getDiskTypeLabel(t)),ce.createElement("dd",null,Ra.getDiskImageLabel(t)))))}};let La=Ra;La.translateDiskType=e=>{const n=e.initializeParams.diskType;return"pd-ssd"===n?"Persistent SSD":"local-ssd"===n?"Local SSD":"Persistent Std"},La.getDiskTypeLabel=e=>`${Ra.translateDiskType(e)}: ${e.initializeParams.diskSizeGb}GB`,La.getDiskImageLabel=e=>`Image: ${Ra.getDiskImageName(e)}`,La.getDiskImageName=e=>Ae(fe(e,"initializeParams.sourceImage","").split("/"));const Ha="spinnaker.gce.serverGroupDiskDescriptions";var Fa,Ua;n(Ha,[]).component("gceServerGroupDiskDescriptions",re(i(La,"gceServerGroupDiskDescriptions"),["application","serverGroup"])),(Ua=Fa||(Fa={})).fixed="fixed",Ua.percent="percent";const Oa={maxScaledInReplicas:{fixed:null,percent:0},timeWindowSec:60};const qa="spinnaker.gce.scaleInControls";n(qa,[]).component("gceScaleInControls",re(i((function({policy:e,updatePolicy:n}){function t(t){if(Object.keys(t).length)n({...e,scaleInControl:t});else{const t=e;delete t.scaleInControl,n({...t})}}function a(){return Number.isInteger(e.scaleInControl.maxScaledInReplicas.percent)?Fa.percent:Fa.fixed}return ce.createElement(l,{value:Ue},ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(o,{...e}),label:"Enable scale-in controls",onChange:e=>{t(e.target.checked?Oa:{})},value:!se(e.scaleInControl)})),!se(e.scaleInControl)&&ce.createElement(ce.Fragment,null,ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(Y,{...e,min:0,max:a()===Fa.percent?100:null}),label:"Max scaled-in replicas",onChange:n=>{t({...e.scaleInControl,maxScaledInReplicas:{[a()]:parseInt(n.target.value,10)}})},value:e.scaleInControl.maxScaledInReplicas[a()]}),ce.createElement(c,{input:e=>ce.createElement(J,{...e,clearable:!1,stringOptions:[Fa.percent,Fa.fixed]}),label:"",onChange:n=>{t({...e.scaleInControl,maxScaledInReplicas:{[n.target.value]:e.scaleInControl.maxScaledInReplicas[a()]}})},value:a()})),ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(Y,{...e,min:60,max:3600}),label:"Time window (seconds)",onChange:n=>{t({...e.scaleInControl,timeWindowSec:parseInt(n.target.value,10)})},value:e.scaleInControl.timeWindowSec}))))}),"gceScaleInControls"),["policy","updatePolicy"]));class _a{constructor(e){this.$uibModal=e}addAutoHealingPolicy(){this.$uibModal.open({templateUrl:"google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",controller:"gceUpsertAutoHealingPolicyModalCtrl",controllerAs:"ctrl",size:"md",resolve:{serverGroup:()=>this.serverGroup,application:()=>this.application}})}}_a.$inject=["$uibModal"];const Va={bindings:{application:"<",serverGroup:"<"},template:'<a href ng-click="$ctrl.addAutoHealingPolicy()">Create new autohealing policy</a>',controller:_a},ja="spinnaker.gce.addAutoHealingPolicyButton.component";n(ja,[]).component("gceAddAutoHealingPolicyButton",Va),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",'<div modal-page class="form-inline gce-auto-healing-policy-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} autohealing policy</h4>\n </div>\n <ng-form name="autoHealingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <gce-auto-healing-policy-selector\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="ctrl.healthChecks"\n enabled="true"\n label-columns="4"\n auto-healing-policy="ctrl.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoHealingPolicyUpsertModal.$valid"\n label="$ctrl.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);class Ka{constructor(e,n){this.$uibModal=e,this.gceAutoscalingPolicyWriter=n}editPolicy(){this.$uibModal.open({templateUrl:"google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",controller:"gceUpsertAutoHealingPolicyModalCtrl",controllerAs:"ctrl",size:"md",resolve:{application:()=>this.application,serverGroup:()=>this.serverGroup}})}deletePolicy(){const e={application:this.application,title:`Deleting autohealing policy for ${this.serverGroup.name}`};h.confirm({header:`Really delete autohealer for ${this.serverGroup.name}?`,buttonText:"Delete autohealer",account:this.serverGroup.account,taskMonitorConfig:e,submitMethod:()=>this.gceAutoscalingPolicyWriter.deleteAutoHealingPolicy(this.application,this.serverGroup)})}}Ka.$inject=["$uibModal","gceAutoscalingPolicyWriter"];const Wa={bindings:{serverGroup:"<",application:"<"},template:'\n <dt>\n Health Check\n <help-field key="gce.serverGroup.autoHealing"></help-field>\n </dt>\n <dd>{{$ctrl.serverGroup.autoHealingPolicyHealthCheck}}</dd>\n <dt>\n Initial Delay\n <help-field key="gce.serverGroup.initialDelaySec"></help-field>\n </dt>\n <dd>{{$ctrl.serverGroup.initialDelaySec}} seconds</dd>\n <dt ng-if="$ctrl.serverGroup.maxUnavailable">\n Max Unavailable\n <help-field key="gce.serverGroup.maxUnavailable"></help-field>\n </dt>\n <dd ng-if="$ctrl.serverGroup.maxUnavailable">{{$ctrl.serverGroup.maxUnavailable}}</dd>\n <action-icons class="text-right"\n edit="$ctrl.editPolicy()"\n edit-info="Edit Policy"\n destroy="$ctrl.deletePolicy()"\n destroy-info="Delete Policy">\n </action-icons>',controller:Ka},Za="spinnaker.gce.autoHealingPolicyDetails.component";n(Za,[]).component("gceAutoHealingPolicyDetails",Wa),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",'<div modal-page class="form-inline gce-auto-healing-policy-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} autohealing policy</h4>\n </div>\n <ng-form name="autoHealingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <gce-auto-healing-policy-selector\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="ctrl.healthChecks"\n enabled="true"\n label-columns="4"\n auto-healing-policy="ctrl.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoHealingPolicyUpsertModal.$valid"\n label="$ctrl.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const Xa="spinnaker.gce.serverGroup.details.scalingPolicy.write.service";e.module(Xa,[]).factory("gceAutoscalingPolicyWriter",(function(){return{upsertAutoscalingPolicy:function(n,t,a,i={}){const l={type:"upsertScalingPolicy",cloudProvider:t.type,credentials:t.account,region:t.region,serverGroupName:t.name,autoscalingPolicy:a};return e.extend(l,i),T.executeTask({application:n,description:"Upsert scaling policy "+t.name,job:[l]})},deleteAutoscalingPolicy:function(e,n){return T.executeTask({application:e,description:"Delete scaling policy "+n.name,job:[{type:"deleteScalingPolicy",cloudProvider:n.type,credentials:n.account,region:n.region,serverGroupName:n.name}]})},upsertAutoHealingPolicy:function(n,t,a,i={}){const l={type:"upsertScalingPolicy",cloudProvider:t.type,credentials:t.account,region:t.region,serverGroupName:t.name,autoHealingPolicy:a};return e.extend(l,i),T.executeTask({application:n,description:"Upsert autohealing policy "+t.name,job:[l]})},deleteAutoHealingPolicy:function(e,n){return T.executeTask({application:e,description:"Delete autohealing policy "+n.name,job:[{type:"deleteScalingPolicy",cloudProvider:n.type,credentials:n.account,region:n.region,serverGroupName:n.name,deleteAutoHealingPolicy:!0}]})}}}));Le(".gce-auto-healing-policy-modal .form-group {\n width: 100%;\n margin-top: 10px;\n}\n.gce-auto-healing-policy-modal .input-group {\n width: 100%;\n}\n.gce-auto-healing-policy-modal .input-group .input-group-addon {\n width: 20%;\n}\n");class Ya{constructor(e,n,t,a,i){this.$uibModalInstance=e,this.application=n,this.serverGroup=t,this.gceHealthCheckReader=a,this.gceAutoscalingPolicyWriter=i,this.initialize()}submit(){this.taskMonitor.submit((()=>this.gceAutoscalingPolicyWriter.upsertAutoHealingPolicy(this.application,this.serverGroup,this.autoHealingPolicy)))}cancel(){this.$uibModalInstance.dismiss()}setAutoHealingPolicy(e){this.autoHealingPolicy=e}onHealthCheckRefresh(){this.gceHealthCheckReader.listHealthChecks().then((e=>{const n=e.filter((e=>e.account===this.serverGroup.account));this.healthChecks=Yt(n)}))}initialize(){this.onHealthCheckRefresh(),this.action=this.serverGroup.autoHealingPolicy?"Edit":"New",this.isNew=!this.serverGroup.autoHealingPolicy,this.submitButtonLabel=this.isNew?"Create":"Update",this.isNew||(this.autoHealingPolicy=ke(this.serverGroup.autoHealingPolicy)),this.taskMonitor=new G({application:this.application,title:`${this.action} autohealing policy for ${this.serverGroup.name}`,modalInstance:this.$uibModalInstance})}}Ya.$inject=["$uibModalInstance","application","serverGroup","gceHealthCheckReader","gceAutoscalingPolicyWriter"];const Ja="spinnaker.gce.upsertAutoHealingPolicy.modal.controller";n(Ja,[je,Xa]).controller("gceUpsertAutoHealingPolicyModalCtrl",Ya);Le(".gce-scaling-policy-modal .error-message {\n margin-top: 4px;\n}\n.gce-scaling-policy-modal input.form-control,\n.gce-scaling-policy-modal select.form-control {\n margin-bottom: 2px;\n width: 100%;\n}\n.gce-scaling-policy-modal .collapsible-subsection .content-body {\n padding-left: 0;\n}\n");const Qa="spinnaker.deck.gce.upsertAutoscalingPolicy.modal.controller";n(Qa,[Xa,qt,_t,jt]).controller("gceUpsertAutoscalingPolicyModalCtrl",["policy","application","serverGroup","$uibModalInstance","gceAutoscalingPolicyWriter","$scope",function(e,n,t,a,i,l){[this.action,this.isNew]=e?["Edit",!1]:["New",!0],this.policy=ke(e||{}),this.cancel=a.dismiss,this.taskMonitor=new G({application:n,title:`${this.action} scaling policy for ${t.name}`,modalInstance:a}),this.save=()=>{this.taskMonitor.submit((()=>i.upsertAutoscalingPolicy(n,t,this.policy)))},this.updatePolicy=e=>{l.$applyAsync((()=>{this.policy=e}))}}]);const ei="spinnaker.gce.serverGroup.details.scalingPolicy.addButton";n(ei,[Ie,Qa]).component("gceAddAutoscalingPolicyButton",{bindings:{serverGroup:"=",application:"="},template:'<a href ng-click="$ctrl.addAutoscalingPolicy()">Create new scaling policy</a>',controller:["$uibModal",function(e){this.addAutoscalingPolicy=()=>{e.open({templateUrl:"google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",controller:"gceUpsertAutoscalingPolicyModalCtrl",controllerAs:"ctrl",size:"lg",resolve:{policy:()=>{},serverGroup:()=>this.serverGroup,application:()=>this.application}})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",'<div modal-page class="scaling-policy-modal gce-scaling-policy-modal form-inline">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} scaling policy</h4>\n </div>\n <ng-form name="autoscalingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <h4 class="section-heading">Basic Settings</h4>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="ctrl.policy" update-policy="ctrl.updatePolicy" />\n </div>\n <h4 class="section-heading">Metric Types</h4>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="ctrl.showNoMetricsWarning"\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h4 class="section-heading">Scaling Schedules</h4>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoscalingPolicyUpsertModal.$valid || ctrl.showNoMetricsWarning()"\n submitting="taskMonitor.submitting"\n on-click="ctrl.save()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const ni="spinnaker.gce.instance.details.scalingPolicy.directive";n(ni,[Ie,Xa,Qa,oa]).component("gceAutoscalingPolicy",{bindings:{policy:"=",application:"=",serverGroup:"="},templateUrl:"google/src/serverGroup/details/autoscalingPolicy/autoscalingPolicy.directive.html",controller:["$uibModal","gceAutoscalingPolicyWriter",function(e,n){var t=this;t.$onInit=function(){const a=t.policy;if(a.bases=[],a.cpuUtilization){const e={description:"CPU Usage",helpKey:"gce.serverGroup.autoscaling.targetCPUUsage"};a.cpuUtilization.utilizationTarget&&(e.targets=[Math.round(100*a.cpuUtilization.utilizationTarget)+"%"]),a.bases.push(e)}if(a.loadBalancingUtilization){const e={description:"HTTP Load Balancing Usage",helpKey:"gce.serverGroup.autoscaling.targetHTTPLoadBalancingUsage"};a.loadBalancingUtilization.utilizationTarget&&(e.targets=[Math.round(100*a.loadBalancingUtilization.utilizationTarget)+"%"]),a.bases.push(e)}if(a.customMetricUtilizations){const e={description:a.customMetricUtilizations.length>1?"Monitoring Metrics":"Monitoring Metric",helpKey:"gce.serverGroup.autoscaling.targetMetric"};a.customMetricUtilizations.length>0&&(e.targets=[],a.customMetricUtilizations.forEach((n=>{let t=n.metric+": "+n.utilizationTarget;"DELTA_PER_SECOND"===n.utilizationTargetType?t+="/sec":"DELTA_PER_MINUTE"===n.utilizationTargetType&&(t+="/min"),e.targets.push(t)}))),a.bases.push(e)}t.scaleInControlsConfigured=a.scaleInControl&&a.scaleInControl.timeWindowSec&&a.scaleInControl.maxScaledInReplicas&&(a.scaleInControl.maxScaledInReplicas.percent||a.scaleInControl.maxScaledInReplicas.fixed),t.scaleInControlsConfigured&&(t.maxScaledInReplicasMessage=a.scaleInControl.maxScaledInReplicas.percent?`${a.scaleInControl.maxScaledInReplicas.percent}%`:`${a.scaleInControl.maxScaledInReplicas.fixed}`,t.timeWindowSecMessage=`${a.scaleInControl.timeWindowSec} seconds`),t.predictiveAutoscalingEnabled=Oe.feature.predictiveAutoscaling&&a.cpuUtilization&&a.cpuUtilization.predictiveMethod,t.editPolicy=()=>{e.open({templateUrl:"google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",controller:"gceUpsertAutoscalingPolicyModalCtrl",controllerAs:"ctrl",size:"lg",resolve:{policy:()=>t.policy,application:()=>t.application,serverGroup:()=>t.serverGroup}})},t.deletePolicy=()=>{const e={application:t.application,title:`Deleting autoscaler for ${t.serverGroup.name}`};h.confirm({header:`Really delete autoscaler for ${t.serverGroup.name}?`,buttonText:"Delete autoscaler",account:t.serverGroup.account,taskMonitorConfig:e,submitMethod:()=>n.deleteAutoscalingPolicy(t.application,t.serverGroup)})}}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/autoscalingPolicy.directive.html",'<dl class="horizontal-when-filters-collapsed" style="margin-bottom: 20px">\n <dt ng-repeat-start="basis in $ctrl.policy.bases">\n Target {{basis.description}}\n <help-field key="{{basis.helpKey}}"></help-field>\n </dt>\n <dd ng-repeat-end>\n <div ng-repeat="target in basis.targets">{{target}}</div>\n </dd>\n <dt ng-if="$ctrl.predictiveAutoscalingEnabled">Predictive Autoscaling</dt>\n <dd ng-if="$ctrl.predictiveAutoscalingEnabled">{{$ctrl.policy.cpuUtilization.predictiveMethod}}</dd>\n <dt>\n Min # VMs\n <help-field key="gce.serverGroup.autoscaling.minVMs"></help-field>\n </dt>\n <dd>{{$ctrl.policy.minNumReplicas}}</dd>\n <dt>\n Max # VMs\n <help-field key="gce.serverGroup.autoscaling.maxVMs"></help-field>\n </dt>\n <dd>{{$ctrl.policy.maxNumReplicas}}</dd>\n <dt>\n Cool-down Period\n <help-field key="gce.serverGroup.autoscaling.cooldown"></help-field>\n </dt>\n <dd>{{$ctrl.policy.coolDownPeriodSec}} sec</dd>\n <dt>\n Mode\n <help-field key="gce.serverGroup.autoscaling.mode"></help-field>\n </dt>\n <dd>{{$ctrl.policy.mode}}</dd>\n <dt ng-if="$ctrl.scaleInControlsEnabled">Scale-in Controls</dt>\n <dd ng-if="$ctrl.scaleInControlsEnabled">{{$ctrl.scaleInControlsConfigured ? \'Enabled\' : \'Disabled\'}}</dd>\n <dt ng-if="$ctrl.scaleInControlsConfigured">Max Scaled-in Replicas</dt>\n <dd ng-if="$ctrl.scaleInControlsConfigured">{{$ctrl.maxScaledInReplicasMessage}}</dd>\n <dt ng-if="$ctrl.scaleInControlsConfigured">Scale-in Time Window</dt>\n <dd ng-if="$ctrl.scaleInControlsConfigured">{{$ctrl.timeWindowSecMessage}}</dd>\n <dt ng-if="$ctrl.serverGroup.autoscalingMessages">Messages</dt>\n <dd ng-if="$ctrl.serverGroup.autoscalingMessages" ng-repeat="message in $ctrl.serverGroup.autoscalingMessages">\n {{message}}\n </dd>\n <action-icons\n class="text-right"\n edit="$ctrl.editPolicy()"\n edit-info="Edit Policy"\n destroy="$ctrl.deletePolicy()"\n destroy-info="Delete Policy"\n >\n </action-icons>\n</dl>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",'<div modal-page class="scaling-policy-modal gce-scaling-policy-modal form-inline">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} scaling policy</h4>\n </div>\n <ng-form name="autoscalingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <h4 class="section-heading">Basic Settings</h4>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="ctrl.policy" update-policy="ctrl.updatePolicy" />\n </div>\n <h4 class="section-heading">Metric Types</h4>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="ctrl.showNoMetricsWarning"\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h4 class="section-heading">Scaling Schedules</h4>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoscalingPolicyUpsertModal.$valid || ctrl.showNoMetricsWarning()"\n submitting="taskMonitor.submitting"\n on-click="ctrl.save()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const ti="spinnaker.google.footer.directive";e.module(ti,[]).directive("gceFooter",(function(){return{restrict:"E",templateUrl:"google/src/common/footer.directive.html",scope:{},bindToController:{action:"&",isValid:"&",cancel:"&",account:"=?",verification:"=?"},controllerAs:"vm",controller:e.noop}})),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/common/footer.directive.html",'<div class="modal-footer">\n <user-verification account="vm.account" verification="vm.verification"></user-verification>\n <button type="submit" ng-click="vm.action()" style="display: none"></button>\n \x3c!-- Allows form submission via enter keypress--\x3e\n <button class="btn btn-default" ng-click="vm.cancel()">Cancel</button>\n <button type="submit" class="btn btn-primary" ng-click="vm.action()" ng-disabled="!vm.isValid()">Submit</button>\n</div>\n')}]);const ai="spinnaker.deck.gce.serverGroup.details.resizeAutoscalingPolicy.component";e.module(ai,[Xa]).component("gceResizeAutoscalingPolicy",{bindings:{serverGroup:"=",command:"=",formMethods:"=",application:"="},templateUrl:"google/src/serverGroup/details/resize/resizeAutoscalingPolicy.component.html",controller:["$scope","gceAutoscalingPolicyWriter",function(n,t){this.$onInit=()=>{const a=["newMinNumReplicas","newMaxNumReplicas"];a.forEach((e=>this.command[e]=null)),e.extend(this.formMethods,{formIsValid:()=>oe.every([oe.chain(a).map((e=>null!==this.command[e])).every().value(),n.resizeAutoscalingPolicyForm.$valid]),submitMethod:()=>t.upsertAutoscalingPolicy(this.application,this.serverGroup,{minNumReplicas:this.command.newMinNumReplicas,maxNumReplicas:this.command.newMaxNumReplicas},{reason:this.command.reason,interestingHealthProviderNames:this.command.interestingHealthProviderNames})})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeAutoscalingPolicy.component.html",'<ng-form name="resizeAutoscalingPolicyForm">\n <div class="form-group">\n <div class="col-md-12">\n <p>\n Sets min and max instance counts for this server group\'s autoscaling policy.\n <help-field key="gce.serverGroup.resizeWithAutoscalingPolicy" placement="right"></help-field>\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3"><b>Min</b></div>\n <div class="col-md-2"><b>Max</b></div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Current</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.autoscalingPolicy.minNumReplicas"\n readonly\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.autoscalingPolicy.maxNumReplicas"\n readonly\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Resize to</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.newMinNumReplicas"\n min="0"\n required\n max="{{ $ctrl.command.newMaxNumReplicas }}"\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.newMaxNumReplicas"\n required\n min="{{ $ctrl.command.newMinNumReplicas }}"\n />\n </div>\n </div>\n</ng-form>\n')}]);const ii="spinnaker.google.serverGroup.details.resize.capacity.component";e.module(ii,[Q]).component("gceResizeCapacity",{bindings:{command:"=",application:"=",serverGroup:"=",formMethods:"="},templateUrl:"google/src/serverGroup/details/resize/resizeCapacity.component.html",controller:["$scope","serverGroupWriter",function(n,t){this.$onInit=()=>{this.command.newSize=null,e.extend(this.formMethods,{formIsValid:()=>oe.every([null!==this.command.newSize,n.resizeCapacityForm.$valid]),submitMethod:()=>t.resizeServerGroup(this.serverGroup,this.application,{capacity:{min:this.command.newSize,max:this.command.newSize,desired:this.command.newSize},serverGroupName:this.serverGroup.name,targetSize:this.command.newSize,region:this.serverGroup.region,interestingHealthProviderNames:this.command.interestingHealthProviderNames,reason:this.command.reason})})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeCapacity.component.html",'<ng-form name="resizeCapacityForm">\n <div>\n <div class="form-group">\n <div class="col-md-12">\n <p>Sets desired instance count for this server group.</p>\n </div>\n </div>\n <div class="form-group form-inline">\n <div class="col-md-3 sm-label-right">Current size</div>\n <div class="col-md-6">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.asg.desiredCapacity"\n style="width: 60px"\n readonly\n />\n instance<span ng-if="$ctrl.currentSize.desired > 1">s</span>\n </div>\n </div>\n <div class="form-group form-inline">\n <div class="col-md-3 sm-label-right">Resize to</div>\n <div class="col-md-6">\n <input\n type="number"\n class="form-control input-sm highlight-pristine"\n ng-model="$ctrl.command.newSize"\n min="0"\n required\n style="width: 60px"\n />\n instances\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const li="spinnaker.google.serverGroup.details.resize.controller";n(li,[ii,ai,ti]).controller("gceResizeServerGroupCtrl",["$scope","$uibModalInstance","application","serverGroup",function(e,n,t,a){e.serverGroup=a,e.application=t,e.verification={},e.command={},e.formMethods={},t&&t.attributes&&(t.attributes.platformHealthOnlyShowOverride&&t.attributes.platformHealthOnly&&(e.command.interestingHealthProviderNames=["Google"]),e.command.platformHealthOnlyShowOverride=t.attributes.platformHealthOnlyShowOverride),this.isValid=function(){return!!e.verification.verified&&e.formMethods.formIsValid()},e.taskMonitor=new G({application:t,title:"Resizing "+a.name,modalInstance:n}),this.resize=function(){this.submitting=!0,this.isValid()&&e.taskMonitor.submit(e.formMethods.submitMethod)},this.cancel=function(){n.dismiss()}}]);const ci="spinnaker.google.serverGroup.details.rollback.controller";n(ci,[Q,ti]).controller("gceRollbackServerGroupCtrl",["$scope","$uibModalInstance","serverGroupWriter","application","serverGroup","disabledServerGroups",function(e,n,t,a,i,l){e.serverGroup=i,e.disabledServerGroups=l.sort(((e,n)=>n.name.localeCompare(e.name))),e.verification={},e.command={rollbackType:"EXPLICIT",rollbackContext:{rollbackServerGroupName:i.name}},a&&a.attributes&&(a.attributes.platformHealthOnlyShowOverride&&a.attributes.platformHealthOnly&&(e.command.interestingHealthProviderNames=["Google"]),e.command.platformHealthOnlyShowOverride=a.attributes.platformHealthOnlyShowOverride),this.isValid=function(){const n=e.command;return!!e.verification.verified&&void 0!==n.rollbackContext.restoreServerGroupName},e.taskMonitor=new G({application:a,title:"Rollback "+i.name,modalInstance:n}),this.rollback=function(){if(this.submitting=!0,!this.isValid())return;e.taskMonitor.submit((function(){return t.rollbackServerGroup(i,a,e.command)}))},this.cancel=function(){n.dismiss()}}]);Le("gce-custom-instance-configurer .Select-menu-outer {\n z-index: 4;\n}\n");const ri="spinnaker.serverGroup.details.gce.controller";e.module(ri,[xe,Ia,Q,We,li,ci,ni,ei]).controller("gceServerGroupDetailsCtrl",["$scope","$state","$templateCache","$interpolate","app","serverGroup","gceServerGroupCommandBuilder","$uibModal","serverGroupWriter","gceXpnNamingService",function(n,t,a,i,l,c,r,o,s,d){this.state={loading:!0},this.firewallsLabel=m.get("Firewalls"),this.application=l;const u=()=>{n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})},g=()=>{this.state.loading=!1},p=()=>{const n=(()=>{let e=oe.find(l.serverGroups.data,(e=>e.name===c.name&&e.account===c.accountId&&e.region===c.region));return e||l.loadBalancers.data.some((n=>{if(n.account===c.accountId&&n.region===c.region)return n.serverGroups.some((n=>{if(n.name===c.name)return e=n,!0}))})),e})();return te.getServerGroup(l.name,c.accountId,c.region,c.name).then((t=>{if(g(),e.extend(t,n),t.account=c.accountId,this.serverGroup=t,oe.isEmpty(this.serverGroup))u();else{t.securityGroups&&(this.securityGroups=oe.chain(t.securityGroups).map((e=>oe.find(l.securityGroups.data,{accountName:c.accountId,region:"global",id:e})||oe.find(l.securityGroups.data,{accountName:c.accountId,region:"global",name:e}))).compact().value()),this.serverGroup.zones.sort();const e=d.deriveProjectId(this.serverGroup.launchConfig.instanceTemplate);this.serverGroup.logsLink="https://console.developers.google.com/project/"+e+'/logs?advancedFilter=resource.type=(gce_instance_group_manager OR gce_instance OR gce_autoscaler)%0A"'+this.serverGroup.name+'"',this.serverGroup.network=T(e),G(e),A(),v(),f(),b(),y(),k(),S(),w(),P(),C()}}),u)},v=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.metadata.items")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.metadata.items,n=oe.find(e,(e=>"startup-script"===e.key));n&&(this.serverGroup.startupScript=n.value)}},f=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.scheduling")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.scheduling;this.serverGroup.availabilityPolicies={preemptibility:e.preemptible?"On":"Off",automaticRestart:e.automaticRestart?"On":"Off",onHostMaintenance:"MIGRATE"===e.onHostMaintenance?"Migrate":"Terminate"}}},b=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.shieldedVmConfig")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.shieldedVmConfig;this.serverGroup.shieldedVmConfig={enableSecureBoot:e.enableSecureBoot?"On":"Off",enableVtpm:e.enableVtpm?"On":"Off",enableIntegrityMonitoring:e.enableIntegrityMonitoring?"On":"Off"}}},y=()=>{if(this.serverGroup.autoHealingPolicy){const e=this.serverGroup.autoHealingPolicy,n=e.healthCheck;this.serverGroup.autoHealingPolicyHealthCheck=n?oe.last(n.split("/")):null,this.serverGroup.initialDelaySec=e.initialDelaySec,e.maxUnavailable&&("number"==typeof e.maxUnavailable.percent?this.serverGroup.maxUnavailable=e.maxUnavailable.percent+"%":this.serverGroup.maxUnavailable=e.maxUnavailable.fixed+" instances")}},k=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.serviceAccounts")){if(this.serverGroup.launchConfig.instanceTemplate.properties.serviceAccounts.length){const e=this.serverGroup.launchConfig.instanceTemplate.properties.serviceAccounts[0];this.serverGroup.serviceAccountEmail=e.email,this.serverGroup.authScopes=oe.map(e.scopes,(e=>e.replace("https://www.googleapis.com/auth/","")))}}},S=()=>{this.serverGroup.currentActions&&(this.serverGroup.currentActionsSummary=[],oe.forOwn(this.serverGroup.currentActions,((e,n)=>{"none"!==n&&e&&this.serverGroup.currentActionsSummary.push({action:oe.startCase(n),count:e})})),this.serverGroup.currentActionsSummary.length||delete this.serverGroup.currentActionsSummary)},w=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.tags.items")&&this.securityGroups){const e={};this.serverGroup.launchConfig.instanceTemplate.properties.tags.items.forEach((n=>{const t=oe.filter(this.securityGroups,(e=>oe.includes(e.targetTags,n))),a=oe.map(t,"name");if(!oe.isEmpty(a)){const t=a.length>1?"groups":"group";e[n]="This tag associates this server group with security "+t+" <em>"+a.join(", ")+"</em>."}})),this.serverGroup.launchConfig.instanceTemplate.properties.tags.helpMap=e}},C=()=>{oe.size(this.serverGroup.instanceTemplateLabels)||delete this.serverGroup.instanceTemplateLabels},T=e=>{const n=oe.get(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].network");return d.decorateXpnResourceIfNecessary(e,n)},G=e=>{B.listNetworksByProvider("gce").then((n=>{if(oe.chain(n).filter({account:this.serverGroup.account,id:this.serverGroup.network}).map("autoCreateSubnets").head().value())this.serverGroup.subnet="(Auto-select)";else{const n=oe.get(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].subnetwork");this.serverGroup.subnet=d.decorateXpnResourceIfNecessary(e,n)}}))},A=()=>{this.serverGroup.associatePublicIPAddress=oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].accessConfigs")};p().then((()=>{n.$$destroyed||l.serverGroups.onRefresh(n,p)})),this.destroyServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Destroying "+e.name,onTaskComplete:()=>{t.includes("**.serverGroup",a)&&t.go("^")}},a={name:e.name,accountId:e.account,region:e.region},i={header:"Really destroy "+e.name+"?",buttonText:"Destroy "+e.name,account:e.account,taskMonitorConfig:n,submitMethod:n=>s.destroyServerGroup(e,l,n),askForReason:!0,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google"};ee.addDestroyWarningMessage(l,e,i),l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(i.interestingHealthProviderNames=["Google"]),h.confirm(i)},this.disableServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Disabling "+e.name},t={header:"Really disable "+e.name+"?",buttonText:"Disable "+e.name,account:e.account,taskMonitorConfig:n,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google",submitMethod:n=>s.disableServerGroup(e,l,n),askForReason:!0};ee.addDisableWarningMessage(l,e,t),l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=["Google"]),h.confirm(t)},this.enableServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Enabling "+e.name},t={header:"Really enable "+e.name+"?",buttonText:"Enable "+e.name,account:e.account,taskMonitorConfig:n,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google",submitMethod:n=>s.enableServerGroup(e,l,n),askForReason:!0};l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=["Google"]),h.confirm(t)},this.rollbackServerGroup=()=>{o.open({templateUrl:"google/src/serverGroup/details/rollback/rollbackServerGroup.html",controller:"gceRollbackServerGroupCtrl as ctrl",resolve:{serverGroup:()=>this.serverGroup,disabledServerGroups:()=>{const e=oe.find(l.clusters,{name:this.serverGroup.cluster,account:this.serverGroup.account});return oe.filter(e.serverGroups,{isDisabled:!0,region:this.serverGroup.region})},application:()=>l}})},this.resizeServerGroup=()=>{o.open({templateUrl:"google/src/serverGroup/details/resize/resizeServerGroup.html",controller:"gceResizeServerGroupCtrl as ctrl",resolve:{serverGroup:()=>this.serverGroup,application:()=>l}})},this.cloneServerGroup=e=>{o.open({templateUrl:"google/src/serverGroup/configure/wizard/serverGroupWizard.html",controller:"gceCloneServerGroupCtrl as ctrl",size:"lg",resolve:{title:()=>"Clone "+e.name,application:()=>l,serverGroup:()=>e,serverGroupCommand:()=>r.buildServerGroupCommandFromExisting(l,e)}})},this.showStartupScript=()=>{n.userDataModalTitle="Startup Script",n.serverGroup={name:this.serverGroup.name},n.userData=this.serverGroup.startupScript,o.open({templateUrl:ne.userData,scope:n})},this.buildJenkinsLink=()=>{if(this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.buildInfoUrl)return this.serverGroup.buildInfo.buildInfoUrl;if(this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.jenkins){const e=this.serverGroup.buildInfo.jenkins;return e.host+"job/"+e.name+"/"+e.number}return null},this.truncateCommitHash=()=>this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.commit?this.serverGroup.buildInfo.commit.substring(0,8):null;const P=()=>{this.entityTagTargets=ae.buildClusterTargets(this.serverGroup)}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/rollback/rollbackServerGroup.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Rollback {{serverGroup.name}}</h4>\n </div>\n <div class="modal-body confirmation-modal">\n <div class="row">\n <div class="col-sm-3 sm-label-right">Restore to</div>\n <div class="col-sm-6">\n <ui-select ng-model="command.rollbackContext.restoreServerGroupName" class="form-control input-sm">\n <ui-select-match placeholder="Select...">{{$select.selected.name}}</ui-select-match>\n <ui-select-choices repeat="serverGroup.name as serverGroup in disabledServerGroups">\n <span ng-bind-html="serverGroup.name"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="row" ng-if="command.platformHealthOnlyShowOverride">\n <div class="col-sm-10 col-sm-offset-1">\n <platform-health-override\n command="command"\n platform-health-type="\'Google\'"\n show-help-details="true"\n field-columns="12"\n >\n </platform-health-override>\n </div>\n </div>\n\n <task-reason command="command"></task-reason>\n </div>\n <gce-footer\n action="ctrl.rollback()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="serverGroup.account"\n verification="verification"\n ></gce-footer>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeServerGroup.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Resize {{serverGroup.name}}</h4>\n </div>\n <div class="modal-body confirmation-modal">\n <div class="form-horizontal">\n <gce-resize-capacity\n ng-if="!serverGroup.autoscalingPolicy"\n server-group="serverGroup"\n form-methods="formMethods"\n application="application"\n command="command"\n ></gce-resize-capacity>\n <gce-resize-autoscaling-policy\n ng-if="serverGroup.autoscalingPolicy"\n server-group="serverGroup"\n form-methods="formMethods"\n application="application"\n command="command"\n ></gce-resize-autoscaling-policy>\n </div>\n <div class="row" ng-if="command.platformHealthOnlyShowOverride">\n <div class="col-sm-10 col-sm-offset-1">\n <platform-health-override command="command" platform-health-type="\'Google\'" show-help-details="true">\n </platform-health-override>\n </div>\n </div>\n <task-reason command="command"></task-reason>\n </div>\n <gce-footer\n action="ctrl.resize()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="serverGroup.account"\n verification="verification"\n ></gce-footer>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal" novalidate>\n <div ng-if="state.requiresTemplateSelection">\n <ng-include src="pages.templateSelection"></ng-include>\n </div>\n <div ng-if="!state.loaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!state.requiresTemplateSelection">\n <v2-modal-wizard ng-show="state.loaded" heading="{{title}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="location" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancers" label="Load Balancers" mark-complete-on-view="false">\n <ng-include src="pages.loadBalancers"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="security-groups" label="{{firewallsLabel}}" done="true">\n <ng-include src="pages.securityGroups"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="instance-type" label="Instance Type" mark-complete-on-view="false">\n <ng-include src="pages.instanceType"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="capacity" label="Capacity" mark-complete-on-view="false">\n <ng-include src="pages.capacity"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autoscaling-policy" label="Autoscaling Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoScalingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autohealing-policy" label="Autohealing Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoHealingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="zones" label="Zones" mark-complete-on-view="false" done="true">\n <ng-include src="pages.zones"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advanced" label="Advanced Settings" mandatory="false" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="state.loaded">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="ctrl.showSubmitButton()"\n is-disabled="!ctrl.isValid() || taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]);const oi="spinnaker.serverGroup.details.gce";n(oi,[ri,ja,Ja,Za]);const si="spinnaker.gce.subnet.renderer";n(si,[]).factory("gceSubnetRenderer",(function(){let e;return B.listNetworksByProvider("gce").then((function(n){e=n})),{render:function(n){return n.subnet?n.subnet:oe.chain(e).filter({account:n.account,name:n.network}).map("autoCreateSubnets").head().value()?"(Auto-select)":"[none]"}}}));const di="spinnaker.gce.validation.applicationName";n(di,[]).factory("gceApplicationNameValidator",(function(){return{validate:function(e){const n=[],t=[];return e&&e.length&&(function(e,n){/^([a-zA-Z][a-zA-Z0-9]*)?$/.test(e)||n.push("The application name must begin with a letter and must contain only letters or digits. No special characters are allowed.")}(e,t),function(e,n,t){const a=63;if(e.length>a)t.push("The maximum length for an application in Google is 63 characters.");else{if(e.length>34)if(e.length>46)n.push(`You will not be able to create a Google load balancer for this application if the\n application's name is longer than 46 characters (currently: ${e.length}\n characters).`);else if(e.length>=44)n.push('With separators ("-"), you will not be able to include a stack and detail field for Google load balancers.');else{const t=44-e.length;n.push(`If you plan to include a stack or detail field for Google load balancers, you will only\n have ~${t} characters to do so.`)}if(e.length>41)if(e.length>53)n.push(`You will not be able to create a Google server group for this application if the\n application's name is longer than 53 characters (currently: ${e.length}\n characters).`);else if(e.length>=51)n.push('With separators ("-"), you will not be able to include a stack and detail field for Google server groups.');else{const t=51-e.length;n.push(`If you plan to include a stack or detail field for Google server groups, you will only\n have ~${t} characters to do so.`)}if(e.length>51)if(e.length>=61)n.push(`With separators ("-"), you will not be able to include a stack and detail field for Google ${m.get("firewalls")}.`);else{const t=61-e.length;n.push(`If you plan to include a stack or detail field for Google ${m.get("firewalls")}, you will only\n have ~${t} characters to do so.`)}}}(e,n,t)),{warnings:n,errors:t}}}})).run(["gceApplicationNameValidator",function(e){ie.registerValidator("gce",e)}]);Le('.cloud-provider-logo .icon-gce {\n -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22UTF-8%22 standalone%3D%22no%22%3F%3E%3Csvg width%3D%2232px%22 height%3D%2232px%22 viewBox%3D%220 0 32 32%22 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 xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E %3C!-- Generator%3A Sketch 3.4.1 (15681) - http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch --%3E %3Ctitle%3Eshaded-32%2Fcompute-engine%3C%2Ftitle%3E %3Cdesc%3ECreated with Sketch.%3C%2Fdesc%3E %3Cdefs%3E%3C%2Fdefs%3E %3Cg id%3D%22Page-1%22 stroke%3D%22none%22 stroke-width%3D%221%22 fill%3D%22none%22 fill-rule%3D%22evenodd%22 sketch%3Atype%3D%22MSPage%22%3E %3Cg id%3D%22shaded-32%2Fcompute-engine%22 sketch%3Atype%3D%22MSArtboardGroup%22%3E %3Crect id%3D%22Container%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%220%22 width%3D%2232%22 height%3D%2232%22%3E%3C%2Frect%3E %3Cg id%3D%22shape%22 sketch%3Atype%3D%22MSLayerGroup%22 transform%3D%22translate(1.000000%2C 1.000000)%22%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Cpath d%3D%22M4%2C4 L4%2C25 L25%2C25 L25%2C4 L4%2C4 L4%2C4 Z M22%2C22 L7%2C22 L7%2C7 L22%2C7 L22%2C22 L22%2C22 Z%22 id%3D%22Shape%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2210%22 y%3D%2210%22 width%3D%229%22 height%3D%229%22%3E%3C%2Frect%3E %3Cpath d%3D%22M19%2C19 L14.5%2C19 L14.5%2C14.4998 L19%2C9.9997 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23424242%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Cpath d%3D%22M19%2C19 L10%2C19 L14.5%2C14.4998 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3C%2Fg%3E %3C%2Fg%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 standalone%3D%22no%22%3F%3E%3Csvg width%3D%2232px%22 height%3D%2232px%22 viewBox%3D%220 0 32 32%22 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 xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E %3C!-- Generator%3A Sketch 3.4.1 (15681) - http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch --%3E %3Ctitle%3Eshaded-32%2Fcompute-engine%3C%2Ftitle%3E %3Cdesc%3ECreated with Sketch.%3C%2Fdesc%3E %3Cdefs%3E%3C%2Fdefs%3E %3Cg id%3D%22Page-1%22 stroke%3D%22none%22 stroke-width%3D%221%22 fill%3D%22none%22 fill-rule%3D%22evenodd%22 sketch%3Atype%3D%22MSPage%22%3E %3Cg id%3D%22shaded-32%2Fcompute-engine%22 sketch%3Atype%3D%22MSArtboardGroup%22%3E %3Crect id%3D%22Container%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%220%22 width%3D%2232%22 height%3D%2232%22%3E%3C%2Frect%3E %3Cg id%3D%22shape%22 sketch%3Atype%3D%22MSLayerGroup%22 transform%3D%22translate(1.000000%2C 1.000000)%22%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Cpath d%3D%22M4%2C4 L4%2C25 L25%2C25 L25%2C4 L4%2C4 L4%2C4 Z M22%2C22 L7%2C22 L7%2C7 L22%2C7 L22%2C22 L22%2C22 Z%22 id%3D%22Shape%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2210%22 y%3D%2210%22 width%3D%229%22 height%3D%229%22%3E%3C%2Frect%3E %3Cpath d%3D%22M19%2C19 L14.5%2C19 L14.5%2C14.4998 L19%2C9.9997 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23424242%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Cpath d%3D%22M19%2C19 L10%2C19 L14.5%2C14.4998 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3C%2Fg%3E %3C%2Fg%3E %3C%2Fg%3E%3C%2Fsvg%3E");\n background-color: #4285f4;\n}\n');const ui="spinnaker.gce";n(ui,[St,Zn,un,it,ot,on,Ha,qa,qe,oi,Ia,za,$a,Zt,Ct,Bt,Tt,Gt,At,Pt,$t,xt,It,zt,Nt,Dt,cn,rn,en,Qn,bt,et,Hn,Xn,an,Ft,Mt,Et,Ot,Ut,si,di,Ke,We]).config((()=>{p.registerProvider("gce",{name:"Google",logo:{path:"gce.logo2ae85d8704b1d6b8.png"},cache:{configurer:"gceCacheConfigurer"},image:{reader:Xe},serverGroup:{transformer:"gceServerGroupTransformer",detailsTemplateUrl:"google/src/serverGroup/details/serverGroupDetails.html",detailsController:"gceServerGroupDetailsCtrl",cloneServerGroupTemplateUrl:"google/src/serverGroup/configure/wizard/serverGroupWizard.html",cloneServerGroupController:"gceCloneServerGroupCtrl",commandBuilder:"gceServerGroupCommandBuilder",configurationService:"gceServerGroupConfigurationService"},instance:{instanceTypeService:"gceInstanceTypeService",detailsTemplateUrl:"google/src/instance/details/instanceDetails.html",detailsController:"gceInstanceDetailsCtrl",multiInstanceTaskTransformer:"gceMultiInstanceTaskTransformer",customInstanceBuilderTemplateUrl:"google/src/serverGroup/configure/wizard/customInstance/customInstanceBuilder.html"},loadBalancer:{transformer:"gceLoadBalancerTransformer",setTransformer:"gceLoadBalancerSetTransformer",detailsTemplateUrl:"google/src/loadBalancer/details/loadBalancerDetails.html",detailsController:"gceLoadBalancerDetailsCtrl",createLoadBalancerTemplateUrl:"google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",createLoadBalancerController:"gceLoadBalancerChoiceCtrl"},securityGroup:{transformer:"gceSecurityGroupTransformer",reader:"gceSecurityGroupReader",detailsTemplateUrl:"google/src/securityGroup/details/securityGroupDetail.html",detailsController:"gceSecurityGroupDetailsCtrl",createSecurityGroupTemplateUrl:"google/src/securityGroup/configure/createSecurityGroup.html",createSecurityGroupController:"gceCreateSecurityGroupCtrl"},subnet:{renderer:"gceSubnetRenderer"},snapshotsEnabled:!0,applicationProviderFields:{templateUrl:"google/src/applicationProviderFields/gceFields.html"}})})),le.registerProvider("gce",["custom","redblack"]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/serverGroupDetails.html",'<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled }">\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>\n {{ctrl.serverGroup.name}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n entity="ctrl.serverGroup"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="serverGroup"\n page-location="details"\n on-update="ctrl.application.serverGroups.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </h3>\n </div>\n <div>\n <div class="actions" ng-class="{ insights: ctrl.serverGroup.insightActions.length > 0 }">\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><a href ng-if="!ctrl.serverGroup.isDisabled" ng-click="ctrl.rollbackServerGroup()">Rollback</a></li>\n <li role="presentation" class="divider" ng-if="!ctrl.serverGroup.isDisabled"></li>\n <li><a href ng-click="ctrl.resizeServerGroup()">Resize</a></li>\n <li><a href ng-if="!ctrl.serverGroup.isDisabled" ng-click="ctrl.disableServerGroup()">Disable</a></li>\n <li>\n <a\n href\n ng-if="ctrl.serverGroup.isDisabled && (ctrl.serverGroup.loadBalancers.length || ctrl.serverGroup.discovery)"\n ng-click="ctrl.enableServerGroup()"\n >Enable</a\n >\n </li>\n <li><a href ng-click="ctrl.destroyServerGroup()">Destroy</a></li>\n <li><a href ng-click="ctrl.cloneServerGroup(ctrl.serverGroup)">Clone</a></li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="ctrl.serverGroup"\n application="ctrl.application"\n entity-type="serverGroup"\n owner-options="ctrl.entityTagTargets"\n on-update="ctrl.application.serverGroups.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n <div class="dropdown" ng-if="ctrl.serverGroup.insightActions.length > 0" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-default dropdown-toggle" uib-dropdown-toggle>\n Insight <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-repeat="action in ctrl.serverGroup.insightActions">\n <a target="_blank" href="{{action.url}}">{{action.label}}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!ctrl.state.loading">\n <h4 class="text-center" ng-if="ctrl.serverGroup.isDisabled">[SERVER GROUP IS DISABLED]</h4>\n <server-group-running-tasks-details\n server-group="ctrl.serverGroup"\n application="ctrl.application"\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.launchConfig.createdTime | timestamp}}</dd>\n <render-if-feature feature="entityTags">\n <entity-source metadata="ctrl.serverGroup.entityTags.creationMetadata"></entity-source>\n </render-if-feature>\n <dt>In</dt>\n <dd>\n <account-tag account="ctrl.serverGroup.account" pad="right"></account-tag>\n {{ctrl.serverGroup.region}}\n </dd>\n <dt>Network</dt>\n <dd>{{ctrl.serverGroup.network}}</dd>\n <dt ng-if="ctrl.serverGroup.subnet">Subnet</dt>\n <dd ng-if="ctrl.serverGroup.subnet">{{ctrl.serverGroup.subnet}}</dd>\n <dt>Zone{{ctrl.serverGroup.regional ? \'s\' : \'\'}}</dt>\n <dd>\n <ul>\n <li ng-repeat="zone in ctrl.serverGroup.zones">{{zone}}</li>\n </ul>\n </dd>\n <dt ng-if="ctrl.serverGroup.regional">Target Shape</dt>\n <dd ng-if="ctrl.serverGroup.regional">{{ctrl.serverGroup.distributionPolicy.targetShape}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Size" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.asg.minSize === ctrl.serverGroup.asg.maxSize">\n <dt>Min/Max</dt>\n <dd>{{ctrl.serverGroup.asg.desiredCapacity}}</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.asg.minSize !== ctrl.serverGroup.asg.maxSize">\n <dt>Min</dt>\n <dd>{{ctrl.serverGroup.asg.minSize}}</dd>\n <dt>Desired</dt>\n <dd>{{ctrl.serverGroup.asg.desiredCapacity}}</dd>\n <dt>Max</dt>\n <dd>{{ctrl.serverGroup.asg.maxSize}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n <a href ng-click="ctrl.resizeServerGroup()">Resize Server Group</a>\n </collapsible-section>\n <collapsible-section heading="Current Actions" ng-if="ctrl.serverGroup.currentActionsSummary">\n <dl class="dl-horizontal dl-narrow">\n <dt ng-repeat-start="currentAction in ctrl.serverGroup.currentActionsSummary">{{currentAction.action}}</dt>\n <dd ng-repeat-end>{{currentAction.count}}</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="Launch Configuration">\n <dl class="horizontal-when-filters-collapsed">\n <dt>Name</dt>\n <dd>{{ctrl.serverGroup.launchConfig.launchConfigurationName}}</dd>\n <dt>Image</dt>\n <dd>{{ctrl.serverGroup.launchConfig.imageId}}</dd>\n <dt>Instance Type</dt>\n <dd>{{ctrl.serverGroup.launchConfig.instanceType | customInstanceFilter }}</dd>\n <dt>Minimum CPU Platform</dt>\n <dd>{{ctrl.serverGroup.launchConfig.minCpuPlatform || \'(Automatic)\'}}</dd>\n <gce-server-group-disk-descriptions application="ctrl.application" server-group="ctrl.serverGroup">\n </gce-server-group-disk-descriptions>\n <dt ng-if="ctrl.serverGroup.serviceAccountEmail">Service Account</dt>\n <dd ng-if="ctrl.serverGroup.serviceAccountEmail">{{ctrl.serverGroup.serviceAccountEmail}}</dd>\n <dt ng-if="ctrl.serverGroup.authScopes">Auth Scopes</dt>\n <dd ng-repeat="authScope in ctrl.serverGroup.authScopes">\n {{authScope}}\n <help-field key="gce.instance.authScopes.{{authScope}}"></help-field>\n </dd>\n <dt>Network</dt>\n <dd>{{ctrl.serverGroup.network}}</dd>\n <dt ng-if="ctrl.serverGroup.subnet">Subnet</dt>\n <dd ng-if="ctrl.serverGroup.subnet">{{ctrl.serverGroup.subnet}}</dd>\n <dt>Associate Public IP Address</dt>\n <dd>{{ctrl.serverGroup.associatePublicIPAddress}}</dd>\n <dt>Can IP Forward</dt>\n <dd>{{ctrl.serverGroup.canIpForward}}</dd>\n <dt>Startup Script</dt>\n <dd ng-if="ctrl.serverGroup.startupScript">\n <a href ng-click="ctrl.showStartupScript()">Show Startup Script</a>\n </dd>\n <dd ng-if="!ctrl.serverGroup.startupScript">[none]</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="{{ctrl.firewallsLabel}}">\n <ul>\n <li ng-repeat="securityGroup in ctrl.securityGroups | orderBy:\'name\'">\n <a\n ui-sref="^.firewallDetails({name: securityGroup.name, accountId: securityGroup.accountName, region: \'global\', provider: ctrl.serverGroup.type})"\n >\n {{securityGroup.name}}\n </a>\n </li>\n </ul>\n </collapsible-section>\n <collapsible-section heading="Autoscaling">\n <gce-add-autoscaling-policy-button\n ng-if="!ctrl.serverGroup.autoscalingPolicy"\n server-group="ctrl.serverGroup"\n application="ctrl.application"\n >\n </gce-add-autoscaling-policy-button>\n <gce-autoscaling-policy\n ng-repeat="policy in [ctrl.serverGroup.autoscalingPolicy]"\n ng-if="ctrl.serverGroup.autoscalingPolicy"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n policy="policy"\n >\n </gce-autoscaling-policy>\n </collapsible-section>\n <collapsible-section heading="AutoHealing">\n <gce-add-auto-healing-policy-button\n ng-if="!ctrl.serverGroup.autoHealingPolicyHealthCheck"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n ></gce-add-auto-healing-policy-button>\n <gce-auto-healing-policy-details\n ng-if="ctrl.serverGroup.autoHealingPolicyHealthCheck"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n ></gce-auto-healing-policy-details>\n </collapsible-section>\n <collapsible-section heading="Custom Metadata">\n <div ng-if="!ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items.length">\n No custom metadata associated with this server group\n </div>\n <dl ng-if="ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items.length">\n <dt ng-repeat-start="metadata in ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items">\n {{metadata.key}}\n <help-field key="gce.serverGroup.customMetadata.{{metadata.key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{metadata.value.length > 90 ? metadata.value.substring(0, 90) + "..." : metadata.value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Tags">\n <div ng-if="!ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items.length">\n No tags associated with this server group\n </div>\n <dl ng-if="ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items.length">\n <dd ng-repeat="tag in ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items">\n {{tag}}\n <help-field\n content="{{ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.helpMap[tag]}}"\n ></help-field>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Labels">\n <div ng-if="!ctrl.serverGroup.instanceTemplateLabels">No labels associated with this server group</div>\n <dl ng-if="ctrl.serverGroup.instanceTemplateLabels">\n <dt ng-repeat-start="(key, value) in ctrl.serverGroup.instanceTemplateLabels">\n {{key}}\n <help-field key="gce.serverGroup.labels.{{key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Shielded VM Policies">\n <div ng-if="!ctrl.serverGroup.shieldedVmConfig">No Shielded VM policies associated with this server group</div>\n <dl ng-if="ctrl.serverGroup.shieldedVmConfig" class="horizontal-when-filters-collapsed">\n <dt>\n Secure Boot\n <help-field key="gce.serverGroup.shieldedVmSecureBoot"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableSecureBoot}}</dd>\n <dt>\n vTPM\n <help-field key="gce.serverGroup.shieldedVmVtpm"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableVtpm}}</dd>\n <dt>\n Integrity Monitoring\n <help-field key="gce.serverGroup.shieldedVmIntegrityMonitoring"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableIntegrityMonitoring}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Availability Policies">\n <div ng-if="!ctrl.serverGroup.availabilityPolicies">\n No availability policies associated with this server group\n </div>\n <div ng-if="ctrl.serverGroup.availabilityPolicies">\n <dl class="horizontal-when-filters-collapsed">\n <dt>\n Preemptibility\n <help-field key="gce.serverGroup.preemptibility"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.preemptibility}}</dd>\n <dt>\n Automatic Restart\n <help-field key="gce.serverGroup.automaticRestart"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.automaticRestart}}</dd>\n <dt>\n On Host Maintenance\n <help-field key="gce.serverGroup.onHostMaintenance"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.onHostMaintenance}}</dd>\n </dl>\n </div>\n </collapsible-section>\n <collapsible-section heading="Package" ng-if="ctrl.serverGroup.buildInfo">\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Job</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">{{ctrl.serverGroup.buildInfo.jenkins.name}}</dd>\n <dt>Package</dt>\n <dd>{{ctrl.serverGroup.buildInfo.package_name}}</dd>\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Build</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">{{ctrl.serverGroup.buildInfo.jenkins.number}}</dd>\n <dt>Commit</dt>\n <dd>{{ctrl.truncateCommitHash()}}</dd>\n <dt>Version</dt>\n <dd>{{ctrl.serverGroup.buildInfo.version}}</dd>\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Build Link</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">\n <a target="_blank" href="{{ctrl.buildJenkinsLink()}}">{{ctrl.buildJenkinsLink()}}</a>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Port Name Mapping" ng-if="ctrl.serverGroup.loadBalancingPolicy">\n <dl class="horizontal-when-filters-collapsed" ng-if="ctrl.serverGroup.loadBalancingPolicy.namedPorts.length">\n <dd ng-repeat="namedPort in ctrl.serverGroup.loadBalancingPolicy.namedPorts">\n {{ namedPort.name }} → {{ namedPort.port }}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Capacity Metrics" ng-if="ctrl.serverGroup.loadBalancingPolicy">\n <dl class="horizontal-when-filters-collapsed" ng-if="ctrl.serverGroup.loadBalancingPolicy.listeningPort">\n <dt>Balancing Mode</dt>\n <dd>{{ctrl.serverGroup.loadBalancingPolicy.balancingMode}}</dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance">Max RPS per instance</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance">\n {{ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxUtilization">Max CPU Utilization</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxUtilization">\n {{ctrl.serverGroup.loadBalancingPolicy.maxUtilization | decimalToPercent}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance">Max connections per instance</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance">\n {{ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.capacityScaler">Capacity</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.capacityScaler">\n {{ctrl.serverGroup.loadBalancingPolicy.capacityScaler | decimalToPercent}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="ctrl.serverGroup.logsLink">\n <a href="{{ctrl.serverGroup.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="ctrl.serverGroup.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal" novalidate>\n <div ng-if="state.requiresTemplateSelection">\n <ng-include src="pages.templateSelection"></ng-include>\n </div>\n <div ng-if="!state.loaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!state.requiresTemplateSelection">\n <v2-modal-wizard ng-show="state.loaded" heading="{{title}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="location" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancers" label="Load Balancers" mark-complete-on-view="false">\n <ng-include src="pages.loadBalancers"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="security-groups" label="{{firewallsLabel}}" done="true">\n <ng-include src="pages.securityGroups"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="instance-type" label="Instance Type" mark-complete-on-view="false">\n <ng-include src="pages.instanceType"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="capacity" label="Capacity" mark-complete-on-view="false">\n <ng-include src="pages.capacity"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autoscaling-policy" label="Autoscaling Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoScalingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autohealing-policy" label="Autohealing Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoHealingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="zones" label="Zones" mark-complete-on-view="false" done="true">\n <ng-include src="pages.zones"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advanced" label="Advanced Settings" mandatory="false" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="state.loaded">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="ctrl.showSubmitButton()"\n is-disabled="!ctrl.isValid() || taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="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("google/src/instance/details/instanceDetails.html",'<div class="details-panel">\n <div class="header">\n <instance-details-header\n health-state="instance.healthState"\n instance-id="instance ? instance.instanceId : instanceIdNotFound"\n loading="state.loading"\n standalone="state.standalone"\n ></instance-details-header>\n <div ng-if="!state.loading">\n <div class="actions" ng-class="{ insights: instance.insightActions.length > 0 }" ng-if="instance.placement">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button\n type="button"\n class="btn btn-sm btn-primary dropdown-toggle"\n ng-disabled="disabled"\n uib-dropdown-toggle\n >\n Instance Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li>\n <a href ng-click="ctrl.enableInstanceInDiscovery()" ng-if="ctrl.canRegisterWithDiscovery()"\n >Enable in Discovery</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.disableInstanceInDiscovery()" ng-if="ctrl.hasHealthState(\'Discovery\', \'Up\')"\n >Disable in Discovery</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.registerInstanceWithLoadBalancer()" ng-if="ctrl.canRegisterWithLoadBalancer()"\n >Register with Load Balancer</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.deregisterInstanceFromLoadBalancer()" ng-if="ctrl.canDeregisterFromLoadBalancer()"\n >Deregister from Load Balancer</a\n >\n </li>\n <li role="presentation" class="divider" ng-if="ctrl.showInstanceActionsDivider()"></li>\n <li><a href ng-click="ctrl.rebootInstance()">Reboot</a></li>\n <li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>\n <li>\n <a href ng-click="ctrl.terminateInstanceAndShrinkServerGroup()" ng-if="instance.serverGroup"\n >Terminate and Shrink Server Group</a\n >\n </li>\n </ul>\n </div>\n <div class="dropdown" ng-if="instance.insightActions.length > 0" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-default dropdown-toggle" uib-dropdown-toggle>\n Insight <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-repeat="action in instance.insightActions">\n <a target="_blank" href="{{action.url}}">{{action.label}}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!state.loading && instance">\n <collapsible-section heading="Instance Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Launched</dt>\n <dd ng-if="instance.launchTime">{{instance.launchTime | timestamp}}</dd>\n <dd ng-if="!instance.launchTime">(Unknown)</dd>\n <dt>In</dt>\n <dd>\n <account-tag account="instance.account" pad="right"></account-tag>\n {{instance.placement.availabilityZone || \'(Unknown)\'}}\n </dd>\n <dt>Type</dt>\n <dd>{{instance.instanceType || \'(Unknown)\' | customInstanceFilter}}</dd>\n <dt>CPU Platform</dt>\n <dd>{{instance.cpuPlatform}}</dd>\n <dt ng-if="instance.serverGroup">Server Group</dt>\n <dd ng-if="instance.serverGroup">\n <a\n ui-sref="^.serverGroup({region: instance.region,\n accountId: instance.account,\n serverGroup: instance.serverGroup,\n provider: instance.provider})"\n >{{instance.serverGroup}}</a\n >\n </dd>\n <dt>Network</dt>\n <dd>{{instance.network || \'(Unknown)\'}}</dd>\n <dt ng-if="instance.subnet">Subnet</dt>\n <dd ng-if="instance.subnet">{{instance.subnet}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <p ng-if="instance.healthState !== \'Starting\' && !healthMetrics.length">\n No health metrics found for this instance\n </p>\n <p ng-if="instance.healthState === \'Starting\' && !healthMetrics.length">\n <span class="glyphicon glyphicon-Starting-triangle"></span> <strong>Starting</strong>\n </p>\n\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-repeat-start="metric in healthMetrics | orderBy: \'type\'">{{metric.type | robotToHuman}}</dt>\n <dd ng-repeat-end>\n <div ng-if="metric.type !== \'LoadBalancer\'">\n <span\n uib-tooltip="{{metric.state.toLowerCase() === \'down\' ? metric.description : \'\'}}"\n tooltip-placement="left"\n >\n <span class="glyphicon glyphicon-{{metric.state}}-triangle"></span>\n {{metric.state | robotToHuman}}\n </span>\n <span class="pad-left small">\n <a ng-if="metric.healthCheckUrl" target="_blank" href="{{metric.healthCheckUrl}}">Health Check</a>\n <span ng-if="metric.healthCheckUrl && metric.statusPageUrl"> | </span>\n <a ng-if="metric.statusPageUrl" target="_blank" href="{{metric.statusPageUrl}}">Status</a>\n </span>\n </div>\n <div\n ng-if="metric.type === \'LoadBalancer\' && metric.loadBalancers.length"\n ng-repeat="loadBalancer in metric.loadBalancers"\n >\n <instance-load-balancer-health load-balancer="loadBalancer"></instance-load-balancer-health>\n </div>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS">\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-if="instance.internalDnsName">Internal DNS Name</dt>\n <dd ng-if="instance.internalDnsName">\n <a href="http://{{instance.internalDnsName}}" target="_blank">{{instance.internalDnsName}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.internalDnsName"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n <dt ng-if="instance.internalIpAddress">Internal IP Address</dt>\n <dd ng-if="instance.internalIpAddress">\n <a href="http://{{instance.internalIpAddress}}" target="_blank">{{instance.internalIpAddress}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.internalIpAddress"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n <dt ng-if="instance.externalIpAddress">External IP Address</dt>\n <dd ng-if="instance.externalIpAddress">\n <a href="http://{{instance.externalIpAddress}}" target="_blank">{{instance.externalIpAddress}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.externalIpAddress"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="{{firewallsLabel}}">\n <ul>\n <li ng-repeat="securityGroup in instance.securityGroups | orderBy:\'groupName\'">\n <a\n ui-sref="^.firewallDetails({name:securityGroup.groupName, accountId: instance.account, region: \'global\', provider: instance.provider})"\n >\n {{securityGroup.groupName}}\n </a>\n </li>\n </ul>\n </collapsible-section>\n <collapsible-section heading="Custom Metadata">\n <div ng-if="!instance.metadata.items.length">No custom metadata associated with this instance</div>\n <dl ng-if="instance.metadata.items.length">\n <dt ng-repeat-start="metadata in instance.metadata.items">\n {{metadata.key}}\n <help-field key="gce.instance.customMetadata.{{metadata.key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{metadata.value.length > 90 ? metadata.value.substring(0, 90) + "..." : metadata.value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Tags">\n <div ng-if="!instance.tags.items.length">No tags associated with this instance</div>\n <dl ng-if="instance.tags.items.length">\n <dd ng-repeat="tag in instance.tags.items">\n {{tag}}\n <help-field content="{{instance.tags.helpMap[tag]}}"></help-field>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Labels">\n <div ng-if="!instance.labels">No labels associated with this instance</div>\n <dl ng-if="instance.labels">\n <dt ng-repeat-start="(key, value) in instance.labels">\n {{key}}\n <help-field key="gce.instance.labels.{{key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="SSH" ng-if="!instance.notFound">\n <dl>\n <dt ng-if="instance.sshLink">SSH into this instance</dt>\n <dd ng-if="instance.sshLink">\n <a href="{{instance.sshLink}}" target="_blank">SSH</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.sshLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="instance.logsLink">\n <a href="{{instance.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n <li>\n <console-output-link instance="instance"></console-output-link>\n </li>\n </ul>\n </collapsible-section>\n <instance-links\n address="baseIpAddress"\n application="application"\n instance="instance"\n moniker="moniker"\n environment="environment"\n ></instance-links>\n </div>\n <div class="content" ng-if="!state.loading && !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("google/src/serverGroup/configure/wizard/customInstance/customInstanceBuilder.html",'<dirty-instance-type-notification command="instanceArchetypeCtrl.command"></dirty-instance-type-notification>\n<div class="form-group">\n <gce-custom-instance-configurer\n instance-family-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.instanceFamilyList"\n selected-instance-family="instanceArchetypeCtrl.command.viewState.customInstance.instanceFamily"\n v-cpu-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.vCpuList"\n selected-v-cpu-count="instanceArchetypeCtrl.command.viewState.customInstance.vCpuCount"\n memory-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.memoryList"\n selected-memory="instanceArchetypeCtrl.command.viewState.customInstance.memory"\n selected-extended-memory="instanceArchetypeCtrl.command.viewState.customInstance.extendedMemory"\n on-change="instanceArchetypeCtrl.command.setCustomInstanceViewState"\n ></gce-custom-instance-configurer>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/loadBalancerDetails.html",'<div class="details-panel">\n <div ng-if="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="!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 <span class="fa icon-sitemap"></span>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>\n {{loadBalancer.name}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n entity="loadBalancer"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="loadBalancer"\n page-location="details"\n on-update="ctrl.application.loadBalancers.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </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="!loadBalancer.instances.length">\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <li\n ng-if="loadBalancer.instances.length"\n class="disabled"\n uib-tooltip="You must detach all instances before you can delete this load balancer."\n >\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="loadBalancer"\n application="ctrl.application"\n entity-type="loadBalancer"\n on-update="ctrl.application.loadBalancers.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div ng-if="!state.loading" class="content">\n <collapsible-section heading="Load Balancer Details" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Created</dt>\n <dd>{{loadBalancer.elb.createdTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="loadBalancer.account" pad="right"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{loadBalancer.region}}</dd>\n <dt>Type</dt>\n <dd>\n <gce-load-balancer-type load-balancer="loadBalancer"></gce-load-balancer-type>\n </dd>\n <dt ng-if="loadBalancer.network">Network</dt>\n <dd ng-if="loadBalancer.network">{{ctrl.getNetworkId(loadBalancer)}}</dd>\n <dt ng-if="loadBalancer.subnet">Subnet</dt>\n <dd ng-if="loadBalancer.subnet">{{ctrl.getSubnetId(loadBalancer)}}</dd>\n <dt ng-if="loadBalancer.serverGroups.length">Server Groups</dt>\n <dd ng-if="loadBalancer.serverGroups.length">\n <ul>\n <li ng-repeat="serverGroup in loadBalancer.serverGroups | orderBy: [\'isDisabled\', \'-name\']">\n <a\n ui-sref="^.serverGroup({region: serverGroup.region,\n accountId: serverGroup.account,\n serverGroup: serverGroup.name,\n provider: \'gce\'})"\n >\n {{serverGroup.name}}\n </a>\n </li>\n </ul>\n </dd>\n <div ng-if="!ctrl.isHttpLoadBalancer(loadBalancer) && loadBalancer.elb.dnsname">\n <dt>DNS Name</dt>\n <dd>\n <a target="_blank" href="{{loadBalancer.elb.dns.protocol}}//{{loadBalancer.elb.dns.dnsname}}"\n >{{loadBalancer.elb.dns.dnsname}}</a\n >\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="loadBalancer.elb.dns.dnsname"\n tool-tip="\'Copy DNS Name to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </div>\n <div ng-if="ctrl.loadBalancer.loadBalancerType === \'HTTP\'">\n <dt>DNS Names</dt>\n <dd>\n <ul>\n <li ng-repeat="dns in loadBalancer.elb.dns">\n <a target="_blank" href="{{dns.protocol}}//{{dns.dnsname}}">{{dns.dnsname}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="dns.dnsname"\n tool-tip="\'Copy DNS Name to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </dd>\n </div>\n <div ng-if="ctrl.isHttpLoadBalancer(loadBalancer)">\n <gce-host-and-path-rules-button\n default-service="loadBalancer.defaultService"\n load-balancer-name="loadBalancer.name"\n host-rules="loadBalancer.hostRules"\n >\n </gce-host-and-path-rules-button>\n </div>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <health-counts class="pull-left" container="loadBalancer.instanceCounts"></health-counts>\n </collapsible-section>\n <collapsible-section heading="Listeners">\n <dl>\n <dt>\n Load Balancer → Instance\n <help-field\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.loadBalancerType === \'SSL\' || loadBalancer.loadBalancerType === \'TCP\'"\n key="gce.httpLoadBalancer.namedPort"\n ></help-field>\n </dt>\n <dd ng-repeat="listener in loadBalancer.elb.listenerDescriptions">\n {{listener.listener.protocol}}:{{listener.listener.loadBalancerPort}} →\n {{listener.listener.instanceProtocol}}:{{listener.listener.instancePort}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="Backend Services"\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.backendService"\n >\n <dl class="horizontal-when-filters-collapsed" ng-switch on="loadBalancer.loadBalancerType">\n <div ng-repeat="service in loadBalancer.elb.backendServices" ng-switch-when="HTTP">\n <hr ng-if="$index > 0" />\n <gce-backend-service-details backend-service="service"></gce-backend-service-details>\n </div>\n\n <gce-backend-service-details\n backend-service="loadBalancer.backendService"\n ng-switch-default\n ></gce-backend-service-details>\n </dl>\n </collapsible-section>\n <collapsible-section\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.healthCheck || loadBalancer.backendService.healthCheck"\n heading="Health Checks"\n >\n <dl class="horizontal-when-filters-collapsed" ng-switch on="loadBalancer.loadBalancerType">\n <gce-health-check ng-switch-when="NETWORK" health-check="loadBalancer.healthCheck"></gce-health-check>\n\n <div ng-switch-when="HTTP" ng-repeat="healthCheck in loadBalancer.elb.healthChecks">\n <hr ng-if="$index > 0" />\n <gce-health-check health-check="healthCheck"></gce-health-check>\n </div>\n\n <gce-health-check ng-switch-default health-check="loadBalancer.backendService.healthCheck"></gce-health-check>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="loadBalancer.logsLink">\n <a href="{{loadBalancer.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="loadBalancer.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Load Balancer Type Selection</h4>\n </div>\n <div class="modal-body">\n <form class="form-horizontal">\n <div class="form-group">\n <div class="col-md-4 col-md-offset-1 sm-label-left">\n <b>Choose load balancer type:</b>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-6 col-md-offset-1">\n <ui-select ng-model="ctrl.choice" class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n <span>{{$select.selected}}</span>\n </ui-select-match>\n <ui-select-choices repeat="choice in ctrl.choices">\n <span>{{choice}}<help-field key="gce.loadBalancerType.{{choice}}"></help-field></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-primary" ng-click="ctrl.choose(ctrl.choice)">\n <span>Create {{ ctrl.choice }} load balancer</span>\n <span class="glyphicon glyphicon-chevron-right"></span>\n </button>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/details/securityGroupDetail.html",'<div class="details-panel">\n <div class="header">\n <div class="close-button" ng-if="!state.standalone">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div ng-if="state.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="header-text horizontal middle" ng-if="!state.loading">\n <span class="glyphicon glyphicon-transfer"></span>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>\n {{securityGroup.name || \'(not found)\'}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n ng-if="!state.loading"\n entity="securityGroup"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="securityGroup"\n page-location="details"\n on-update="ctrl.application.securityGroups.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </h3>\n </div>\n <div class="actions">\n <div ng-if="!securityGroup.id.includes(\'/\')" class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n <firewall-label label="Firewall"></firewall-label> Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.editInboundRules()">Edit Inbound Rules</a></li>\n <li>\n <a href ng-click="ctrl.deleteSecurityGroup()">Delete <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <li>\n <a href ng-click="ctrl.cloneSecurityGroup()">Clone <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="securityGroup"\n application="ctrl.application"\n entity-type="securityGroup"\n on-update="ctrl.application.securityGroups.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n <div ng-if="securityGroup.id.includes(\'/\')" class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n <firewall-label label="Firewall"></firewall-label> Actions <span class="caret"></span>\n </button>\n <ul\n uib-tooltip="You cannot modify shared VPC host project firewall rules."\n class="dropdown-menu"\n uib-dropdown-menu\n role="menu"\n >\n <li class="disabled"><a>Edit Inbound Rules</a></li>\n <li class="disabled">\n <a>Delete <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <li class="disabled">\n <a>Clone <firewall-label label="Firewall"></firewall-label></a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!state.loading">\n <collapsible-section heading="{{firewallLabel}} Details" expanded="true">\n <dl class="dl-horizontal dl-medium">\n <dt>ID</dt>\n <dd>{{securityGroup.id}}</dd>\n <dt>Account</dt>\n <dd><account-tag account="securityGroup.accountName"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{securityGroup.region}}</dd>\n <dt>Network</dt>\n <dd>{{securityGroup.network}}</dd>\n <dt>Description</dt>\n <dd>{{securityGroup.description}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="IP Range Rules ({{securityGroup.sourceRanges.length || 0}})"\n expanded="{{!!(securityGroup.sourceRanges && securityGroup.sourceRanges.length)}}"\n >\n <div ng-if="!securityGroup.sourceRanges.length">None</div>\n\n <dl\n ng-class="insightCtrl.vm.filtersExpanded ? \'\' : \'dl-horizontal dl-medium\'"\n ng-repeat="sourceRange in securityGroup.sourceRanges"\n >\n <dt>IP Range</dt>\n <dd>{{sourceRange}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="Protocol/Port Ranges ({{securityGroup.protocolPortRangeCount || 0}})"\n expanded="{{!!(securityGroup.protocolPortRangeCount)}}"\n >\n <div ng-if="!securityGroup.ipIngressRules.length">None</div>\n\n <dl\n ng-class="insightCtrl.vm.filtersExpanded ? \'\' : \'dl-horizontal dl-medium\'"\n ng-repeat="ipIngressRule in securityGroup.ipIngressRules"\n >\n <dt ng-if="ipIngressRule.portRanges.length === 0">Protocol</dt>\n <dd ng-if="ipIngressRule.portRanges.length === 0">{{ipIngressRule.protocol}}</dd>\n <dt\n ng-if="ipIngressRule.portRanges && ipIngressRule.portRanges.length === 1 && ipIngressRule.portRanges[0].startPort"\n >\n Port Range\n </dt>\n <dt\n ng-if="ipIngressRule.portRanges && ipIngressRule.portRanges.length > 1 && ipIngressRule.portRanges[0].startPort"\n >\n Port Ranges\n </dt>\n <dd ng-repeat="portRange in ipIngressRule.portRanges" ng-if="portRange.startPort && portRange.endPort">\n {{ipIngressRule.protocol}}: {{portRange.startPort}} → {{portRange.endPort}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Target Tags ({{securityGroup.targetTags.length || 0}})">\n <ul ng-if="securityGroup.targetTags.length">\n <li ng-repeat="tag in securityGroup.targetTags">\n {{tag}} <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n </li>\n </ul>\n <div ng-if="!securityGroup.targetTags.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Source Tags ({{securityGroup.sourceTags.length || 0}})">\n <ul ng-if="securityGroup.sourceTags.length">\n <li ng-repeat="tag in securityGroup.sourceTags">\n {{tag}} <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n </li>\n </ul>\n <div ng-if="!securityGroup.sourceTags.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Target Service Accounts ({{securityGroup.targetServiceAccounts.length || 0}})">\n <ul ng-if="securityGroup.targetServiceAccounts.length">\n <li ng-repeat="serviceAccount in securityGroup.targetServiceAccounts">{{serviceAccount}}</li>\n </ul>\n <div ng-if="!securityGroup.targetServiceAccounts.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Source Service Accounts ({{securityGroup.sourceServiceAccounts.length || 0}})">\n <ul ng-if="securityGroup.sourceServiceAccounts.length">\n <li ng-repeat="serviceAccount in securityGroup.sourceServiceAccounts">{{serviceAccount}}</li>\n </ul>\n <div ng-if="!securityGroup.sourceServiceAccounts.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="securityGroup.logsLink">\n <a href="{{securityGroup.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="securityGroup.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroup.html",'<ng-form role="form" name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard heading="Create New {{firewallLabel}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/applicationProviderFields/gceFields.html",'<div class="form-group row">\n <div class="col-sm-3 sm-label-right">GCE Settings</div>\n <div class="col-sm-9 checkbox" style="margin-bottom: 0; margin-top: 5px">\n <label>\n <input\n type="checkbox"\n ng-init="$ctrl.initializeApplicationField(\'gce.associatePublicIpAddress\')"\n ng-model="$ctrl.application.providerSettings.gce.associatePublicIpAddress"\n />\n Associate Public IP Address <help-field key="gce.serverGroup.associatePublicIpAddress.providerField"></help-field>\n </label>\n </div>\n</div>\n')}]);export{ui as GOOGLE_MODULE};
|
|
1
|
+
import*as e from"angular";import{module as n,noop as t}from"angular";import{SETTINGS as a,withErrorBoundary as i,LayoutProvider as l,FormField as c,CheckboxInput as r,HelpField as o,SearchService as s,InfrastructureCaches as d,REST as u,HelpContentsRegistry as g,CloudProviderRegistry as p,FirewallLabels as m,ConfirmationModalService as h,InstanceWriter as f,RecentHistoryService as v,InstanceReader as b,AccountService as y,CACHE_INITIALIZER_SERVICE as k,NameUtils as S,LOAD_BALANCER_READ_SERVICE as w,SubnetReader as C,NetworkReader as B,TaskExecutor as T,TaskMonitor as G,LoadBalancerWriter as A,ModalWizard as P,Registry as $,BakeExecutionLabel as x,ArtifactReferenceService as I,ExpectedArtifactService as M,AuthenticationService as z,PipelineTemplates as N,BakeryReader as D,StageConstants as E,PipelineConfigService as R,SecurityGroupWriter as L,SECURITY_GROUP_READER as H,IMAGE_READER as F,excludeAllTypesExcept as U,ArtifactTypePatterns as O,NgGCEImageArtifactDelegate as q,ExpectedArtifactSelectorViewController as V,INSTANCE_TYPE_SERVICE as j,WizardModal as K,WizardPage as W,FormikFormField as Z,ReactModal as X,NumberInput as Y,ReactSelectInput as J,SERVER_GROUP_WRITER as Q,ServerGroupWarningMessageService as ee,ServerGroupTemplates as ne,ServerGroupReader as te,ClusterTargetBuilder as ae,ApplicationNameValidator as ie,DeploymentStrategyRegistry as le}from"@spinnaker/core";import ce from"react";import{react2angular as re}from"react2angular";import oe,{isEmpty as se,defaults as de,uniqWith as ue,chain as ge,uniq as pe,map as me,find as he,trimEnd as fe,get as ve,partition as be,groupBy as ye,cloneDeep as ke,has as Se,intersectionBy as we,isFinite as Ce,without as Be,set as Te,intersection as Ge,last as Ae}from"lodash";import Pe,{Async as $e}from"react-select";import xe from"@uirouter/angularjs";import Ie from"angular-ui-bootstrap";import{$http as Me,$q as ze}from"ngimport";import Ne from"ui-select";import{Subject as De,from as Ee}from"rxjs";import{switchMap as Re}from"rxjs/operators";function Le(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))}}function He(e){const{label:n,help:t,input:a,actions:i,validation:l,required:c}=e,r=!se(n)||!se(t),{hidden:o,messageNode:s}=l;return ce.createElement("div",{className:"gce-autoscaling-layout"},r&&ce.createElement("label",{className:"col-md-3 sm-label-right"},n,c&&ce.createElement("span",null,"*")," ",t),ce.createElement("div",{className:"col-md-2 content-fields"},ce.createElement("div",null,a," ",i),!o&&ce.createElement("div",{className:"message"},s)))}Le(".gce-autoscaling-layout .checkbox {\n margin-left: 5px;\n margin-top: 7px;\n}\n");var Fe=(e=>(e.NONE="NONE",e.STANDARD="OPTIMIZE_AVAILABILITY",e))(Fe||{});const Ue=de(a.providers.gce||{},{defaults:{},feature:{},resetToOriginal:a.resetProvider("gce")});const Oe="spinnaker.gce.predictiveAutoscaling";n(Oe,[]).component("gcePredictiveAutoscaling",re(i(function({policy:e,updatePolicy:n}){var t;return Ue.feature.predictiveAutoscaling?ce.createElement(l,{value:He},ce.createElement("div",{className:"row"},ce.createElement(c,{help:ce.createElement(o,{id:"gce.serverGroup.scalingPolicy.predictiveAutoscaling"}),input:e=>ce.createElement(r,{...e}),label:"Enable predictive autoscaling",onChange:t=>{var a;a=t.target.checked?Fe.STANDARD:Fe.NONE,n({...e,cpuUtilization:{...e.cpuUtilization,predictiveMethod:a}})},value:(null==(t=null==e?void 0:e.cpuUtilization)?void 0:t.predictiveMethod)===Fe.STANDARD}))):null},"gcePredictiveAutoscaling"),["policy","updatePolicy"]));const qe="spinnaker.gce.addressReader.service";n(qe,[]).service("gceAddressReader",class{listAddresses(e){return e?this.listAddresses(null).then(n=>n.filter(n=>n.region===e)):s.search({q:"",type:"addresses",allowShortQuery:"true"},d.get("addresses")).then(e=>e&&e.results?e.results.filter(e=>"gce"===e.provider).map(e=>{const n=JSON.parse(e.address);return n.account=e.account,n.region=e.region,n}):[]).catch(()=>[])}});const _e="spinnaker.deck.gce.backendService.reader.service";n(_e,[]).factory("gceBackendServiceReader",function(){return{listBackendServices:function e(n){return n?e().then(([e])=>{if(e){return(e.results||[]).filter(e=>e.kind===n)}return[]}):u("/search").useCache(d.get("backendServices")).query({q:"",type:"backendServices",allowShortQuery:"true"}).get()}}});const Ve="spinnaker.gce.healthCheck.reader";n(Ve,[]).service("gceHealthCheckReader",class{listHealthChecks(e){return e?this.listHealthChecks().then(n=>n.filter(n=>n.healthCheckType===e)):s.search({q:"",type:"healthChecks",allowShortQuery:"true"},d.get("healthChecks")).then(e=>{if(e&&e.results){const n=e.results.filter(e=>"gce"===e.provider).map(e=>{const n=JSON.parse(e.healthCheck);return n.account=e.account,n});return ue(n,(e,n)=>e.name===n.name&&e.healthCheckType===n.healthCheckType&&e.account===n.account)}return[]}).catch(()=>[])}});const je="spinnaker.gce.cache.initializer";n(je,[_e,qe,Ve]).factory("gceCacheConfigurer",["gceAddressReader","gceBackendServiceReader","gceCertificateReader","gceHealthCheckReader",function(e,n,t,a){const i=Object.create(null);return i.addresses={initializers:[()=>e.listAddresses()]},i.backendServices={initializers:[()=>n.listBackendServices()]},i.certificates={initializers:[()=>t.listCertificates()]},i.healthChecks={initializers:[()=>a.listHealthChecks()]},i}]);const Ke="spinnaker.gce.common.xpnNaming.service";n(Ke,[]).factory("gceXpnNamingService",function(){return{deriveProjectId:e=>{const n=e.selfLink.split("/");return n[n.indexOf("projects")+1]},decorateXpnResourceIfNecessary:(e,n)=>{if(!n)return null;const t=n.split("/"),a=t[t.indexOf("projects")+1],i=oe.last(t);return a!==e?a+"/"+i:i}}});const We={"gce.httpLoadBalancer.certificate":"The name of an SSL certificate. If specified, Spinnaker will create an HTTPS load balancer.","gce.httpLoadBalancer.certificateMap":"A Certificate Manager certificate map name. Certificate maps let GCP manage and auto-rotate TLS certificates, replacing individual certificate selection.","gce.httpLoadBalancer.defaultService":"A default service handles any requests that do not match a specified host rule or path matching rule.","gce.httpLoadBalancer.externalIP":"The IP address for this listener. If you do not specify an IP, your listener will be assigned an ephemeral IP.","gce.httpLoadBalancer.hostRule.hostPattern":"For example, <b>example.com</b>.","gce.httpLoadBalancer.namedPort":"\n For HTTP(S) and SSL/TCP load balancers,\n incoming traffic is directed through a named port (for Spinnaker, the named port is <b>http</b>).\n The mapping from named port to port number is specified per server group\n and can be configured within the server group creation dialogue under <b>Port Name Mapping</b>.","gce.httpLoadBalancer.pathRule.paths":"For example, <b>/path</b> in <b>example.com/path</b>","gce.httpLoadBalancer.port":"HTTP requests can be load balanced based on port 80 or port 8080. HTTPS requests can be load balanced on port 443.","gce.image.artifact":"The artifact that is to be deployed to this cluster. The artifact should represent a deployable image.","gce.image.source":"\n <p>Where the image to deploy is read from.</p>\n <p>\n <b>Artifact:</b> Deploy an artifact that was supplied/created upstream. The expected artifact must be referenced here, and will be bound at runtime.\n </p>\n <p>\n <b>Prior Stage:</b> Deploy the result of the most recent Bake or Find Image stage.\n </p>\n ","gce.instance.customInstance.cores":"<ul><li>Above 1, vCPU count must be even.</li><li>Zones that support Haswell and Ivy Bridge processors can support custom machine types up to 32 vCPUs.</li><li>Zones that support Sandy Bridge processors can support up to 16 vCPUs.</li></ul>","gce.instance.customInstance.memory":"<ul><li>Memory per vCPU must be between .9 GB and 6.5 GB.</li><li>Total memory must be a multiple of 256 MB.</li><li>Memory per vCPU must be between .9 GB and 800 GB if selected extended memory feature.</li></ul>","gce.instance.customInstance.extendedmemory":'Add more memory per vCPU to your VM instance. <a href="https://cloud.google.com/compute/docs/instances/creating-instance-with-custom-machine-type#extendedmemory">Click here </a>for more information regarding extended memory',"gce.instance.customMetadata.instance-template":"The instance template used to configure this instance.","gce.instance.customMetadata.load-balancer-names":'This field is used to "remember" what load balancers this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.global-load-balancer-names":'This field is used to "remember" what global load balancers this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.backend-service-names":'This field is used to "remember" what backend services this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.load-balancing-policy":'This field is used to "remember" the load balancing policy this instance is associated with, even if it is deregistered.',"gce.instance.customMetadata.startup-script":"This script will run automatically on every boot.","gce.instance.labels.spinnaker-region":"This label can be used to group instances when querying for metrics.","gce.instance.labels.spinnaker-server-group":"This label can be used to group instances when querying for metrics.","gce.instance.storage":"<p>Storage options can be fully-configured on the <b>Advanced Settings</b> tab. These are just default settings. Please be aware of your Local SSD quotas as you provision VMs.</p>","gce.instance.storage.localSSD":"<p>Local SSD disks are always 375GB. All non shared-core instance types support attaching up to 8 Local SSD disks. Please be aware of your Local SSD quotas as you provision VMs.</p>","gce.instance.serviceAccount":'<p>Service accounts authenticate applications running on your virtual machine instances to other Google Cloud Platform services. Valid values are either "default" or the full email address of a custom service account.</p>',"gce.instance.authScopes":"<p>Service account scopes specify which Google Cloud Platform APIs your instances can authenticate with, and define the level of access that your instances have with those services. You can enter custom auth scopes by typing into the blank field.</p>","gce.instance.authScopes.cloud-platform":"<p>The instances in this server group have full API access to all Google Cloud services.</p>","gce.instanceType.32core":"<p>32-core machine types are in Beta and are available only in Ivy Bridge and Haswell zones.</p>","gce.internalLoadBalancer.ports":"Use a comma to separate up to five TCP ports.","gce.internalHttpLoadBalancer.network":"Network must have a subnet whose 'purpose' is 'INTERNAL_HTTPS_LOAD_BALANCER'","gce.loadBalancer.connectionDraining":"(Optional) If set, enables connection draining for this backend service. This field defines the number of seconds to wait before instances that belong to this backend service are terminated in order to drain in-flight connections.","gce.loadBalancer.detail":"<p>(Optional) <b>Detail</b> is a string of free-form alphanumeric characters and hyphens to describe any other variables.</p>","gce.loadBalancer.advancedSettings.healthInterval":"<p>Configures the interval, in seconds, between load balancer health checks.</p><p>Default: <b>10</b></p>","gce.loadBalancer.advancedSettings.healthyThreshold":"<p>Configures the number of healthy observations before reinstituting an instance into the load balancer’s traffic rotation.</p><p>Default: <b>10</b></p>","gce.loadBalancer.advancedSettings.unhealthyThreshold":"<p>Configures the number of unhealthy observations before deservicing an instance from the load balancer.</p><p>Default: <b>2</b></p>","gce.loadBalancer.healthCheck":"(Optional) <b>Health Checks</b> use HTTP requests to determine if a VM instance is healthy.","gce.loadBalancer.portName":"(Required) The <b>Port Name</b> this backend service will forward traffic to. Load balancers in GCP specify a <b>Port Name</b>, and each server group added to a load balancer needs to specify a mapping from that <b>Port Name</b> to a port to actually receive traffic.","gce.loadBalancer.portRange":"(Optional) Only packets addressed to ports in the specified <b>Port Range</b> will be forwarded. If left empty, all ports are forwarded. Must be a single port number or two port numbers separated by a dash. Each port number must be between 1 and 65535, inclusive. For example: 5000-5999.","gce.securityGroup.sourceCIDRs":"Traffic is only allowed from sources that match one of these CIDR ranges, or one of the source tags above.","gce.securityGroup.sourceTags":"Traffic is only allowed from sources that match one of these tags, or one of the source CIDR ranges below.","gce.securityGroup.targetTags":"Traffic is only allowed if the target instance has one of these tags.","gce.securityGroup.targetServiceAccounts":"Traffic is allowed if the target instance has one of these service accounts.","gce.securityGroup.sourceServiceAccounts":"Traffic is allowed if the source instance has one of these service accounts.","gce.serverGroup.associatePublicIpAddress.providerField":"Check if new GCE server groups in this application should be assigned a public IP address by default.","gce.serverGroup.resizeWithAutoscalingPolicy":"\n Setting the desired instance count for a server group with an autoscaler is not supported by Spinnaker;\n if the desired instance count differs from the instance count that the autoscaler wants to maintain for its configured metrics,\n it will quickly override the desired instance count.","gce.serverGroup.scalingPolicy.coolDownPeriodSec":"How long to wait before collecting information from a new instance. This should be at least the time it takes to initialize the instance.","gce.serverGroup.scalingPolicy.cpuUtilization":"Autoscaler adds or removes instances to maintain this CPU usage on each instance.","gce.serverGroup.scalingPolicy.predictiveAutoscaling":"Autoscaler adds or removes instances based on forecasted load. You must set a CPU utilization target to enable predictive autoscaling.","gce.serverGroup.scalingPolicy.loadBalancingUtilization":"Autoscaler adds or removes instances to maintain this usage of load-balancing capacity.","gce.serverGroup.scalingPolicy.customMetricUtilizations":"Autoscaler adds or removes instances to maintain this usage for custom metric.","gce.serverGroup.scalingPolicy.cronExpression":"\n Start time and recurrence using a cron expression. Examples: <br/>\n Every day at 3 PM:<br/>\n 0 15 * * *<br/>\n Every week Monday to Friday at 8:30 AM: <br/>\n 30 8 * * Mon-Fri<br/>\n Once on 30 January 2030 at midnight: <br/>\n 0 0 30 1 * 2030","gce.serverGroup.imageName":"(Required) <b>Image</b> is the Google Compute Engine image. Images are restricted to the account selected.","gce.serverGroup.capacity":"The number of instances that the instance group manager will attempt to maintain. Deleting or abandoning instances will affect this number, as will resizing the group.","gce.serverGroup.minCpuPlatform":'The baseline minimum CPU platform to use for your instances, whenever available. Select "Automatic" unless you have a specific need.',"gce.serverGroup.customMetadata":"<b>Custom Metadata</b> will be propagated to the instances in this server group. This is useful for passing in arbitrary values that can be queried by your code on the instance.","gce.serverGroup.userData":"<p>Custom user data will be propagated to the instances in this server group. Key/value pairs can either be newline or comma delimited.</p><strong>Example:</strong><br/> <pre>key=value<br>key2=value2</pre>","gce.serverGroup.customMetadata.load-balancer-names":'This field is used to "remember" what load balancers this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.global-load-balancer-names":'This field is used to "remember" what global load balancers this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.backend-service-names":'This field is used to "remember" what backend services this server group is associated with, even if the instances are deregistered.',"gce.serverGroup.customMetadata.load-balancing-policy":'This field is used to "remember" the load balancing policy this server group is configured with, even if the server group is deregistered from the load balancer. This allows us to re-enable the server group with the same load balancing policy specified on creation.',"gce.serverGroup.customMetadata.select-zones":"This regional server group will be deployed only to specific zones within the region.","gce.serverGroup.customMetadata.startup-script":"This script will run automatically on every boot.","gce.serverGroup.labels.spinnaker-region":"This label can be used to group instances when querying for metrics.","gce.serverGroup.labels.spinnaker-server-group":"This label can be used to group instances when querying for metrics.","gce.serverGroup.shieldedVmConfig":"Shielded VM features include trusted UEFI firmware and come with options for Secure Boot, Virtual Trusted Platform Module (vTPM), and Integrity Monitoring.","gce.serverGroup.shieldedVmSecureBoot":"Secure boot helps protect your VM instances against boot-level and kernel-level malware and rootkits.","gce.serverGroup.shieldedVmVtpm":"Virtual Trusted Platform Module (vTPM) validates your guest VM pre-boot and boot integrity, and offers key generation and protection.","gce.serverGroup.shieldedVmIntegrityMonitoring":"Integrity monitoring lets you monitor and verify the runtime boot integrity of your shielded VM instances using Stackdriver reports. Note: requires vTPM to be enabled.","gce.serverGroup.preemptibility":"A preemptible VM costs much less, but lasts only 24 hours. It can be terminated sooner due to system demands.","gce.serverGroup.automaticRestart":"Compute Engine can automatically restart VM instances if they are terminated for non-user-initiated reasons (maintenance event, hardware failure, software failure, etc.).","gce.serverGroup.onHostMaintenance":"When Compute Engine performs periodic infrastructure maintenance it can migrate your VM instances to other hardware without downtime.","gce.serverGroup.canIpForward":'Forwarding allows the instance to help route packets. See <a target="_blank" href="https://cloud.google.com/compute/docs/networking?hl=en_US#canipforward">here</a> for more information.',"gce.serverGroup.securityGroups.implicit":"Firewall rules with no target tags defined will permit incoming connections that match the ingress rules to all instances in the network.","gce.serverGroup.securityGroups.targetTags":"This {{firewall}} rule will be associated with this server group only if a target tag is selected.","gce.serverGroup.autoscaling.targetCPUUsage":"Autoscaling adds or removes VMs in the group to maintain this level of CPU usage on each VM.","gce.serverGroup.autoscaling.targetHTTPLoadBalancingUsage":"Autoscaling adds or removes VMs in the group to maintain this usage of load-balancing capacity. This value is a percentage of the 'Maximum rate' setting of the load balancer this group is used by.","gce.serverGroup.autoscaling.targetMetric":"Autoscaling adds or removes VMs in the group to maintain these target levels.","gce.serverGroup.autoscaling.minVMs":"The least number of VM instances the group will contain, even if the target is not met.","gce.serverGroup.autoscaling.maxVMs":"The largest number of VM instances allowed, even if the target is exceeded.","gce.serverGroup.autoscaling.cooldown":"How long to wait before collecting information from a new instance. This should be at least the time it takes to initialize the instance. To find the minimum, create an instance from the same image and note how long it takes to start.","gce.serverGroup.autoscaling.mode":"Mode of operation of the autoscaling policy. This guides the autoscaler by defining the types of scaling operations it can perform. Options are ON, ONLY_SCALE_OUT, and OFF.","gce.serverGroup.autoHealing":"VMs in the group are recreated as needed. You can use a health check to recreate a VM if the health check finds the VM unresponsive. If you do not select a health check, VMs are recreated only when stopped.","gce.serverGroup.initialDelaySec":"The time to allow an instance to boot and applications to fully start before the first health check.","gce.serverGroup.maxUnavailable":"\n Maximum number of instances that can be unavailable when auto-healing. The instance is considered available if all of the following conditions are satisfied:\n <ul>\n <li>1. Instance's status is RUNNING.</li>\n <li>2. Instance's liveness health check result was observed to be HEALTHY at least once.</li>\n </ul>","gce.serverGroup.subnet":"\n Subnetworks allow you to regionally segment the network IP space into prefixes (subnets) and control which prefix a VM instance's internal IP address is allocated from. There are several types of GCE networks:\n <ul>\n <li><b>Legacy (non-subnet) Network</b>: IP address allocation occurs at the global network level. This means the network address space spans across all regions.</li>\n <li><b>Auto Subnet Network</b>: Server groups will be automatically assigned to the specified region's subnet.</li>\n <li><b>Custom Subnet Network</b>: A subnet must be selected for the server group. If no subnets have been created for the specified region, you will not be able to provision the server group.</li>\n </ul>","gce.serverGroup.loadBalancingPolicy.balancingMode":"Tells the load balancer when the backend is at capacity. If a backend is at capacity, new requests are routed to the nearest region that can handle requests. The balancing mode can be based on CPU utilization or requests per second (RPS).","gce.serverGroup.loadBalancingPolicy.maxRatePerInstance":"The maximum number of requests per second that can be sent to the backend instance group. Input must be a number greater than zero.","gce.serverGroup.loadBalancingPolicy.maxUtilization":"The maximum CPU utilization allowed for the backend. CPU utilization is calculated by averaging CPU use across all instances in the backend instance group. Input must be a number between 0 and 100.","gce.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance":"The target connections per second for individual instances. When this number is exceeded, requests are directed to another backend.","gce.serverGroup.loadBalancingPolicy.capacityScaler":"\n An additional control to manage your maximum CPU utilization or RPS.\n If you want your instances to operate at a max 80% CPU utilization, set your balancing mode to 80% max CPU utilization and your capacity to 100%.\n If you want to cut instance utilization by half, set your balancing mode to 80% max CPU utilization and your capacity to 50%. Input must be a number between 0 and 100.","gce.serverGroup.loadBalancingPolicy.portName":"A load balancer sends traffic to an instance group through a named port. Input must be a port name.","gce.serverGroup.loadBalancingPolicy.listeningPort":"A load balancer sends traffic to an instance group through a named port. Input must be a port number (i.e., between 1 and 65535).","gce.serverGroup.traffic":"Registers the server group with any associated load balancers. These registrations are used by Spinnaker to determine if the server group is enabled.","gce.serverGroup.accelerator":"Attaches GPUs to instances in this server group. The set of available GPUs is dictated by the selected zone. You cannot attach GPUs to instances with shared-core machine types.","gce.tagImage.consideredStages":"Limit which previous stages will be considered when locating images to tag. If left unchecked, images generated by any upstream stage will be tagged.","pipeline.config.gce.bake.accountName":"<p>(Optional) The name of a Google account configured within Rosco. If left blank, the first configured account will be used.</p>","pipeline.config.gce.bake.baseImage":"<p>(Optional) A GCE image name. For example: ubuntu-1204-precise-v20150910.</p>","gce.loadBalancerType.Network":"\n <p>Use Network Load Balancing to balance the load on your systems based on incoming IP protocol data, such as address, port, and protocol type.</p>\n <p>Network Load Balancing is a regional, non-proxied load balancer. You can use it to load balance UDP traffic, and TCP and SSL traffic on ports that are not supported by the SSL proxy and TCP proxy load balancers.</p>","gce.loadBalancerType.HTTP(S)":"<p>Google Cloud Platform (GCP) HTTP(S) Load Balancing provides global load balancing for HTTP(S) requests destined for your instances.</p>","gce.loadBalancerType.Internal":"<p>Internal TCP/UDP Load Balancing is a regional load balancer that enables you to run and scale your services behind a private load balancing IP address that is accessible only to your internal virtual machine instances.</p>","gce.loadBalancerType.SSL":"<p>Google Cloud SSL Proxy Load Balancing terminates user SSL (TLS) connections at the load balancing layer, then balances the connections across your instances using the SSL or TCP protocols. This supports both IPv4 and IPv6 addresses for client traffic.</p>","gce.loadBalancerType.TCP":"<p>Google Cloud Platform (GCP) TCP Proxy Load Balancing allows you to use a single IP address for all users around the world. GCP TCP proxy load balancing automatically routes traffic to the instances that are closest to the user.</p>"};Object.keys(We).forEach(e=>g.register(e,We[e]));class Ze{static findImages(e){return u("/images/find").query(e).get().catch(()=>[])}static getImage(){return null}}class Xe extends ce.Component{constructor(){super(...arguments),this.loadOptions=e=>new Promise(n=>{if(!e||e.length<3)n({options:[],complete:!1});else{n({options:ge(this.props.availableImages).filter(n=>n.imageName.toLowerCase().includes(e)).take(20).map(e=>({value:e.imageName,label:e.imageName})).value(),complete:!1})}})}render(){return ce.createElement($e,{cache:null,clearable:!1,ignoreAccents:!1,loadOptions:this.loadOptions,onChange:e=>this.props.selectImage(e.value,this.props.target),placeholder:"Type at least 3 characters to search for an image...",searchPromptText:"Type at least 3 characters to search for an image...",value:{value:this.props.selectedImage,label:this.props.selectedImage}})}}const Ye="spinnaker.gce.imageSelect";n(Ye,[]).component("gceImageSelect",re(i(Xe,"gceImageSelect"),["availableImages","selectedImage","selectImage","target"]));const Je="spinnaker.serverGroup.customInstanceBuilder.gce.service";n(Je,[]).factory("gceCustomInstanceBuilderService",function(){function e(e,n,t,a){let i=0;if("E2"===e)i=32;else i=a[t].vCpuMax;return n<=i}function n(e,n){if(1===n)return!0;switch(e){case"N2":case"N2D":return n%4==0;default:return n%2==0}}function t(e,n){switch(e){case"E2":case"N2":case"N2D":return Math.ceil(.5*n*4)/4;default:return Math.ceil(.9*n*4)/4}}function a(e,n){switch(e){case"E2":return 8*n>128?128:8*n;case"N2":case"N2D":return 8*n;default:return 6.5*n}}function i(e,n,i,l){const c=t(e,i),r=l?800:a(e,i);return oe.inRange(n,c,r)||n===r}return{generateValidVCpuListForLocation:function(e,n){const t=n[e].vCpuMax;return[1,...oe.range(2,t,2),t]},generateValidMemoryListForVCpuCount:function(e,n){const i=t(e,n),l=a(e,n);return[...oe.range(i,l,.25),l]},generateValidInstanceFamilyList:function(){return["n1","e2","n2","n2d"].map(e=>e.toUpperCase())},generateInstanceTypeString:function(e,n,t,a){const i=a?"-ext":"",l=1024*Number(t);return"n1"===(e=e.toLowerCase())?`custom-${n}-${l}${i}`:`${e}-custom-${n}-${l}${i}`},customInstanceChoicesAreValid:function(t,a,l,c,r,o){return oe.every([n(t,a),i(t,l,a,o),e(t,a,c,r)])},memoryIsValid:i,vCpuCountForLocationIsValid:e,parseInstanceTypeString:function(e){const[n,t]=oe.chain(e.split("-")).takeRight(2).map(e=>Number(e)).value();let a=e.split("-")[0].toUpperCase();return"CUSTOM"===a&&(a="N1"),{instanceFamily:a,vCpuCount:n,memory:t/1024}}}});const Qe="spinnaker.gce.customInstance.filter";n(Qe,[Je]).filter("customInstanceFilter",["gceCustomInstanceBuilderService",function(e){return function(n){if(oe.includes(n,"custom-")){const{instanceFamily:t,vCpuCount:a,memory:i}=e.parseInstanceTypeString(n);return`${t} ${a} vCPU / ${i} GB RAM`}return n}}]);class en{isHttpLoadBalancer(e){return!("gce"!==e.provider&&"gce"!==e.type||"HTTP"!==e.loadBalancerType&&"INTERNAL_MANAGED"!==e.loadBalancerType)}normalizeLoadBalancerNamesForAccount(e,n,t){const a=[];return e.forEach(e=>{const i=t.find(t=>n===t.account&&this.isHttpLoadBalancer(t)&&t.listeners.map(e=>e.name).includes(e));i?a.push(i.name):a.push(e)}),pe(a)}}en.REGION="global";const nn="spinnaker.gce.httpLoadBalancerUtils.service";n(nn,[]).service("gceHttpLoadBalancerUtils",en);const tn="spinnaker.instance.detail.gce.controller";n(tn,[xe,Ie,Ke,nn]).controller("gceInstanceDetailsCtrl",["$scope","$state","$uibModal","instance","app","moniker","environment","$q","gceHttpLoadBalancerUtils","gceXpnNamingService",function(e,n,t,a,i,l,c,r,o,s){function d(){const n={};let t,l,c,d,g;return i.serverGroups?(i.serverGroups.data.some(function(e){return e.instances.some(function(i){if(i.id===a.instanceId)return t=i,l=e.loadBalancers,c=e.account,d=e.region,n.serverGroup=e.name,!0})}),t||(i.loadBalancers.data.some(function(e){return e.instances.some(function(n){if(n.id===a.instanceId)return t=n,l=[e.name],c=e.account,d=e.region,g=e.vpcId,!0})}),t||i.loadBalancers.data.some(function(e){return e.serverGroups.some(function(n){return!!n.isDisabled&&n.instances.some(function(n){if(n.id===a.instanceId)return t=n,l=[e.name],c=e.account,d=e.region,g=e.vpcId,!0})})}))):(t={},l=[],c=a.account,d=a.region),t&&c&&d?(n.account=c,n.region=d,v.addExtraDataToLatest("instances",n),b.getInstanceDetails(c,d,a.instanceId).then(function(n){e.state.loading=!1,function(n,t){i.isStandalone&&(n.health=t.health),n.health=n.health||[];const a=n.health.filter(function(e){return"Google"!==e.type||"Unknown"!==e.state});t.health&&a.forEach(function(e){const n=t.health.filter(function(n){return n.type===e.type});n.length&&oe.defaults(e,n[0])}),e.healthMetrics=a}(t,n),e.instance=oe.defaults(n,t),e.instance.account=c,e.instance.region=d,e.instance.vpcId=g,i.getDataSource("loadBalancers")&&(e.instance.loadBalancers=o.normalizeLoadBalancerNamesForAccount(l,c,i.getDataSource("loadBalancers").data)),e.instance.internalDnsName=e.instance.instanceId,e.instance.internalIpAddress=e.instance.networkInterfaces[0].networkIP,e.instance.networkInterfaces[0].accessConfigs&&(e.instance.externalIpAddress=e.instance.networkInterfaces[0].accessConfigs[0].natIP),e.baseIpAddress=e.instance.externalIpAddress||e.instance.internalIpAddress;const r=s.deriveProjectId(e.instance);e.instance.logsLink="https://console.developers.google.com/project/"+r+"/logs?service=gce_instance&minLogLevel=0&filters=text:"+e.instance.instanceId,e.instance.network=function(n){const t=oe.get(e.instance,"networkInterfaces[0].network");return s.decorateXpnResourceIfNecessary(n,t)}(r),e.instance.subnet=function(n){const t=oe.get(e.instance,"networkInterfaces[0].subnetwork");return s.decorateXpnResourceIfNecessary(n,t)}(r),e.instance.sshLink=e.instance.selfLink.replace(/www.googleapis.com\/compute\/(alpha|beta|v1)/,"cloudssh.developers.google.com")+"?authuser=0&hl=en_US",e.instance.gcloudSSHCommand="gcloud compute ssh --project "+r+" --zone "+e.instance.placement.availabilityZone+" "+a.instanceId,function(){if(oe.has(e,"instance.tags.items")&&oe.has(e,"instance.securityGroups")){const n=oe.chain(e.instance.securityGroups).map(n=>oe.find(i.securityGroups.data,{accountName:e.instance.account,region:"global",id:n.groupId})).compact().value(),t={};e.instance.tags.items.forEach(e=>{const a=oe.filter(n,n=>oe.includes(n.targetTags,e)),i=oe.map(a,"name");if(!oe.isEmpty(i)){const n=i.length>1?"groups":"group";t[e]="This tag associates this instance with security "+n+" <em>"+i.join(", ")+"</em>."}}),e.instance.tags.helpMap=t}}()},u)):(t||(e.instanceIdNotFound=a.instanceId,e.state.loading=!1),r.when(null))}function u(){e.$$destroyed||n.go("^",{allowModalToStayOpen:!0},{location:"replace"})}e.detailsTemplateUrl=p.getValue("gce","instance.detailsTemplateUrl"),e.firewallsLabel=m.get("Firewalls"),e.state={loading:!0,standalone:i.isStandalone},e.application=i,e.moniker=l,e.environment=c,this.canRegisterWithLoadBalancer=function(){const n=e.instance,t=!i.loadBalancers||oe.chain(i.loadBalancers.data).filter(e=>"NETWORK"!==e.loadBalancerType&&e.account===n.account).map("name").intersection(n.loadBalancers||[]).value().length;if(!n.loadBalancers||!n.loadBalancers.length||t)return!1;const a=n.health.some(function(e){return"LoadBalancer"===e.type&&"OutOfService"===e.state}),l=n.health.some(function(e){return"LoadBalancer"===e.type});return a||!l},this.canDeregisterFromLoadBalancer=function(){const n=e.instance,t=!i.loadBalancers||oe.chain(i.loadBalancers.data).filter(e=>"NETWORK"!==e.loadBalancerType&&e.account===n.account).map("name").intersection(n.loadBalancers||[]).value().length;if(!n.loadBalancers||!n.loadBalancers.length||t)return!1;return n.health.some(function(e){return"LoadBalancer"===e.type})},this.canRegisterWithDiscovery=function(){const n=e.instance.health.filter(function(e){return"Discovery"===e.type});return!!n.length&&"OutOfService"===n[0].state},this.showInstanceActionsDivider=function(){return this.canRegisterWithDiscovery()||this.hasHealthState("Discovery","Up")||this.canRegisterWithLoadBalancer()||this.canDeregisterFromLoadBalancer()},this.terminateInstance=function(){const t=e.instance,a={application:i,title:"Terminating "+t.instanceId,onTaskComplete:function(){n.includes("**.instanceDetails",{instanceId:t.instanceId})&&n.go("^")}};h.confirm({header:"Really terminate "+t.instanceId+"?",buttonText:"Terminate "+t.instanceId,account:t.account,taskMonitorConfig:a,submitMethod:function(){const e={cloudProvider:"gce"};return t.serverGroup&&(e.managedInstanceGroupName=t.serverGroup),f.terminateInstance(t,i,e)}})},this.terminateInstanceAndShrinkServerGroup=function(){const t=e.instance,a={application:i,title:"Terminating "+t.instanceId+" and shrinking server group",onTaskComplete:function(){n.includes("**.instanceDetails",{instanceId:t.instanceId})&&n.go("^")}};h.confirm({header:"Really terminate "+t.instanceId+" and shrink "+t.serverGroup+"?",buttonText:"Terminate "+t.instanceId+" and shrink "+t.serverGroup,account:t.account,taskMonitorConfig:a,submitMethod:function(){return f.terminateInstanceAndShrinkServerGroup(t,i,{serverGroupName:t.serverGroup,instanceIds:[t.instanceId],zone:t.placement.availabilityZone})}})},this.rebootInstance=function(){const n=e.instance,t={application:i,title:"Rebooting "+n.instanceId};h.confirm({header:"Really reboot "+n.instanceId+"?",buttonText:"Reboot "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return f.rebootInstance(n,i,{interestingHealthProviderNames:[]})}})},this.registerInstanceWithLoadBalancer=function(){const n=e.instance,t=n.loadBalancers.join(" and "),a={application:i,title:"Registering "+n.instanceId+" with "+t};h.confirm({header:"Really register "+n.instanceId+" with "+t+"?",buttonText:"Register "+n.instanceId,account:n.account,taskMonitorConfig:a,submitMethod:function(){return f.registerInstanceWithLoadBalancer(n,i)}})},this.deregisterInstanceFromLoadBalancer=function(){const n=e.instance,t=n.loadBalancers.join(" and "),a={application:i,title:"Deregistering "+n.instanceId+" from "+t};h.confirm({header:"Really deregister "+n.instanceId+" from "+t+"?",buttonText:"Deregister "+n.instanceId,account:n.account,taskMonitorConfig:a,submitMethod:function(){return f.deregisterInstanceFromLoadBalancer(n,i)}})},this.enableInstanceInDiscovery=function(){const n=e.instance,t={application:i,title:"Enabling "+n.instanceId+" in discovery"};h.confirm({header:"Really enable "+n.instanceId+" in discovery?",buttonText:"Enable "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return f.enableInstanceInDiscovery(n,i)}})},this.disableInstanceInDiscovery=function(){const n=e.instance,t={application:i,title:"Disabling "+n.instanceId+" in discovery"};h.confirm({header:"Really disable "+n.instanceId+" in discovery?",buttonText:"Disable "+n.instanceId,account:n.account,taskMonitorConfig:t,submitMethod:function(){return f.disableInstanceInDiscovery(n,i)}})},this.hasHealthState=function(n,t){return e.instance.health.some(function(e){return e.type===n&&e.state===t})};(i.isStandalone?d():r.all([i.serverGroups.ready(),i.loadBalancers.ready()]).then(d)).then(()=>{e.$$destroyed||i.isStandalone||i.serverGroups.onRefresh(e,d)}),e.account=a.account}]);const an=Object.freeze([{instanceType:"n1-standard-1",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-standard-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"f1-micro",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"g1-small",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-small",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-micro",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"e2-medium",supportsLocalSSD:!1,disks:[{type:"pd-standard",sizeGb:10}]},{instanceType:"n1-highmem-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highmem-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-2",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-4",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-8",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-16",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-32",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-64",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"n1-highcpu-96",disks:[{type:"pd-ssd",sizeGb:10}]},{instanceType:"default",disks:[{type:"pd-ssd",sizeGb:10}]}]),ln="spinnaker.gce.instanceType.service";n(ln,[]).factory("gceInstanceTypeService",["$q","$log",function(e,n){const t=[{type:"general",label:"General Purpose",families:[{type:"n1-standard",description:"This family provides a balance of compute, memory, and network resources, and it is a good choice for general purpose applications.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-standard-1",label:"Small",cpu:1,memory:3.75,costFactor:1},{name:"n1-standard-2",label:"Medium",cpu:2,memory:7.5,costFactor:2},{name:"n1-standard-4",label:"Large",cpu:4,memory:15,costFactor:2},{name:"n1-standard-8",label:"XLarge",cpu:8,memory:30,costFactor:3},{name:"n1-standard-16",label:"2XLarge",cpu:16,memory:60,costFactor:3},{name:"n1-standard-32",label:"4XLarge",cpu:32,memory:120,costFactor:4},{name:"n1-standard-64",label:"8XLarge",cpu:64,memory:240,costFactor:4},{name:"n1-standard-96",label:"12XLarge",cpu:96,memory:360,costFactor:4}]}],icon:"hdd"},{type:"memory",label:"High Memory",families:[{type:"n1-highmem",description:"High memory machine types are ideal for tasks that require more memory relative to virtual cores. High memory machine types have 6.50GB of RAM per virtual core.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-highmem-2",label:"Medium",cpu:2,memory:13,costFactor:2},{name:"n1-highmem-4",label:"Large",cpu:4,memory:26,costFactor:2},{name:"n1-highmem-8",label:"XLarge",cpu:8,memory:52,costFactor:3},{name:"n1-highmem-16",label:"2XLarge",cpu:16,memory:104,costFactor:3},{name:"n1-highmem-32",label:"4XLarge",cpu:32,memory:208,costFactor:4},{name:"n1-highmem-64",label:"8XLarge",cpu:64,memory:416,costFactor:4},{name:"n1-highmem-96",label:"12XLarge",cpu:96,memory:624,costFactor:4}]}],icon:"hdd"},{type:"cpu",label:"High CPU",families:[{type:"n1-highcpu",description:"High CPU machine types are ideal for tasks that require more virtual cores relative to memory. High CPU machine types have one virtual core for every 0.90GB of RAM.",storageType:"SSD",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"n1-highcpu-2",label:"Medium",cpu:2,memory:1.8,costFactor:1},{name:"n1-highcpu-4",label:"Large",cpu:4,memory:3.6,costFactor:2},{name:"n1-highcpu-8",label:"XLarge",cpu:8,memory:7.2,costFactor:2},{name:"n1-highcpu-16",label:"2XLarge",cpu:16,memory:14.4,costFactor:3},{name:"n1-highcpu-32",label:"4XLarge",cpu:32,memory:28.8,costFactor:4},{name:"n1-highcpu-64",label:"8XLarge",cpu:64,memory:57.6,costFactor:4},{name:"n1-highcpu-96",label:"12XLarge",cpu:96,memory:86.4,costFactor:4}]}],icon:"hdd"},{type:"micro",label:"Micro Utility",families:[{type:"f1-micro bursting",description:"This family of machine types is a good choice for small, non-resource intensive workloads that don’t use the full CPU often or consistently, but occasionally need to burst (e.g. web servers, developer environments and small databases).",storageType:"Std",storageHelpFieldKey:"gce.instance.storage",instanceTypes:[{name:"f1-micro",label:"Micro",cpu:1,memory:.6,costFactor:1},{name:"g1-small",label:"Small",cpu:1,memory:1.7,costFactor:1},{name:"e2-micro",label:"E2 Micro",cpu:2,memory:1,costFactor:1},{name:"e2-small",label:"E2 Small",cpu:2,memory:2,costFactor:2},{name:"e2-medium",label:"E2 Medium",cpu:2,memory:4,costFactor:2}]}],icon:"hdd"},{type:"custom",label:"Custom Type",families:[],icon:"asterisk"},{type:"buildCustom",label:"Build Custom",families:[{type:"buildCustom",instanceTypes:[{name:"n1buildCustom",nameRegex:/custom-\d{1,2}-\d{4,6}/,storage:{localSSDSupported:!0}},{name:"e2buildCustom",nameRegex:/e2-custom-(\d{1,2})-(\d{3,6})/,storage:{localSSDSupported:!1}},{name:"otherbuildCustom",nameRegex:/(.*)-?custom-(\d{1,2})-(\d{3,6})/,storage:{localSSDSupported:!0}}]}],icon:"wrench"}],i=oe.memoize(()=>{const e=oe.cloneDeep(t);return y.getAllAccountDetailsForProvider("gce").then(t=>{let a=oe.get(t,"[0].instanceTypeDisks");if(oe.isEmpty(a)&&(a=an),a){oe.flatten(e.map(e=>e.families)).forEach(e=>{e.instanceTypes.forEach(e=>{const t=a.find(n=>n.instanceType===e.name);if(t){const a=t.disks.map(e=>{switch(e.type){case"PD_SSD":return{type:"pd-ssd",sizeGb:e.sizeGb};case"PD_STANDARD":return{type:"pd-standard",sizeGb:e.sizeGb};case"LOCAL_SSD":return{type:"local-ssd",sizeGb:375};default:return n.warn(`Disk type '${e.type}' not supported.`),null}}).filter(e=>!!e);let i=0,l=0;if(t.supportsLocalSSD)l=a.filter(e=>"local-ssd"===e.type).length,i=375;else{const e=a.filter(e=>e.type.startsWith("pd-"));e.length&&(i=e.reduce((e,n)=>Math.max(e,n.sizeGb),0),l=e.filter(e=>e.sizeGb===i).length)}e.storage={localSSDSupported:t.supportsLocalSSD,size:i,count:l,defaultSettings:{disks:a}}}})})}return e})});function l(e,n){if(!e||!n||0===n.length)return console.error("Invalid input parameters"),[];return n.filter(n=>e[n]).flatMap(n=>e[n].instanceTypes||[])}return{getCategories:i,getAvailableTypesForRegions:l,getAllTypesByRegion:function(){return i().then(e=>oe.chain(e).map("families").flatten().map("instanceTypes").flatten().map("name").filter(e=>"n1buildCustom"!==e&&"e2buildCustom"!==e&&"otherbuildCustom"!==e).value())},getAvailableTypesForLocations:l,resolveInstanceTypeDetails:e=>({name:e,storage:Object.assign({isDefault:!0},a.providers.gce.defaults.instanceTypeStorage)})}}]);const cn="spinnaker.google.instance.multiInstance.task.transformer";n(cn,[]).factory("gceMultiInstanceTaskTransformer",function(){const e={rebootInstances:e=>{e.interestingHealthProviderNames=[]}};return{transform:(n,t)=>{((e,n)=>{n.zone=e.instances[0].availabilityZone})(n,t),e[t.type]&&e[t.type](t)}}});const rn="spinnaker.gce.iap.interceptor";n(rn,[]).service("iapInterceptor",class{constructor(){this.responseError=e=>{const{config:n,status:t}=e;return 401===t&&a.feature.iapRefresherEnabled?Me.get("/_gcp_iap/do_session_refresh").then(()=>Me.get(n.url,n).then(e=>e),()=>ze.reject(e)):ze.reject(e)}}}).config(["$httpProvider",e=>{e.interceptors.push("iapInterceptor")}]);const on="spinnaker.gce.loadBalancerTypeToWizard.constant";n(on,[]).constant("loadBalancerTypeToWizardMap",{NETWORK:{label:"Network",createTemplateUrl:"google/src/loadBalancer/configure/network/createLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/network/editLoadBalancer.html",controller:"gceCreateLoadBalancerCtrl"},HTTP:{label:"HTTP(S)",createTemplateUrl:"google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",controller:"gceCreateHttpLoadBalancerCtrl"},INTERNAL_MANAGED:{label:"Internal HTTP(S)",createTemplateUrl:"google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",controller:"gceCreateInternalHttpLoadBalancerCtrl"},INTERNAL:{label:"Internal",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceInternalLoadBalancerCtrl"},SSL:{label:"SSL",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceSslLoadBalancerCtrl"},TCP:{label:"TCP",createTemplateUrl:"google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",editTemplateUrl:"google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",controller:"gceTcpLoadBalancerCtrl"}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/createLoadBalancer.html",'<form name="form" class="form-horizontal">\n <v2-modal-wizard heading="Create New Load Balancer" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Listener" label="Listener" done="true">\n <ng-include src="pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Health Check" label="Health Check" done="true">\n <ng-include src="pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Advanced Settings" label="Advanced Settings" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/editLoadBalancer.html",'<v2-modal-wizard\n heading="Edit {{loadBalancer.name}}: {{loadBalancer.region}}: {{loadBalancer.credentials}}"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n>\n <v2-wizard-page key="Listener" label="Listener" done="true">\n <ng-include src="pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Health Check" label="Health Check" done="true" render="!!loadBalancer.listeners[0].healthCheck">\n <ng-include src="pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="Advanced Settings"\n label="Advanced Settings"\n done="true"\n render="!!loadBalancer.listeners[0].healthCheck"\n >\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n</v2-modal-wizard>\n<div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="isNew"\n ></submit-button>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listeners" label="Listeners" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listeners" label="Listeners" done="true">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/createHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listeners" label="Listeners" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/editHttpLoadBalancer.html",'<form name="form" novalidate class="form-horizontal gce-http-lb">\n <div ng-if="!ctrl.command" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="{{:: ctrl.modalDescriptor}}"\n ng-if="ctrl.command"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listeners" label="Listeners" done="true">\n <ng-include src="ctrl.pages.listeners"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="default-service" label="Default Service" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.defaultService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="host-rules" label="Host Rules" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.hostRules"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="backend-services" label="Backend Services" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.backendServices"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="health-checks" label="Health Checks" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthChecks"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || taskMonitor.submitting"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Create New Load Balancer"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="location" label="Location" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="ctrl.taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonEditLoadBalancer.html",'<form name="form" class="form-horizontal">\n <div ng-if="!ctrl.accounts" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <v2-modal-wizard\n heading="Edit {{ctrl.loadBalancer.name}}: {{ctrl.loadBalancer.region}}: {{ctrl.loadBalancer.credentials}}"\n ng-if="ctrl.accounts"\n task-monitor="ctrl.taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="listener" label="Listener" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.listener"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page\n key="backendService"\n label="Backend Service"\n ng-if="ctrl.hasBackendService"\n mark-complete-on-view="false"\n >\n <ng-include src="ctrl.pages.backendService"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="healthCheck" label="Health Check" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.healthCheck"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advancedSettings" label="Advanced Settings" mark-complete-on-view="false">\n <ng-include src="ctrl.pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || ctrl.taskMonitor.submitting"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</form>\n')}]);class sn{constructor(e,n,t,a,i){this.$uibModal=e,this.$uibModalInstance=n,this.application=t,this.loadBalancerTypeToWizardMap=a,this.forPipelineConfig=i,this.choice="Network"}$onInit(){this.choices=me(this.loadBalancerTypeToWizardMap,e=>e.label)}choose(e){const n=he(this.loadBalancerTypeToWizardMap,n=>n.label===e);if(!n)return void this.$uibModalInstance.dismiss("No wizard config for choice");if(this.forPipelineConfig)return void this.$uibModalInstance.close(n);const t=this.$uibModal.open({templateUrl:n.createTemplateUrl,controller:`${n.controller} as ctrl`,size:"lg",resolve:{application:()=>this.application,loadBalancer:()=>null,isNew:()=>!0,forPipelineConfig:()=>this.forPipelineConfig}}).result;this.$uibModalInstance.close(t)}}sn.$inject=["$uibModal","$uibModalInstance","application","loadBalancerTypeToWizardMap","forPipelineConfig"];const dn="spinnaker.gce.loadBalancerChoice.modal.controller";n(dn,[on]).controller("gceLoadBalancerChoiceCtrl",sn);const un=({application:e,loadBalancer:n,isNew:t,$uibModal:a})=>a.open({templateUrl:"google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",controller:"gceLoadBalancerChoiceCtrl as ctrl",size:"lg",resolve:{application:()=>e,forPipelineConfig:()=>!0}}).result.then(i=>{if(!i)return null;const l=t?i.createTemplateUrl:i.editTemplateUrl;return a.open({templateUrl:l,controller:`${i.controller} as ctrl`,size:"lg",resolve:{application:()=>e,loadBalancer:()=>n,isNew:()=>t,forPipelineConfig:()=>!0}}).result});window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Load Balancer Type Selection</h4>\n </div>\n <div class="modal-body">\n <form class="form-horizontal">\n <div class="form-group">\n <div class="col-md-4 col-md-offset-1 sm-label-left">\n <b>Choose load balancer type:</b>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-6 col-md-offset-1">\n <ui-select ng-model="ctrl.choice" class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n <span>{{$select.selected}}</span>\n </ui-select-match>\n <ui-select-choices repeat="choice in ctrl.choices">\n <span>{{choice}}<help-field key="gce.loadBalancerType.{{choice}}"></help-field></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-primary" ng-click="ctrl.choose(ctrl.choice)">\n <span>Create {{ ctrl.choice }} load balancer</span>\n <span class="glyphicon glyphicon-chevron-right"></span>\n </button>\n </div>\n</div>\n')}]);class gn{constructor(e){this.credentials=e,this.provider="gce",this.stack="",this.detail="",this.region="global",this.loadBalancerType="HTTP",this.certificate="",this.certificateMap="",this.hostRules=[],this.listeners=[]}}class pn{constructor(){this.backends=[],this.sessionAffinity="NONE",this.affinityCookieTtlSec=null}}class mn{constructor(){this.requestPath="/",this.port=80,this.checkIntervalSec=10,this.timeoutSec=5,this.healthyThreshold=10,this.unhealthyThreshold=2}}class hn{constructor(){this.pathRules=[]}}class fn{constructor(){this.pathMatcher=new hn}}class vn{}class bn{constructor(){this.certificate=null,this.certificateMap=null,this.certificateSource="certificate"}}const yn="spinnaker.deck.gce.httpLoadBalancer.backendService.component";n(yn,[]).component("gceHttpLoadBalancerBackendServiceSelector",{bindings:{deleteService:"&",backendService:"=",command:"=",index:"="},templateUrl:"google/src/loadBalancer/configure/http/backendService/backendService.component.html",controller:function(){this.$onInit=()=>{this.backingData=this.command.backingData,this.loadBalancer=this.command.loadBalancer;const e=this.backingData.backendServicesKeyedByName;this.onBackendServiceSelect=e=>{n(e),this.command.onHealthCheckSelected(e.healthCheck,this.command)},this.toggleEditExisting=()=>{if(this.editExisting=!this.editExisting,this.editExisting)delete this.backendService.name;else{const e=new pn;n(e)}},this.getAllHealthChecks=()=>{const e=this.loadBalancer.healthChecks.concat(this.backingData.healthChecks);return oe.chain(e).filter(e=>e.account===this.loadBalancer.credentials||!e.account).map(e=>e.name).uniq().value()},this.getSessionAffinitySuggestions=()=>"HTTP"===this.loadBalancer.loadBalancerType?["None","Client IP","Generated Cookie"]:["None","Client IP","Generated Cookie","Header Field","HTTP Cookie"],this.getAllServiceNames=()=>this.command.backingData.backendServices.filter(e=>e.account===this.loadBalancer.credentials).map(e=>e.name),this.maxCookieTtl=86400;e[(()=>oe.get(this,"backendService.name"))()]&&(this.editExisting=!0);const n=e=>{this.loadBalancer.backendServices[this.index]=this.backendService=e}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendService.component.html",'<div class="container-fluid form-horizontal">\n <hr ng-if="$ctrl.index > 0" />\n <ng-form name="backendService">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="backendService.serviceName.$error.validateUnique">\n <validation-error message="There is already a backend service with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="backendService.serviceName.$error.pattern || backendService.serviceName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters, numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.backendService"\n on-select="$ctrl.onBackendServiceSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="backendService in $ctrl.backingData.backendServices | filter: {name: $select.search, account: $ctrl.loadBalancer.credentials}"\n >\n <div ng-bind-html="backendService.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n required\n type="text"\n name="serviceName"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n max="63"\n validate-unique="$ctrl.getAllServiceNames()"\n ng-model-options="{ debounce: 250 }"\n ng-model="$ctrl.backendService.name"\n />\n </div>\n\n <div class="col-md-2">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteService()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing backend services</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Health Check</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.backendService.healthCheck"\n required\n on-select="$ctrl.command.onHealthCheckSelected($item, $ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices repeat="healthCheck in $ctrl.getAllHealthChecks() | filter: $select.search">\n <div ng-bind-html="healthCheck | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Session Affinity</b>\n </div>\n <div class="col-md-4">\n <ui-select ng-model="$ctrl.backendService.sessionAffinity" required class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n {{ $select.selected }}\n </ui-select-match>\n <ui-select-choices repeat="sessionAffinity in $ctrl.getSessionAffinitySuggestions() | filter: $select.search">\n <div ng-bind-html="sessionAffinity | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Port Name</b>\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input required type="string" class="form-control input-sm" ng-model="$ctrl.backendService.portName" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n\n <div ng-if="$ctrl.loadBalancer.loadBalancerType === \'HTTP\'" class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Enable CDN</b>\n </div>\n <div class="col-md-4">\n <input type="checkbox" ng-model="$ctrl.backendService.enableCDN" />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.backendService.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="$ctrl.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ $ctrl.maxCookieTtl }}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);const kn="spinnaker.deck.gce.httpLoadBalancer.basicSettings.component";n(kn,[]).component("gceHttpLoadBalancerBasicSettings",{bindings:{command:"=",application:"="},templateUrl:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.component.html",controller:function(){this.$onInit=()=>{const e=this.command;this.loadBalancer=e.loadBalancer,this.accounts=e.backingData.accounts;const n=e.backingData.loadBalancerMap;this.getName=(e,n)=>{const t=[n,e.stack||"",e.detail||""].join("-");return oe.trimEnd(t,"-")},this.updateName=(e,n)=>{e.urlMapName=this.getName(e,n)},this.accountChanged=t=>{this.existingLoadBalancerNames=oe.get(n,[t,"urlMapNames"])||[],e.onAccountChange(e)},this.loadBalancer.name||this.updateName(this.loadBalancer,this.application.name),this.existingLoadBalancerNames=oe.get(n,[this.loadBalancer.credentials,"urlMapNames"])||[]}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.component.html",'<div class="container-fluid form-horizontal">\n <ng-form name="basicSettings">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\n \'alert-danger\': basicSettings.urlMapName.$error.validateUnique,\n \'alert-info\': !basicSettings.urlMapName.$error.validateUnique\n }"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ $ctrl.getName($ctrl.loadBalancer, $ctrl.application.name) }}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.urlMapName"\n validate-unique="$ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="urlMapName"\n />\n <validation-error\n ng-if="basicSettings.urlMapName.$error.validateUnique"\n message="There is already a load balancer in {{ $ctrl.loadBalancer.credentials }} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="$ctrl.loadBalancer"\n field="credentials"\n accounts="$ctrl.accounts"\n provider="\'gce\'"\n on-change="$ctrl.accountChanged($ctrl.loadBalancer.credentials)"\n >\n </account-select-field>\n </div>\n </div>\n <gce-region-select-field\n ng-if="$ctrl.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'"\n label-columns="2"\n field-columns="8"\n component="$ctrl.loadBalancer"\n field="region"\n account="$ctrl.loadBalancer.credentials"\n on-change="$ctrl.command.onRegionSelected($ctrl.command)"\n regions="$ctrl.command.backingData.regions"\n ></gce-region-select-field>\n <gce-network-select-field\n ng-if="$ctrl.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'"\n label-columns="2"\n field-columns="8"\n component="$ctrl.loadBalancer"\n field="network"\n helpKey="gce.internalHttpLoadBalancer.network"\n account="$ctrl.loadBalancer.credentials"\n networks="$ctrl.command.backingData.internalHttpLbNetworks"\n ></gce-network-select-field>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="$ctrl.updateName($ctrl.loadBalancer, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="$ctrl.updateName($ctrl.loadBalancer, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="basicSettings.stackName.$error.pattern">\n <validation-error message="Stack can only contain lowercase letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="basicSettings.detailName.$error.pattern">\n <validation-error\n message="Detail can only contain lowercase letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="basicSettings.urlMapName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]);class Sn{constructor(e){this.cacheInitializer=e,this.refreshing=!1,this.tooltipTemplate="google/src/cache/cacheRefreshTooltip.html"}$onInit(){const e=this.cacheKeyAlias||this.cacheKey;this.capitalizedKey=e[0].toUpperCase()+e.substring(1),this.depluralizedKey=e.substring(0,e.length-1)}getRefreshTime(){return d.get(this.cacheKey).getStats().ageMax}refresh(){this.refreshing=!0,this.cacheInitializer.refreshCache(this.cacheKey).then(()=>this.onRefresh()).then(()=>{this.refreshing=!1})}}Sn.$inject=["cacheInitializer"];const wn={bindings:{onRefresh:"&",cacheKey:"@",cacheKeyAlias:"@",renderCompact:"<"},controller:Sn,templateUrl:"google/src/cache/cacheRefresh.component.html"},Cn="spinnaker.gce.cacheRefresh.component";n(Cn,[k]).component("gceCacheRefresh",wn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/cache/cacheRefreshTooltip.html",'<p ng-if="$ctrl.refreshing">{{$ctrl.capitalizedKey}} data is <strong>refreshing</strong></p>\n<p ng-if="!$ctrl.refreshing">(click <span class="fa fa-sync-alt"></span> to refresh)</p>\n<p>Last refresh: {{$ctrl.getRefreshTime() | timestamp }}</p>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/cache/cacheRefresh.component.html",'<div ng-if="!$ctrl.renderCompact">\n <p>\n <span ng-if="$ctrl.refreshing"><span class="fa fa-sync-alt fa-spin"></span></span>\n {{::$ctrl.capitalizedKey}}\n <span ng-if="!$ctrl.refreshing">last refreshed {{ $ctrl.getRefreshTime() | timestamp }}</span>\n <span ng-if="$ctrl.refreshing"> refreshing...</span>\n </p>\n <p>\n If you\'re not finding a {{::$ctrl.depluralizedKey}} that was recently added,\n <a href ng-click="$ctrl.refresh()">click here</a> to refresh the list.\n </p>\n</div>\n<div ng-if="$ctrl.renderCompact">\n <a href uib-tooltip-template="$ctrl.tooltipTemplate">\n <span class="fa fa-sync-alt" ng-click="$ctrl.refresh()" ng-class="{ \'fa-spin\': $ctrl.refreshing }"></span>\n </a>\n</div>\n')}]);const Bn="spinnaker.gce.certificateReader.service";n(Bn,[]).service("gceCertificateReader",class{listCertificates(){return s.search({q:"",type:"sslCertificates",allowShortQuery:"true"},d.get("certificates")).then(e=>e&&e.results?e.results.filter(e=>"gce"===e.provider):[]).catch(()=>[])}});const Tn={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE","Client IP and protocol":"CLIENT_IP_PROTO","Client IP, port and protocol":"CLIENT_IP_PORT_PROTO","Header Field":"HEADER_FIELD","HTTP Cookie":"HTTP_COOKIE","Strong Cookie":"STRONG_COOKIE_AFFINITY"},Gn=oe.invert(Tn),An="spinnaker.gce.deck.httpLoadBalancer.transformer";n(An,[]).factory("gceHttpLoadBalancerTransformer",function(){const e=["backendServices","healthChecks","listeners","stack","detail"];return{serialize:function(n,t){const a=oe.cloneDeep(n),{loadBalancer:i,backingData:l}=a;!function(e,n){const t=oe.assign(n.healthChecksKeyedByName,oe.keyBy(e.healthChecks,"name")),a=oe.assign(n.backendServicesKeyedByName,oe.keyBy(e.backendServices,"name"));oe.forEach(a,e=>{e.healthCheck=t[e.healthCheck],e.sessionAffinity=Tn[e.sessionAffinity]||e.sessionAffinity}),e.defaultService=a[e.defaultService],i=e.hostRules,l=a,i.forEach(e=>{const n=e.pathMatcher;n.defaultService=l[n.defaultService],n.pathRules.forEach(e=>{e.backendService=l[e.backendService]})});var i,l}(i,l),i.hostRules=i.hostRules.reduce((e,n)=>e.concat(n.hostPatterns.map(e=>({hostPatterns:[e],pathMatcher:oe.cloneDeep(n.pathMatcher)}))),[]);const c=function(n){return n.listeners.map(t=>{let a=oe.cloneDeep(n);a=oe.omit(a,e);const i="certificateMap"===t.certificateSource||!t.certificate&&!!t.certificateMap;return a.name=t.name,a.portRange=t.port,a.certificate=i?null:t.certificate||null,a.certificateMap=i&&t.certificateMap||null,a.ipAddress=t.ipAddress,a.subnet=t.subnet,a})}(i);return t&&(c[0].listenersToDelete=oe.chain(t.listeners).map("name").difference(oe.map(n.loadBalancer.listeners,"name")).value()),c},deserialize:function(e){const{backendServices:n,healthChecks:t,defaultService:a}=function(e){const n=e.defaultService;let t=[e.defaultService];e.hostRules&&(t=e.hostRules.reduce((e,n)=>(e=e.concat(n.pathMatcher.defaultService),n.pathMatcher.pathRules.reduce((e,n)=>e.concat(n.backendService),e)),t));const a=oe.chain(t).map("healthCheck").uniqBy("name").cloneDeep().value();return a.forEach(n=>{n.account=e.account||e.credentials}),t=oe.uniqBy(t,"name"),t.forEach(e=>{e.healthCheck=e.healthCheck.name,e.sessionAffinity=Gn[e.sessionAffinity]||e.sessionAffinity}),{backendServices:t,healthChecks:a,defaultService:n}}(e),i=function(e){return function(e){return e.forEach(e=>{const n=e.pathMatcher;n.defaultService=n.defaultService.name,n.pathRules.forEach(e=>{e.backendService=e.backendService.name})}),e}(oe.cloneDeep(e.hostRules))}(e),l=function(e){return e.listeners.forEach(e=>{const{stack:n,freeFormDetails:t}=S.parseLoadBalancerName(e.name);e.stack=n,e.detail=t,e.created=!0,e.certificate=e.certificate||null,e.certificateMap=e.certificateMap?oe.last(e.certificateMap.split("/")):null,e.certificateSource=e.certificateMap?"certificateMap":"certificate"}),e.listeners}(e);return{defaultService:a.name,backendServices:n,healthChecks:t,hostRules:i,listeners:l,network:e.network,region:e.region,urlMapName:e.urlMapName,credentials:e.credentials||e.account}}}});const Pn="spinnaker.deck.gce.httpLoadBalancer.backing.service";n(Pn,[_e,Bn,w,nn,qe,Ve,An,Ke]).factory("gceHttpLoadBalancerCommandBuilder",["$q","gceHttpLoadBalancerUtils","gceBackendServiceReader","gceCertificateReader","gceHealthCheckReader","gceHttpLoadBalancerTransformer","loadBalancerReader","gceAddressReader","gceXpnNamingService",function(e,n,t,a,i,l,c,r,o){function s(n){const t={backendServices:g(n),healthChecks:u(n),certificates:p(),loadBalancerMap:m(),networks:B.listNetworksByProvider("gce").then(e=>oe.chain(e).map("name").compact().uniq().value()),subnets:C.listSubnetsByProvider("gce"),addresses:r.listAddresses(n),accounts:y.listAccounts("gce")};return e.all(t)}function d(e,n){!function(e,n){const t=oe.map(e,"name"),a=oe.get(n,"credentials.name")||n.credentials;t.includes(a)||(n.credentials=oe.first(t))}(e.accounts,n),function(e,n){const t=e.accounts.map(e=>e.name);t.forEach(t=>{oe.has(e,["loadBalancerMap",t,"listeners"])&&(e.loadBalancerMap[t].listeners=oe.without(e.loadBalancerMap[t].listeners,...n.map(e=>e.name)))})}(e,n.listeners);const t=oe.keyBy(n.healthChecks,"name"),a=oe.keyBy(e.healthChecks,"name");e.healthChecksKeyedByName=oe.assign(a,oe.cloneDeep(t)),e.healthChecksKeyedByNameCopy=oe.cloneDeep(a),e.healthChecks=oe.map(a,oe.identity);const i=oe.keyBy(n.backendServices,"name"),l=oe.keyBy(e.backendServices,"name");e.backendServicesKeyedByName=oe.assign(l,oe.cloneDeep(i)),e.backendServicesKeyedByNameCopy=oe.cloneDeep(l),e.backendServices=oe.map(l,oe.identity),e.regions=e.accounts.find(e=>e.name===n.credentials).regions.map(e=>e.name),e.subnetMap=oe.groupBy(e.subnets,"network"),e.internalHttpLbNetworks=e.networks.filter(n=>e.subnetMap[n].find(e=>"INTERNAL_HTTPS_LOAD_BALANCER"===e.purpose))}function u(e){return i.listHealthChecks().then(n=>n.filter(n=>!e||n.region===e))}function g(e){const n=e?"regionBackendService":"globalBackendService";return t.listBackendServices(n).then(n=>((n=n.filter(n=>!e||n.region===e)).forEach(e=>{e.healthCheck=e.healthCheckLink.split("/").pop();const n="string"==typeof e.affinityCookieTtlSec;e.affinityCookieTtlSec=n?Number(e.affinityCookieTtlSec):null,e.sessionAffinity=Gn[e.sessionAffinity]||e.sessionAffinity}),n))}function p(){return a.listCertificates()}function m(e){return c.listLoadBalancers("gce").then(e=>oe.chain(e).map(e=>e.accounts).flatten().groupBy("name").mapValues(e=>{const t=oe.chain(e).map(e=>e.regions).flatten().filter(e=>e.name===(e||n.REGION)).map(e=>e.loadBalancers).flatten().value();return{urlMapNames:oe.chain(t).map("urlMapName").uniq().value(),listeners:oe.chain(t).map("name").uniq().value()}}).valueOf())}function h(e){u(e.loadBalancer.region).then(n=>{e.backingData.healthChecks=n,e.backingData.healthChecksKeyedByName=oe.keyBy(n,"name"),e.backingData.healthChecksKeyedByNameCopy=oe.cloneDeep(e.backingData.healthChecksKeyedByName),e.loadBalancer.healthChecks=e.loadBalancer.healthChecks.map(n=>{const t=e.backingData.healthChecksKeyedByName[oe.get(n,"name")];return t?oe.cloneDeep(t):n})})}function f(e){p().then(n=>{e.backingData.certificates=n})}function v(e){g(e.loadBalancer.region).then(n=>{e.backingData.backendServices=n,e.backingData.backendServicesKeyedByName=oe.keyBy(n,"name"),e.backingData.backendServicesKeyedByNameCopy=oe.cloneDeep(e.backingData.backendServicesKeyedByName),e.loadBalancer.backendServices=e.loadBalancer.backendServices.map(n=>{const t=e.backingData.backendServicesKeyedByName[oe.get(n,"name")];return t?oe.cloneDeep(t):n})})}function b(e){s(e.loadBalancer.region).then(n=>{Object.assign(e.backingData,n),d(e.backingData,e.loadBalancer)})}function k(e,n){if(!n.loadBalancer.healthChecks.find(n=>oe.get(n,"name")===e)){const t=n.backingData.healthChecksKeyedByName[e];t&&n.loadBalancer.healthChecks.push(t)}}function S(e,n){if(!n.loadBalancer.backendServices.find(n=>n.name===e)){const t=n.backingData.backendServicesKeyedByName[e];n.loadBalancer.backendServices.push(t),t.healthCheck&&k(t.healthCheck,n)}}function w(e){const n=e.loadBalancer.backendServices.concat(e.backingData.backendServices);return oe.chain(n).filter(n=>n.account===e.loadBalancer.credentials||n.account===e.loadBalancer.account).map("name").compact().uniq().value()}function T(e){return oe.chain(e.loadBalancer.healthChecks).map("name").difference(oe.map(e.loadBalancer.backendServices,"healthCheck")).compact().uniq().value()}function G(e){const n=e.loadBalancer.defaultService,t=oe.map(e.loadBalancer.hostRules,"pathMatcher.defaultService"),a=oe.chain(e.loadBalancer.hostRules).map("pathMatcher.pathRules").flatten().map("backendService").value(),i=oe.chain([n,...t,...a]).compact().uniq().value();return oe.chain(e.loadBalancer.backendServices).map("name").difference(i).compact().uniq().value()}function A(e){const n=G(e);e.loadBalancer.backendServices=e.loadBalancer.backendServices.filter(e=>!n.includes(e.name))}function P(e){const n=T(e);e.loadBalancer.healthChecks=e.loadBalancer.healthChecks.filter(e=>!n.includes(e.name))}function $(e){e.loadBalancer.backendServices=[],e.loadBalancer.healthChecks=[],e.loadBalancer.hostRules=[],e.loadBalancer.listeners=[new bn],e.loadBalancer.defaultService=null,e.backingData.regions=e.backingData.accounts.find(n=>n.name===e.loadBalancer.credentials).regions.map(e=>e.name)}function x(e){r.listAddresses("global").then(n=>{e.backingData.addresses=n})}return{buildCommand:function({originalLoadBalancer:e,isNew:n,isInternal:t}){return function(e,n,t){let a=null;t&&(a=e?e.region:Ue.defaults.region);return s(a).then(a=>{const i=function(e,n,t){const a=new gn(Ue.defaults.account||null);t?(a.loadBalancerType="INTERNAL_MANAGED",a.isInternal=!0,n&&(n.network=o.decorateXpnResourceIfNecessary(n.project,n.network),n.listeners.forEach(e=>{e.subnet=o.decorateXpnResourceIfNecessary(n.project,e.subnet)})),a.region=Ue.defaults.region,a.network="default"):(a.loadBalancerType="HTTP",a.isInternal=!1);let i;i=e?{backendServices:[],listeners:[new bn],healthChecks:[]}:l.deserialize(n);return oe.assign(a,i)}(n,e,t);return d(a,i),{backingData:a,loadBalancer:i}})}(e=oe.cloneDeep(e),n,t).then(({backingData:e,loadBalancer:t})=>({backingData:e,getAllBackendServices:w,isNew:n,loadBalancer:t,onAccountChange:$,onBackendServiceRefresh:v,onBackendServiceSelected:S,onRegionSelected:b,onCertificateRefresh:f,onHealthCheckRefresh:h,onHealthCheckSelected:k,getUnusedBackendServices:G,removeUnusedBackendServices:A,getUnusedHealthChecks:T,removeUnusedHealthChecks:P,onAddressRefresh:x}))}}}]);const $n="spinnaker.deck.gce.loadBalancer.hostAndPathRules.service";n($n,[]).factory("hostAndPathRulesService",function(){function e(e,n,t){return{hostPattern:e,path:n,backend:t}}return{buildTable:function(n,t){const a=e("Any unmatched (default)","Any unmatched (default)",t.name);return 0===n.length?[a]:n.reduce((n,t)=>{const[a]=t.hostPatterns,{defaultService:i,pathRules:l}=t.pathMatcher;return n.push(e(a,"/*",i.name)),n.concat(l.reduce((n,t)=>{const{backendService:i,paths:l}=t;return n.concat(l.map(n=>e(a,n,i.name)))},[]))},[a])}}});const xn="spinnaker.deck.gce.loadBalancer.hostAndPathRules.controller";n(xn,[$n]).controller("gceHostAndPathRulesCtrl",["hostRules","defaultService","loadBalancerName","$uibModalInstance","hostAndPathRulesService",function(e,n,t,a,i){this.table=i.buildTable(e,n),this.loadBalancerName=t,this.close=a.dismiss}]);const In="spinnaker.deck.gce.loadBalancer.hostAndPathRulesButton.component";n(In,[Ie,xn]).component("gceHostAndPathRulesButton",{bindings:{hostRules:"=",defaultService:"=",loadBalancerName:"="},template:'<a href ng-click="$ctrl.viewHostAndPathRules()">View Host and Path Rules</a>',controller:["$uibModal",function(e){this.viewHostAndPathRules=()=>{e.open({templateUrl:"google/src/loadBalancer/details/hostAndPathRules/hostAndPathRules.modal.html",controller:"gceHostAndPathRulesCtrl",controllerAs:"ctrl",size:"lg",resolve:{hostRules:()=>this.hostRules,defaultService:()=>this.defaultService,loadBalancerName:()=>this.loadBalancerName}})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/hostAndPathRules/hostAndPathRules.modal.html",'<div modal-page class="form-inline">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Host And Path Rules for {{ ctrl.loadBalancerName }}</h4>\n </div>\n <div class="modal-body clearfix">\n <div class="section-body">\n <div class="well well-sm">\n <div class="row">\n <div class="col-md-12 content-fields">\n <input placeholder="Filter" ng-model="searchText" class="form-control input-sm" />\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>Host</th>\n <th>Path</th>\n <th>Backend Service</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in ctrl.table | filter:searchText">\n <td>{{ row.hostPattern }}</td>\n <td>{{ row.path }}</td>\n <td>{{ row.backend }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.close()">Close</button>\n </div>\n</div>\n')}]);const Mn="spinnaker.deck.httpLoadBalancer.healthCheck.component";n(Mn,[]).component("gceHttpLoadBalancerHealthCheck",{bindings:{command:"=",deleteHealthCheck:"&",healthCheck:"=",index:"="},templateUrl:"google/src/loadBalancer/configure/http/healthCheck/healthCheck.component.html",controller:function(){this.$onInit=()=>{this.max=Number.MAX_SAFE_INTEGER,this.backingData=this.command.backingData,this.loadBalancer=this.command.loadBalancer;const e=this.backingData.healthChecksKeyedByName;this.onHealthCheckSelect=e=>{n(e)},this.getAllHealthCheckNames=()=>this.command.backingData.healthChecks.filter(e=>e.account===this.loadBalancer.credentials).map(e=>e.name),this.toggleEditExisting=()=>{this.editExisting=!this.editExisting,this.editExisting?delete this.healthCheck.name:n(new mn)};const n=e=>{this.loadBalancer.healthChecks[this.index]=this.healthCheck=e};this.onProtocolChange=()=>{this.healthCheck.healthCheckType!==this.healthCheckType&&n(Object.assign({},new mn,{healthCheckType:this.healthCheckType}))},e[(()=>oe.get(this,"healthCheck.name"))()]&&(this.editExisting=!0),this.healthCheckType=this.healthCheck.healthCheckType}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthCheck.component.html",'<hr ng-if="$ctrl.index > 0" />\n<ng-form name="healthCheck">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="healthCheck.healthCheckName.$error.validateUnique">\n <validation-error message="There is already a health check with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="healthCheck.healthCheckName.$error.pattern || healthCheck.healthCheckName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters, numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Protocol</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-change="$ctrl.onProtocolChange()"\n ng-model="$ctrl.healthCheckType"\n ng-options="protocol as protocol for protocol in [\'HTTP\', \'HTTPS\', \'TCP\', \'SSL\', \'HTTP2\', \'GRPC\']"\n ></select>\n </div>\n <div class="col-md-2">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteHealthCheck()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.healthCheck"\n on-select="$ctrl.onHealthCheckSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="healthCheck in $ctrl.backingData.healthChecks | filter: {name: $select.search, account: $ctrl.loadBalancer.credentials, healthCheckType: $ctrl.healthCheckType}"\n >\n <div ng-bind-html="healthCheck.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n type="text"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n name="healthCheckName"\n max="63"\n required\n validate-unique="$ctrl.getAllHealthCheckNames()"\n ng-model-options="{ debounce: 250 }"\n ng-model="$ctrl.healthCheck.name"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing health checks</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div\n class="form-group"\n ng-if="\n $ctrl.healthCheck.healthCheckType === \'HTTP\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTPS\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTP2\'\n "\n >\n <div class="col-md-4 sm-label-right">Request Path</div>\n <div class="col-md-4">\n <input class="form-control input-sm" required ng-model="$ctrl.healthCheck.requestPath" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n required\n min="1"\n max="65535"\n ng-model="$ctrl.healthCheck.port"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.healthCheck.healthCheckType === \'GRPC\'">\n <div class="col-md-4 sm-label-right">Service Name</div>\n <div class="col-md-4">\n <input class="form-control input-sm" ng-model="$ctrl.healthCheck.grpcServiceName" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.timeoutSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.checkIntervalSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.healthyThreshold"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.unhealthyThreshold"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const zn="spinnaker.deck.gce.httpLoadBalancer.pathRule.component";n(zn,[]).component("gcePathRule",{bindings:{pathRule:"=",command:"=",index:"=",deletePathRule:"&"},templateUrl:"google/src/loadBalancer/configure/http/pathRule/pathRule.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/pathRule/pathRule.component.html",'<hr />\n<div class="form-group">\n <div class="col-md-4 sm-label-right">\n Paths\n <help-field key="gce.httpLoadBalancer.pathRule.paths"> </help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n multiple\n tagging\n tagging-label=""\n ng-model="$ctrl.pathRule.paths"\n sortable="true"\n class="form-control input-sm"\n required\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="path in []">\n {{ path }}\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deletePathRule()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n</div>\n\n<div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Backend Service</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.pathRule.backendService"\n on-select="$ctrl.command.onBackendServiceSelected($item, $ctrl.command)"\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="backendService in $ctrl.command.getAllBackendServices($ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Nn="spinnaker.deck.gce.httpLoadBalancer.hostRule.component";n(Nn,[zn]).component("gceHostRule",{bindings:{hostRule:"=",index:"=",command:"=",deleteHostRule:"&"},templateUrl:"google/src/loadBalancer/configure/http/hostRule/hostRule.component.html",controller:function(){this.$onInit=()=>{this.loadBalancer=this.command.loadBalancer;const e=this.hostRule.pathMatcher.pathRules;this.addPathRule=()=>{e.push(new vn)},this.deletePathRule=n=>{e.splice(n,1)}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRule.component.html",'<hr class="host-rule" ng-if="$ctrl.index > 0" />\n<div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Host Patterns\n <help-field key="gce.httpLoadBalancer.hostRule.hostPattern"> </help-field>\n </div>\n <div class="col-md-4">\n <ui-select multiple tagging tagging-label="" ng-model="$ctrl.hostRule.hostPatterns" class="form-control input-sm">\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="hostPattern in []">\n {{ hostPattern }}\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteHostRule()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="$ctrl.hostRule.pathMatcher.defaultService"\n on-select="$ctrl.command.onBackendServiceSelected($item, $ctrl.command)"\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="backendService in $ctrl.command.getAllBackendServices($ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <gce-path-rule\n path-rule="pathRule"\n index="$index"\n command="$ctrl.command"\n delete-path-rule="$ctrl.deletePathRule($index)"\n ng-repeat="pathRule in $ctrl.hostRule.pathMatcher.pathRules"\n >\n </gce-path-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="$ctrl.addPathRule()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add path rule\n </button>\n </div>\n </div>\n</div>\n')}]);const Dn="spinnaker.deck.gce.httpLoadBalancer.write.service";e.module(Dn,[]).factory("gceHttpLoadBalancerWriter",function(){return{upsertLoadBalancers:function(n,t,a){return n.forEach(n=>{e.extend(n,{type:"upsertLoadBalancer",cloudProvider:"gce",loadBalancerName:n.name})}),d.clearCache("backendServices"),d.clearCache("healthChecks"),T.executeTask({job:n,application:t,description:`${a} Load Balancer: ${n[0].urlMapName}`})},deleteLoadBalancers:function(n,t,a={}){const i={type:"deleteLoadBalancer",loadBalancerName:n.listeners[0].name,regions:[n.region||"global"],region:n.region||"global",loadBalancerType:n.loadBalancerType,cloudProvider:n.provider,credentials:n.account};return e.extend(i,a),d.clearCache("backendServices"),d.clearCache("healthChecks"),T.executeTask({job:[i],application:t,description:`Delete load balancer: ${n.urlMapName} in ${n.account}:global`})}}});const En={bindings:{initialIpAddress:"<",addressList:"<",readOnly:"<",onAddressSelect:"&",account:"<"},template:'\n <ui-select on-select="$ctrl.onAddressSelect({address: $ctrl.selectedAddress})"\n ng-disabled="$ctrl.readOnly"\n ng-model="$ctrl.selectedAddress"\n class="form-control input-sm">\n <ui-select-match allow-clear>\n {{$ctrl.selectedAddress.address}} <span ng-if="$ctrl.selectedAddress.name">({{$ctrl.selectedAddress.name}})</span>\n </ui-select-match>\n <ui-select-choices repeat="address in $ctrl.addressList | filter: {name: $select.search, account: $ctrl.account}">\n <span>\n {{address.address}} <span ng-if="address.name">({{address.name}})</span> <br>\n </span>\n </ui-select-choices>\n </ui-select>',controller:class{$onInit(){this.selectedAddress=this.addressList.find(e=>e.address===this.initialIpAddress),this.selectedAddress||(this.selectedAddress={address:this.initialIpAddress,account:this.account})}}},Rn="spinnaker.gce.addressSelector.component";n(Rn,[]).component("gceAddressSelector",En);const Ln="spinnaker.deck.gce.httpLoadBalancer.listener.component";n(Ln,[Rn]).component("gceListener",{bindings:{command:"=",listener:"=",deleteListener:"&",index:"=",application:"="},templateUrl:"google/src/loadBalancer/configure/http/listeners/listener.component.html",controller:function(){this.$onInit=()=>{this.certificates=this.command.backingData.certificates;const e=this.command.backingData.loadBalancerMap;this.getName=(e,n)=>{const t=[n,e.stack||"",e.detail||""].join("-");return oe.trimEnd(t,"-")},this.getCertificates=()=>this.command.backingData.certificates.filter(e=>e.account===this.command.loadBalancer.credentials).map(e=>e.name),this.getSubnets=()=>{const e=this.command.backingData.subnetMap[this.command.loadBalancer.network].filter(e=>e.region===this.command.loadBalancer.region).map(e=>e.name);return oe.uniq(e)},this.getInternalAddresses=()=>this.command.backingData.addresses.filter(e=>"INTERNAL"===e.addressType&&e.subnetwork.split("/").pop()===this.listener.subnet),this.updateName=(e,n)=>{e.name=this.getName(e,n)},this.localListenerHasSameName=()=>this.command.loadBalancer.listeners.filter(e=>e.name===this.listener.name).length>1,this.existingListenerNames=()=>oe.get(e,[this.command.loadBalancer.credentials,"listeners"]),this.isHttps=e=>443===e||"443"===e,this.supportsCertificateMap=()=>"HTTP"===this.command.loadBalancer.loadBalancerType,this.certificateMapPattern=/^[a-z]([-a-z0-9]*[a-z0-9])?$/,this.getCertificateMapName=e=>{if(!oe.isString(e))return e;const n=e.trim();return n?oe.last(n.split("/")):n},this.syncCertificateState=e=>{if(!this.supportsCertificateMap())return e.certificateSource="certificate",void(e.certificateMap=null);e.certificateSource||(e.certificateSource=e.certificateMap?"certificateMap":"certificate"),"certificateMap"===e.certificateSource?(e.certificate=null,oe.isString(e.certificateMap)&&(e.certificateMap=this.getCertificateMapName(e.certificateMap))):e.certificateMap=null},this.onCertificateSourceChanged=e=>{if(!this.supportsCertificateMap())return e.certificateSource="certificate",void(e.certificateMap=null);"certificateMap"===e.certificateSource?e.certificate=null:e.certificateMap=null},this.onCertificateSelected=e=>{e.certificate&&(e.certificateSource="certificate",e.certificateMap=null)},this.onCertificateMapChanged=e=>{oe.isString(e.certificateMap)&&(e.certificateMap=this.getCertificateMapName(e.certificateMap)),e.certificateMap&&(e.certificateSource="certificateMap",e.certificate=null)},this.onPortChanged=e=>{this.isHttps(e.port)?this.syncCertificateState(e):(e.certificate=null,e.certificateMap=null,e.certificateSource="certificate")},this.listener.name||this.updateName(this.listener,this.application.name),this.onPortChanged(this.listener),this.onAddressSelect=e=>{this.listener.ipAddress=e?e.address:null}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listener.component.html",'<hr ng-if="$ctrl.index > 0" />\n<div class="container-fluid form-horizontal">\n <ng-form name="listener">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-show="listener.stackName.$dirty || listener.detailName.$dirty || !$ctrl.listener.created"\n ng-class="{\n \'alert-danger\': listener.listenerName.$error.validateUnique,\n \'alert-info\': !listener.listenerName.$error.validateUnique\n }"\n >\n <strong>Your listener will be named:</strong>\n <span>{{ $ctrl.getName($ctrl.listener, $ctrl.application.name) }}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="$ctrl.listener.name"\n ng-readonly="$ctrl.listener.created"\n validate-unique="$ctrl.existingListenerNames()"\n validate-ignore-case="true"\n name="listenerName"\n />\n <validation-error\n ng-if="listener.listenerName.$error.validateUnique || $ctrl.localListenerHasSameName()"\n message="There is already a listener in {{ $ctrl.command.loadBalancer.credentials }} with that name."\n >\n </validation-error>\n </div>\n\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.listener.stack"\n ng-readonly="$ctrl.listener.created"\n name="stackName"\n ng-change="$ctrl.updateName($ctrl.listener, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail <help-field key="gce.loadBalancer.detail"></help-field></div>\n\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="$ctrl.listener.detail"\n ng-readonly="$ctrl.listener.created"\n name="detailName"\n ng-change="$ctrl.updateName($ctrl.listener, $ctrl.application.name)"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteListener()">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span>Delete</span>\n </button>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'">\n <div class="col-md-2 sm-label-right">Subnet</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="$ctrl.listener.subnet"\n ng-disabled="$ctrl.listener.created"\n ng-required="true"\n >\n <option\n ng-repeat="subnet in $ctrl.getSubnets()"\n value="{{ subnet }}"\n ng-selected="$ctrl.listener.subnet === subnet"\n >\n {{ subnet }}\n </option>\n </select>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'INTERNAL_MANAGED\'">\n <div class="col-md-2 sm-label-right">Internal IP</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="$ctrl.listener.ipAddress"\n ng-disabled="$ctrl.listener.created"\n ng-required="true"\n >\n <option\n ng-repeat="address in $ctrl.getInternalAddresses()"\n value="{{ address.address }}"\n ng-selected="$ctrl.listener.ipAddress === address.address"\n >\n {{ address.name }}\n </option>\n </select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Port<help-field key="gce.httpLoadBalancer.port"></help-field></div>\n <div class="col-md-3">\n <ui-select\n ng-model="$ctrl.listener.port"\n ng-disabled="$ctrl.listener.created"\n ng-change="$ctrl.onPortChanged($ctrl.listener)"\n required\n class="form-control input-sm"\n >\n <ui-select-match>\n <span>{{ $select.selected }}</span>\n </ui-select-match>\n <ui-select-choices repeat="choice as choice in [8080, 80, 443]">\n <span>{{ choice }}</span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n \x3c!-- Certificate controls on a separate row to avoid exceeding Bootstrap\'s 12-column\n grid when cert-source radios are visible (Port 2+3 + CertSrc 2+3 + Cert 2+3+1 = 16). --\x3e\n <div class="form-group" ng-if="$ctrl.isHttps($ctrl.listener.port)">\n <div class="col-md-2 sm-label-right" ng-if="$ctrl.supportsCertificateMap()">Certificate Source</div>\n <div class="col-md-3" ng-if="$ctrl.supportsCertificateMap()">\n <label class="radio-inline">\n <input\n type="radio"\n ng-model="$ctrl.listener.certificateSource"\n value="certificate"\n ng-change="$ctrl.onCertificateSourceChanged($ctrl.listener)"\n />\n Certificate\n </label>\n <label class="radio-inline">\n <input\n type="radio"\n ng-model="$ctrl.listener.certificateSource"\n value="certificateMap"\n ng-change="$ctrl.onCertificateSourceChanged($ctrl.listener)"\n />\n Certificate Map\n </label>\n </div>\n <div class="col-md-2 sm-label-right">\n <span ng-if="$ctrl.listener.certificateSource !== \'certificateMap\'">Certificate</span>\n <span ng-if="$ctrl.listener.certificateSource === \'certificateMap\'">Certificate Map</span>\n <help-field\n ng-if="$ctrl.listener.certificateSource !== \'certificateMap\'"\n key="gce.httpLoadBalancer.certificate"\n class="help-field-absolute"\n ></help-field>\n <help-field\n ng-if="$ctrl.listener.certificateSource === \'certificateMap\'"\n key="gce.httpLoadBalancer.certificateMap"\n class="help-field-absolute"\n ></help-field>\n </div>\n <div class="col-md-3" ng-if="$ctrl.listener.certificateSource !== \'certificateMap\'">\n <ui-select\n ng-model="$ctrl.listener.certificate"\n ng-change="$ctrl.onCertificateSelected($ctrl.listener)"\n ng-required="$ctrl.isHttps($ctrl.listener.port) && $ctrl.listener.certificateSource !== \'certificateMap\'"\n class="form-control input-sm"\n >\n <ui-select-match allow-clear placeholder="Select...">{{ $select.selected }}</ui-select-match>\n <ui-select-choices repeat="certificate in $ctrl.getCertificates() | filter: $select.search">\n <span ng-bind-html="certificate | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-3" ng-if="$ctrl.listener.certificateSource === \'certificateMap\'">\n <input\n type="text"\n class="form-control input-sm"\n name="certificateMap"\n ng-model="$ctrl.listener.certificateMap"\n ng-change="$ctrl.onCertificateMapChanged($ctrl.listener)"\n ng-required="$ctrl.isHttps($ctrl.listener.port) && $ctrl.listener.certificateSource === \'certificateMap\'"\n ng-pattern="$ctrl.certificateMapPattern"\n placeholder="certificate-map-name"\n />\n </div>\n <div\n class="col-md-1"\n style="padding-left: 0; margin-top: 4px"\n ng-if="$ctrl.listener.certificateSource !== \'certificateMap\'"\n >\n <gce-cache-refresh\n cache-key="certificates"\n render-compact="true"\n on-refresh="$ctrl.command.onCertificateRefresh($ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.command.loadBalancer.loadBalancerType === \'HTTP\'">\n <div class="col-md-2 sm-label-right">\n External IP\n <help-field key="gce.httpLoadBalancer.externalIP" class="help-field-absolute"></help-field>\n </div>\n <div class="col-md-5">\n <gce-address-selector\n address-list="$ctrl.command.backingData.addresses"\n read-only="$ctrl.listener.created"\n account="$ctrl.command.loadBalancer.credentials"\n initial-ip-address="$ctrl.listener.ipAddress"\n on-address-select="$ctrl.onAddressSelect(address)"\n ></gce-address-selector>\n </div>\n <div class="col-md-1" style="padding-left: 0; margin-top: 4px">\n <gce-cache-refresh\n ng-if="!$ctrl.listener.created"\n cache-key="addresses"\n render-compact="true"\n on-refresh="$ctrl.command.onAddressRefresh($ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-7 col-md-offset-2" ng-if="listener.stackName.$error.pattern">\n <validation-error message="Stack can only contain lowercase letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-2" ng-if="listener.detailName.$error.pattern">\n <validation-error\n message="Detail can only contain lowercase letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-2" ng-if="listener.certificateMap.$error.pattern">\n <validation-error\n message="Certificate map can only contain lowercase letters, numbers, and dashes(-), and must start with a letter."\n ></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]);Le(".gce-http-lb input[type='checkbox'] {\n margin: 10px 0 0 5px;\n position: absolute;\n}\n.gce-http-lb help-field.help-field-absolute span {\n position: absolute;\n top: 7px;\n right: 2px;\n}\n.gce-http-lb .ui-select-multiple[tagging] {\n width: 220px;\n}\n.gce-http-lb gce-host-rule hr.host-rule {\n border-top: 1px solid var(--color-concrete);\n}\n");const Hn="spinnaker.deck.gce.loadBalancer.createHttp.controller";n(Hn,[Ie,xe,yn,kn,Cn,Pn,In,Mn,Nn,Dn,Ln,An]).controller("gceCreateHttpLoadBalancerCtrl",["$scope","$uibModal","$uibModalInstance","application","loadBalancer","isNew","forPipelineConfig","gceHttpLoadBalancerWriter","$state","wizardSubFormValidation","gceHttpLoadBalancerCommandBuilder","gceHttpLoadBalancerTransformer",function(e,n,t,a,i,l,c,r,o,s,d,u){this.application=a,this.isNew=l,this.modalDescriptor=this.isNew?"Create HTTP(S) load balancer":`Edit ${i.name}:global:${i.account}`,this.pages={location:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",listeners:"google/src/loadBalancer/configure/http/listeners/listeners.html",defaultService:"google/src/loadBalancer/configure/http/defaultService/defaultService.html",backendServices:"google/src/loadBalancer/configure/http/backendService/backendServices.html",healthChecks:"google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",hostRules:"google/src/loadBalancer/configure/http/hostRule/hostRules.html"};const g={backendServices:pn,healthChecks:mn,hostRules:fn,listeners:bn};this.add=e=>{this.command.loadBalancer[e].push(new g[e])},this.remove=(e,n)=>{this.command.loadBalancer[e].splice(n,1)};const p=()=>{if(e.$$destroyed)return;t.close();const n=this.command.loadBalancer,a={name:n.urlMapName,accountId:n.credentials,region:n.region,provider:"gce"};o.includes("**.loadBalancerDetails")?o.go("^.loadBalancerDetails",a):o.go(".loadBalancerDetails",a)};e.taskMonitor=this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:t,onTaskComplete:()=>{a.loadBalancers.refresh(),a.loadBalancers.onNextRefresh(e,p)}}),this.submit=()=>{const e=u.serialize(this.command,i),n=this.isNew?"Create":"Update";if(c){const n=e.map(e=>({...e,cloudProvider:"gce",loadBalancerName:e.name,listeners:[{name:e.name,port:e.portRange,certificate:e.certificate||null,certificateMap:e.certificateMap||null,ipAddress:e.ipAddress,subnet:e.subnet}]}));return void t.close(n)}this.taskMonitor.submit(()=>r.upsertLoadBalancers(e,a,n))},d.buildCommand({isNew:l,originalLoadBalancer:i}).then(n=>{this.command=n,s.config({scope:e,form:"form"}).register({page:"location",subForm:"location"}).register({page:"listeners",subForm:"listeners",validators:[{watchString:"ctrl.command.loadBalancer.listeners",validator:e=>e.length>0,collection:!0}]}).register({page:"default-service",subForm:"defaultService"}).register({page:"health-checks",subForm:"healthChecks"}).register({page:"backend-services",subForm:"backendServices"}).register({page:"host-rules",subForm:"hostRules"})}),this.cancel=t.dismiss}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",'<ng-form name="location">\n <gce-http-load-balancer-basic-settings\n application="ctrl.application"\n command="ctrl.command"\n ></gce-http-load-balancer-basic-settings>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listeners.html",'<ng-form name="listeners">\n <gce-listener\n command="ctrl.command"\n delete-listener="ctrl.remove(\'listeners\', $index)"\n index="$index"\n application="ctrl.application"\n listener="listener"\n ng-repeat="listener in ctrl.command.loadBalancer.listeners"\n ></gce-listener>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'listeners\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add listener\n </button>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/defaultService/defaultService.html",'<ng-form name="defaultService">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b><help-field key="gce.httpLoadBalancer.defaultService"></help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="ctrl.command.loadBalancer.defaultService"\n required\n on-select="ctrl.command.onBackendServiceSelected($item, ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select..."> {{$select.selected}} </ui-select-match>\n <ui-select-choices\n repeat="backendService in ctrl.command.getAllBackendServices(ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendServices.html",'<ng-form name="backendServices">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedBackendServices(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following backend services have not been associated with a host or path rule, or set as this load balancer\'s\n default service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedBackendServices(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedBackendServices(ctrl.command)"\n >Remove backend services</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.backendServices.length && !ctrl.command.backingData.backendServices.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any backend services.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-backend-service-selector\n ng-repeat="backendService in ctrl.command.loadBalancer.backendServices"\n backend-service="backendService"\n index="$index"\n command="ctrl.command"\n delete-service="ctrl.remove(\'backendServices\', $index)"\n >\n </gce-http-load-balancer-backend-service-selector>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'backendServices\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add backend service\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="backendServices"\n cache-key-alias="backend services"\n on-refresh="ctrl.command.onBackendServiceRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",'<ng-form name="healthChecks">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedHealthChecks(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following health checks have not been associated with a backend service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedHealthChecks(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedHealthChecks(ctrl.command)"\n >Remove health checks</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.healthChecks.length && !ctrl.command.backingData.healthChecks.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any health checks.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-health-check\n ng-repeat="healthCheck in ctrl.command.loadBalancer.healthChecks"\n index="$index"\n command="ctrl.command"\n delete-health-check="ctrl.remove(\'healthChecks\', $index)"\n health-check="healthCheck"\n ></gce-http-load-balancer-health-check>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'healthChecks\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add health check\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.command.onHealthCheckRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRules.html",'<ng-form name="hostRules">\n <gce-host-rule\n ng-repeat="hostRule in ctrl.command.loadBalancer.hostRules"\n host-rule="hostRule"\n index="$index"\n command="ctrl.command"\n delete-host-rule="ctrl.remove(\'hostRules\', $index)"\n ></gce-host-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'hostRules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add host rule\n </button>\n </div>\n </div>\n</ng-form>\n')}]);class Fn{constructor(e,n,t,a){this.$scope=e,this.application=n,this.$uibModalInstance=t,this.$state=a}onTaskComplete(e){d.clearCache("healthChecks"),this.application.getDataSource("loadBalancers").refresh(),this.application.getDataSource("loadBalancers").onNextRefresh(this.$scope,()=>this.onApplicationRefresh(e))}cancel(){this.$uibModalInstance.dismiss()}getName(e,n){const t=[n.name,e.stack||"",e.detail||""].join("-");return fe(t,"-")}onApplicationRefresh(e){if(this.$scope.$$destroyed)return;this.$uibModalInstance.close();const n={name:e.loadBalancerName,accountId:e.credentials,region:e.region,provider:"gce"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",n):this.$state.go(".loadBalancerDetails",n)}}Fn.$inject=["$scope","application","$uibModalInstance","$state"];class Un{constructor(e,n,t,a){this.$q=e,this.loadBalancerReader=n,this.gceHealthCheckReader=t,this.gceCertificateReader=a,this.dataFetchers={existingLoadBalancerNamesByAccount:()=>this.loadBalancerReader.listLoadBalancers("gce").then(e=>oe.chain(e).map("accounts").flatten().groupBy("name").mapValues(e=>oe.chain(e).map("regions").flatten().map("loadBalancers").flatten().map("name").value()).value()),accounts:()=>y.listAccounts("gce"),networks:()=>B.listNetworksByProvider("gce"),subnets:()=>C.listSubnetsByProvider("gce"),healthChecks:()=>this.gceHealthCheckReader.listHealthChecks(),certificates:()=>this.gceCertificateReader.listCertificates()}}getBackingData(e){const n=e.reduce((e,n)=>(this.dataFetchers[n]&&(e[n]=this.dataFetchers[n]()),e),{});return this.$q.all(n)}groupHealthChecksByAccountAndType(e){return oe.chain(e).groupBy("account").mapValues(e=>oe.groupBy(e,"healthCheckType")).value()}groupHealthCheckNamesByAccount(e,n){return oe.chain(e).groupBy("account").mapValues(e=>oe.chain(e).map("name").difference(n).value()).value()}}Un.$inject=["$q","loadBalancerReader","gceHealthCheckReader","gceCertificateReader"];const On="spinnaker.gce.commonLoadBalancerCommandBuilder.service";n(On,[w,Bn,Ve]).service("gceCommonLoadBalancerCommandBuilder",Un);class qn{constructor(e){this.$scope=e,this.editExisting=!1,this.max=Number.MAX_SAFE_INTEGER}$onInit(){this.healthCheck.name?(this.healthCheckPlaceholder=this.healthCheck,this.existingHealthChecksForProtocol=this.healthChecksByAccountAndType[this.credentials][this.healthCheck.healthCheckType],this.editExisting=!0):this.existingHealthChecksForProtocol=this.healthChecksByAccountAndType[this.credentials].TCP,this.$scope.$watch("$ctrl.credentials",()=>this.setExistingHealthChecksForProtocol())}onProtocolChange(){delete this.healthCheck.name,delete this.healthCheckPlaceholder,this.setExistingHealthChecksForProtocol()}setExistingHealthChecksForProtocol(){this.existingHealthChecksForProtocol=ve(this,["healthChecksByAccountAndType",this.credentials,this.healthCheck.healthCheckType])||[],this.existingHealthChecksForProtocol.find(e=>e.name===this.healthCheck.name)||(delete this.healthCheck.name,delete this.healthCheckPlaceholder)}toggleEditExisting(){this.editExisting=!this.editExisting,delete this.healthCheck.name,delete this.healthCheckPlaceholder}onHealthCheckSelect(e){Object.assign(this.healthCheck,e)}onHealthCheckNameChange(e){this.healthCheck.name=e}}qn.$inject=["$scope"];const _n={bindings:{healthCheck:"=",credentials:"<",healthChecksByAccountAndType:"<",existingHealthCheckNames:"<"},templateUrl:"google/src/loadBalancer/configure/common/healthCheck.component.html",controller:qn},Vn="spinnaker.gce.healthCheckSelector.component";n(Vn,[]).component("gceHealthCheckSelector",_n),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/healthCheck.component.html",'<ng-form name="healthCheckForm" novalidate>\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-12 well alert-danger" ng-if="healthCheckForm.healthCheckName.$error.validateUnique">\n <validation-error message="There is already a health check with that name."></validation-error>\n </div>\n <div\n class="col-md-12 well alert-danger"\n ng-if="healthCheckForm.healthCheckName.$error.pattern || healthCheckForm.healthCheckName.$error.max"\n >\n <validation-error\n message="Name must start with a lowercase letter followed by up to 62 lowercase letters,\n numbers, or hyphens, and cannot end with a hyphen."\n >\n </validation-error>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Protocol</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-change="$ctrl.onProtocolChange()"\n ng-options="protocol as protocol for protocol in [\'HTTP\', \'HTTPS\', \'TCP\', \'SSL\', \'HTTP2\', \'GRPC\']"\n ng-model="$ctrl.healthCheck.healthCheckType"\n ></select>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Name</div>\n\n <div class="col-md-4" ng-if="$ctrl.editExisting">\n <ui-select\n ng-model="$ctrl.healthCheckPlaceholder"\n on-select="$ctrl.onHealthCheckSelect($item)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select...">\n {{ $select.selected.name }}\n </ui-select-match>\n <ui-select-choices\n repeat="healthCheck in $ctrl.existingHealthChecksForProtocol | filter: {name: $select.search}"\n >\n <div ng-bind-html="healthCheck.name | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-4" ng-if="!$ctrl.editExisting">\n <input\n class="form-control input-sm"\n novalidate\n type="text"\n ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"\n name="healthCheckName"\n max="63"\n required\n validate-unique="$ctrl.existingHealthCheckNames"\n ng-model-options="{ debounce: 250 }"\n ng-change="$ctrl.onHealthCheckNameChange($ctrl.healthCheckPlaceholder.name)"\n ng-model="$ctrl.healthCheckPlaceholder.name"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 col-md-offset-4">\n <a href class="small" ng-if="!$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()"\n >Toggle for list of existing health checks</a\n >\n <a href class="small" ng-if="$ctrl.editExisting" ng-click="$ctrl.toggleEditExisting()">Toggle for text input</a>\n </div>\n </div>\n\n <div\n class="form-group"\n ng-if="\n $ctrl.healthCheck.healthCheckType === \'HTTP\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTPS\' ||\n $ctrl.healthCheck.healthCheckType === \'HTTP2\'\n "\n >\n <div class="col-md-4 sm-label-right">Request Path</div>\n <div class="col-md-4">\n <input class="form-control input-sm" required ng-model="$ctrl.healthCheck.requestPath" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n required\n min="1"\n max="65535"\n ng-model="$ctrl.healthCheck.port"\n />\n </div>\n </div>\n\n <div class="form-group" ng-if="$ctrl.healthCheck.healthCheckType === \'GRPC\'">\n <div class="col-md-4 sm-label-right">Service Name</div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n ng-model="$ctrl.healthCheck.grpcServiceName"\n placeholder="com.example.HealthService"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.timeoutSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.checkIntervalSec"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.healthyThreshold"\n />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n max="{{:: $ctrl.max}}"\n required\n ng-model="$ctrl.healthCheck.unhealthyThreshold"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);let jn=class{constructor(e){this.sessionAffinity=e}};class Kn{constructor(e){this.region=e,this.ipProtocol="TCP",this.loadBalancerType="INTERNAL",this.network="default",this.cloudProvider="gce",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class Wn extends Fn{constructor(e,n,t,a,i,l,c,r,o,s){super(e,n,t,s),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.forPipelineConfig=c,this.wizardSubFormValidation=r,this.gceXpnNamingService=o,this.pages={location:"google/src/loadBalancer/configure/internal/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/internal/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Client IP and protocol":"CLIENT_IP_PROTO","Client IP, port and protocol":"CLIENT_IP_PORT_PROTO"},this.viewState=new jn("None"),this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","networks","subnets","healthChecks"]).then(e=>{this.isNew?this.loadBalancer=new Kn(Ue?Ue.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map(e=>e.name);n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.networks=e.networks,this.subnets=e.subnets,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})})}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then(e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)})}networkUpdated(){this.subnetOptions=this.subnets.filter(e=>e.region===this.loadBalancer.region&&(e.account===this.loadBalancer.credentials||e.account===this.loadBalancer.account)&&e.network===this.loadBalancer.network).map(e=>e.id),this.subnetOptions.includes(this.loadBalancer.subnet)||(this.loadBalancer.subnet=this.subnetOptions[0])}protocolUpdated(){"UDP"===this.loadBalancer.ipProtocol&&(this.viewState=new jn("None"),this.loadBalancer.backendService.sessionAffinity="NONE")}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],this.networkOptions=this.networks.filter(e=>e.account===this.loadBalancer.credentials).map(e=>e.id),y.getRegionsForAccount(this.loadBalancer.credentials).then(e=>{this.regions=e.map(e=>e.name),this.networkUpdated()})}regionUpdated(){this.networkUpdated()}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);if(n.ports=n.ports.split(",").map(e=>e.trim()),n.cloudProvider="gce",n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,delete n.instances,this.forPipelineConfig)return Object.assign(n,{healthCheck:{}}),void this.$uibModalInstance.close(n);this.taskMonitor.submit(()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}}))}initializeEditMode(){this.loadBalancer.ports=this.loadBalancer.ports.join(", "),this.loadBalancer.subnet=this.gceXpnNamingService.decorateXpnResourceIfNecessary(this.loadBalancer.project,this.loadBalancer.subnet),this.loadBalancer.network=this.gceXpnNamingService.decorateXpnResourceIfNecessary(this.loadBalancer.project,this.loadBalancer.network),this.viewState=new jn(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}Wn.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","forPipelineConfig","wizardSubFormValidation","gceXpnNamingService","$state"];const Zn="spinnaker.gce.internalLoadBalancer.controller";n(Zn,[Vn,On,Ke]).controller("gceInternalLoadBalancerCtrl",Wn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/internal/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="2"\n field-columns="8"\n component="ctrl.loadBalancer"\n field="region"\n account="ctrl.loadBalancer.credentials"\n on-change="ctrl.regionUpdated()"\n regions="ctrl.regions"\n ></gce-region-select-field>\n\n <gce-network-select-field\n networks="ctrl.networkOptions"\n component="ctrl.loadBalancer"\n on-change="ctrl.networkUpdated()"\n field="network"\n account="ctrl.loadBalancer.credentials"\n field-columns="8"\n label-columns="2"\n ></gce-network-select-field>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/internal/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Subnet</th>\n <th>Ports<help-field key="gce.internalLoadBalancer.ports"></help-field></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <select\n ng-disabled="!ctrl.isNew"\n class="form-control input-sm"\n ng-change="ctrl.protocolUpdated()"\n ng-model="ctrl.loadBalancer.ipProtocol"\n ng-options="protocol for protocol in [\'TCP\', \'UDP\']"\n ></select>\n </td>\n\n <td>\n <select\n class="form-control input-sm"\n ng-options="subnet for subnet in ctrl.subnetOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.subnet"\n ></select>\n </td>\n\n <td><input class="form-control input-sm" required ng-model="ctrl.loadBalancer.ports" /></td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const Xn="spinnaker.deck.gce.loadBalancer.createInternalHttp.controller";class Yn{constructor(e,n,t,a,i,l,c,r,o,s,d){this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceHttpLoadBalancerCommandBuilder=i,this.isNew=l,this.forPipelineConfig=c,this.wizardSubFormValidation=r,this.gceHttpLoadBalancerTransformer=o,this.gceHttpLoadBalancerWriter=s,this.$state=d,this.pages={location:"google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",listeners:"google/src/loadBalancer/configure/http/listeners/listeners.html",defaultService:"google/src/loadBalancer/configure/http/defaultService/defaultService.html",backendServices:"google/src/loadBalancer/configure/http/backendService/backendServices.html",healthChecks:"google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",hostRules:"google/src/loadBalancer/configure/http/hostRule/hostRules.html"},this.keyToTemplateMap={backendServices:pn,healthChecks:mn,hostRules:fn,listeners:bn},this.modalDescriptor=this.isNew?"Create Internal HTTP(S) load balancer":`Edit ${this.loadBalancer.name}:${this.loadBalancer.region}:${this.loadBalancer.account}`;e.taskMonitor=this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>{n.loadBalancers.refresh(),n.loadBalancers.onNextRefresh(e,this.onApplicationRefresh.bind(this))}})}$onInit(){this.gceHttpLoadBalancerCommandBuilder.buildCommand({isNew:this.isNew,originalLoadBalancer:this.loadBalancer,isInternal:!0}).then(e=>{this.command=e,this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"location"}).register({page:"listeners",subForm:"listeners",validators:[{watchString:"ctrl.command.loadBalancer.listeners",validator:e=>e.length>0,collection:!0}]}).register({page:"default-service",subForm:"defaultService"}).register({page:"health-checks",subForm:"healthChecks"}).register({page:"backend-services",subForm:"backendServices"}).register({page:"host-rules",subForm:"hostRules"})})}add(e){this.command.loadBalancer[e].push(new this.keyToTemplateMap[e])}remove(e,n){this.command.loadBalancer[e].splice(n,1)}cancel(){this.$uibModalInstance.dismiss()}submit(){const e=this.gceHttpLoadBalancerTransformer.serialize(this.command,this.loadBalancer),n=this.isNew?"Create":"Update";if(this.forPipelineConfig){const n=e.map(e=>({...e,cloudProvider:"gce",loadBalancerName:e.name,listeners:[{name:e.name,port:e.portRange,certificate:e.certificate||null,certificateMap:e.certificateMap||null,ipAddress:e.ipAddress,subnet:e.subnet}]}));return void this.$uibModalInstance.close(n)}this.taskMonitor.submit(()=>this.gceHttpLoadBalancerWriter.upsertLoadBalancers(e,this.application,n))}onApplicationRefresh(){if(this.$scope.$$destroyed)return;this.$uibModalInstance.close();const e=this.command.loadBalancer,n={name:e.urlMapName,accountId:e.credentials,region:e.region,provider:"gce"};this.$state.includes("**.loadBalancerDetails")?this.$state.go("^.loadBalancerDetails",n):this.$state.go(".loadBalancerDetails",n)}}Yn.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceHttpLoadBalancerCommandBuilder","isNew","forPipelineConfig","wizardSubFormValidation","gceHttpLoadBalancerTransformer","gceHttpLoadBalancerWriter","$state"],n(Xn,["ui.bootstrap",xe,yn,kn,Cn,Pn,In,Mn,Nn,Dn,Ln,An]).controller("gceCreateInternalHttpLoadBalancerCtrl",Yn),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/basicSettings/basicSettings.html",'<ng-form name="location">\n <gce-http-load-balancer-basic-settings\n application="ctrl.application"\n command="ctrl.command"\n ></gce-http-load-balancer-basic-settings>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/listeners/listeners.html",'<ng-form name="listeners">\n <gce-listener\n command="ctrl.command"\n delete-listener="ctrl.remove(\'listeners\', $index)"\n index="$index"\n application="ctrl.application"\n listener="listener"\n ng-repeat="listener in ctrl.command.loadBalancer.listeners"\n ></gce-listener>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'listeners\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add listener\n </button>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/defaultService/defaultService.html",'<ng-form name="defaultService">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n <b>Default Service</b><help-field key="gce.httpLoadBalancer.defaultService"></help-field>\n </div>\n <div class="col-md-4">\n <ui-select\n ng-model="ctrl.command.loadBalancer.defaultService"\n required\n on-select="ctrl.command.onBackendServiceSelected($item, ctrl.command)"\n class="form-control input-sm"\n >\n <ui-select-match placeholder="Select..."> {{$select.selected}} </ui-select-match>\n <ui-select-choices\n repeat="backendService in ctrl.command.getAllBackendServices(ctrl.command) | filter: $select.search"\n >\n <div ng-bind-html="backendService | highlight: $select.search"></div>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/backendService/backendServices.html",'<ng-form name="backendServices">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedBackendServices(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following backend services have not been associated with a host or path rule, or set as this load balancer\'s\n default service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedBackendServices(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedBackendServices(ctrl.command)"\n >Remove backend services</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.backendServices.length && !ctrl.command.backingData.backendServices.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any backend services.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-backend-service-selector\n ng-repeat="backendService in ctrl.command.loadBalancer.backendServices"\n backend-service="backendService"\n index="$index"\n command="ctrl.command"\n delete-service="ctrl.remove(\'backendServices\', $index)"\n >\n </gce-http-load-balancer-backend-service-selector>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'backendServices\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add backend service\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="backendServices"\n cache-key-alias="backend services"\n on-refresh="ctrl.command.onBackendServiceRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/healthCheck/healthChecks.html",'<ng-form name="healthChecks">\n <div class="col-md-12" ng-if="ctrl.command.getUnusedHealthChecks(ctrl.command).length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following health checks have not been associated with a backend service:\n </p>\n <ul>\n <li ng-repeat="service in ctrl.command.getUnusedHealthChecks(ctrl.command)" ng-bind-html="service"></li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="ctrl.command.removeUnusedHealthChecks(ctrl.command)"\n >Remove health checks</a\n >\n </p>\n </div>\n </div>\n <div\n class="col-md-12"\n ng-if="!ctrl.command.loadBalancer.healthChecks.length && !ctrl.command.backingData.healthChecks.length"\n >\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n You have not configured any health checks.\n </p>\n </div>\n </div>\n <gce-http-load-balancer-health-check\n ng-repeat="healthCheck in ctrl.command.loadBalancer.healthChecks"\n index="$index"\n command="ctrl.command"\n delete-health-check="ctrl.remove(\'healthChecks\', $index)"\n health-check="healthCheck"\n ></gce-http-load-balancer-health-check>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'healthChecks\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add health check\n </button>\n </div>\n </div>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.command.onHealthCheckRefresh(ctrl.command)"\n ></gce-cache-refresh>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/http/hostRule/hostRules.html",'<ng-form name="hostRules">\n <gce-host-rule\n ng-repeat="hostRule in ctrl.command.loadBalancer.hostRules"\n host-rule="hostRule"\n index="$index"\n command="ctrl.command"\n delete-host-rule="ctrl.remove(\'hostRules\', $index)"\n ></gce-host-rule>\n <div class="row">\n <div class="col-md-12">\n <button class="add-new btn btn-block" ng-click="ctrl.add(\'hostRules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add host rule\n </button>\n </div>\n </div>\n</ng-form>\n')}]);const Jn="spinnaker.google.regionSelectField.directive";n(Jn,[]).directive("gceRegionSelectField",function(){return{restrict:"E",templateUrl:"google/src/regionSelectField.directive.html",scope:{regions:"=",component:"=",field:"@",account:"=",onChange:"&",labelColumns:"@",readOnly:"=",fieldColumns:"@"}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/regionSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">Region</div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!account">(Select an account)</div>\n <div class="col-md-{{fieldColumns || 7}}">\n <select\n class="form-control input-sm"\n ng-if="account && !readOnly"\n ng-model="component.region"\n ng-change="onChange()"\n required\n >\n <option ng-repeat="region in regions" value="{{region}}" ng-selected="component[field] === region">\n {{region}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const Qn="spinnaker.gce.loadBalancer.transformer";n(Qn,[]).factory("gceLoadBalancerTransformer",["$q",function(e){function n(e){const n=e.instances,t=e.serverGroups||[e];e.instanceCounts={up:n.filter(e=>"InService"===e.health[0].state).length,down:n.filter(e=>"OutOfService"===e.health[0].state).length,outOfService:t.reduce((e,n)=>n.instances.filter(e=>"OutOfService"===e.healthState).length+e,0)}}return{normalizeLoadBalancer:function(t){t.serverGroups.forEach(function(e){e.account=t.account,e.detachedInstances?(e.detachedInstances=e.detachedInstances.map(function(e){return{id:e}}),e.instances=e.instances.concat(e.detachedInstances)):e.detachedInstances=[],e.instances.forEach(function(e){!function(e,n){e.health=e.health||{},e.provider=n.type,e.account=n.account,e.region=n.region,e.health.type="LoadBalancer",e.healthState=e.health.state?"InService"===e.health.state?"Up":"Down":"OutOfService",e.health=[e.health],e.loadBalancers=[n.name]}(e,t)}),n(e)});const a=oe.filter(t.serverGroups,{isDisabled:!1});return t.provider=t.type,t.instances=oe.chain(a).map("instances").flatten().value(),t.detachedInstances=oe.chain(a).map("detachedInstances").flatten().value(),oe.get(t,"backendService.healthCheck")&&(t.backendService.healthCheck.timeout=t.backendService.healthCheck.timeoutSec,t.backendService.healthCheck.interval=t.backendService.healthCheck.checkIntervalSec),n(t),e.when(t)},convertLoadBalancerForEditing:function(e){const n={provider:"gce",region:e.region,credentials:e.account,listeners:[],name:e.name,regionZones:e.availabilityZones};if(e.elb){const t=e.elb;if(n.vpcId=t.vpcid,t.listenerDescriptions&&(n.listeners=t.listenerDescriptions.map(function(e){const n=e.listener;return{protocol:n.protocol,portRange:n.loadBalancerPort,healthCheck:void 0!==t.healthCheck}})),t.healthCheck&&t.healthCheck.target){n.healthTimeout=t.healthCheck.timeout,n.healthInterval=t.healthCheck.interval,n.healthyThreshold=t.healthCheck.healthyThreshold,n.unhealthyThreshold=t.healthCheck.unhealthyThreshold;const a=e.elb.healthCheck.target,i=a.indexOf(":"),l=a.indexOf("/");-1!==i&&-1!==l&&(n.healthCheckProtocol=a.substring(0,i),n.healthCheckPort=a.substring(i+1,l),n.healthCheckPath=a.substring(l),isNaN(n.healthCheckPort)||(n.healthCheckPort=Number(n.healthCheckPort)))}else n.healthCheckProtocol="HTTP",n.healthCheckPort=80,n.healthCheckPath="/",n.healthTimeout=5,n.healthInterval=10,n.healthyThreshold=10,n.unhealthyThreshold=2;n.sessionAffinity=e.sessionAffinity||"None"}return n},constructNewLoadBalancerTemplate:function(){return{provider:"gce",stack:"",detail:"",credentials:Ue.defaults.account,region:Ue.defaults.region,healthCheckProtocol:"HTTP",healthCheckPort:80,healthCheckPath:"/",healthTimeout:5,healthInterval:10,healthyThreshold:10,unhealthyThreshold:2,sessionAffinity:"NONE",regionZones:[],listeners:[{protocol:"TCP",portRange:"8080",healthCheck:!0}]}}}}]);const et="spinnaker.loadBalancer.gce.create.controller";n(et,[xe,Qn,Jn]).controller("gceCreateLoadBalancerCtrl",["$scope","$uibModalInstance","$state","gceLoadBalancerTransformer","application","loadBalancer","isNew","forPipelineConfig",function(e,n,t,a,i,l,c,r){const o=this;function s(){if(e.$$destroyed)return;n.close();const a={name:e.loadBalancer.name,accountId:e.loadBalancer.credentials,region:e.loadBalancer.region,provider:"gce"};t.includes("**.loadBalancerDetails")?t.go("^.loadBalancerDetails",a):t.go(".loadBalancerDetails",a)}function d(){const n=e.loadBalancer.credentials,t={};i.getDataSource("loadBalancers").refresh(!0).then(()=>{i.getDataSource("loadBalancers").data.forEach(e=>{e.account===n&&(t[e.region]=t[e.region]||[],t[e.region].push(e.name))}),e.existingLoadBalancerNames=_.flatten(_.map(t))})}e.isNew=c,e.pages={location:"google/src/loadBalancer/configure/network/createLoadBalancerProperties.html",listeners:"google/src/loadBalancer/configure/network/listeners.html",healthCheck:"google/src/loadBalancer/configure/network/healthCheck.html",advancedSettings:"google/src/loadBalancer/configure/network/advancedSettings.html"},e.state={accountsLoaded:!1,submitting:!1},e.sessionAffinityOptions=[{value:"NONE",name:"None"},{value:"CLIENT_IP",name:"Client IP"},{value:"CLIENT_IP_PROTO",name:"Client IP and protocol"}],e.taskMonitor=new G({application:i,title:(c?"Creating ":"Updating ")+"your load balancer",modalInstance:n,onTaskComplete:function(){i.loadBalancers.refresh(),i.loadBalancers.onNextRefresh(e,s)}}),l?e.loadBalancer=a.convertLoadBalancerForEditing(l):(e.loadBalancer=a.constructNewLoadBalancerTemplate(),d(),y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accountsLoaded=!0;const t=_.map(e.accounts,"name");t.length&&!t.includes(e.loadBalancer.credentials)&&(e.loadBalancer.credentials=t[0]),o.accountUpdated()})),this.requiresHealthCheckPath=function(){return e.loadBalancer.healthCheckProtocol&&0===e.loadBalancer.healthCheckProtocol.indexOf("HTTP")},this.prependForwardSlash=e=>e&&0!==e.indexOf("/")?`/${e}`:e,this.updateName=function(){e.loadBalancer.name=this.getName()},this.getName=function(){const n=e.loadBalancer,t=[i.name,n.stack||"",n.detail||""].join("-");return _.trimEnd(t,"-")},this.accountUpdated=function(){y.getRegionsForAccount(e.loadBalancer.credentials).then(function(n){_.isArray(n)?e.regions=_.map(n,"name"):e.regions=_.keys(n),o.regionUpdated()})},this.regionUpdated=function(){d(),o.updateName()},this.setVisibilityHealthCheckTab=function(){const n=P;e.loadBalancer.listeners[0].healthCheck?(n.includePage("Health Check"),n.markIncomplete("Health Check"),n.includePage("Advanced Settings"),n.markIncomplete("Advanced Settings")):(n.excludePage("Health Check"),n.markComplete("Health Check"),n.excludePage("Advanced Settings"),n.markComplete("Advanced Settings"),n.markComplete("Listener"))},this.submit=function(){const t=c?"Create":"Update",a={cloudProvider:"gce",loadBalancerName:e.loadBalancer.name};if(e.loadBalancer.listeners&&e.loadBalancer.listeners.length>0){const n=e.loadBalancer.listeners[0];n.protocol&&(a.ipProtocol=n.protocol),n.portRange&&(a.portRange=n.portRange),n.healthCheck?a.healthCheck={port:e.loadBalancer.healthCheckPort,requestPath:e.loadBalancer.healthCheckPath,timeoutSec:e.loadBalancer.healthTimeout,checkIntervalSec:e.loadBalancer.healthInterval,healthyThreshold:e.loadBalancer.healthyThreshold,unhealthyThreshold:e.loadBalancer.unhealthyThreshold}:a.healthCheck=null,a.sessionAffinity=e.loadBalancer.sessionAffinity}if(r){const t=Object.assign({},e.loadBalancer,a);return void n.close(t)}e.taskMonitor.submit(function(){return A.upsertLoadBalancer(e.loadBalancer,i,t,a)})},this.cancel=function(){n.dismiss()}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <div ng-if="!state.accountsLoaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="modal-body" ng-if="state.accountsLoaded">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.loadBalancerName.$error.validateUnique, \'alert-info\': !form.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName()}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="loadBalancer.name"\n validate-unique="existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="form.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{loadBalancer.credentials}} with that name."\n ></validation-error>\n </div>\n </div>\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 component="loadBalancer"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="3"\n component="loadBalancer"\n field="region"\n account="loadBalancer.credentials"\n on-change="ctrl.regionUpdated()"\n regions="regions"\n ></gce-region-select-field>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-zA-Z0-9]*$/"\n />\n </div>\n <div class="col-md-6 form-inline">\n <label class="sm-label-right"> Detail <help-field key="gce.loadBalancer.detail"></help-field> </label>\n <input\n type="text"\n class="form-control input-sm"\n ng-model="loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-zA-Z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="form.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="form.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="form.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/listeners.html",'<div class="container-fluid form-horizontal">\n <form name="listenerForm">\n <div class="form-group">\n <div class="alert alert-warning" ng-if="listenerForm.protocolSelect.$dirty || listenerForm.portRangeInput.$dirty">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n Changing your load balancer\'s protocol or port range will cause the forwarding rule to be destroyed and\n recreated. This will most likely cause the IP address of this load balancer to change. If you want to preserve\n the IP for this load balancer, you need to reserve it and manipulate the forwarding rule in Google Cloud\n Console.\n </p>\n </div>\n <div class="col-md-3 sm-label-right table-offset">Listener</div>\n <div class="col-md-9">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>\n Port Range\n <help-field key="gce.loadBalancer.portRange"></help-field>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="listener in loadBalancer.listeners">\n <td>\n <select\n class="form-control input-sm"\n ng-model="listener.protocol"\n ng-options="protocol for protocol in [\'TCP\', \'UDP\']"\n name="protocolSelect"\n ></select>\n </td>\n <td><input class="form-control input-sm" ng-model="listener.portRange" name="portRangeInput" /></td>\n </tr>\n <tr>\n <td>\n <label\n ><input\n type="checkbox"\n ng-model="loadBalancer.listeners[0].healthCheck"\n ng-change="ctrl.setVisibilityHealthCheckTab()"\n />\n Enable health check?\n <help-field key="gce.loadBalancer.healthCheck"></help-field>\n </label>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/healthCheck.html",'<div class="container-fluid form-horizontal">\n <div class="col-md-4 sm-label-right">Ping</div>\n <div class="col-md-8">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th width="30%">Port</th>\n <th><span ng-if="ctrl.requiresHealthCheckPath()">Path</span></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n ng-model="loadBalancer.healthCheckPort"\n required\n min="0"\n />\n </td>\n <td>\n <input\n ng-if="ctrl.requiresHealthCheckPath()"\n class="form-control input-sm"\n type="text"\n ng-change="loadBalancer.healthCheckPath = ctrl.prependForwardSlash(loadBalancer.healthCheckPath)"\n ng-model="loadBalancer.healthCheckPath"\n required\n />\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/network/advancedSettings.html",'<div class="form-group">\n <div class="col-md-8 nest">\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Timeout</b><help-field key="loadBalancer.advancedSettings.healthTimeout"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthTimeout" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Interval</b><help-field key="gce.loadBalancer.advancedSettings.healthInterval"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthInterval" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Healthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.healthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.healthyThreshold" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Unhealthy Threshold</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <input class="form-control input-sm" type="number" min="0" ng-model="loadBalancer.unhealthyThreshold" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-6 sm-label-right">\n <b>Session Affinity</b><help-field key="gce.loadBalancer.advancedSettings.unhealthyThreshold"></help-field>\n </div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-model="loadBalancer.sessionAffinity"\n ng-options="option.value as option.name for option in sessionAffinityOptions"\n ng-disabled="!isNew"\n name="sessionAffinitySelect"\n ></select>\n </div>\n </div>\n </div>\n</div>\n')}]);let nt=class{constructor(e){this.sessionAffinity=e}};class tt{constructor(e="global"){this.region=e,this.portRange="443",this.ipProtocol="TCP",this.loadBalancerType="SSL",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class at extends Fn{constructor(e,n,t,a,i,l,c,r,o){super(e,n,t,o),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.forPipelineConfig=c,this.wizardSubFormValidation=r,this.pages={location:"google/src/loadBalancer/configure/ssl/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/ssl/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",backendService:"google/src/loadBalancer/configure/ssl/backendService.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE"},this.portOptions=["25","43","110","143","195","443","465","587","700","993","995"],this.viewState=new nt("None"),this.maxCookieTtl=86400,this.hasBackendService=!0,this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","healthChecks","certificates"]).then(e=>{this.isNew?this.loadBalancer=new tt(Ue?Ue.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map(e=>e.name);n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.certificateWrappers=e.certificates,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})})}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then(e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)})}onCertificateRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["certificates"]).then(e=>{this.certificateWrappers=e.certificates})}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],y.getRegionsForAccount(this.loadBalancer.credentials).then(e=>{this.regions=e.map(e=>e.name)})}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);if(n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,n.cloudProvider="gce",delete n.instances,this.forPipelineConfig)return Object.assign(n,{healthCheck:{}}),void this.$uibModalInstance.close(n);this.taskMonitor.submit(()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}}))}initializeEditMode(){this.loadBalancer.portRange=this.loadBalancer.portRange.split("-")[0],this.loadBalancer.certificate=this.loadBalancer.certificate.split("/").pop(),this.viewState=new nt(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}at.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","forPipelineConfig","wizardSubFormValidation","$state"];const it="spinnaker.gce.sslLoadBalancer.controller";n(it,[Vn,On]).controller("gceSslLoadBalancerCtrl",at),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Region</div>\n <div class="col-md-8">\n <input readonly class="form-control input-sm" ng-model="ctrl.loadBalancer.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-9 col-md-offset-2">\n <table class="table table-condensed packed" style="table-layout: fixed; width: 390px">\n <thead>\n <tr>\n <th>Certificate</th>\n <th>Port</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>\n <ui-select ng-model="ctrl.loadBalancer.certificate" required class="form-control input-sm">\n <ui-select-match allow-clear placeholder="Select...">{{$select.selected.name}}</ui-select-match>\n <ui-select-choices\n repeat="certificateWrapper.name as certificateWrapper in ctrl.certificateWrappers | filter: {name: $select.search, account: ctrl.loadBalancer.credentials }"\n >\n <span ng-bind-html="certificateWrapper.name | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </td>\n <td>\n <select\n class="form-control input-sm"\n ng-options="port for port in ctrl.portOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.portRange"\n ></select>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </ng-form>\n <div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh cache-key="certificates" on-refresh="ctrl.onCertificateRefresh()"></gce-cache-refresh>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/ssl/backendService.html",'<div class="container-fluid form-horizontal">\n <ng-form name="portName">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Port Name\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="text"\n required\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.portName"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);class lt{constructor(e){this.sessionAffinity=e}}class ct{constructor(e="global"){this.region=e,this.portRange="443",this.ipProtocol="TCP",this.loadBalancerType="TCP",this.backendService={healthCheck:{healthCheckType:"TCP"}}}}class rt extends Fn{constructor(e,n,t,a,i,l,c,r,o){super(e,n,t,o),this.$scope=e,this.application=n,this.$uibModalInstance=t,this.loadBalancer=a,this.gceCommonLoadBalancerCommandBuilder=i,this.isNew=l,this.forPipelineConfig=c,this.wizardSubFormValidation=r,this.pages={location:"google/src/loadBalancer/configure/tcp/createLoadBalancerProperties.html",listener:"google/src/loadBalancer/configure/tcp/listener.html",healthCheck:"google/src/loadBalancer/configure/common/commonHealthCheckPage.html",advancedSettings:"google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",backendService:"google/src/loadBalancer/configure/tcp/backendService.html"},this.sessionAffinityViewToModelMap={None:"NONE","Client IP":"CLIENT_IP","Generated Cookie":"GENERATED_COOKIE"},this.portOptions=["25","43","110","143","195","443","465","587","700","993","995"],this.viewState=new lt("None"),this.maxCookieTtl=86400,this.hasBackendService=!0,this.sessionAffinityModelToViewMap=oe.invert(this.sessionAffinityViewToModelMap)}$onInit(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["existingLoadBalancerNamesByAccount","accounts","healthChecks"]).then(e=>{this.isNew?this.loadBalancer=new ct(Ue?Ue.defaults.region:null):this.initializeEditMode(),this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application);const n=e.accounts.map(e=>e.name);n.length&&!n.includes(this.loadBalancer.account)?this.loadBalancer.credentials=n[0]:this.loadBalancer.credentials=this.loadBalancer.account,this.accounts=e.accounts,this.existingLoadBalancerNamesByAccount=e.existingLoadBalancerNamesByAccount,this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const t=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,t),this.accountUpdated(),this.wizardSubFormValidation.config({scope:this.$scope,form:"form"}).register({page:"location",subForm:"locationForm"}).register({page:"listener",subForm:"listenerForm"}).register({page:"healthCheck",subForm:"healthCheckForm"}).register({page:"advancedSettings",subForm:"advancedSettingsForm"}),this.taskMonitor=new G({application:this.application,title:(this.isNew?"Creating ":"Updating ")+"your load balancer",modalInstance:this.$uibModalInstance,onTaskComplete:()=>this.onTaskComplete(this.loadBalancer)})})}onHealthCheckRefresh(){this.gceCommonLoadBalancerCommandBuilder.getBackingData(["healthChecks"]).then(e=>{this.healthChecksByAccountAndType=this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(e.healthChecks);const n=this.isNew?[]:[this.loadBalancer.backendService.healthCheck.name];this.existingHealthCheckNamesByAccount=this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(e.healthChecks,n)})}accountUpdated(){const e=oe.get(this,["existingHealthCheckNamesByAccount",this.loadBalancer.credentials]);this.existingHealthCheckNames=e||[];const n=oe.get(this,["existingLoadBalancerNamesByAccount",this.loadBalancer.credentials]);this.existingLoadBalancerNames=n||[],y.getRegionsForAccount(this.loadBalancer.credentials).then(e=>{this.regions=e.map(e=>e.name)})}updateName(){this.loadBalancer.loadBalancerName=this.getName(this.loadBalancer,this.application)}setSessionAffinity(e){this.loadBalancer.backendService.sessionAffinity=this.sessionAffinityViewToModelMap[e.sessionAffinity]}submit(){const e=this.isNew?"Create":"Update",n=oe.cloneDeep(this.loadBalancer);if(n.name=n.loadBalancerName,n.backendService.name=n.loadBalancerName,n.cloudProvider="gce",delete n.instances,this.forPipelineConfig)return Object.assign(n,{healthCheck:{}}),void this.$uibModalInstance.close(n);this.taskMonitor.submit(()=>A.upsertLoadBalancer(n,this.application,e,{healthCheck:{}}))}initializeEditMode(){this.loadBalancer.portRange=this.loadBalancer.portRange.split("-")[0],this.viewState=new lt(this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity])}}rt.$inject=["$scope","application","$uibModalInstance","loadBalancer","gceCommonLoadBalancerCommandBuilder","isNew","forPipelineConfig","wizardSubFormValidation","$state"];const ot="spinnaker.gce.tcpLoadBalancer.controller";n(ot,[Vn,On]).controller("gceTcpLoadBalancerCtrl",rt),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/createLoadBalancerProperties.html",'<div class="container-fluid form-horizontal">\n <ng-form name="locationForm">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': locationForm.loadBalancerName.$error.validateUnique, \'alert-info\': !locationForm.loadBalancerName.$error.validateUnique}"\n >\n <strong>Your load balancer will be named:</strong>\n <span>{{ctrl.getName(ctrl.loadBalancer, ctrl.application)}}</span>\n \x3c!-- Angular does not seem to run length validation on hidden inputs, hence the text + display:none --\x3e\n <input\n type="text"\n style="display: none"\n ng-maxlength="63"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.loadBalancerName"\n validate-unique="ctrl.existingLoadBalancerNames"\n validate-ignore-case="true"\n name="loadBalancerName"\n />\n <validation-error\n ng-if="locationForm.loadBalancerName.$error.validateUnique"\n message="There is already a load balancer in {{ctrl.loadBalancer.credentials}} with that name."\n >\n </validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="ctrl.loadBalancer"\n field="credentials"\n accounts="ctrl.accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Region</div>\n <div class="col-md-8">\n <input readonly class="form-control input-sm" ng-model="ctrl.loadBalancer.region" />\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Stack <help-field key="aws.loadBalancer.stack"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.stack"\n name="stackName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9]*$/"\n />\n </div>\n <div class="col-md-2 sm-label-right">Detail<help-field key="gce.loadBalancer.detail"></help-field></div>\n <div class="col-md-3">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.detail"\n name="detailName"\n ng-change="ctrl.updateName()"\n ng-pattern="/^[a-z0-9-]*$/"\n />\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.stackName.$error.pattern">\n <validation-error message="Stack can only contain letters and numbers."></validation-error>\n </div>\n <div class="col-md-7 col-md-offset-3" ng-if="locationForm.detailName.$error.pattern">\n <validation-error message="Detail can only contain letters, numbers, and dashes(-)."></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-3" ng-if="locationForm.loadBalancerName.$error.maxlength">\n <validation-error message="Load Balancer name can only be 63 characters."></validation-error>\n </div>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/listener.html",'<div class="container-fluid form-horizontal">\n <ng-form name="listenerForm">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Port</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-options="port for port in ctrl.portOptions"\n ng-disabled="!ctrl.isNew"\n ng-model="ctrl.loadBalancer.portRange"\n ></select>\n </div>\n </div>\n </ng-form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonHealthCheckPage.html",'<gce-health-check-selector\n health-checks-by-account-and-type="ctrl.healthChecksByAccountAndType"\n existing-health-check-names="ctrl.existingHealthCheckNames"\n credentials="ctrl.loadBalancer.credentials"\n health-check="ctrl.loadBalancer.backendService.healthCheck"\n></gce-health-check-selector>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-offset-2 col-md-6">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="health checks"\n on-refresh="ctrl.onHealthCheckRefresh()"\n ></gce-cache-refresh>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/common/commonAdvancedSettingsPage.html",'<ng-form name="advancedSettingsForm">\n <div class="container-fluid form-horizontal">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Session Affinity</div>\n <div class="col-md-4">\n <select\n class="form-control input-sm"\n ng-disabled="ctrl.loadBalancer.ipProtocol === \'UDP\'"\n ng-model="ctrl.viewState.sessionAffinity"\n ng-change="ctrl.setSessionAffinity(ctrl.viewState)"\n ng-options="view for (model, view) in ctrl.sessionAffinityModelToViewMap"\n ></select>\n </div>\n </div>\n <div class="form-group" ng-if="ctrl.viewState.sessionAffinity === \'Generated Cookie\'">\n <div class="col-md-4 sm-label-right">\n <b>Affinity Cookie TTL Seconds</b>\n </div>\n <div class="col-md-4">\n <input\n ng-model="ctrl.loadBalancer.backendService.affinityCookieTtlSec"\n required\n type="number"\n name="cookieTtl"\n min="0"\n max="{{ctrl.maxCookieTtl}}"\n class="form-control input-sm"\n />\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/tcp/backendService.html",'<div class="container-fluid form-horizontal">\n <ng-form name="portName">\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Port Name\n <help-field key="gce.loadBalancer.portName"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="text"\n required\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.portName"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">\n Connection Draining\n <help-field key="gce.loadBalancer.connectionDraining"></help-field>\n </div>\n <div class="col-md-4">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="ctrl.loadBalancer.backendService.connectionDrainingTimeoutSec"\n />\n </div>\n </div>\n </ng-form>\n</div>\n')}]);const st={bindings:{backendService:"<"},template:'\n <dt>Name</dt>\n <dd>{{$ctrl.backendService.name}}</dd>\n <dt>Health Check</dt>\n <dd>{{$ctrl.backendService.healthCheck.name}}</dd>\n <dt ng-if="$ctrl.backendService.sessionAffinity">Session Affinity</dt>\n <dd ng-if="$ctrl.backendService.sessionAffinity">{{$ctrl.backendService.sessionAffinity | gceSessionAffinityFilter}}</dd>\n <dt ng-if="$ctrl.backendService.sessionAffinity === \'GENERATED_COOKIE\'">Affinity Cookie TTL</dt>\n <dd ng-if="$ctrl.backendService.sessionAffinity === \'GENERATED_COOKIE\'">{{$ctrl.backendService.affinityCookieTtlSec}}</dd>'},dt="spinnaker.gce.loadBalancer.details.backendServiceDetails.component";n(dt,[]).component("gceBackendServiceDetails",st);const ut="spinnaker.gce.loadBalancer.details.sessionAffinity.filter";n(ut,[]).filter("gceSessionAffinityFilter",function(){return function(e){return Gn[e]||e}});class gt{constructor(){this.verified=!1}}class pt{constructor(){this.deleteHealthChecks=!1}}class mt{constructor(e,n,t,a,i){this.application=e,this.gceHttpLoadBalancerUtils=n,this.gceHttpLoadBalancerWriter=t,this.loadBalancer=a,this.$uibModalInstance=i,this.verification=new gt,this.params=new pt}$onInit(){const e={application:this.application,title:"Deleting "+this.loadBalancer.name,modalInstance:this.$uibModalInstance};this.taskMonitor=new G(e)}isValid(){return this.verification.verified}submit(){this.taskMonitor.submit(this.getSubmitMethod())}cancel(){this.$uibModalInstance.dismiss()}hasHealthChecks(){return!!this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(this.loadBalancer)||!!this.loadBalancer.healthCheck}getSubmitMethod(){return this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(this.loadBalancer)?()=>this.gceHttpLoadBalancerWriter.deleteLoadBalancers(this.loadBalancer,this.application,this.params):()=>{const e={cloudProvider:"gce",loadBalancerName:this.loadBalancer.name,accountName:this.loadBalancer.account,credentials:this.loadBalancer.account,region:this.loadBalancer.region,loadBalancerType:this.loadBalancer.loadBalancerType||"NETWORK",deleteHealthChecks:this.params.deleteHealthChecks};return A.deleteLoadBalancer(e,this.application)}}}mt.$inject=["application","gceHttpLoadBalancerUtils","gceHttpLoadBalancerWriter","loadBalancer","$uibModalInstance"];const ht="spinnaker.gce.loadBalancer.deleteModal.controller";n(ht,[Ie,Dn,nn]).controller("gceLoadBalancerDeleteModalCtrl",mt);const ft="spinnaker.deck.gce.loadBalancer.details.healthCheck.component";n(ft,[]).component("gceHealthCheck",{bindings:{healthCheck:"="},templateUrl:"google/src/loadBalancer/details/healthCheck/healthCheck.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/healthCheck/healthCheck.component.html",'<dt>Name</dt>\n<dd>{{ $ctrl.healthCheck.name }}</dd>\n<dt ng-if="$ctrl.healthCheck.healthCheckType">Type</dt>\n<dd ng-if="$ctrl.healthCheck.healthCheckType">{{ $ctrl.healthCheck.healthCheckType }}</dd>\n<dt ng-if="$ctrl.healthCheck.target">Target</dt>\n<dd ng-if="$ctrl.healthCheck.target">{{ $ctrl.healthCheck.target }}</dd>\n<dt>Timeout</dt>\n<dd>{{ $ctrl.healthCheck.timeout || $ctrl.healthCheck.timeoutSec }} seconds</dd>\n<dt>Interval</dt>\n<dd>{{ $ctrl.healthCheck.interval || $ctrl.healthCheck.checkIntervalSec }} seconds</dd>\n<dt>Healthy Threshold</dt>\n<dd>{{ $ctrl.healthCheck.healthyThreshold }}</dd>\n<dt>Unhealthy Threshold</dt>\n<dd>{{ $ctrl.healthCheck.unhealthyThreshold }}</dd>\n')}]);const vt="spinnaker.deck.gce.loadBalancer.loadBalancerType";n(vt,[]).component("gceLoadBalancerType",{template:"<span>{{ $ctrl.type }}</span>",bindings:{loadBalancer:"="},controller:function(){this.$onInit=()=>{this.type=function(e){if("HTTP"===e.loadBalancerType){const n=oe.isString(e.certificate)&&!oe.isEmpty(e.certificate),t=oe.isString(e.certificateMap)&&!oe.isEmpty(e.certificateMap);return n||t?"HTTPS":"HTTP"}return e.loadBalancerType}(this.loadBalancer)}}});const bt="spinnaker.loadBalancer.gce.details.controller";e.module(bt,[xe,w,Ke,In,vt,nn,ft,dt,ht,ut,on]).controller("gceLoadBalancerDetailsCtrl",["$scope","$state","$uibModal","loadBalancer","app","gceHttpLoadBalancerUtils","loadBalancerReader","$q","loadBalancerTypeToWizardMap","gceXpnNamingService",function(n,t,a,i,l,c,r,o,s,d){const u=this.application=l;function g(){return n.loadBalancer=u.loadBalancers.data.filter(function(e){const n=e.vpcId||null;return e.name===i.name&&(e.region===i.region||"global"===e.region)&&e.account===i.accountId&&n===i.vpcId})[0],n.loadBalancer?function(){if(c.isHttpLoadBalancer(n.loadBalancer)){const e=n.loadBalancer.listeners.map(e=>r.getLoadBalancerDetails(n.loadBalancer.provider,i.accountId,n.loadBalancer.region,e.name));return o.all(e).then(e=>{const n=(e=oe.flatten(e))[0];return n.dns=e.map(e=>{let n;return n="443"===e.listenerDescriptions[0].listener.loadBalancerPort?"https:":"http:",{dnsname:e.dnsname,protocol:n}}),n.dns=oe.uniqBy(n.dns,"dnsname"),n.listenerDescriptions=oe.flatten(e.map(e=>e.listenerDescriptions)),[n]})}return r.getLoadBalancerDetails(n.loadBalancer.provider,i.accountId,n.loadBalancer.region,n.loadBalancer.name).then(e=>{const n=e[0];let t;return t="443"===n.listenerDescriptions[0].listener.loadBalancerPort?"https:":"http:",n.dns={dnsname:n.dnsname,protocol:t},e})}().then(function(e){n.state.loading=!1;const t=e.filter(function(e){return e.vpcid===i.vpcId||!e.vpcid&&!i.vpcId});t.length&&(n.loadBalancer.elb=t[0],n.loadBalancer.account=i.accountId,y.getCredentialsKeyedByAccount("gce").then(function(){c.isHttpLoadBalancer(n.loadBalancer)&&(n.loadBalancer.elb.backendServices=function(e){let n=[e.defaultService];e.hostRules.length&&(n=oe.chain(e.hostRules).reduce((e,n)=>(e.push(n.pathMatcher.defaultService),e.concat(oe.map(n.pathMatcher.pathRules,"backendService"))),n).uniqBy("name").value());return n}(n.loadBalancer),n.loadBalancer.elb.healthChecks=oe.chain(n.loadBalancer.elb.backendServices).map("healthCheck").uniqBy("name").value())})),y.getAccountDetails(i.accountId).then(function(e){let t;t="INTERNAL"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_backend_service"]:"NETWORK"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_target_pool","gce_health_check"]:"SSL"===n.loadBalancer.loadBalancerType||"TCP"===n.loadBalancer.loadBalancerType?["gce_forwarding_rule","gce_backend_service"]:(n.loadBalancer.loadBalancerType,["http_load_balancer","gce_target_http_proxy","gce_url_map","gce_backend_service"]),t=oe.join(t," OR "),n.loadBalancer.project=e.project,n.loadBalancer.logsLink="https://console.developers.google.com/project/"+e.project+"/logs?advancedFilter=resource.type=("+t+')%0A"'+n.loadBalancer.name+'"'})},p):(n.loadBalancer||p(),o.when(null))}function p(){n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})}n.state={loading:!0},l.loadBalancers.ready().then(g).then(()=>{n.$$destroyed||l.loadBalancers.onRefresh(n,g)}),this.editLoadBalancer=function(){const t=s[n.loadBalancer.loadBalancerType];a.open({templateUrl:t.editTemplateUrl,controller:`${t.controller} as ctrl`,size:"lg",resolve:{application:function(){return u},loadBalancer:function(){return e.copy(n.loadBalancer)},isNew:function(){return!1}}})},this.deleteLoadBalancer=function(){n.loadBalancer.instances&&n.loadBalancer.instances.length||a.open({controller:"gceLoadBalancerDeleteModalCtrl as ctrl",templateUrl:"google/src/loadBalancer/details/deleteModal/deleteModal.html",resolve:{application:()=>u,loadBalancer:()=>n.loadBalancer}})},this.isHttpLoadBalancer=e=>c.isHttpLoadBalancer(e),this.getNetworkId=function(e){return d.decorateXpnResourceIfNecessary(e.project,e.network)},this.getSubnetId=function(e){return d.decorateXpnResourceIfNecessary(e.project,e.subnet)}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/deleteModal/deleteModal.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Really delete {{ctrl.loadBalancer.name}}?</h4>\n </div>\n <div class="modal-body confirmation-modal" ng-if="ctrl.hasHealthChecks()">\n <div class="row">\n <div class="col-md-offset-2 col-md-8" style="padding-left: 2px">\n <div class="checkbox">\n <label> <input ng-model="ctrl.params.deleteHealthChecks" type="checkbox" />Delete health checks? </label>\n <help-field\n expand="true"\n content="If the health checks associated with this load balancer\n are being used by other resources, they will not be deleted,\n whether this option is selected or not."\n ></help-field>\n </div>\n </div>\n </div>\n </div>\n <gce-footer\n action="ctrl.submit()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="ctrl.loadBalancer.account"\n verification="ctrl.verification"\n ></gce-footer>\n </form>\n</div>\n')}]);const yt=class e{constructor(n){this.gceHttpLoadBalancerUtils=n,this.normalizeLoadBalancerSet=n=>{const[t,a]=be(n,e=>this.gceHttpLoadBalancerUtils.isHttpLoadBalancer(e)),i=ye(t,"urlMapName");return me(i,e.normalizeHttpLoadBalancerGroup).concat(a)}}static normalizeHttpLoadBalancerGroup(n){const t=ke(n[0]);return t.listeners=n.map(n=>({port:n.portRange?e.parsePortRange(n.portRange):null,name:n.name,certificate:n.certificate,certificateMap:n.certificateMap,ipAddress:n.ipAddress,subnet:n.subnet})),t.name=t.urlMapName,delete t.subnet,t}static parsePortRange(e){return e.split("-")[0]}};yt.$inject=["gceHttpLoadBalancerUtils"];let kt=yt;const St="spinnaker.gce.loadBalancer.setTransformer.service";n(St,[nn]).service("gceLoadBalancerSetTransformer",kt);const wt="spinnaker.gce.pipeline.stage.bake.executionDetails.controller";n(wt,[xe]).controller("gceBakeExecutionDetailsCtrl",["$scope","$stateParams","executionDetailsSectionService","$interpolate",function(e,n,t,i){e.configSections=["bakeConfig","taskStatus","artifactStatus"];const l=()=>{e.detailsSection=n.details,e.provider=e.stage.context.cloudProviderType||"gce",e.roscoMode=a.feature.roscoMode||"function"==typeof a.feature.roscoSelector&&a.feature.roscoSelector(e.stage.context),e.bakeryDetailUrl=i(e.roscoMode&&a.roscoDetailUrl?a.roscoDetailUrl:a.bakeryDetailUrl)},c=()=>t.synchronizeSection(e.configSections,l);c(),e.$on("$stateChangeSuccess",c)}]);const Ct="spinnaker.gce.pipeline.stage..bakeStage";n(Ct,[wt]).config(function(){$.pipeline.registerStage({provides:"bake",cloudProvider:"gce",label:"Bake",description:"Bakes an image",templateUrl:"google/src/pipeline/stages/bake/bakeStage.html",executionDetailsUrl:"google/src/pipeline/stages/bake/bakeExecutionDetails.html",executionLabelComponent:x,extraLabelLines:e=>e.masterStage.context.allPreviouslyBaked||e.masterStage.context.somePreviouslyBaked?1:0,producesArtifacts:!0,supportsCustomTimeout:!0,validators:[{type:"anyFieldRequired",fields:[{fieldName:"package",fieldLabel:"Package"},{fieldName:"packageArtifactIds",fieldLabel:"Package Artifacts"}]}],restartable:!0,artifactExtractor:M.accumulateArtifacts(["packageArtifactIds"]),artifactRemover:I.removeArtifactFromFields(["packageArtifactIds"])})}).controller("gceBakeStageCtrl",["$scope","$q","$uibModal",function(e,n,t){e.stage.extendedAttributes=e.stage.extendedAttributes||{},e.stage.region="global",e.stage.cloudProvider||(e.stage.cloudProvider="gce"),e.stage.user||(e.stage.user=z.getAuthenticatedUser().name),e.viewState={loading:!0},this.addExtendedAttribute=function(){e.stage.extendedAttributes||(e.stage.extendedAttributes={}),t.open({templateUrl:N.addExtendedAttributes,controller:"bakeStageAddExtendedAttributeController",controllerAs:"addExtendedAttribute",resolve:{extendedAttribute:function(){return{key:"",value:""}}}}).result.then(function(n){e.stage.extendedAttributes[n.key]=n.value}).catch(()=>{})},this.removeExtendedAttribute=function(n){delete e.stage.extendedAttributes[n]},this.showTemplateFileName=function(){return e.viewState.roscoMode||e.stage.templateFileName},this.showAccountName=function(){return e.viewState.roscoMode||e.stage.accountName},this.showExtendedAttributes=function(){return e.viewState.roscoMode||e.stage.extendedAttributes&&oe.size(e.stage.extendedAttributes)>0},this.showVarFileName=function(){return e.viewState.roscoMode||e.stage.varFileName},e.$watch("stage",function(){oe.forOwn(e.stage,function(n,t){""===n&&delete e.stage[t]}),"function"==typeof a.feature.roscoSelector&&(e.viewState.roscoMode=a.feature.roscoSelector(e.stage))},!0),e.viewState.providerSelected=!0,n.all([D.getBaseOsOptions("gce"),D.getBaseLabelOptions(),M.getExpectedArtifactsAvailableToStage(e.stage,e.pipeline)]).then(function([n,t,i]){e.baseOsOptions=n.baseImages,e.baseLabelOptions=t,e.viewState.expectedArtifacts=i,!e.stage.baseOs&&e.baseOsOptions&&e.baseOsOptions.length&&(e.stage.baseOs=e.baseOsOptions[0].id),!e.stage.baseLabel&&e.baseLabelOptions&&e.baseLabelOptions.length&&(e.stage.baseLabel=e.baseLabelOptions[0]),e.viewState.roscoMode=a.feature.roscoMode||"function"==typeof a.feature.roscoSelector&&a.feature.roscoSelector(e.stage),e.showAdvancedOptions=function(){const n=e.stage;return!!(n.templateFileName||n.extendedAttributes&&oe.size(n.extendedAttributes)>0||n.varFileName||n.baseAmi||n.accountName)}(),e.viewState.loading=!1})}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/bake/bakeStage.html",'<div ng-controller="gceBakeStageCtrl as bakeStageCtrl">\n <stage-config-field label="Package" help-key="pipeline.config.bake.package">\n <input type="text" class="form-control input-sm" ng-model="stage.package" />\n </stage-config-field>\n <expected-artifact-multi-selector\n command="stage"\n ids-field="packageArtifactIds"\n artifact-label="Package Artifacts"\n expected-artifacts="viewState.expectedArtifacts"\n help-field-key="pipeline.config.bake.packageArtifacts"\n show-icons="true"\n >\n </expected-artifact-multi-selector>\n\n <hr />\n\n <stage-config-field label="Base OS">\n <bake-stage-choose-os model="stage.baseOs" base-os-options="baseOsOptions"></bake-stage-choose-os>\n </stage-config-field>\n\n <stage-config-field label="Base Label">\n <label class="radio-inline" ng-repeat="baseLabel in baseLabelOptions">\n <input type="radio" ng-model="stage.baseLabel" ng-value="baseLabel" />\n {{baseLabel}}\n </label>\n </stage-config-field>\n\n <hr />\n\n \x3c!-- Even if the roscoMode flag is false, we should show the control if rebake is set. --\x3e\n <stage-config-field label="Rebake" ng-if="viewState.roscoMode || stage.rebake">\n <div class="checkbox" style="margin-bottom: 0">\n <label>\n <input type="checkbox" ng-model="stage.rebake" />\n Rebake image without regard to the status of any existing bake\n </label>\n </div>\n </stage-config-field>\n <div class="form-group">\n <div class="col-md-9 col-md-offset-1">\n <div class="checkbox">\n <label>\n <input type="checkbox" ng-model="showAdvancedOptions" />\n <strong>Show Advanced Options</strong>\n </label>\n </div>\n </div>\n </div>\n <div ng-class="{collapse: showAdvancedOptions !== true, \'collapse.in\': showAdvancedOptions === true}">\n <stage-config-field\n label="Template File Name"\n help-key="pipeline.config.bake.templateFileName"\n ng-if="bakeStageCtrl.showTemplateFileName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.templateFileName" />\n </stage-config-field>\n <stage-config-field\n label="Account Name"\n help-key="pipeline.config.gce.bake.accountName"\n ng-if="bakeStageCtrl.showAccountName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.accountName" />\n </stage-config-field>\n <stage-config-field\n label="Extended Attributes"\n help-key="pipeline.config.bake.extendedAttributes"\n ng-if="bakeStageCtrl.showExtendedAttributes()"\n >\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th style="width: 40%">Key</th>\n <th style="width: 60%">Value</th>\n <th class="text-right">Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="(key,value) in stage.extendedAttributes">\n <td>\n <strong class="small">{{key}}</strong>\n </td>\n <td>\n <input\n type="text"\n ng-model="stage.extendedAttributes[key]"\n value="{{value}}"\n class="form-control input-sm"\n />\n </td>\n <td class="text-right">\n <a class="small" href ng-click="bakeStageCtrl.removeExtendedAttribute(key)">Remove</a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="7">\n <button class="btn btn-block btn-sm add-new" ng-click="bakeStageCtrl.addExtendedAttribute()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Extended Attribute\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </stage-config-field>\n <stage-config-field\n label="Var File Name"\n help-key="pipeline.config.bake.varFileName"\n ng-if="bakeStageCtrl.showVarFileName()"\n >\n <input type="text" class="form-control input-sm" ng-model="stage.varFileName" />\n </stage-config-field>\n <stage-config-field label="Base Image" help-key="pipeline.config.gce.bake.baseImage">\n <input type="text" class="form-control input-sm" ng-model="stage.baseAmi" />\n </stage-config-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/bake/bakeExecutionDetails.html",'<div ng-controller="gceBakeExecutionDetailsCtrl">\n <execution-details-section-nav sections="configSections"></execution-details-section-nav>\n <div class="step-section-details" ng-if="detailsSection === \'bakeConfig\'">\n <div class="row">\n <div class="col-md-6">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Image</dt>\n <dd>{{stage.context.ami}}</dd>\n <dt>Region</dt>\n <dd>{{stage.context.region}}</dd>\n <dt>Package</dt>\n <dd>{{stage.context.package}}</dd>\n </dl>\n </div>\n <div class="col-md-6">\n <dl class="dl-narrow dl-horizontal">\n <dt>Base OS</dt>\n <dd>{{stage.context.baseOs}}</dd>\n <dt>Label</dt>\n <dd>{{stage.context.baseLabel}}</dd>\n <dt ng-if="roscoMode || execution.trigger.rebake || stage.context.rebake">Rebake</dt>\n <dd ng-if="roscoMode || execution.trigger.rebake || stage.context.rebake">\n {{execution.trigger.rebake || stage.context.rebake || false}}\n </dd>\n <dt ng-if="stage.context.templateFileName">Template</dt>\n <dd ng-if="stage.context.templateFileName">{{stage.context.templateFileName}}</dd>\n <dt ng-if="stage.context.accountName">Account Name</dt>\n <dd ng-if="stage.context.accountName">{{stage.context.accountName}}</dd>\n <dt ng-if="stage.context.varFileName">Var File</dt>\n <dd ng-if="stage.context.varFileName">{{stage.context.varFileName}}</dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n\n <div class="row" ng-if="stage.context.region && stage.context.status.resourceId">\n <div class="col-md-12">\n <div class="alert alert-{{stage.isFailed ? \'danger\' : \'info\'}}">\n <div ng-if="stage.context.previouslyBaked">No changes detected; reused existing bake</div>\n <div ng-if="stage.context.imageName">\n <strong>Image:</strong>\n <div select-on-dbl-click>{{stage.context.imageName}}</div>\n </div>\n <a target="_blank" href="{{ bakeryDetailUrl(stage) }}"> View Bakery Details </a>\n </div>\n </div>\n </div>\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\n <div class="step-section-details" ng-if="detailsSection === \'artifactStatus\'">\n <execution-artifact-tab stage="stage" execution="execution" config="config"></execution-artifact-tab>\n </div>\n</div>\n')}]);const Bt="spinnaker.gce.pipeline.stage..cloneServerGroupStage";n(Bt,[]).config(function(){$.pipeline.registerStage({provides:"cloneServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html",executionStepLabelUrl:"google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html",validators:[{type:"requiredField",fieldName:"targetCluster",fieldLabel:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"region"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceCloneServerGroupStageCtrl",["$scope",function(e){const n=e.stage;e.viewState={accountsLoaded:!1},y.listAccounts("gce").then(n=>{e.accounts=n,e.viewState.accountsLoaded=!0}),this.cloneTargets=E.TARGET_LIST,n.target=n.target||this.cloneTargets[0].val,n.application=e.application.name,n.cloudProvider="gce",n.cloudProviderType="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),this.targetClusterUpdated=()=>{if(n.targetCluster){const e=S.parseServerGroupName(n.targetCluster);n.stack=e.stack,n.freeFormDetails=e.freeFormDetails}else n.stack="",n.freeFormDetails=""},e.$watch("stage.targetCluster",this.targetClusterUpdated),this.removeCapacity=()=>{delete n.capacity},oe.has(n,"useSourceCapacity")||(n.useSourceCapacity=!0),this.toggleDisableTraffic=()=>{n.disableTraffic=!n.disableTraffic}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStage.html",'<div ng-controller="gceCloneServerGroupStageCtrl as cloneServerGroupStageCtrl">\n <div ng-if="!pipeline.strategy">\n <div ng-if="viewState.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!viewState.loading">\n <account-region-cluster-selector\n application="application"\n component="stage"\n accounts="accounts"\n single-region="true"\n cluster-field="targetCluster"\n >\n </account-region-cluster-selector>\n </div>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="cloneServerGroupStageCtrl.cloneTargets"></target-select>\n </stage-config-field>\n <div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Capacity</div>\n <div class="col-md-9 radio">\n <label>\n <input\n type="radio"\n ng-model="stage.useSourceCapacity"\n ng-value="true"\n ng-click="cloneServerGroupStageCtrl.removeCapacity()"\n id="useSourceCapacityTrue"\n />\n Copy the capacity from the current server group\n <help-field key="serverGroupCapacity.useSourceCapacityTrue"></help-field>\n </label>\n </div>\n <div class="col-md-9 col-md-offset-3 radio">\n <label>\n <input type="radio" ng-model="stage.useSourceCapacity" ng-value="false" id="useSourceCapacityFalse" />\n Let me specify the capacity\n <help-field key="serverGroupCapacity.useSourceCapacityFalse"></help-field>\n </label>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3">Min</div>\n <div class="col-md-2">Max</div>\n <div class="col-md-2">Desired</div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.min"\n min="0"\n max="{{stage.capacity.max}}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.max"\n min="{{stage.capacity.min}}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n ng-disabled="stage.useSourceCapacity"\n class="form-control input-sm"\n ng-model="stage.capacity.desired"\n min="{{stage.capacity.min}}"\n max="{{stage.capacity.max}}"\n required\n />\n </div>\n </div>\n </div>\n <stage-config-field label="Traffic" help-key="gce.serverGroup.traffic">\n <div class="checkbox">\n <label>\n <input\n type="checkbox"\n ng-click="cloneServerGroupStageCtrl.toggleDisableTraffic()"\n ng-checked="!stage.disableTraffic"\n />\n Send client requests to new instances\n </label>\n </div>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n <deployment-strategy-selector field-columns="6" command="stage"></deployment-strategy-selector>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/cloneServerGroup/cloneServerGroupStepLabel.html",'<span class="task-label"> Clone Server Group: {{step.context.source.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Tt="spinnaker.gce.pipeline.stage..destroyAsgStage";n(Tt,[]).config(function(){$.pipeline.registerStage({provides:"destroyServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/destroyAsg/destroyAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],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:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceDestroyAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/destroyAsg/destroyAsgStage.html",'<div ng-controller="gceDestroyAsgStageCtrl as destroyAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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("google/src/pipeline/stages/destroyAsg/destroyAsgStepLabel.html",'<span class="task-label"> Destroy Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Gt="spinnaker.gce.pipeline.stage..disableAsgStage";n(Gt,[]).config(function(){$.pipeline.registerStage({provides:"disableServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/disableAsg/disableAsgStage.html",executionStepLabelUrl:"google/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:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceDisableAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableAsg/disableAsgStage.html",'<div ng-controller="gceDisableAsgStageCtrl as disableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableAsg/disableAsgStepLabel.html",'<span class="task-label"> Disable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const At="spinnaker.gce.pipeline.stage..disableClusterStage";n(At,[]).config(function(){$.pipeline.registerStage({provides:"disableCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/disableCluster/disableClusterStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"remainingEnabledServerGroups",fieldLabel:"Keep [X] enabled Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceDisableClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.remainingEnabledServerGroups&&(n.remainingEnabledServerGroups=1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.preferLargerOverNewer&&(n.preferLargerOverNewer="false"),n.preferLargerOverNewer=n.preferLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/disableCluster/disableClusterStage.html",'<div ng-controller="gceDisableClusterStageCtrl as disableClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Disable Options">\n <div class="form-inline">\n Keep the\n <input\n type="number"\n min="0"\n required\n ng-model="stage.remainingEnabledServerGroups"\n class="form-control input-sm"\n style="width: 50px"\n />\n <select class="form-control input-sm" ng-model="stage.preferLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n {{disableClusterStageCtrl.pluralize(\'server group\', stage.remainingEnabledServerGroups)}} enabled.\n </div>\n </stage-config-field>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]);const Pt="spinnaker.gce.pipeline.stage..enableAsgStage";n(Pt,[]).config(function(){$.pipeline.registerStage({provides:"enableServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/enableAsg/enableAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceEnableAsgStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),e.targets=E.TARGET_LIST,n.regions=n.regions||[],n.cloudProvider="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),n.target||(n.target=e.targets[0].val),e.$watch("stage.credentials",e.accountUpdated)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/enableAsg/enableAsgStage.html",'<div ng-controller="gceEnableAsgStageCtrl as enableAsgStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/enableAsg/enableAsgStepLabel.html",'<span class="task-label"> Enable Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const $t="spinnaker.gce.pipeline.stage..findAmiStage";e.module($t,[]).config(function(){$.pipeline.registerStage({provides:"findImage",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/findAmi/findAmiStage.html",validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"selectionStrategy",fieldLabel:"Server Group Selection"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials"}]})}).controller("gceFindAmiStageCtrl",["$scope",function(n){const t=n.stage;n.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(e){n.accounts=e,n.state.accounts=!0}),n.selectionStrategies=[{label:"Largest",val:"LARGEST",description:"When multiple server groups exist, prefer the server group with the most instances"},{label:"Newest",val:"NEWEST",description:"When multiple server groups exist, prefer the newest"},{label:"Oldest",val:"OLDEST",description:"When multiple server groups exist, prefer the oldest"},{label:"Fail",val:"FAIL",description:"When multiple server groups exist, fail"}],t.regions=t.regions||[],t.cloudProvider="gce",t.selectionStrategy=t.selectionStrategy||n.selectionStrategies[0].val,e.isUndefined(t.onlyEnabled)&&(t.onlyEnabled=!0),!t.credentials&&n.application.defaultCredentials.gce&&(t.credentials=n.application.defaultCredentials.gce),!t.regions.length&&n.application.defaultRegions.gce&&t.regions.push(n.application.defaultRegions.gce),n.$watch("stage.credentials",n.accountUpdated)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findAmi/findAmiStage.html",'<div ng-controller="gceFindAmiStageCtrl as findAmiCtrl" class="form-horizontal">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n\n <stage-config-field label="Server Group Selection">\n <ui-select ng-model="stage.selectionStrategy" class="form-control input-sm">\n <ui-select-match placeholder="None">{{$select.selected.label}}</ui-select-match>\n <ui-select-choices repeat="strategy.val as strategy in selectionStrategies | filter: $select.search">\n <strong ng-bind-html="strategy.label | highlight: $select.search"></strong>\n <div ng-bind-html="strategy.description"></div>\n </ui-select-choices>\n </ui-select>\n </stage-config-field>\n <stage-config-field label="Server Group Filters">\n <label class="checkbox-inline">\n <input type="checkbox" ng-model="stage.onlyEnabled" />\n Only consider enabled Server Groups\n </label>\n </stage-config-field>\n</div>\n')}]);const xt="spinnaker.gce.pipeline.stage..findImageFromTagsStage";n(xt,[]).config(function(){$.pipeline.registerStage({provides:"findImageFromTags",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html",executionDetailsUrl:"google/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.html",executionConfigSections:["findImageConfig","taskStatus"],validators:[{type:"requiredField",fieldName:"packageName"},{type:"requiredField",fieldName:"tags"}]})}).controller("gceFindImageFromTagsStageCtrl",["$scope",function(e){e.stage.tags=e.stage.tags||{},e.stage.regions=e.stage.regions||[],e.stage.cloudProvider=e.stage.cloudProvider||"gce",D.getRegions("gce").then(function(n){e.regions=n})}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findImageFromTags/findImageFromTagsStage.html",'<div ng-controller="gceFindImageFromTagsStageCtrl as findImageFromTagsCtrl" class="form-horizontal">\n <stage-config-field label="Package">\n <input type="text" class="form-control input-sm" ng-model="stage.packageName" />\n </stage-config-field>\n <stage-config-field label="Tags">\n <map-editor model="stage.tags" allow-empty="true"></map-editor>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/findImageFromTags/findImageFromTagsExecutionDetails.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 === \'findImageConfig\'">\n <div class="row">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Package</dt>\n <dd>{{stage.context.packageName}}</dd>\n <dt>Tags</dt>\n <dd>\n <span ng-repeat="(key, val) in stage.context.tags"> {{key}}:{{val}}{{$last ? \'\' : \', \'}} </span>\n </dd>\n </dl>\n </div>\n </div>\n <stage-failure-message stage="stage" message="stage.failureMessage"></stage-failure-message>\n\n <div class="row" ng-if="stage.context.amiDetails">\n <div class="col-md-12">\n <div class="well alert alert-info">\n <h4>Results</h4>\n <dl ng-repeat="image in stage.context.amiDetails" class="dl-narrow dl-horizontal">\n <dt>Region</dt>\n <dd>{{image.region}}</dd>\n <dt>Image</dt>\n <dd>{{image.imageName}}</dd>\n </dl>\n </div>\n </div>\n </div>\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')}]);const It="spinnaker.gce.pipeline.stage..resizeAsgStage";n(It,[]).config(function(){$.pipeline.registerStage({provides:"resizeServerGroup",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/resizeAsg/resizeAsgStage.html",executionStepLabelUrl:"google/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],supportsCustomTimeout:!0,validators:[{type:"targetImpedance",message:"This pipeline will attempt to resize a server group without deploying a new version into the same cluster."},{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"target"},{type:"requiredField",fieldName:"action"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceResizeAsgStageCtrl",["$scope",function(e){const n=e.stage;e.viewState={accountsLoaded:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.viewState.accountsLoaded=!0}),e.resizeTargets=E.TARGET_LIST,e.scaleActions=[{label:"Scale Up",val:"scale_up"},{label:"Scale Down",val:"scale_down"},{label:"Scale to Cluster Size",val:"scale_to_cluster"},{label:"Scale to Exact Size",val:"scale_exact"}],e.resizeTypes=[{label:"Percentage",val:"pct"},{label:"Incremental",val:"incr"}],n.capacity=n.capacity||{},n.regions=n.regions||[],n.target=n.target||e.resizeTargets[0].val,n.action=n.action||e.scaleActions[0].val,n.resizeType=n.resizeType||e.resizeTypes[0].val,n.action||"exact"!==n.resizeType||(n.action="scale_exact"),n.cloudProvider="gce",n.cloudProviderType="gce",n.isNew&&e.application.attributes.platformHealthOnlyShowOverride&&e.application.attributes.platformHealthOnly&&(n.interestingHealthProviderNames=["Google"]),!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),this.updateResizeType=function(){"scale_exact"===n.action?(n.resizeType="exact",delete n.scalePct,delete n.scaleNum):(n.capacity={},"pct"===n.resizeType?delete n.scaleNum:(n.resizeType="incr",delete n.scalePct,n.scaleNum=n.scaleNum||0))}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/resizeAsg/resizeAsgStage.html",'<div ng-controller="gceResizeAsgStageCtrl as resizeAsgStageCtrl">\n <div ng-if="!pipeline.strategy">\n <div ng-if="viewState.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!viewState.loading">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n </div>\n <stage-config-field label="Target">\n <target-select model="stage" options="resizeTargets"></target-select>\n </stage-config-field>\n <stage-config-field label="Action" help-key="pipeline.config.resizeAsg.action">\n <select\n class="form-control input-sm"\n required\n ng-model="stage.action"\n ng-change="resizeAsgStageCtrl.updateResizeType()"\n ng-options="a.val as a.label for a in scaleActions"\n >\n <option>Select an action...</option>\n </select>\n </stage-config-field>\n <div ng-if="stage.action !== \'scale_exact\'">\n <stage-config-field label="{{stage.action === \'scale_to_cluster\' ? \'Additional Capacity\' : \'Type\'}}">\n <select\n class="form-control input-sm"\n required\n ng-model="stage.resizeType"\n ng-change="resizeAsgStageCtrl.updateResizeType()"\n ng-options="t.val as t.label for t in resizeTypes"\n >\n <option>Select an action...</option>\n </select>\n </stage-config-field>\n <div class="form-group" ng-if="stage.resizeType === \'pct\'">\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right" style="margin-left: 0; padding-left: 0">Resize Percentage</label>\n <div class="col-md-2">\n <input type="number" min="0" ng-model="stage.scalePct" class="form-control input-sm" />\n </div>\n </div>\n <div class="col-md-9 col-md-offset-3">\n <em class="subinput-note"\n >This is the percentage by which the target server group\'s capacity will be increased</em\n >\n </div>\n </div>\n <div class="form-group" ng-if="stage.resizeType === \'incr\'">\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right" style="margin-left: 0; padding-left: 0">Resize-by Amount</label>\n <div class="col-md-2">\n <input type="number" min="0" ng-model="stage.scaleNum" class="form-control input-sm" />\n </div>\n </div>\n\n <div class="col-md-9 col-md-offset-3">\n <em class="subinput-note"\n >This is the exact amount by which the target server group\'s capacity will be increased</em\n >\n </div>\n </div>\n </div>\n <div class="form-group" ng-if="stage.action === \'scale_exact\'">\n <div class="col-md-9 col-md-offset-3 small">\n <div class="col-md-9">\n <div class="col-md-3 col-md-offset-3">Min</div>\n <div class="col-md-3">Max</div>\n <div class="col-md-3">Desired</div>\n </div>\n </div>\n <div class="col-md-9 col-md-offset-3">\n <label class="col-md-2 sm-label-right small" style="margin-left: 0; padding-left: 0">Match Capacity</label>\n <div class="col-md-9">\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.min" class="form-control input-sm" />\n </div>\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.max" class="form-control input-sm" />\n </div>\n <div class="col-md-3">\n <input type="number" ng-model="stage.capacity.desired" class="form-control input-sm" />\n </div>\n </div>\n </div>\n\n <div class="col-md-6 col-md-offset-3">\n <em class="subinput-note"\n >If the targeted server group has an Autoscaler configured, the min/max values will be used to update the\n scaling policy. If not, the desired size will be used to update the server group\'s target size.</em\n >\n </div>\n </div>\n <stage-platform-health-override application="application" stage="stage" platform-health-type="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/resizeAsg/resizeAsgStepLabel.html",'<span class="task-label"> Resize Server Group: {{step.context.serverGroupName}} ({{step.context.region}}) </span>\n')}]);const Mt="spinnaker.gce.pipeline.stage..scaleDownClusterStage";n(Mt,[]).config(function(){$.pipeline.registerStage({provides:"scaleDownCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"remainingFullSizeServerGroups",fieldLabel:"Keep [X] full size Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}],strategy:!0})}).controller("gceScaleDownClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.remainingFullSizeServerGroups&&(n.remainingFullSizeServerGroups=1),void 0===n.allowScaleDownActive&&(n.allowScaleDownActive=!1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.preferLargerOverNewer&&(n.preferLargerOverNewer="false"),n.preferLargerOverNewer=n.preferLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/scaleDownCluster/scaleDownClusterStage.html",'<div ng-controller="gceScaleDownClusterStageCtrl as scaleDownClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\n </account-region-cluster-selector>\n </div>\n <stage-config-field label="Scale Down Options">\n <div class="form-inline">\n <p>\n Keep the\n <input\n type="number"\n min="0"\n required\n ng-model="stage.remainingFullSizeServerGroups"\n class="form-control input-sm"\n style="width: 50px"\n />\n <select class="form-control input-sm" ng-model="stage.preferLargerOverNewer" style="width: 100px">\n <option value="true">largest</option>\n <option value="false">newest</option>\n </select>\n {{scaleDownClusterStageCtrl.pluralize(\'server group\', stage.remainingFullSizeServerGroups)}} at current size.\n </p>\n <p>The remaining server groups will be scaled down to zero instances.</p>\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.allowScaleDownActive" />\n Allow scale down of active server groups\n </label>\n </div>\n </div>\n</div>\n')}]);const zt="spinnaker.gce.pipeline.stage..shrinkClusterStage";n(zt,[]).config(function(){$.pipeline.registerStage({provides:"shrinkCluster",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",accountExtractor:e=>[e.context.credentials],configAccountExtractor:e=>[e.credentials],validators:[{type:"requiredField",fieldName:"cluster"},{type:"requiredField",fieldName:"shrinkToSize",fieldLabel:"shrink to [X] Server Groups"},{type:"requiredField",fieldName:"regions"},{type:"requiredField",fieldName:"credentials",fieldLabel:"account"}]})}).controller("gceShrinkClusterStageCtrl",["$scope",function(e){const n=e.stage;e.state={accounts:!1,regionsLoaded:!1},y.listAccounts("gce").then(function(n){e.accounts=n,e.state.accounts=!0}),n.regions=n.regions||[],n.cloudProvider="gce",!n.credentials&&e.application.defaultCredentials.gce&&(n.credentials=e.application.defaultCredentials.gce),!n.regions.length&&e.application.defaultRegions.gce&&n.regions.push(e.application.defaultRegions.gce),void 0===n.shrinkToSize&&(n.shrinkToSize=1),void 0===n.allowDeleteActive&&(n.allowDeleteActive=!1),this.pluralize=function(e,n){return 1===n?e:e+"s"},void 0===n.retainLargerOverNewer&&(n.retainLargerOverNewer="false"),n.retainLargerOverNewer=n.retainLargerOverNewer.toString()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/shrinkCluster/shrinkClusterStage.html",'<div ng-controller="gceShrinkClusterStageCtrl as shrinkClusterStageCtrl" class="form-horizontal">\n <div ng-if="!pipeline.strategy">\n <account-region-cluster-selector application="application" component="stage" accounts="accounts">\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="\'Google\'">\n </stage-platform-health-override>\n</div>\n')}]);const Nt="spinnaker.gce.pipeline.stage..tagImageStage";n(Nt,[]).config(function(){$.pipeline.registerStage({provides:"upsertImageTags",cloudProvider:"gce",templateUrl:"google/src/pipeline/stages/tagImage/tagImageStage.html",executionDetailsUrl:"google/src/pipeline/stages/tagImage/tagImageExecutionDetails.html",executionConfigSections:["tagImageConfig","taskStatus"]})}).controller("gceTagImageStageCtrl",["$scope",e=>{y.listAccounts("gce").then(n=>e.accounts=n),e.stage.tags=e.stage.tags||{},e.stage.cloudProvider=e.stage.cloudProvider||"gce";e.$watch("pipeline.stages",()=>{const n=R.getAllUpstreamDependencies(e.pipeline,e.stage).filter(e=>E.IMAGE_PRODUCING_STAGES.includes(e.type));e.consideredStages=new Map(n.map(e=>[e.refId,e.name]))})}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/tagImage/tagImageStage.html",'<div ng-controller="gceTagImageStageCtrl as tagImageStageCtrl" class="form-horizontal">\n <stage-config-field label="Account">\n <account-select-field component="stage" field="credentials" accounts="accounts" provider="\'gce\'" required>\n </account-select-field>\n </stage-config-field>\n <stage-config-field label="Tags">\n <map-editor model="stage.tags" allow-empty="true"></map-editor>\n </stage-config-field>\n <stage-config-field label="Stages (optional)" help-key="gce.tagImage.consideredStages" help-key-expand="true">\n <div class="checkbox">\n <checklist model="stage.consideredStages" items="consideredStages"></checklist>\n </div>\n </stage-config-field>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/pipeline/stages/tagImage/tagImageExecutionDetails.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 === \'tagImageConfig\'">\n <div class="row" ng-if="stage.context.targets">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt if-multiple-providers>Provider</dt>\n <dd if-multiple-providers>Google</dd>\n <dt>Account</dt>\n <dd>\n <account-tag account="stage.context.credentials"></account-tag>\n </dd>\n <dt>Images</dt>\n <dd ng-repeat="target in stage.context.targets">{{target.imageName}}</dd>\n </dl>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <dl class="dl-narrow dl-horizontal">\n <dt>Tags</dt>\n <dd ng-repeat="(key, val) in stage.context.tags" ng-if="val !== null">{{key}} = {{val}}</dd>\n <dt ng-if="stage.context.consideredStages">Stages</dt>\n <dd ng-repeat="consideredStage in stage.context.consideredStages">\n <stage-name stages="execution.stages" ref-id="consideredStage"></stage-name>\n </dd>\n </dl>\n </div>\n </div>\n\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')}]);const Dt="spinnaker.gce.securityGroup.create.controller";e.module(Dt,[xe]).controller("gceCreateSecurityGroupCtrl",["$scope","$uibModalInstance","$state","$controller","application","securityGroup",function(n,t,a,i,l,c){n.pages={location:"google/src/securityGroup/configure/createSecurityGroupProperties.html",targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"};const r=this;c.backingData={},c.network="default",c.sourceRanges=[],c.sourceTags=[],c.ipIngress=[],e.extend(this,i("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:l,securityGroup:c,mode:"create"})),y.listAccounts("gce").then(function(e){n.accounts=e,r.accountUpdated()}),this.getSecurityGroupRefreshTime=function(){return d.get("securityGroups").getStats().ageMax},r.upsert=function(){r.mixinUpsert("Create")},r.initializeSecurityGroups()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupProperties.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.securityGroupName.$error.validateUnique, \'alert-info\': !form.securityGroupName.$error.validateUnique}"\n >\n <strong>Your <firewall-label label="firewall"></firewall-label> will be named:</strong>\n <span ng-bind="namePreview"></span>\n <input\n type="hidden"\n class="form-control input-sm"\n ng-model="securityGroup.name"\n validate-unique="existingSecurityGroupNames"\n validate-ignore-case="true"\n name="securityGroupName"\n ng-pattern="ctrl.namePattern"\n trigger-validation="securityGroup.subnet"\n required\n />\n <validation-error\n ng-if="form.securityGroupName.$error.validateUnique && securityGroup.credentials"\n message="There is already a {{firewallLabel}} in {{securityGroup.credentials}} with that name."\n ></validation-error>\n <validation-error\n ng-if="form.securityGroupName.$error.pattern"\n message="Name can only contain letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Detail (optional)</div>\n <div class="col-md-4">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="securityGroup.detail"\n ng-change="ctrl.updateName()"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Description</div>\n <div class="col-md-8">\n <input type="text" required class="form-control input-sm" ng-model="securityGroup.description" />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="securityGroup"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-network-select-field\n label-columns="4"\n field-columns="8"\n component="securityGroup"\n field="network"\n account="securityGroup.credentials"\n networks="securityGroup.backingData.networks"\n on-change="ctrl.registerHelpTextService()"\n ></gce-network-select-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Et="spinnaker.google.securityGroup.edit.controller";e.module(Et,[xe]).controller("gceEditSecurityGroupCtrl",["$scope","$uibModalInstance","$state","application","securityGroup","$controller",function(n,t,a,i,l,c){n.pages={targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"},n.securityGroup=l,n.state={refreshingSecurityGroups:!1},e.extend(this,c("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:i,securityGroup:l,mode:"edit"})),n.isNew=!1,n.taskMonitor=new G({application:i,title:`Updating your ${m.get("firewall")}`,modalInstance:t,onTaskComplete:()=>i.securityGroups.refresh()}),l.sourceRanges=_.map(l.sourceRanges,function(e){return{value:e}}),l.ipIngress=_.chain(l.ipIngressRules).map(function(e){return e.portRanges&&e.portRanges.length>0?e.portRanges.map(function(n){return{type:e.protocol,startPort:n.startPort,endPort:n.endPort}}):[{type:e.protocol}]}).flatten().value(),l.sourceTags=l.sourceTags||[],this.getSecurityGroupRefreshTime=function(){return d.get("securityGroups").getStats().ageMax},this.addSourceCIDR=function(e){e.push({value:"0.0.0.0/0"})},this.removeSourceCIDR=function(e,n){e.splice(n,1)},this.addRule=function(e){e.push({type:"tcp",startPort:7001,endPort:7001})},this.removeRule=function(e,n){e.splice(n,1)},this.upsert=function(){n.taskMonitor.submit(function(){const e=_.map(n.securityGroup.ipIngress,function(e){const n={ipProtocol:e.type};return e.startPort&&e.endPort&&(n.portRanges=[e.startPort+"-"+e.endPort]),n});return L.upsertSecurityGroup(n.securityGroup,i,"Update",{cloudProvider:"gce",sourceRanges:_.uniq(_.map(n.securityGroup.sourceRanges,"value")),allowed:e,targetTags:n.securityGroup.targetTags||[],region:"global"})})},this.cancel=function(){t.dismiss()}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Rt="spinnaker.gce.securityGroupHelpText.service";n(Rt,[]).service("gceSecurityGroupHelpTextService",class{register(e,n,t){return this.reset(),this.application=e,this.account=n,this.network=t,this.indexServerGroupsByTag()}getHelpTextForTag(e,n){const t=this.getServerGroupsWithTag(e);let a;switch(t.length){case 0:a=null;break;case 1:a=`This ${n} tag associates this ${m.get("firewall")} with the server group <em>${t[0]}</em>.`;break;default:a=`This ${n} tag associates this ${m.get("firewall")} with the server groups\n ${t.map(e=>`<em>${e}</em>`).join(", ")}.`}return a}getServerGroupsWithTag(e){return this.serverGroupsIndexedByTag.get(e)?Array.from(this.serverGroupsIndexedByTag.get(e)).sort():[]}reset(){this.application=null,this.account=null,this.serverGroupsIndexedByTag=new Map}indexServerGroupsByTag(){return this.application.ready().then(()=>{this.application.getDataSource("serverGroups").data.forEach(e=>{var n;Se(e,"providerMetadata.tags.length")&&e.account===this.account&&(null==(n=e.providerMetadata)?void 0:n.networkName)===this.network&&e.providerMetadata.tags.forEach(n=>{this.serverGroupsIndexedByTag.get(n)?this.serverGroupsIndexedByTag.get(n).add(e.name):this.serverGroupsIndexedByTag.set(n,new Set([e.name]))})})})}});Le(".gce-security-group-wizard .sm-label-left {\n border-bottom: 2px solid var(--color-seashell);\n}\n.gce-security-group-wizard .table {\n border-top: 2px solid var(--color-white);\n}\n.gce-security-group-wizard .table .fixed-width {\n width: 100%;\n}\n.gce-security-group-wizard .table .table-label {\n padding: 0.8rem 0 0 1rem;\n}\n.gce-security-group-wizard .table .table-remove-button {\n padding: 0 0 1rem 1rem;\n margin-right: 65%;\n}\n");const Lt="spinnaker.google.securityGroup.baseConfig.controller";n(Lt,[xe,H,Rt]).controller("gceConfigSecurityGroupMixin",["$scope","$state","$uibModalInstance","application","securityGroup","securityGroupReader","cacheInitializer","gceSecurityGroupHelpTextService","mode",function(e,n,t,a,i,l,c,r,o){const s=this;function d(){if(e.$$destroyed)return;t.close();const a={name:e.securityGroup.name,accountId:e.securityGroup.credentials||e.securityGroup.accountName,region:"global",vpcId:e.securityGroup.vpcId,provider:"gce"};n.includes("**.firewallDetails")?n.go("^.firewallDetails",a):n.go(".firewallDetails",a)}e.isNew=!0,e.state={submitting:!1,refreshingSecurityGroups:!1,removedRules:[],infiniteScroll:{numToAdd:20,currentItems:20},mode:o,target:null,targetOptions:null},e.wizard=P,s.getTagHelpText=function(e,n){return r.getHelpTextForTag(e,n)},s.addMoreItems=function(){e.state.infiniteScroll.currentItems+=e.state.infiniteScroll.numToAdd},s.registerHelpTextService=function(){r.register(a,e.securityGroup.credentials||e.securityGroup.accountName,i.network)},s.initializeTargetOptions=function(){const n=["allowAllTraffic","specifyTags"];"edit"===e.state.mode?e.state.targetOptions=n:e.state.targetOptions=["autoGenerate"].concat(n)},s.initializeTarget=function(){"create"===e.state.mode?e.state.target="autoGenerate":e.securityGroup.targetTags&&e.securityGroup.targetTags.length>0?e.state.target="specifyTags":e.state.target="allowAllTraffic"},s.getTargetLabel=function(e){switch(e){case"autoGenerate":return"Auto-generate target tag";case"allowAllTraffic":return"Allow traffic to all server groups";case"specifyTags":return"Specify target tags";default:return null}},s.onTargetChange=function(){switch(e.state.target){case"autoGenerate":e.securityGroup.targetTags=null;break;case"allowAllTraffic":e.securityGroup.targetTags=[];break;case"specifyTags":e.securityGroup.targetTags=e.securityGroup.targetTags||[]}},e.taskMonitor=new G({application:a,title:`Creating your ${m.get("firewall")}`,modalInstance:t,onTaskComplete:function(){a.securityGroups.refresh(),a.securityGroups.onNextRefresh(e,d)}}),e.securityGroup=i,s.initializeTargetOptions(),s.initializeTarget(),s.onTargetChange(),s.registerHelpTextService(),s.upsert=function(){e.taskMonitor.submit(function(){return L.upsertSecurityGroup(e.securityGroup,a,"Create")})},s.mixinUpsert=function(n){e.taskMonitor.submit(function(){const t=oe.map(e.securityGroup.ipIngress,function(e){const n={ipProtocol:e.type};return e.startPort&&e.endPort&&(n.portRanges=[e.startPort+"-"+e.endPort]),n});return L.upsertSecurityGroup(e.securityGroup,a,n,{cloudProvider:"gce",sourceRanges:oe.uniq(oe.map(e.securityGroup.sourceRanges,"value")),targetTags:e.securityGroup.targetTags,sourceTags:e.securityGroup.sourceTags,allowed:t,region:"global",network:e.securityGroup.network})})},s.accountUpdated=function(){s.initializeSecurityGroups(),s.updateNetworks(),s.updateName()},s.refreshSecurityGroups=function(){return e.state.refreshingSecurityGroups=!0,c.refreshCache("securityGroups").then(function(){return s.initializeSecurityGroups().then(function(){e.state.refreshingSecurityGroups=!1})})},s.initializeSecurityGroups=function(){return l.getAllSecurityGroups().then(function(n){const t=e.securityGroup.credentials||e.securityGroup.accountName;let a;a=t?n[t].gce.global:n,e.existingSecurityGroupNames=oe.map(a,"name")})},s.cancel=function(){t.dismiss()},s.updateNetworks=function(){B.listNetworksByProvider("gce").then(function(n){const t=e.securityGroup.credentials||e.securityGroup.accountName;e.securityGroup.backingData.networks=oe(n).filter(e=>e.account===t&&!e.id.includes("/")).map(e=>e.id).value()})},s.getCurrentNamePattern=function(){return/^[a-zA-Z0-9-]*$/},s.updateName=function(){const n=e.securityGroup;let t=a.name;n.detail&&(t+="-"+n.detail,t=oe.trimEnd(t,"-")),n.name=t,e.namePreview=t},s.namePattern={test:function(e){return s.getCurrentNamePattern().test(e)}},s.addSourceCIDR=function(e){e.push({value:"0.0.0.0/0"})},s.removeSourceCIDR=function(e,n){e.splice(n,1)},s.addRule=function(e){e.push({type:"tcp",startPort:7001,endPort:7001})},s.removeRule=function(e,n){e.splice(n,1)},s.dismissRemovedRules=function(){e.state.removedRules=[],P.markClean("Ingress"),P.markComplete("Ingress")},s.isValid=function(){return("specifyTags"!==e.state.target||e.securityGroup.targetTags.length>0)&&e.securityGroup.ipIngress.length>0&&(e.securityGroup.sourceTags.length>0||e.securityGroup.sourceRanges.length>0)},s.addTargetTag=function(){e.securityGroup.targetTags.push("")},s.removeTargetTag=function(n){e.securityGroup.targetTags.splice(n,1)},s.addSourceTag=function(){e.securityGroup.sourceTags.push("")},s.removeSourceTag=function(n){e.securityGroup.sourceTags.splice(n,1)}}]);const Ht="spinnaker.google.securityGroup.clone.controller";e.module(Ht,[Lt]).controller("gceCloneSecurityGroupController",["$scope","$uibModalInstance","$controller","securityGroup","application",function(n,t,a,i,l){const c=this;n.pages={location:"google/src/securityGroup/configure/createSecurityGroupProperties.html",targets:"google/src/securityGroup/configure/createSecurityGroupTargets.html",sourceFilters:"google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",ingress:"google/src/securityGroup/configure/createSecurityGroupIngress.html"},n.firewallLabel=m.get("Firewall"),e.extend(this,a("gceConfigSecurityGroupMixin",{$scope:n,$uibModalInstance:t,application:l,securityGroup:i,mode:"clone"})),y.listAccounts("gce").then(function(e){n.accounts=e,c.accountUpdated()}),i.sourceRanges=oe.map(i.sourceRanges,function(e){return{value:e}}),i.ipIngress=oe.chain(i.ipIngressRules).map(function(e){return e.portRanges&&e.portRanges.length>0?e.portRanges.map(function(n){return{type:e.protocol,startPort:n.startPort,endPort:n.endPort}}):[{type:e.protocol}]}).flatten().value(),i.backingData={},i.sourceTags=i.sourceTags||[],c.upsert=function(){c.mixinUpsert("Clone")},c.initializeSecurityGroups()}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupProperties.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div\n class="col-md-12 well"\n ng-class="{\'alert-danger\': form.securityGroupName.$error.validateUnique, \'alert-info\': !form.securityGroupName.$error.validateUnique}"\n >\n <strong>Your <firewall-label label="firewall"></firewall-label> will be named:</strong>\n <span ng-bind="namePreview"></span>\n <input\n type="hidden"\n class="form-control input-sm"\n ng-model="securityGroup.name"\n validate-unique="existingSecurityGroupNames"\n validate-ignore-case="true"\n name="securityGroupName"\n ng-pattern="ctrl.namePattern"\n trigger-validation="securityGroup.subnet"\n required\n />\n <validation-error\n ng-if="form.securityGroupName.$error.validateUnique && securityGroup.credentials"\n message="There is already a {{firewallLabel}} in {{securityGroup.credentials}} with that name."\n ></validation-error>\n <validation-error\n ng-if="form.securityGroupName.$error.pattern"\n message="Name can only contain letters, numbers, and dashes(-)."\n ></validation-error>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Detail (optional)</div>\n <div class="col-md-4">\n <input\n type="text"\n class="form-control input-sm"\n ng-model="securityGroup.detail"\n ng-change="ctrl.updateName()"\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Description</div>\n <div class="col-md-8">\n <input type="text" required class="form-control input-sm" ng-model="securityGroup.description" />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-4 sm-label-right">Account</div>\n <div class="col-md-8">\n <account-select-field\n component="securityGroup"\n field="credentials"\n accounts="accounts"\n provider="\'gce\'"\n on-change="ctrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-network-select-field\n label-columns="4"\n field-columns="8"\n component="securityGroup"\n field="network"\n account="securityGroup.credentials"\n networks="securityGroup.backingData.networks"\n on-change="ctrl.registerHelpTextService()"\n ></gce-network-select-field>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupTargets.html",'<div class="container-fluid form-horizontal">\n <div class="modal-body">\n <div class="form-group">\n <div class="col-md-2 sm-label-right">Targets</div>\n <div class="col-md-8">\n <select\n class="form-control input-sm"\n ng-model="state.target"\n ng-change="ctrl.onTargetChange()"\n ng-options="option as ctrl.getTargetLabel(option) for option in state.targetOptions"\n ></select>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12" ng-if="state.target === \'specifyTags\'">\n <div class="sm-label-left">Target Tags <help-field key="gce.securityGroup.targetTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.targetTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm fixed-width"\n type="text"\n ng-model="securityGroup.targetTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeTargetTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addTargetTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Target Tag\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupSourceFilters.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source Tags <help-field key="gce.securityGroup.sourceTags"></help-field></div>\n <table class="table table-condensed packed tags">\n <tbody>\n <tr ng-repeat="tag in securityGroup.sourceTags track by $index">\n <td style="width: 80%">\n <input\n class="form-control input-sm"\n class="fixed-width"\n type="text"\n ng-model="securityGroup.sourceTags[$index]"\n required\n />\n </td>\n <td>\n <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n <a class="btn btn-link sm-label table-remove-button pull-right" ng-click="ctrl.removeSourceTag($index)">\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source Tag\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <div class="sm-label-left">Source CIDRs <help-field key="gce.securityGroup.sourceCIDRs"></help-field></div>\n <table class="table table-condensed packed">\n <tbody>\n <tr ng-repeat="sourceRange in securityGroup.sourceRanges">\n <td style="width: 80%">\n <input class="form-control input-sm fixed-width" type="text" ng-model="sourceRange.value" required />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button pull-right"\n ng-click="ctrl.removeSourceCIDR(securityGroup.sourceRanges, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="btn btn-block add-new" ng-click="ctrl.addSourceCIDR(securityGroup.sourceRanges)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Source CIDR\n </button>\n </div>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroupIngress.html",'<div class="container-fluid form-horizontal">\n <div class="row">\n <div class="col-md-12" ng-if="state.removedRules.length">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following\n <firewall-label label="firewalls"></firewall-label> could not be found in the selected account/region/VPC and\n were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in state.removedRules">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="ctrl.dismissRemovedRules()">Okay</a>\n </p>\n </div>\n </div>\n </div>\n <div class="row">\n <div class="col-md-12">\n <p class="info">\n <span class="glyphicon glyphicon-info-sign"></span> Connections via each protocol/port will be permitted if they\n match the rules defined by the target tags and source filters above.\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-12">\n <table class="table table-condensed packed">\n <thead>\n <tr>\n <th>Protocol</th>\n <th>Start Port</th>\n <th>End Port</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="rule in securityGroup.ipIngress">\n <td>\n <select\n class="form-control input-sm"\n ng-model="rule.type"\n ng-options="protocol as protocol.toUpperCase() for protocol in [\'tcp\', \'udp\', \'icmp\', \'esp\', \'ah\', \'sctp\']"\n ></select>\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.startPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <input\n class="form-control input-sm"\n type="number"\n min="0"\n ng-model="rule.endPort"\n ng-required="rule.type === \'tcp\' || rule.type === \'udp\' || rule.type === \'sctp\'"\n />\n </td>\n <td>\n <a\n class="btn btn-link sm-label table-remove-button"\n ng-click="ctrl.removeRule(securityGroup.ipIngress, $index)"\n >\n <span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n </table>\n <button class="add-new col-md-12" ng-click="ctrl.addRule(securityGroup.ipIngress)">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Protocol and Port Range\n </button>\n </div>\n </div>\n</div>\n')}]);const Ft="spinnaker.securityGroup.gce.details.controller";e.module(Ft,[xe,H,Ht,Rt]).controller("gceSecurityGroupDetailsCtrl",["$scope","$state","resolvedSecurityGroup","app","securityGroupReader","$uibModal","gceSecurityGroupHelpTextService",function(n,t,a,i,l,c,r){const o=this.application=i,s=a;function d(){return l.getSecurityGroupDetails(o,s.accountId,s.provider,s.region,s.vpcId,s.name).then(function(t){if(n.state.loading=!1,!t||oe.isEmpty(t))u();else{n.securityGroup=t;const a=l.getApplicationSecurityGroup(o,s.accountId,s.region,s.name);if(n.securityGroup=e.extend(oe.cloneDeep(a),n.securityGroup),"string"==typeof n.securityGroup.targetTags){const e=n.securityGroup.targetTags;n.securityGroup.targetTags=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.sourceTags){const e=n.securityGroup.sourceTags;n.securityGroup.sourceTags=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.targetServiceAccounts){const e=n.securityGroup.targetServiceAccounts;n.securityGroup.targetServiceAccounts=e.substring(1,e.length-1).split(", ")}if("string"==typeof n.securityGroup.sourceServiceAccounts){const e=n.securityGroup.sourceServiceAccounts;n.securityGroup.sourceServiceAccounts=e.substring(1,e.length-1).split(", ")}n.securityGroup.sourceRanges=oe.chain(n.securityGroup.ipRangeRules).map(e=>e.range.ip&&e.range.cidr?e.range.ip+e.range.cidr:null).compact().uniq().value();const i=oe.map(n.securityGroup.ipRangeRules,function(e){return{protocol:e.protocol,portRanges:e.portRanges}});let c={};i.forEach(function(e){oe.has(c,e.protocol)?(c[e.protocol]=c[e.protocol].concat(e.portRanges),c[e.protocol]=oe.uniqBy(c[e.protocol],function(e){return e.startPort+"->"+e.endPort})):c[e.protocol]=e.portRanges}),c=oe.map(c,function(e,n){return{protocol:n,portRanges:e}}),n.securityGroup.ipIngressRules=c,n.securityGroup.protocolPortRangeCount=oe.sumBy(c,function(e){return e.portRanges.length>1?e.portRanges.length:1}),y.getAccountDetails(s.accountId).then(function(e){n.securityGroup.logsLink="https://console.developers.google.com/project/"+e.project+"/logs?service=gce_firewall_rule&minLogLevel=0&filters=text:"+s.name}),r.register(o,n.securityGroup.accountName,n.securityGroup.network)}},u)}function u(){n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})}n.detailsTemplateUrl=p.getValue("gce","securityGroup.detailsTemplateUrl"),n.state={loading:!0,standalone:i.isStandalone},n.firewallLabel=m.get("Firewall"),o.securityGroups.ready().then(()=>d()).then(()=>{n.$$destroyed||i.isStandalone||i.securityGroups.onRefresh(n,d)}),this.getTagHelpText=function(e,n){return r.getHelpTextForTag(e,n)},this.editInboundRules=function(){c.open({templateUrl:"google/src/securityGroup/configure/editSecurityGroup.html",controller:"gceEditSecurityGroupCtrl as ctrl",size:"lg",resolve:{securityGroup:function(){return e.copy(n.securityGroup)},application:function(){return o}}})},this.cloneSecurityGroup=function(){c.open({templateUrl:"google/src/securityGroup/clone/cloneSecurityGroup.html",controller:"gceCloneSecurityGroupController as ctrl",size:"lg",resolve:{securityGroup:function(){const t=e.copy(n.securityGroup);return t.region&&(t.regions=[t.region]),t},application:function(){return o}}})},this.deleteSecurityGroup=function(){const e={application:o,title:"Deleting "+s.name};h.confirm({header:"Really delete "+s.name+"?",buttonText:"Delete "+s.name,account:s.accountId,taskMonitorConfig:e,submitMethod:function(){return L.deleteSecurityGroup(s,o,{cloudProvider:n.securityGroup.type,securityGroupName:s.name})}})},i.isStandalone&&(i.securityGroups={refresh:d})}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/editSecurityGroup.html",'<ng-form name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard\n heading="Edit {{securityGroup.name}}: {{securityGroup.accountName}}"\n task-monitor="taskMonitor"\n dismiss="$dismiss()"\n >\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/clone/cloneSecurityGroup.html",'<ng-form name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard heading="Clone {{firewallLabel}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]);const Ut="spinnaker.gce.securityGroup.reader";n(Ut,[]).factory("gceSecurityGroupReader",function(){return{resolveIndexedSecurityGroup:function(e,n,t){return e[n.account].global[t]}}});const Ot="spinnaker.gce.securityGroup.transformer";n(Ot,[]).factory("gceSecurityGroupTransformer",["$q",function(e){return{normalizeSecurityGroup:function(n){return e.when(n)}}}]);const qt="spinnaker.deck.gce.autoscalingPolicy.basicSettings.component";n(qt,[]).component("gceAutoscalingPolicyBasicSettings",{bindings:{policy:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/basicSettings/basicSettings.component.html",controller:function(){this.modes=["ON","OFF","ONLY_SCALE_OUT"]}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/basicSettings/basicSettings.component.html",'<ng-form name="basicSettingsForm">\n <div class="row">\n <div class="col-md-3 sm-label-right">Minimum number of instances</div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n name="minNumReplicas"\n ng-model="$ctrl.policy.minNumReplicas"\n min="0"\n max="{{ $ctrl.policy.maxNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-4 error-message" ng-if="basicSettingsForm.minNumReplicas.$error.min">\n Must be greater than zero.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Maximum number of instances</div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n min="{{ $ctrl.policy.minNumReplicas }}"\n required\n name="maxNumReplicas"\n ng-model="$ctrl.policy.maxNumReplicas"\n />\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="basicSettingsForm.maxNumReplicas.$error.min || basicSettingsForm.minNumReplicas.$error.max"\n >\n Must be greater than or equal to minimum.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Cool-down period (seconds)\n <help-field key="gce.serverGroup.scalingPolicy.coolDownPeriodSec"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n class="form-control input-sm"\n min="15"\n required\n name="coolDownPeriodSec"\n ng-model="$ctrl.policy.coolDownPeriodSec"\n />\n </div>\n <div class="col-md-4 error-message" ng-if="basicSettingsForm.coolDownPeriodSec.$error.min">\n Must be greater than or equal to 15 seconds.\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Mode\n <help-field key="gce.serverGroup.autoscaling.mode"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <select class="form-control input-sm" name="mode" ng-model="$ctrl.policy.mode">\n <option ng-repeat="mode in $ctrl.modes" value="{{ mode }}" ng-selected="$ctrl.policy.mode === mode">\n {{ mode }}\n </option>\n </select>\n </div>\n </div>\n <gce-scale-in-controls policy="$ctrl.policy" update-policy="$ctrl.updatePolicy"> </gce-scale-in-controls>\n</ng-form>\n')}]);const _t="spinnaker.deck.gce.autoscalingPolicy.metricSettings.component";n(_t,[]).component("gceAutoscalingPolicyMetricSettings",{bindings:{policy:"=",showNoMetricsWarning:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/metricSettings/metricSettings.component.html",controller:function(){this.$onInit=()=>{const e={cpuUtilization:!1,loadBalancingUtilization:!1,customMetricUtilizations:!0},n=Object.keys(e);function t(e){return oe.isEqual(e,{})||oe.isUndefined(e)}this.targetTypesToDisplayMap={GAUGE:"Gauge",DELTA_PER_SECOND:"Delta / second",DELTA_PER_MINUTE:"Delta / minute"},this.metricScopeTypesToDisplayMap={TIME_SERIES_PER_INSTANCE:"Time series per instance",SINGLE_TIME_SERIES_PER_GROUP:"Single time series per group"},this.scalingpolicyTypesToDisplayMap={UTILIZATION_TARGET:"Utilization target",SINGLE_INSTANCE_ASSIGNMENT:"singleInstanceAssignment"},this.addMetric=n=>{e[n]?(this.policy[n]=this.policy[n]||[],this.policy[n].push({})):t(this.policy[n])&&(this.policy[n]={utilizationTarget:null})},this.deleteMetric=(n,t)=>{e[n]?this.policy[n].splice(t,1):this.policy[n]={}},this.showMetric=e=>!t(this.policy[e]),this.isSingleTimeSeriesPerGroup=(e,n)=>{if(this.policy.customMetricUtilizations[n].metricExportScope===e)return!0},this.isScalingPolicySingleInstanceAssignment=(e,n)=>{if(this.policy.customMetricUtilizations[n].scalingpolicy===e)return!0},this.showNoMetricsWarning=()=>oe.every(n.map(n=>oe.some([e[n]&&!oe.get(this.policy,[n,"length"]),t(this.policy[n])]))),this.setUtilizationTargetFromDisplay=(e,n)=>{this.policy[e].utilizationTarget=n/100},this.initializeTargetDisplay=e=>{this[`${e}TargetDisplay`]=function(e){if(0===e)return 0;return e?Math.round(100*e):void 0}(this.policy[e].utilizationTarget)}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/metricSettings/metricSettings.component.html",'<collapsible-section heading="CPU Utilization" expanded="true" subsection="true">\n <div class="section-body">\n <ng-form name="cpuUtilization">\n <div ng-if="$ctrl.showMetric(\'cpuUtilization\')">\n <div class="row">\n <div class="col-md-4 sm-label-right">\n Utilization Target (%)\n <help-field key="gce.serverGroup.scalingPolicy.cpuUtilization"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n min="1"\n max="99"\n name="utilizationTarget"\n class="form-control input-sm"\n required\n ng-model="$ctrl.cpuUtilizationTargetDisplay"\n ng-init="$ctrl.initializeTargetDisplay(\'cpuUtilization\')"\n ng-change="$ctrl.setUtilizationTargetFromDisplay(\'cpuUtilization\', $ctrl.cpuUtilizationTargetDisplay)"\n />\n </div>\n <div class="col-md-5 error-message">\n <span ng-if="cpuUtilization.utilizationTarget.$error.min || cpuUtilization.utilizationTarget.$error.max">\n Must be between 1 and 99.</span\n >\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'cpuUtilization\')">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <gce-predictive-autoscaling policy="$ctrl.policy" update-policy="$ctrl.updatePolicy">\n </gce-predictive-autoscaling>\n </div>\n <button\n ng-hide="$ctrl.showMetric(\'cpuUtilization\')"\n class="add-new col-md-12"\n ng-click="$ctrl.addMetric(\'cpuUtilization\')"\n >\n <span class="glyphicon glyphicon-plus-sign"></span> Add CPU Utilization metric\n </button>\n </ng-form>\n </div>\n</collapsible-section>\n<collapsible-section heading="Load Balancing Utilization" expanded="true" subsection="true">\n <div class="section-body">\n <ng-form name="loadBalancingUtilization">\n <div ng-if="$ctrl.showMetric(\'loadBalancingUtilization\')" class="row">\n <div class="col-md-3 sm-label-right">\n Utilization Target (%)\n <help-field key="gce.serverGroup.scalingPolicy.loadBalancingUtilization"></help-field>\n </div>\n <div class="col-md-2 content-fields">\n <input\n type="number"\n min="1"\n max="99"\n class="form-control input-sm"\n required\n name="utilizationTarget"\n ng-model="$ctrl.loadBalancingUtilizationTargetDisplay"\n ng-init="$ctrl.initializeTargetDisplay(\'loadBalancingUtilization\')"\n ng-change="$ctrl.setUtilizationTargetFromDisplay(\'loadBalancingUtilization\', $ctrl.loadBalancingUtilizationTargetDisplay)"\n />\n </div>\n <div class="col-md-5 error-message">\n <span\n ng-if="\n loadBalancingUtilization.utilizationTarget.$error.min ||\n loadBalancingUtilization.utilizationTarget.$error.max\n "\n >\n Must be between 1 and 99.\n </span>\n </div>\n <div class="col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'loadBalancingUtilization\')">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <button\n ng-hide="$ctrl.showMetric(\'loadBalancingUtilization\')"\n class="add-new col-md-12"\n ng-click="$ctrl.addMetric(\'loadBalancingUtilization\')"\n >\n <span class="glyphicon glyphicon-plus-sign"></span> Add Load Balancing metric\n </button>\n </ng-form>\n </div>\n</collapsible-section>\n\n<collapsible-section heading="Custom Metric Utilizations" expanded="true" subsection="true">\n <div class="section-body" ng-repeat="custom in $ctrl.policy.customMetricUtilizations track by $index">\n <hr ng-if="$index > 0" />\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Metric Identifier</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="custom.metric" />\n </div>\n <div class="col-md-offset-4 col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric(\'customMetricUtilizations\', $index)">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Additional Filter Expression</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" ng-model="custom.filter" />\n </div>\n </div>\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Metric Export Scope</div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.metricScopeTypesToDisplayMap"\n ng-model="custom.metricExportScope"\n required\n ></select>\n </div>\n </div>\n\n <div ng-if="$ctrl.isSingleTimeSeriesPerGroup(\'SINGLE_TIME_SERIES_PER_GROUP\', $index)">\n <div class="row">\n <div class="col-md-3 sm-label-right">Scaling Policy</div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.scalingpolicyTypesToDisplayMap"\n ng-model="custom.scalingpolicy"\n required\n ></select>\n </div>\n </div>\n\n <div ng-if="$ctrl.isScalingPolicySingleInstanceAssignment(\'SINGLE_INSTANCE_ASSIGNMENT\', $index)">\n <div class="row">\n <div class="col-md-3 sm-label-right">Single Instance Assignment</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="singleInstanceAssignment"\n class="form-control input-sm"\n required\n ng-model="custom.singleInstanceAssignment"\n min="0"\n />\n </div>\n </div>\n </div>\n </div>\n <div\n ng-if="\n !$ctrl.isScalingPolicySingleInstanceAssignment(\'SINGLE_INSTANCE_ASSIGNMENT\', $index) ||\n !$ctrl.isSingleTimeSeriesPerGroup(\'SINGLE_TIME_SERIES_PER_GROUP\', $index)\n "\n >\n <div class="row">\n <div class="col-md-3 sm-label-right">\n Utilization Target\n <help-field key="gce.serverGroup.scalingPolicy.customMetricUtilizations"></help-field>\n </div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="utilizationTarget"\n class="form-control input-sm"\n required\n ng-model="custom.utilizationTarget"\n />\n </div>\n <div class="col-md-3">\n <select\n class="form-control input-sm"\n ng-options="value as displayValue for (value, displayValue) in $ctrl.targetTypesToDisplayMap"\n ng-model="custom.utilizationTargetType"\n required\n >\n <option value="">-- Target Type --</option>\n </select>\n </div>\n <div class="col-md-5 error-message" ng-if="custom.utilizationTarget <= 0">\n Utilization target must be greater than 0.0.\n </div>\n </div>\n </div>\n </div>\n <button class="add-new col-md-12" ng-click="$ctrl.addMetric(\'customMetricUtilizations\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add custom metric\n </button>\n</collapsible-section>\n<div class="row">\n <div class="col-md-12" ng-if="$ctrl.showNoMetricsWarning()">\n <div class="alert alert-warning text-center">\n <i class="fa fa-exclamation-triangle pull-left"></i>\n <span>At least one metric is required.</span>\n </div>\n </div>\n</div>\n')}]);var Vt=["Africa/Abidjan","Africa/Accra","Africa/Addis_Ababa","Africa/Algiers","Africa/Asmara","Africa/Asmera","Africa/Bamako","Africa/Bangui","Africa/Banjul","Africa/Bissau","Africa/Blantyre","Africa/Brazzaville","Africa/Bujumbura","Africa/Cairo","Africa/Casablanca","Africa/Ceuta","Africa/Conakry","Africa/Dakar","Africa/Dar_es_Salaam","Africa/Djibouti","Africa/Douala","Africa/El_Aaiun","Africa/Freetown","Africa/Gaborone","Africa/Harare","Africa/Johannesburg","Africa/Juba","Africa/Kampala","Africa/Khartoum","Africa/Kigali","Africa/Kinshasa","Africa/Lagos","Africa/Libreville","Africa/Lome","Africa/Luanda","Africa/Lubumbashi","Africa/Lusaka","Africa/Malabo","Africa/Maputo","Africa/Maseru","Africa/Mbabane","Africa/Mogadishu","Africa/Monrovia","Africa/Nairobi","Africa/Ndjamena","Africa/Niamey","Africa/Nouakchott","Africa/Ouagadougou","Africa/Porto-Novo","Africa/Sao_Tome","Africa/Timbuktu","Africa/Tripoli","Africa/Tunis","Africa/Windhoek","America/Adak","America/Anchorage","America/Anguilla","America/Antigua","America/Araguaina","America/Argentina/Buenos_Aires","America/Argentina/Catamarca","America/Argentina/ComodRivadavia","America/Argentina/Cordoba","America/Argentina/Jujuy","America/Argentina/La_Rioja","America/Argentina/Mendoza","America/Argentina/Rio_Gallegos","America/Argentina/Salta","America/Argentina/San_Juan","America/Argentina/San_Luis","America/Argentina/Tucuman","America/Argentina/Ushuaia","America/Aruba","America/Asuncion","America/Atikokan","America/Atka","America/Bahia","America/Bahia_Banderas","America/Barbados","America/Belem","America/Belize","America/Blanc-Sablon","America/Boa_Vista","America/Bogota","America/Boise","America/Buenos_Aires","America/Cambridge_Bay","America/Campo_Grande","America/Cancun","America/Caracas","America/Catamarca","America/Cayenne","America/Cayman","America/Chicago","America/Chihuahua","America/Coral_Harbour","America/Cordoba","America/Costa_Rica","America/Creston","America/Cuiaba","America/Curacao","America/Danmarkshavn","America/Dawson","America/Dawson_Creek","America/Denver","America/Detroit","America/Dominica","America/Edmonton","America/Eirunepe","America/El_Salvador","America/Ensenada","America/Fort_Nelson","America/Fort_Wayne","America/Fortaleza","America/Glace_Bay","America/Godthab","America/Goose_Bay","America/Grand_Turk","America/Grenada","America/Guadeloupe","America/Guatemala","America/Guayaquil","America/Guyana","America/Halifax","America/Havana","America/Hermosillo","America/Indiana/Indianapolis","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Inuvik","America/Iqaluit","America/Jamaica","America/Jujuy","America/Juneau","America/Kentucky/Louisville","America/Kentucky/Monticello","America/Knox_IN","America/Kralendijk","America/La_Paz","America/Lima","America/Los_Angeles","America/Louisville","America/Lower_Princes","America/Maceio","America/Managua","America/Manaus","America/Marigot","America/Martinique","America/Matamoros","America/Mazatlan","America/Mendoza","America/Menominee","America/Merida","America/Metlakatla","America/Mexico_City","America/Miquelon","America/Moncton","America/Monterrey","America/Montevideo","America/Montreal","America/Montserrat","America/Nassau","America/New_York","America/Nipigon","America/Nome","America/Noronha","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Nuuk","America/Ojinaga","America/Panama","America/Pangnirtung","America/Paramaribo","America/Phoenix","America/Port_of_Spain","America/Port-au-Prince","America/Porto_Acre","America/Porto_Velho","America/Puerto_Rico","America/Punta_Arenas","America/Rainy_River","America/Rankin_Inlet","America/Recife","America/Regina","America/Resolute","America/Rio_Branco","America/Rosario","America/Santa_Isabel","America/Santarem","America/Santiago","America/Santo_Domingo","America/Sao_Paulo","America/Scoresbysund","America/Shiprock","America/Sitka","America/St_Barthelemy","America/St_Johns","America/St_Kitts","America/St_Lucia","America/St_Thomas","America/St_Vincent","America/Swift_Current","America/Tegucigalpa","America/Thule","America/Thunder_Bay","America/Tijuana","America/Toronto","America/Tortola","America/Vancouver","America/Virgin","America/Whitehorse","America/Winnipeg","America/Yakutat","America/Yellowknife","Antarctica/Casey","Antarctica/Davis","Antarctica/DumontDUrville","Antarctica/Macquarie","Antarctica/Mawson","Antarctica/McMurdo","Antarctica/Palmer","Antarctica/Rothera","Antarctica/South_Pole","Antarctica/Syowa","Antarctica/Troll","Antarctica/Vostok","Arctic/Longyearbyen","Asia/Aden","Asia/Almaty","Asia/Amman","Asia/Anadyr","Asia/Aqtau","Asia/Aqtobe","Asia/Ashgabat","Asia/Ashkhabad","Asia/Atyrau","Asia/Baghdad","Asia/Bahrain","Asia/Baku","Asia/Bangkok","Asia/Barnaul","Asia/Beirut","Asia/Bishkek","Asia/Brunei","Asia/Calcutta","Asia/Chita","Asia/Choibalsan","Asia/Chongqing","Asia/Chungking","Asia/Colombo","Asia/Dacca","Asia/Damascus","Asia/Dhaka","Asia/Dili","Asia/Dubai","Asia/Dushanbe","Asia/Famagusta","Asia/Gaza","Asia/Harbin","Asia/Hebron","Asia/Ho_Chi_Minh","Asia/Hong_Kong","Asia/Hovd","Asia/Irkutsk","Asia/Istanbul","Asia/Jakarta","Asia/Jayapura","Asia/Jerusalem","Asia/Kabul","Asia/Kamchatka","Asia/Karachi","Asia/Kashgar","Asia/Kathmandu","Asia/Katmandu","Asia/Khandyga","Asia/Kolkata","Asia/Krasnoyarsk","Asia/Kuala_Lumpur","Asia/Kuching","Asia/Kuwait","Asia/Macao","Asia/Macau","Asia/Magadan","Asia/Makassar","Asia/Manila","Asia/Muscat","Asia/Nicosia","Asia/Novokuznetsk","Asia/Novosibirsk","Asia/Omsk","Asia/Oral","Asia/Phnom_Penh","Asia/Pontianak","Asia/Pyongyang","Asia/Qatar","Asia/Qostanay","Asia/Qyzylorda","Asia/Rangoon","Asia/Riyadh","Asia/Saigon","Asia/Sakhalin","Asia/Samarkand","Asia/Seoul","Asia/Shanghai","Asia/Singapore","Asia/Srednekolymsk","Asia/Taipei","Asia/Tashkent","Asia/Tbilisi","Asia/Tehran","Asia/Tel_Aviv","Asia/Thimbu","Asia/Thimphu","Asia/Tokyo","Asia/Tomsk","Asia/Ujung_Pandang","Asia/Ulaanbaatar","Asia/Ulan_Bator","Asia/Urumqi","Asia/Ust-Nera","Asia/Vientiane","Asia/Vladivostok","Asia/Yakutsk","Asia/Yangon","Asia/Yekaterinburg","Asia/Yerevan","Atlantic/Azores","Atlantic/Bermuda","Atlantic/Canary","Atlantic/Cape_Verde","Atlantic/Faeroe","Atlantic/Faroe","Atlantic/Jan_Mayen","Atlantic/Madeira","Atlantic/Reykjavik","Atlantic/South_Georgia","Atlantic/St_Helena","Atlantic/Stanley","Australia/ACT","Australia/Adelaide","Australia/Brisbane","Australia/Broken_Hill","Australia/Canberra","Australia/Currie","Australia/Darwin","Australia/Eucla","Australia/Hobart","Australia/LHI","Australia/Lindeman","Australia/Lord_Howe","Australia/Melbourne","Australia/North","Australia/NSW","Australia/Perth","Australia/Queensland","Australia/South","Australia/Sydney","Australia/Tasmania","Australia/Victoria","Australia/West","Australia/Yancowinna","Brazil/Acre","Brazil/DeNoronha","Brazil/East","Brazil/West","Canada/Atlantic","Canada/Central","Canada/Eastern","Canada/Mountain","Canada/Newfoundland","Canada/Pacific","Canada/Saskatchewan","Canada/Yukon","CET","Chile/Continental","Chile/EasterIsland","CST6CDT","Cuba","EET","Egypt","Eire","EST","EST5EDT","Etc/GMT","Etc/GMT-0","Etc/GMT-1","Etc/GMT-10","Etc/GMT-11","Etc/GMT-12","Etc/GMT-13","Etc/GMT-14","Etc/GMT-2","Etc/GMT-3","Etc/GMT-4","Etc/GMT-5","Etc/GMT-6","Etc/GMT-7","Etc/GMT-8","Etc/GMT-9","Etc/GMT+0","Etc/GMT+1","Etc/GMT+10","Etc/GMT+11","Etc/GMT+12","Etc/GMT+2","Etc/GMT+3","Etc/GMT+4","Etc/GMT+5","Etc/GMT+6","Etc/GMT+7","Etc/GMT+8","Etc/GMT+9","Etc/GMT0","Etc/Greenwich","Etc/UCT","Etc/Universal","Etc/UTC","Etc/Zulu","Europe/Amsterdam","Europe/Andorra","Europe/Astrakhan","Europe/Athens","Europe/Belfast","Europe/Belgrade","Europe/Berlin","Europe/Bratislava","Europe/Brussels","Europe/Bucharest","Europe/Budapest","Europe/Busingen","Europe/Chisinau","Europe/Copenhagen","Europe/Dublin","Europe/Gibraltar","Europe/Guernsey","Europe/Helsinki","Europe/Isle_of_Man","Europe/Istanbul","Europe/Jersey","Europe/Kaliningrad","Europe/Kiev","Europe/Kirov","Europe/Lisbon","Europe/Ljubljana","Europe/London","Europe/Luxembourg","Europe/Madrid","Europe/Malta","Europe/Mariehamn","Europe/Minsk","Europe/Monaco","Europe/Moscow","Europe/Nicosia","Europe/Oslo","Europe/Paris","Europe/Podgorica","Europe/Prague","Europe/Riga","Europe/Rome","Europe/Samara","Europe/San_Marino","Europe/Sarajevo","Europe/Saratov","Europe/Simferopol","Europe/Skopje","Europe/Sofia","Europe/Stockholm","Europe/Tallinn","Europe/Tirane","Europe/Tiraspol","Europe/Ulyanovsk","Europe/Uzhgorod","Europe/Vaduz","Europe/Vatican","Europe/Vienna","Europe/Vilnius","Europe/Volgograd","Europe/Warsaw","Europe/Zagreb","Europe/Zaporozhye","Europe/Zurich","GB","GB-Eire","GMT","GMT-0","GMT+0","GMT0","Greenwich","Hongkong","HST","Iceland","Indian/Antananarivo","Indian/Chagos","Indian/Christmas","Indian/Cocos","Indian/Comoro","Indian/Kerguelen","Indian/Mahe","Indian/Maldives","Indian/Mauritius","Indian/Mayotte","Indian/Reunion","Iran","Israel","Jamaica","Japan","Kwajalein","Libya","MET","Mexico/BajaNorte","Mexico/BajaSur","Mexico/General","MST","MST7MDT","Navajo","NZ","NZ-CHAT","Pacific/Apia","Pacific/Auckland","Pacific/Bougainville","Pacific/Chatham","Pacific/Chuuk","Pacific/Easter","Pacific/Efate","Pacific/Enderbury","Pacific/Fakaofo","Pacific/Fiji","Pacific/Funafuti","Pacific/Galapagos","Pacific/Gambier","Pacific/Guadalcanal","Pacific/Guam","Pacific/Honolulu","Pacific/Johnston","Pacific/Kanton","Pacific/Kiritimati","Pacific/Kosrae","Pacific/Kwajalein","Pacific/Majuro","Pacific/Marquesas","Pacific/Midway","Pacific/Nauru","Pacific/Niue","Pacific/Norfolk","Pacific/Noumea","Pacific/Pago_Pago","Pacific/Palau","Pacific/Pitcairn","Pacific/Pohnpei","Pacific/Ponape","Pacific/Port_Moresby","Pacific/Rarotonga","Pacific/Saipan","Pacific/Samoa","Pacific/Tahiti","Pacific/Tarawa","Pacific/Tongatapu","Pacific/Truk","Pacific/Wake","Pacific/Wallis","Pacific/Yap","Poland","Portugal","PRC","PST8PDT","ROC","ROK","Singapore","Turkey","UCT","Universal","US/Alaska","US/Aleutian","US/Arizona","US/Central","US/East-Indiana","US/Eastern","US/Hawaii","US/Indiana-Starke","US/Michigan","US/Mountain","US/Pacific","US/Samoa","UTC","W-SU","WET","Zulu"];const jt="spinnaker.deck.gce.autoscalingPolicy.scalingSchedules.component";n(jt,[]).component("gceAutoscalingPolicyScalingSchedules",{bindings:{policy:"=",updatePolicy:"<"},templateUrl:"google/src/autoscalingPolicy/components/scalingSchedules/scalingSchedules.component.html",controller:function(){this.$onInit=()=>{const e={scalingSchedules:!0};this.timezones=Vt,this.addSchedule=n=>{var t;e[n]?(this.policy[n]=this.policy[n]||[],this.policy[n].push({})):(t=this.policy[n],(oe.isEqual(t,{})||oe.isUndefined(t))&&(this.policy[n]={}))},this.deleteSchedule=(n,t)=>{e[n]?this.policy[n].splice(t,1):this.policy[n]={}},this.selectTimezone=(e,n)=>{const{scalingSchedules:t}=this.policy,a=t[n];t[n]={...a,timezone:e},this.updatePolicy({...this.policy,scalingSchedules:[...t]})}}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/autoscalingPolicy/components/scalingSchedules/scalingSchedules.component.html",'<ng-form name="scalingSchedulesForm">\n <div class="section-body" ng-repeat="schedule in $ctrl.policy.scalingSchedules track by $index">\n <ng-form name="scalingSchedule">\n <hr ng-if="$index > 0" />\n\n <div class="row">\n <div class="col-md-3 sm-label-right">Name</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="schedule.scheduleName" />\n </div>\n <div class="col-md-offset-4 col-md-1">\n <button class="btn btn-sm btn-default" ng-click="$ctrl.deleteSchedule(\'scalingSchedules\', $index)">\n <span class="glyphicon glyphicon-trash visible-lg-inline"></span>\n <span class="visible-lg-inline">Delete</span>\n </button>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Description</div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" ng-model="schedule.scheduleDescription" />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Enabled</div>\n <input type="checkbox" class="col-md-1" ng-model="schedule.enabled" value="{schedule.enabled}" />\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Minimum required instances</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="minimumRequiredInstances"\n class="form-control input-sm"\n min="0"\n required\n ng-model="schedule.minimumRequiredInstances"\n />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">\n CRON Expression\n <help-field key="gce.serverGroup.scalingPolicy.cronExpression"></help-field>\n </div>\n <div class="col-md-3 content-fields">\n <input class="form-control input-sm" required ng-model="schedule.scheduleCron" />\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Time Zone</div>\n <div class="col-md-3">\n <gce-timezone-select\n available-timezones="$ctrl.timezones"\n selected-timezone="$ctrl.policy.scalingSchedules[$index].timezone"\n select-timezone="$ctrl.selectTimezone"\n target="$index"\n ></gce-timezone-select>\n </div>\n </div>\n <div class="row">\n <div class="col-md-3 sm-label-right">Duration (seconds)</div>\n <div class="col-md-3 content-fields">\n <input\n type="number"\n name="duration"\n class="form-control input-sm"\n required\n ng-model="schedule.duration"\n min="301"\n />\n </div>\n <div class="col-md-4 error-message" ng-if="scalingSchedule.duration.$error.min">\n Must be greater than 300 seconds.\n </div>\n </div>\n </ng-form>\n </div>\n <div class="schedule-container">\n <button class="add-new col-md-12" ng-click="$ctrl.addSchedule(\'scalingSchedules\')">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Scaling Schedule\n </button>\n </div>\n</ng-form>\n')}]);class Kt extends ce.Component{constructor(){super(...arguments),this.loadOptions=e=>new Promise(n=>{if(!e||e.length<3)n({options:[],complete:!1});else{n({options:ge(this.props.availableTimezones).filter(n=>n.toLowerCase().includes(e)).map(e=>({value:e,label:e})).value(),complete:!1})}})}render(){return ce.createElement($e,{cache:null,clearable:!1,ignoreAccents:!1,loadOptions:this.loadOptions,onChange:e=>this.props.selectTimezone(e.value,this.props.target),placeholder:"Type at least 3 characters to search for an timezone...",searchPromptText:"Type at least 3 characters to search for an timezone...",value:{value:this.props.selectedTimezone,label:this.props.selectedTimezone},required:!0})}}const Wt="spinnaker.gce.timezoneSelect";n(Wt,[]).component("gceTimezoneSelect",re(i(Kt,"gceTimezoneSelect"),["availableTimezones","selectedTimezone","selectTimezone","target"]));const Zt="spinnaker.gce.serverGroup.transformer";function Xt(e){const n=e.split("/");if(n.length<2)throw new Error(`Health check url ${e} missing expected segments.`);return{healthCheckName:n[n.length-1],healthCheckKind:n[n.length-2].slice(0,-1)}}function Yt(e){const n=function(e){const n=new Set,t=new Set;return e.forEach(e=>{n.has(e.name)?t.add(e.name):n.add(e.name)}),t}(e);return e.map(e=>({displayName:n.has(e.name)?`${e.name} (${e.kind})`:e.name,kind:e.kind,name:e.name,selfLink:e.selfLink}))}n(Zt,[nn]).factory("gceServerGroupTransformer",["gceHttpLoadBalancerUtils",function(e){return{convertServerGroupCommandToDeployConfiguration:function(e){const n=e.backingData.filtered.truncatedZones,t=de({backingData:[],viewState:[]},e);return"clone"!==e.viewState.mode&&delete t.source,t.disableTraffic=!t.enableTraffic,t.cloudProvider="gce",t.availabilityZones={},t.availabilityZones[t.region]=e.zone?[e.zone]:n,t.account=t.credentials,delete t.viewState,delete t.backingData,delete t.selectedProvider,delete t.implicitSecurityGroups,delete t.enableTraffic,delete t.providerType,delete t.enableAutoHealing,t},normalizeServerGroup:function(n,t){return t.getDataSource("loadBalancers").ready().then(()=>(n.loadBalancers&&(n.loadBalancers=e.normalizeLoadBalancerNamesForAccount(n.loadBalancers,n.account,t.getDataSource("loadBalancers").data)),n))}}}]);class Jt{static getAvailableAccelerators({backingData:e,credentials:n,distributionPolicy:t,regional:a,selectZones:i,zone:l}){const c=this.getZoneToAcceleratorTypesMap(e,n);if(l&&!a)return this.getAcceleratorTypesForZone(c,l);if(a&&i){const e=ve(t,"zones",[]).map(e=>this.getAcceleratorTypesForZone(c,e));return we.apply(null,[...e,"name"])}return[]}static getZoneToAcceleratorTypesMap(e,n){return ve(e,["credentialsKeyedByAccount",n,"zoneToAcceleratorTypesMap"],{})}static getAcceleratorTypesForZone(e,n){return ve(e,[n,"acceleratorTypes","acceleratorTypes"],[]).map(e=>({...e,availableCardCounts:this.getAvailableCardCounts(e)}))}static getAvailableCardCounts(e){const n=Ce(e.maximumCardsPerInstance)?e.maximumCardsPerInstance:4,t=[];for(let e=1;e<=n;e*=2)t.push(e);return t}}const Qt="spinnaker.deck.gce.tagManager.service";n(Qt,[]).factory("gceTagManager",function(){const e=["command","securityGroups","securityGroupObjectsKeyedByTag","securityGroupObjectsKeyedById"];this.reset=()=>{e.forEach(e=>delete this[e])},this.register=e=>{this.command=e;const{credentials:a,backingData:i}=e;if(void 0!==i.securityGroups[a]){this.securityGroupObjects=oe.cloneDeep(i.securityGroups[a].gce.global),n(this.securityGroupObjects,e.tags);const{byTag:l,bySecurityGroupId:c}=t(this.securityGroupObjects);this.securityGroupObjectsKeyedByTag=l,this.securityGroupObjectsKeyedById=c}e.securityGroups||(e.securityGroups=this.inferSecurityGroupIdsFromTags(e.tags))},this.inferSecurityGroupIdsFromTags=(e=[])=>oe.chain(e).map(e=>this.securityGroupObjectsKeyedByTag[e.value]).flatten().compact().filter(e=>e.network===this.command.network).map("id").uniq().value();const n=(e,n=[])=>{e.forEach(e=>{const t=e.targetTags;e.tagsArray=t?t.substring(1,t.length-1).split(", "):[],e.selectedTags=n?oe.intersection(n.map(e=>e.value),e.tagsArray):[]})},t=e=>e.reduce((e,n)=>{const{bySecurityGroupId:t}=e;return t[n.id]=n,n.tagsArray.reduce((e,t)=>{const{byTag:a}=e;return a[t]||(a[t]=[]),a[t].push(n),e},e)},{byTag:{},bySecurityGroupId:{}});this.inferSelectedSecurityGroupFromTag=oe.debounce(e=>{let n=this.securityGroupObjectsKeyedByTag[e];const t=this.command;n&&(n=oe.filter(n,e=>e.network===this.command.network)),n&&(i(n,e),t.securityGroups=oe.map(l(a(t.securityGroups),n),"id")),this.updateSelectedTags()},100),this.updateSelectedTags=()=>{const e=this.command,n=e.tags.map(e=>e.value);a(e.securityGroups).forEach(t=>{t.selectedTags&&(t.selectedTags=oe.intersection(t.selectedTags,n)),oe.get(t,"selectedTags.length")||(e.securityGroups=oe.without(e.securityGroups,t.id))})},this.addTag=e=>{const n=this.command,t=n.tags;let c=this.securityGroupObjectsKeyedByTag[e];c&&(c=oe.filter(c,e=>e.network===this.command.network)),oe.includes(t.map(e=>e.value),e)||t.push({value:e}),c&&(i(c,e),n.securityGroups=oe.map(l(a(n.securityGroups),c),"id"))};const a=e=>e.map(e=>this.securityGroupObjectsKeyedById[e]),i=(e,n)=>{e.forEach(e=>{e.selectedTags=oe.chain(e.selectedTags||[]).concat([n]).uniq().value()})},l=(e,n)=>oe.chain(e).concat(n).uniq().value();return this.removeTag=e=>{const n=this.command,t=n.securityGroups||[];a(t).forEach(n=>{n.selectedTags&&(n.selectedTags=n.selectedTags.filter(n=>n!==e))}),n.tags=n.tags.filter(n=>n.value!==e)},this.removeSecurityGroup=e=>{const n=this.securityGroupObjectsKeyedById[e],t=n.selectedTags,i=this.command;a(i.securityGroups).forEach(e=>{e.selectedTags&&(e.selectedTags=oe.difference(e.selectedTags,t))}),n.selectedTags=[],i.tags=oe.chain(i.tags).map(e=>e.value).difference(t).map(e=>({value:e})).value()},this.getToolTipContent=e=>{const n=oe.get(this,["securityGroupObjectsKeyedByTag",e]),t=n?n.filter(e=>e.network===this.command.network).map(e=>e.id):[];return`This tag associates this server group with ${t.length>1?m.get("firewalls"):m.get("firewall")}\n <em>${t.join(", ")}</em>.`},this.showToolTip=e=>!!oe.get(this,["securityGroupObjectsKeyedByTag",e]),this});const ea="spinnaker.serverGroup.configure.gce.configuration.service";e.module(ea,[St,H,k,w,ln,Je,nn,Ve,Qt]).factory("gceServerGroupConfigurationService",["securityGroupReader","gceInstanceTypeService","cacheInitializer","$q","loadBalancerReader","gceCustomInstanceBuilderService","gceHttpLoadBalancerUtils","gceHealthCheckReader","gceTagManager","gceLoadBalancerSetTransformer",function(n,t,a,i,l,c,r,o,s,d){const u=["pd-standard","pd-ssd","hyperdisk-balanced"],g=["cloud-platform","userinfo.email","compute.readonly","compute","cloud.useraccounts.readonly","cloud.useraccounts","devstorage.read_only","devstorage.write_only","devstorage.full_control","taskqueue","bigquery","sqlservice.admin","datastore","logging.write","logging.read","logging.admin","monitoring.write","monitoring.read","monitoring","bigtable.data.readonly","bigtable.data","bigtable.admin","bigtable.admin.table"];function p(e){e.backingData.credentialsKeyedByAccount[e.credentials],e.distributionPolicy.targetShape||(e.distributionPolicy.targetShape="EVEN")}function m(n){const a={dirty:{}};if(n.region){const i=[a.dirty];i.push(f(n).dirty),i.push(function(e){const n=e,a={dirty:{}},i=n.regional?n.distributionPolicy.zones:[n.zone],{credentialsKeyedByAccount:l}=n.backingData,{locationToInstanceTypesMap:c}=l[n.credentials];if(i.every(e=>!e))return a;let r=t.getAvailableTypesForLocations(c,i);r=function(e){const n=oe.map(e,e=>{const n=e.split("-");return{class:n[0],group:n[1],index:Number(n[2])||0}}),t=oe.sortBy(n,["class","group","index"]);return oe.map(t,e=>e.class+"-"+e.group+(e.index?"-"+e.index:""))}(r);const o=n.instanceType;oe.every([o,!oe.includes(o,"custom-"),!oe.includes(r,o)])&&(a.dirty.instanceType=n.instanceType,n.instanceType=null);return n.backingData.filtered.instanceTypes=r,a}(n).dirty),e.extend(...i)}else n.backingData.filtered.instanceTypes=[];return a}function h(e){const n={dirty:{}},t=e.backingData.filtered,a=e.backingData.credentialsKeyedByAccount[e.credentials].locationToCpuPlatformsMap;t.cpuPlatforms=["(Automatic)"];const i=e.regional?e.region:e.zone;return oe.has(a,i)&&(t.cpuPlatforms=oe.concat(t.cpuPlatforms,a[i])),oe.includes(t.cpuPlatforms,e.minCpuPlatform)||(delete e.minCpuPlatform,n.dirty.minCpuPlatform=!0),n}function f(e){const n=e,t={dirty:{}};let a=oe.get(n,"viewState.customInstance.vCpuCount");const i=oe.get(n,"viewState.customInstance.instanceFamily"),l=oe.get(n,"viewState.customInstance.memory"),r=oe.get(n,"viewState.customInstance.extendedMemory"),{zone:o,regional:s,region:d}=n,{locationToInstanceTypesMap:u}=n.backingData.credentialsKeyedByAccount[n.credentials],g=s?d:o;return g?((o||s)&&oe.set(n,"backingData.customInstanceTypes.vCpuList",c.generateValidVCpuListForLocation(g,u)),a&&c.vCpuCountForLocationIsValid(i,a,g,u)||(a=oe.get(n,"backingData.customInstanceTypes.vCpuList[0]"),oe.set(n,"viewState.customInstance.vCpuCount",a)),oe.set(n,"backingData.customInstanceTypes.memoryList",c.generateValidMemoryListForVCpuCount(i,a)),oe.every([l,a,!c.memoryIsValid(i,l,a,r)])&&(oe.set(n,"viewState.customInstance.memory",void 0),t.dirty.instanceType=n.instanceType,n.instanceType=null),oe.set(n,"backingData.customInstanceTypes.instanceFamilyList",c.generateValidInstanceFamilyList()),t):t}function v(e){const n={dirty:{}},t=Jt.getAvailableAccelerators(e);oe.set(e,["viewState","acceleratorTypes"],t),n.dirty.acceleratorTypes=!0;const a=oe.get(e,"acceleratorConfigs",[]);return a.length>0&&(e.acceleratorConfigs=a.filter(e=>!!t.find(n=>n.name===e.acceleratorType)),0===e.acceleratorConfigs.length&&delete e.acceleratorConfigs),n}function b(e){e.image=e.viewState.imageId;const n={dirty:{}};return e.credentials!==e.viewState.lastImageAccount&&(e.viewState.lastImageAccount=e.credentials,oe.chain(e.backingData.allImages).find({imageName:e.image}).value()||(e.image=null,n.dirty.imageName=!0)),n}function k(e){const n={dirty:{}},t=e.backingData.filtered;if(null===e.region)return n;const a=e.backingData.credentialsKeyedByAccount[e.credentials].regions;return oe.isArray(a)?(t.zones=oe.find(a,{name:e.region}).zones,t.truncatedZones=oe.takeRight(t.zones.sort(),3)):t.zones=a[e.region],oe.chain(t.zones).includes(e.zone).value()||(delete e.zone,e.regional||(n.dirty.zone=!0)),n}function S(e){return Yt(oe.filter(e.backingData.healthChecks,{account:e.credentials}))}function w(e){const n={dirty:{}},t=e.backingData.filtered;return null===e.credentials||(t.healthChecks=S(e),oe.has(e,"autoHealingPolicy.healthCheck")&&!oe.chain(t.healthChecks).map("selfLink").includes(e.autoHealingPolicy.healthCheckUrl).value()?(delete e.autoHealingPolicy.healthCheck,n.dirty.autoHealingPolicy=!0):n.dirty.autoHealingPolicy=null),n}function T(e,n){return n.region===e.region||"global"===n.region}function G(e){const n={dirty:{}},t=e.loadBalancers,a=d.normalizeLoadBalancerSet(function(e){return oe.chain(e.backingData.loadBalancers).map("accounts").flattenDeep().filter({name:e.credentials}).map("regions").flattenDeep().map("loadBalancers").flattenDeep().filter(oe.curry(T)(e)).uniq().value()}(e));if(e.backingData.filtered.loadBalancerIndex=oe.keyBy(a,"name"),e.backingData.filtered.loadBalancers=oe.map(a,"name"),t&&e.loadBalancers){e.loadBalancers=r.normalizeLoadBalancerNamesForAccount(e.loadBalancers,e.credentials,a);const t=oe.intersection(e.backingData.filtered.loadBalancers,e.loadBalancers),i=oe.xor(t,e.loadBalancers);e.loadBalancers=t,function(e){const n=e.backendServiceMetadata,t=e.backingData.filtered.loadBalancerIndex,a=e.loadBalancers.reduce((e,a)=>(r.isHttpLoadBalancer(t[a])&&(e[a]=oe.intersection(t[a].backendServices,n)),e),{});Object.keys(a).length>0&&(e.backendServices=a)}(e),i.length&&(n.dirty.loadBalancers=i)}return n}function A(e,n){return a.refreshCache("healthChecks").then(function(){return o.listHealthChecks()}).then(function(t){e.backingData.healthChecks=t,n||w(e)})}function P(e){const n={dirty:{}},t=e.backingData.filtered;return null===e.region||(t.subnets=oe.chain(e.backingData.subnets).filter({account:e.credentials,network:e.network,region:e.region}).map("id").value(),oe.chain(t.subnets).includes(e.subnet).value()||(e.subnet="",n.dirty.subnet=!0)),n}function $(e){let n=e.backingData.securityGroups[e.credentials]||{gce:{}};return n=oe.filter(n.gce.global,function(n){return n.network===e.network}),oe.chain(n).sortBy("name").value()}function x(e){const n={dirty:{}},t=e.backingData.filtered.securityGroups,a=$(e);if(t&&e.securityGroups){const i=e.securityGroups.map(function(e){const n=oe.chain(t).find({id:e}).value();return n?n.id:e}),l=e.securityGroups.map(function(e){const n=oe.chain(t).find({id:e}).value();return n?n.id:null}).map(function(e){return oe.chain(a).find({id:e}).value()}).filter(function(e){return e});e.securityGroups=oe.map(l,"id");const c=oe.xor(i,e.securityGroups);c.length&&(n.dirty.securityGroups=c)}e.backingData.filtered.securityGroups=oe.filter(a,function(e){return!oe.isEmpty(e.targetTags)}),e.implicitSecurityGroups=oe.filter(a,function(e){return oe.isEmpty(e.targetTags)});const i=(l=e.network)&&l.includes("/")?l.split("/")[0]+"/":"";var l;const c=oe.map(e.securityGroups,e=>e.startsWith(i)?e:i+e);return e.securityGroups=oe.difference(c,oe.map(e.implicitSecurityGroups,"id")),n}function I(e,t){return a.refreshCache("securityGroups").then(function(){return n.getAllSecurityGroups().then(function(n){e.backingData.securityGroups=n,t||x(e)})})}function M(e){return oe.map(oe.filter(e.backingData.networks,{account:e.credentials}),"id")}return{configureCommand:function(a,c){return i.all([y.getCredentialsKeyedByAccount("gce"),n.getAllSecurityGroups(),B.listNetworksByProvider("gce"),C.listSubnetsByProvider("gce"),l.listLoadBalancers("gce"),(r=c.credentials,Ze.findImages({account:r,provider:"gce",q:"*"})),t.getAllTypesByRegion(),i.when(e.copy(u)),i.when(e.copy(g)),o.listHealthChecks(),y.listAccounts("gce")]).then(function([n,t,a,l,r,o,d,u,g,y,C]){const T={credentialsKeyedByAccount:n,securityGroups:t,networks:a,subnets:l,loadBalancers:r,allImages:o,instanceTypes:d,persistentDiskTypes:u,authScopes:g,healthChecks:y,accounts:C};let z=i.when(null),N=i.when(null),D=i.when(null);if(T.filtered={},T.distributionPolicyTargetShapes=["ANY","EVEN"],c.backingData=T,b(c),c.securityGroups&&c.securityGroups.length){const e=oe.map($(c),"id");oe.intersection(c.securityGroups,e).length<c.securityGroups.length&&(z=I(c,!0))}if(c.network){M(c).includes(c.network)||(N=function(e){B.listNetworksByProvider("gce").then(function(n){e.backingData.networks=n})}(c))}if(c.autoScalingPolicy&&(c.enableAutoScaling=!0),c.autoHealingPolicy&&(c.enableAutoHealing=!0),oe.has(c,"autoHealingPolicy.healthCheck")){const e=S(c);oe.chain(e).map("selfLink").includes(c.autoHealingPolicy.healthCheckUrl).value()||(D=A(c,!0))}return i.all([z,N,D]).then(()=>{var n;s.register(c),(n=c).regionalChanged=function(n){const t={dirty:{}},a=n.backingData.filtered,i=Ue.defaults;return n.regional?(n.zone=null,p(n)):n.zone||(n.region===i.region?n.zone=i.zone:n.zone=a.zones[0],e.extend(t.dirty,k(n).dirty)),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.regionChanged=function(n){const t={dirty:{}},a=n.backingData.filtered;return e.extend(t.dirty,P(n).dirty),n.region?(e.extend(t.dirty,m(n).dirty),e.extend(t.dirty,k(n).dirty),e.extend(t.dirty,h(n).dirty),e.extend(t.dirty,G(n).dirty),e.extend(t.dirty,b(n).dirty)):a.zones=null,n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.credentialsChanged=function(n){const t={dirty:{}},a=n.backingData;if(n.credentials){const i=a.credentialsKeyedByAccount[n.credentials].regions;oe.isArray(i)?a.filtered.regions=oe.map(i,"name"):a.filtered.regions=oe.keys(i),a.filtered.regions.includes(n.region)?e.extend(t.dirty,n.regionChanged(n).dirty):(n.region=null,t.dirty.region=!0),a.filtered.networks=M(n),a.filtered.networks.includes(n.network)?e.extend(t.dirty,n.networkChanged(n).dirty):(n.network=null,t.dirty.network=!0),e.extend(t.dirty,w(n).dirty),e.extend(t.dirty,m(n).dirty),p(n)}else n.region=null;return n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.networkChanged=function(n){const t={dirty:{}};return n.viewState.autoCreateSubnets=oe.chain(n.backingData.networks).filter({account:n.credentials,id:n.network}).map("autoCreateSubnets").head().value(),n.viewState.subnets=oe.chain(n.backingData.networks).filter({account:n.credentials,id:n.network}).map("subnets").head().value(),e.extend(t.dirty,P(n).dirty),e.extend(t.dirty,x(n).dirty),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),t},n.zoneChanged=function(n){const t={dirty:{}};return void 0!==n.zone||n.regional||(t.dirty.zone=!0),n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),e.extend(n.viewState.dirty,m(n).dirty),e.extend(n.viewState.dirty,h(n).dirty),e.extend(n.viewState.dirty,v(n).dirty),t},n.selectZonesChanged=function(n){const t={dirty:{}};return n.viewState.dirty=n.viewState.dirty||{},e.extend(n.viewState.dirty,t.dirty),e.extend(n.viewState.dirty,v(n).dirty),t},n.customInstanceChanged=function(n){const t={dirty:{}};return n.viewState.dirty=n.viewState.dirty||{},e.extend(t,n.viewState.dirty,f(n).dirty),t}})});var r},configureInstanceTypes:m,configureImages:b,configureZones:k,configureSubnets:P,configureLoadBalancerOptions:G,refreshLoadBalancers:function(e,n){return l.listLoadBalancers("gce").then(function(t){e.backingData.loadBalancers=t,n||G(e)})},refreshSecurityGroups:I,refreshInstanceTypes:function(e){return a.refreshCache("instanceTypes").then(function(){return t.getAllTypesByRegion().then(function(n){e.backingData.instanceTypes=n,m(e)})})},refreshHealthChecks:A}}]);const na="spinnaker.gce.accelerator.component";n(na,[]).component("gceAcceleratorConfigurer",re(i(function({acceleratorConfigs:e=[],availableAccelerators:n=[],regional:t,setAcceleratorConfigs:a,zone:i}){const l=e=>{const t=n.find(({name:n})=>n===e);return ve(t,"availableCardCounts",[])},c=e=>l(e).map(e=>({label:e.toString(),value:e})),r=(e,n)=>{const t=l(e);return t.includes(n)?n:t.reduce((e,t)=>t>n?e:t,1)};return ce.createElement("div",{className:"form-group"},ce.createElement("div",{className:"sm-label-left"},"Accelerators ",ce.createElement(o,{id:"gce.serverGroup.accelerator"})),ce.createElement("table",{className:"table table-condensed packed tags"},ce.createElement("thead",null,ce.createElement("tr",null,ce.createElement("th",{style:{width:"75%"}},"Type"),ce.createElement("th",{style:{width:"15%"}},"Count"),ce.createElement("th",null))),ce.createElement("tbody",null,e.map(({acceleratorType:t,acceleratorCount:i},l)=>ce.createElement("tr",{key:l},ce.createElement("td",null,ce.createElement(Pe,{clearable:!1,onChange:n=>{return t=l,i=n.value,void a(e.map((e,n)=>t!==n?e:{acceleratorType:i,acceleratorCount:r(i,e.acceleratorCount)}));var t,i},options:n.map(({description:e,name:n})=>({label:e,value:n})),value:t})),ce.createElement("td",null,ce.createElement(Pe,{clearable:!1,onChange:n=>{return t=l,i=n.value,void a(e.map((e,n)=>t!==n?e:{...e,acceleratorCount:i}));var t,i},options:c(t),value:i})),ce.createElement("td",null,ce.createElement("button",{className:"btn btn-link",onClick:()=>{return n=l,void a(e.filter((e,t)=>n!==t));var n}},ce.createElement("span",{className:"glyphicon glyphicon-trash"}))))),!se(e)&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"Adding Accelerators places constraints on the instances that you can deploy. See",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#restrictions",target:"_blank"}," ","the complete list of these restrictions")," ","for more information.")),!se(n)&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},ce.createElement("button",{className:"btn btn-block btn-sm add-new",onClick:()=>{se(n)||a(e.concat([{acceleratorType:n[0].name,acceleratorCount:1}]))}},ce.createElement("span",{className:"glyphicon glyphicon-plus-sign"})," Add Accelerator"))),se(n)&&!t&&!i&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"A zone must be selected to configure accelerators. Please note: the set of available accelerator types are limited by zone. See"," ",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#gpus-list",target:"_blank"},"the complete list of types in each zone")," ","for more information.")),se(n)&&!t&&i&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"There are no accelerators available in the currently selected zone.")),se(n)&&t&&ce.createElement("tr",null,ce.createElement("td",{colSpan:3},"There are no accelerators available in all of the currently selected zone(s). Please explicitly select only zones that each support the accelerators you would like to configure. See"," ",ce.createElement("a",{href:"https://cloud.google.com/compute/docs/gpus/#gpus-list",target:"_blank"},"the complete list of types in each zone")," ","for more information.")))))},"gceAcceleratorConfigurer"),["acceleratorConfigs","availableAccelerators","regional","setAcceleratorConfigs","zone"]));const ta="spinnaker.google.serverGroup.configure.wizard.advancedSettings.selector.directive";n(ta,[Ne,Qt]).directive("gceServerGroupAdvancedSettingsSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupAdvancedSettingsSelectorCtrl"}}).controller("gceServerGroupAdvancedSettingsSelectorCtrl",["$scope","gceTagManager",function(e,n){this.addTag=()=>{this.command.tags.push({})},this.removeTag=e=>{this.command.tags.splice(e,1),n.updateSelectedTags()},this.setDisks=e=>{this.command.disks=e},this.inferSelectedSecurityGroupFromTag=n.inferSelectedSecurityGroupFromTag,this.showToolTip=n.showToolTip,this.getToolTipContent=n.getToolTipContent,this.setPreemptible=()=>{this.command.preemptible?(this.command.automaticRestart=!1,this.command.onHostMaintenance="TERMINATE"):(this.command.automaticRestart=!0,this.command.onHostMaintenance="MIGRATE")},this.setEnableVtpm=()=>{this.command.enableVtpm||(this.command.enableIntegrityMonitoring=!1)},this.setAcceleratorConfigs=n=>{e.$apply(()=>{this.command.acceleratorConfigs=n})}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.directive.html",'<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Minimum CPU Platform</b>\n <help-field key="gce.serverGroup.minCpuPlatform"></help-field>\n </div>\n <div class="col-md-6">\n <ui-select ng-model="vm.command.minCpuPlatform" class="form-control input-sm" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices\n repeat="minCpuPlatform in vm.command.backingData.filtered.cpuPlatforms | filter: $select.search"\n >\n <span ng-bind-html="minCpuPlatform | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n\n<gce-disk-configurer\n command="vm.command"\n disks="vm.command.disks"\n update-disks="vm.setDisks(disks)"\n></gce-disk-configurer>\n\n<gce-accelerator-configurer\n accelerator-configs="vm.command.acceleratorConfigs"\n regional="vm.command.regional"\n set-accelerator-configs="vm.setAcceleratorConfigs"\n zone="vm.command.zone"\n available-accelerators="vm.command.viewState.acceleratorTypes"\n></gce-accelerator-configurer>\n\n<div class="form-group">\n <div class="sm-label-left" style="margin-bottom: 5px">\n User Data <help-field key="gce.serverGroup.userData"></help-field>\n </div>\n <div class="col-md-12">\n <textarea class="form-control" ng-model="vm.command.userData" rows="3" placeholder="Plaintext custom user data">\n </textarea>\n </div>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Custom Metadata</b>\n <help-field key="gce.serverGroup.customMetadata"></help-field>\n </div>\n <map-editor model="vm.command.instanceMetadata" add-button-label="Add New Metadata" allow-empty="true"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <table class="table table-condensed packed tags">\n <b>Tags</b>\n <tbody>\n <tr ng-repeat="tag in vm.command.tags">\n <td>\n <input\n class="form-control input-sm"\n type="text"\n ng-model="tag.value"\n ng-change="vm.inferSelectedSecurityGroupFromTag(tag.value)"\n required\n />\n </td>\n <td>\n <help-field ng-if="vm.showToolTip(tag.value)" content="{{vm.getToolTipContent(tag.value)}}"></help-field>\n <a class="btn btn-link sm-label" ng-click="vm.removeTag($index)"\n ><span class="glyphicon glyphicon-trash"></span\n ></a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="1">\n <button class="btn btn-block btn-sm add-new" ng-click="vm.addTag()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Tag\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Labels</b>\n <help-field key="gce.serverGroup.labels"></help-field>\n </div>\n <map-editor model="vm.command.labels" add-button-label="Add New Label" allow-empty="true"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Resource Manager Tags</b>\n <help-field key="gce.serverGroup.resourceManagerTags"></help-field>\n </div>\n <map-editor model="vm.command.resourceManagerTags" add-button-label="Add New Tag" allow-empty="false"></map-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n <b>Partner Metadata</b>\n <help-field key="gce.serverGroup.partnerMetadata"></help-field>\n </div>\n <map-object-editor\n model="vm.command.partnerMetadata"\n add-button-label="Add New Metadata"\n allow-empty="false"\n ></map-object-editor>\n</div>\n<div class="form-group">\n <div class="sm-label-left">\n Shielded VMs\n <help-field key="gce.serverGroup.shieldedVmConfig"></help-field>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableSecureBoot" />\n Turn on Secure Boot\n <help-field key="gce.serverGroup.shieldedVmSecureBoot"></help-field>\n </label>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableVtpm" ng-change="vm.setEnableVtpm()" />\n Turn on vTPM\n <help-field key="gce.serverGroup.shieldedVmVtpm"></help-field>\n </label>\n </div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="vm.command.enableIntegrityMonitoring" ng-disabled="!vm.command.enableVtpm" />\n Turn on Integrity Monitoring\n <help-field key="gce.serverGroup.shieldedVmIntegrityMonitoring"></help-field>\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Preemptibility</b>\n <help-field key="gce.serverGroup.preemptibility"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.preemptible"\n ng-value="false"\n id="preemptibleFalse"\n ng-change="vm.setPreemptible()"\n />\n Off\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.preemptible"\n ng-value="true"\n id="preemptibleTrue"\n ng-change="vm.setPreemptible()"\n />\n On\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Automatic Restart</b>\n <help-field key="gce.serverGroup.automaticRestart"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.automaticRestart" ng-value="false" id="automaticRestartFalse" />\n Off\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.automaticRestart" ng-value="true" id="automaticRestartTrue" />\n On\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>On Host Maintenance</b>\n <help-field key="gce.serverGroup.onHostMaintenance"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.onHostMaintenance" ng-value="\'MIGRATE\'" id="onHostMaintenanceMigrate" />\n Migrate\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.onHostMaintenance"\n ng-value="\'TERMINATE\'"\n id="onHostMaintenanceTerminate"\n />\n Terminate\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right"><b>Associate Public IP Address</b></div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.associatePublicIpAddress"\n ng-value="true"\n id="associatePublicIpAddressTrue"\n />\n Yes\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input\n type="radio"\n ng-model="vm.command.associatePublicIpAddress"\n ng-value="false"\n id="associatePublicIpAddressFalse"\n />\n No\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Can IP Forward</b>\n <help-field key="gce.serverGroup.canIpForward"></help-field>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.canIpForward" ng-value="true" id="canIpForwardTrue" />\n Yes\n </label>\n </div>\n <div class="col-md-2 radio">\n <label>\n <input type="radio" ng-model="vm.command.canIpForward" ng-value="false" id="canIpForwardFalse" />\n No\n </label>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Service Account</b>\n <help-field key="gce.instance.serviceAccount"></help-field>\n </div>\n <div class="col-md-6">\n <input type="text" class="form-control input-sm" ng-model="vm.command.serviceAccountEmail" />\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Auth Scopes</b>\n <help-field key="gce.instance.authScopes"></help-field>\n </div>\n <div class="col-md-6">\n <ui-select\n multiple\n tagging\n tagging-label="(custom auth scope)"\n ng-model="vm.command.authScopes"\n class="form-control input-sm"\n >\n <ui-select-match>{{$item}}</ui-select-match>\n <ui-select-choices repeat="authScope in vm.command.backingData.authScopes | filter: $select.search">\n <span ng-bind-html="authScope | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);class aa{constructor(e){this.$scope=e,this.handleImageChange=(e,n)=>{this.$scope.$apply(()=>{n.sourceImage=e,this.handlePersistentDiskChange()})}}$onInit(){var e,n,t,a,i,l,c,r;this.setLocalSSDCount(),this.setPersistentDisks(),this.getLocalSSDDisks().length&&!(null==(a=null==(t=null==(n=null==(e=this.command)?void 0:e.viewState)?void 0:n.instanceTypeDetails)?void 0:t.storage)?void 0:a.localSSDSupported)&&this.updateDisks({disks:this.sortDisks(this.getPersistentDisks())}),this.isDefault=!!(null==(r=null==(c=null==(l=null==(i=this.command)?void 0:i.viewState)?void 0:l.instanceTypeDetails)?void 0:c.storage)?void 0:r.isDefault)}$onChanges(){this.$onInit()}handleLocalSSDCountChange(){if(void 0===this.localSSDCount)return;let e=this.getPersistentDisks();for(let n=0;n<this.localSSDCount;n++)e=e.concat([{type:"local-ssd",sizeGb:375}]);e=this.sortDisks(e),this.updateDisks({disks:e})}handlePersistentDiskChange(){let e=this.persistentDisks.concat(this.getLocalSSDDisks());e=this.sortDisks(e),this.updateDisks({disks:e})}addPersistentDisk(){this.persistentDisks=this.persistentDisks.concat([{type:"pd-ssd",sizeGb:10,sourceImage:null}]),this.handlePersistentDiskChange()}removePersistentDisk(e){this.persistentDisks.splice(e,1),this.handlePersistentDiskChange()}setPersistentDisks(){this.persistentDisks=this.getPersistentDisks()}setLocalSSDCount(){this.localSSDCount=this.getLocalSSDDisks().length}sortDisks(e){const n=e.find(e=>(e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-"))&&void 0===e.sourceImage);return[n].concat(Be(e,n))}getLocalSSDDisks(){return(this.command.disks||[]).filter(e=>"local-ssd"===e.type)}getPersistentDisks(){return(this.command.disks||[]).filter(e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-"))}}aa.$inject=["$scope"];const ia={controller:aa,bindings:{command:"<",disks:"<",updateDisks:"&"},template:'\n <ng-form name="diskConfigurer">\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Number of Local SSD Disks</b>\n <help-field key="gce.instance.storage.localSSD"></help-field>\n </div>\n <div class="col-md-2">\n <input type="number"\n class="form-control input-sm"\n ng-model="$ctrl.localSSDCount"\n ng-change="$ctrl.handleLocalSSDCountChange()"\n required\n min="0"\n max="{{$ctrl.command.viewState.instanceTypeDetails.storage.localSSDSupported ? 8 : 0}}"\n ng-disabled="!$ctrl.command.viewState.instanceTypeDetails.storage.localSSDSupported"/>\n </div>\n </div>\n <div class="form-group">\n <div class="sm-label-left" style="margin-bottom: 5px;">Persistent Disks\n <span class="glyphicon glyphicon-warning-sign"\n style="color: #EEBB3C;"\n ng-if="$ctrl.isDefault && !diskConfigurer.$dirty"\n uib-tooltip="This instance type does not have an explicitly configured persistent disk option and is using the\n default disk storage specified in settings.js."></span>\n </div>\n <table class="table table-condensed packed tags">\n <thead>\n <tr>\n <th style="width: 25%;">Type</th>\n <th>Size (GB)</th>\n <th style="width: 50%;">Image</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="disk in $ctrl.persistentDisks">\n <td>\n <ui-select ng-model="disk.type" class="form-control input-sm" on-select="$ctrl.handlePersistentDiskChange()" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices repeat="persistentDiskType in $ctrl.command.backingData.persistentDiskTypes | filter: $select.search">\n <span ng-bind-html="persistentDiskType | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </td>\n <td>\n <input type="number"\n class="form-control input-sm"\n ng-model="disk.sizeGb"\n ng-change="$ctrl.handlePersistentDiskChange()"\n required\n min="10"/>\n </td>\n <td>\n <div ng-if="$index === 0">\n <p class="small" style="margin: 0;" ng-if="$ctrl.command.viewState.mode === \'create\' || $ctrl.command.viewState.mode === \'clone\'">\n This disk will use the image selected at the top of this dialogue.\n </p>\n <p class="small" style="margin: 0;" ng-if="$ctrl.command.viewState.mode === \'createPipeline\' || $ctrl.command.viewState.mode === \'editPipeline\'">\n This disk will use the image inferred from this pipeline\'s execution context.\n </p>\n </div>\n <gce-image-select\n ng-if="$index > 0"\n available-images="$ctrl.command.backingData.allImages"\n selected-image="disk.sourceImage"\n select-image="$ctrl.handleImageChange"\n target="disk"\n ></gce-image-select>\n </td>\n <td ng-if="$index > 0">\n <a class="btn btn-link sm-label" style="margin-top: 0;" ng-click="$ctrl.removePersistentDisk($index)">\n <span class="glyphicon glyphicon-trash"></span>\n </a>\n </td>\n </tr>\n </tbody>\n <tfoot>\n <tr>\n <td colspan="4">\n <button class="btn btn-block btn-sm add-new" ng-click="$ctrl.addPersistentDisk()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add New Persistent Disk\n </button>\n </td>\n </tr>\n </tfoot>\n </table>\n </div>\n </ng-form>'},la="spinnaker.gce.diskConfigurer.component";n(la,[]).component("gceDiskConfigurer",ia);const ca={bindings:{onHealthCheckRefresh:"&",setAutoHealingPolicy:"&",healthChecks:"<",autoHealingPolicy:"<",enabled:"<",labelColumns:"@?"},templateUrl:"google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicySelector.component.html",controller:class{$onInit(){this.autoHealingPolicy&&this.autoHealingPolicy.maxUnavailable&&("number"==typeof this.autoHealingPolicy.maxUnavailable.fixed?this.viewState={maxUnavailableMetric:"fixed"}:"number"==typeof this.autoHealingPolicy.maxUnavailable.percent&&(this.viewState={maxUnavailableMetric:"percent"})),this.autoHealingPolicy||this.setAutoHealingPolicy({autoHealingPolicy:{initialDelaySec:300}})}$onDestroy(){this.setAutoHealingPolicy({autoHealingPolicy:null})}manageMaxUnavailableMetric(e){if(e){const n="percent"===e?"fixed":"percent";Te(this.autoHealingPolicy,["maxUnavailable",n],void 0)}else this.autoHealingPolicy.maxUnavailable={}}onHealthCheckChange(e,n){if(n){const{healthCheckName:e,healthCheckKind:t}=Xt(n);this.autoHealingPolicy.healthCheck=e,this.autoHealingPolicy.healthCheckKind=t}}}},ra="spinnaker.gce.autoHealingPolicy.selector.component";n(ra,[]).component("gceAutoHealingPolicySelector",ca),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicySelector.component.html",'<div class="form-group">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Health Check</b>\n </div>\n <div class="col-md-6">\n <ui-select\n ng-model="$ctrl.autoHealingPolicy.healthCheckUrl"\n on-select="$ctrl.onHealthCheckChange($item, $model)"\n class="form-control input-sm"\n required\n >\n <ui-select-match placeholder="Select...">{{ $select.selected.displayName }}</ui-select-match>\n <ui-select-choices\n repeat="healthCheck.selfLink as healthCheck in $ctrl.healthChecks | orderBy: \'displayName\' | filter: { displayName: $select.search }"\n >\n <span ng-bind-html="healthCheck.displayName | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Initial Delay</b>\n </div>\n <div class="col-md-6">\n <div class="input-group">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.autoHealingPolicy.initialDelaySec"\n min="0"\n max="2147483647"\n required\n />\n <span class="input-group-addon">seconds</span>\n </div>\n </div>\n</div>\n<div class="form-group" ng-switch on="$ctrl.viewState.maxUnavailableMetric">\n <div class="col-md-{{::$ctrl.labelColumns || 3}} sm-label-right">\n <b>Max Unavailable <help-field key="gce.serverGroup.maxUnavailable"></help-field></b>\n </div>\n <div class="col-md-3" ng-switch-default>\n <input type="number" style="width: 130px" readonly class="form-control input-sm" />\n </div>\n <div class="col-md-3" ng-switch-when="percent">\n <div class="input-group" style="width: 130px">\n <input\n class="form-control input-sm"\n ng-model="$ctrl.autoHealingPolicy.maxUnavailable.percent"\n required\n type="number"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div class="col-md-3" ng-switch-when="fixed">\n <input\n class="form-control input-sm"\n style="width: 130px"\n required\n ng-model="$ctrl.autoHealingPolicy.maxUnavailable.fixed"\n type="number"\n min="0"\n max="2147483647"\n />\n </div>\n <div class="col-md-3">\n <select\n ng-model="$ctrl.viewState.maxUnavailableMetric"\n ng-change="$ctrl.manageMaxUnavailableMetric($ctrl.viewState.maxUnavailableMetric)"\n ng-options="metric for metric in [\'percent\', \'fixed\']"\n class="form-control input-sm"\n >\n <option value="">-- select metric --</option>\n </select>\n </div>\n</div>\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-7 col-md-offset-{{::$ctrl.labelColumns || 3}}">\n <gce-cache-refresh\n cache-key="healthChecks"\n cache-key-alias="Health checks"\n on-refresh="$ctrl.onHealthCheckRefresh()"\n >\n </gce-cache-refresh>\n </div>\n</div>\n')}]);Le(".gce-scaling-policy-wizard .error-message {\n margin-top: 4px;\n}\n.gce-scaling-policy-wizard input.form-control,\n.gce-scaling-policy-wizard select.form-control {\n margin-bottom: 2px;\n width: 100%;\n}\n.gce-scaling-policy-wizard .schedule-container {\n overflow: hidden;\n}\n");const oa="spinnaker.gce.autoScalingPolicy.selector.component";n(oa,[qt,_t,jt]).controller("gceUpsertAutoscalingPolicyCtrl",["$scope",function(e){this.$onInit=function(){this.policy=ke({}),this.setAutoScalingPolicy({autoscalingPolicy:this.policy}),this.updatePolicy=n=>{e.$applyAsync(()=>{this.policy=n,this.setAutoScalingPolicy({autoscalingPolicy:this.policy})})}}}]).component("gceAutoScalingPolicySelectorComponent",{bindings:{policy:"<",enabled:"<",setAutoScalingPolicy:"&",autoscalingPolicy:"<"},templateUrl:"google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.html",controller:"gceUpsertAutoscalingPolicyCtrl"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.html",'<div class="gce-scaling-policy-wizard">\n <h5 class="section-heading">Basic Settings</h5>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="$ctrl.policy" update-policy="$ctrl.updatePolicy">\n </gce-autoscaling-policy-basic-settings>\n </div>\n <h5 class="section-heading">Metric Types</h5>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="$ctrl.showNoMetricsWarning"\n policy="$ctrl.policy"\n update-policy="$ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h5 class="section-heading">Scaling Schedules</h5>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="$ctrl.policy"\n update-policy="$ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n</div>\n')}]);const sa="spinnaker.deck.gce.serverGroup.configure.advancedCapacitySelector.component";n(sa,[]).component("gceServerGroupAdvancedCapacitySelector",{bindings:{command:"="},templateUrl:"google/src/serverGroup/configure/wizard/capacity/advancedCapacitySelector.component.html"}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/advancedCapacitySelector.component.html",'<div class="form-group">\n <div class="col-md-12">\n <p>Sets min, max, and desired instance counts for this server group\'s autoscaling policy.</p>\n </div>\n</div>\n<div class="form-group">\n <div class="col-md-2 col-md-offset-3">Min</div>\n <div class="col-md-2">Max</div>\n <div class="col-md-2">Desired</div>\n</div>\n<div class="form-group">\n <div class="col-md-3 sm-label-right">Capacity</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.autoscalingPolicy.minNumReplicas"\n min="0"\n max="{{ $ctrl.command.autoscalingPolicy.maxNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.autoscalingPolicy.maxNumReplicas"\n min="{{ $ctrl.command.autoscalingPolicy.minNumReplicas }}"\n required\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.capacity.desired"\n min="{{ $ctrl.command.autoscalingPolicy.minNumReplicas }}"\n max="{{ $ctrl.command.autoscalingPolicy.maxNumReplicas }}"\n required\n />\n </div>\n</div>\n')}]);const da="spinnaker.google.serverGroup.configure.wizard.simpleCapacity.selector.component";n(da,[]).component("gceServerGroupSimpleCapacitySelector",{templateUrl:"google/src/serverGroup/configure/wizard/capacity/simpleCapacitySelector.component.html",bindings:{command:"=",setSimpleCapacity:"="},controller:"gceServerGroupSimpleCapacitySelectorCtrl"}).controller("gceServerGroupSimpleCapacitySelectorCtrl",function(){this.setMinMax=e=>{this.command.capacity.min=e,this.command.capacity.max=e}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/simpleCapacitySelector.component.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right">Number of Instances</div>\n <help-field key="gce.serverGroup.capacity"></help-field>\n <div class="col-md-2">\n <input\n type="number"\n ng-change="$ctrl.setMinMax($ctrl.command.capacity.desired)"\n class="form-control input-sm"\n ng-model="$ctrl.command.capacity.desired"\n min="0"\n required\n />\n </div>\n</div>\n')}]);Le(".gce-instance-build-custom-select {\n margin-top: 5px;\n}\n.gce-instance-build-custom-select .gce-instance-build-custom-extended-memory-checkbox {\n display: flex;\n}\n.gce-instance-build-custom-select .gce-instance-build-custom-extended-memory-checkbox-helptext {\n margin: 8px 0px 0px 3px;\n}\n");class ua extends ce.Component{constructor(){super(...arguments),this.handleVCpuChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:n,memory:this.props.selectedMemory,extendedMemory:this.props.selectedExtendedMemory})},this.handleMemoryChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:n,extendedMemory:this.props.selectedExtendedMemory})},this.handleInstanceFamilyChange=e=>{const n=e?e.value:null;this.props.onChange({instanceFamily:n,vCpuCount:this.props.selectedVCpuCount,memory:this.props.selectedMemory,extendedMemory:"E2"!==n&&this.props.selectedExtendedMemory})},this.handleMemoryChangeCustom=e=>{const n=+e;this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:n,extendedMemory:this.props.selectedExtendedMemory})},this.handleExtendedMemory=e=>{this.props.onChange({instanceFamily:this.props.selectedInstanceFamily,vCpuCount:this.props.selectedVCpuCount,memory:this.props.selectedMemory,extendedMemory:e})}}render(){const e=(this.props.instanceFamilyList||[]).map(e=>({label:e,value:e})),n=(this.props.vCpuList||[]).map(e=>({label:e+"",value:e})),t=(this.props.memoryList||[]).map(e=>({label:e+"",value:e})),a=this.props.selectedVCpuCount?this.props.selectedVCpuCount+"":null,i=this.props.selectedMemory?this.props.selectedMemory+"":null,l=this.props.selectedInstanceFamily?this.props.selectedInstanceFamily:null;return ce.createElement("div",null,ce.createElement("div",{className:"row"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Family ")),ce.createElement("div",{className:"col-md-3"},ce.createElement(Pe,{options:e,clearable:!1,value:{label:l,value:this.props.selectedInstanceFamily},onChange:this.handleInstanceFamilyChange}))),ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Cores "),ce.createElement(o,{id:"gce.instance.customInstance.cores"})),ce.createElement("div",{className:"col-md-3"},ce.createElement(Pe,{options:n,clearable:!1,value:{label:a,value:this.props.selectedVCpuCount},onChange:this.handleVCpuChange}))),ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"},ce.createElement("b",null,"Memory (Gb) "),ce.createElement(o,{id:"gce.instance.customInstance.memory"})),ce.createElement("div",{className:"col-md-3"},this.props.selectedExtendedMemory?ce.createElement("input",{type:"number",name:"memory",className:"form-control",value:this.props.selectedMemory,onChange:e=>this.handleMemoryChangeCustom(e.target.value)}):ce.createElement(Pe,{options:t,clearable:!1,value:{label:i,value:this.props.selectedMemory},onChange:this.handleMemoryChange}))),"E2"!==this.props.selectedInstanceFamily?ce.createElement("div",{className:"row gce-instance-build-custom-select"},ce.createElement("div",{className:"col-md-5 sm-label-right"}),ce.createElement("div",{className:"col-md-3"},ce.createElement("span",{className:"gce-instance-build-custom-extended-memory-checkbox"},ce.createElement(r,{name:"extendedMemory",text:"Extended Memory",checked:this.props.selectedExtendedMemory,onChange:e=>{this.handleExtendedMemory(e.target.checked)}}),ce.createElement("div",{className:"gce-instance-build-custom-extended-memory-checkbox-helptext"},ce.createElement(o,{id:"gce.instance.customInstance.extendedmemory"}))))):null)}}const ga="spinnaker.gce.customInstanceConfigurer";n(ga,[]).component("gceCustomInstanceConfigurer",re(i(ua,"gceCustomInstanceConfigurer"),["vCpuList","instanceFamilyList","memoryList","selectedVCpuCount","selectedMemory","selectedInstanceFamily","selectedExtendedMemory","onChange"]));const pa="spinnaker.deck.gce.backendServiceSelector.component";n(pa,[]).component("gceBackendServiceSelector",{bindings:{command:"=",loadBalancerName:"="},templateUrl:"google/src/serverGroup/configure/wizard/loadBalancers/elSevenOptions/backendServiceSelector.component.html",controller:["$scope",function(e){e.$on("$destroy",()=>{this.command.backendServices&&delete this.command.backendServices[this.loadBalancerName]}),e.$on("uis:select",function(e){e.preventDefault()})}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/elSevenOptions/backendServiceSelector.component.html",'<div class="row">\n <div class="col-md-4 sm-label-right">\n <b style="font-family: \'Source Sans 3\', sans-serif; font-size: 14px; color: #333">Backend services</b>\n </div>\n <div class="col-md-8">\n <ui-select\n multiple\n nested\n required\n ng-model="$ctrl.command.backendServices[$ctrl.loadBalancerName]"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices\n repeat="backendService in $ctrl.command.backingData.filtered.loadBalancerIndex[$ctrl.loadBalancerName].backendServices | filter: $select.search"\n >\n <span ng-bind-html="backendService | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const ma="spinnaker.deck.gce.elSevenOptionsGenerator.component";e.module(ma,[pa]).directive("gceElSevenOptionsGenerator",["$compile",function(n){return{restrict:"E",scope:{command:"=",loadBalancerName:"@"},link:function(t,a){const i=n('<gce-backend-service-selector load-balancer-name="loadBalancerName" command="command">\n </gce-backend-service-selector>')(t),l=e.element(a).closest(".ui-select-match-item");l.after(i),t.$on("$destroy",()=>{l.next().remove()})}}}]);const ha="spinnaker.google.serverGroup.configure.wizard.loadBalancers.selector.directive";e.module(ha,[nn,ma,ea]).directive("gceServerGroupLoadBalancerSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/loadBalancers/loadBalancerSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupLoadBalancerSelectorCtrl"}}).controller("gceServerGroupLoadBalancerSelectorCtrl",["gceHttpLoadBalancerUtils",function(n){this.showLoadBalancingPolicy=()=>{if(oe.has(this,"command.backingData.filtered.loadBalancerIndex")){const n=this.command.backingData.filtered.loadBalancerIndex,t=this.command.loadBalancers;return e.isDefined(t)&&oe.some(t,e=>"HTTP"===n[e].loadBalancerType||"INTERNAL_MANAGED"===n[e].loadBalancerType||"SSL"===n[e].loadBalancerType||"TCP"===n[e].loadBalancerType)}},this.isHttpLoadBalancer=e=>n.isHttpLoadBalancer(e)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/loadBalancerSelector.directive.html",'<div class="row">\n <div class="col-md-12" ng-if="vm.command.viewState.dirty.loadBalancers">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i>\n The following load balancers could not be found in the selected account/region and were removed:\n </p>\n <ul>\n <li ng-repeat="loadBalancer in vm.command.viewState.dirty.loadBalancers">{{loadBalancer}}</li>\n </ul>\n <p class="text-right">\n <a\n class="btn btn-sm btn-default dirty-flag-dismiss"\n href\n ng-click="vm.command.viewState.dirty.loadBalancers = null"\n >Okay</a\n >\n </p>\n </div>\n </div>\n\n <div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Load Balancers</b></div>\n <div class="col-md-9">\n <ui-select\n ng-if="vm.command.backingData.filtered.loadBalancers.length"\n multiple\n ng-model="vm.command.loadBalancers"\n class="form-control input-sm"\n >\n <ui-select-match>\n {{$item}}\n <gce-el-seven-options-generator\n ng-if="vm.isHttpLoadBalancer(vm.command.backingData.filtered.loadBalancerIndex[$item])"\n command="vm.command"\n load-balancer-name="{{:: $item}}"\n >\n </gce-el-seven-options-generator>\n </ui-select-match>\n <ui-select-choices\n repeat="loadBalancer in vm.command.backingData.filtered.loadBalancers | filter: $select.search"\n >\n <span ng-bind-html="loadBalancer | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <gce-load-balancing-policy-selector\n ng-if="vm.showLoadBalancingPolicy()"\n command="vm.command"\n ></gce-load-balancing-policy-selector>\n</div>\n')}]);Le("gce-load-balancing-policy-selector .collapsible-subsection .collapsible-subheading {\n padding-left: 4%;\n}\ngce-load-balancing-policy-selector .collapsible-subsection .ng-invalid {\n border: 1px solid var(--color-danger);\n}\n");class fa{constructor(e){this.gceBackendServiceReader=e,this.maxPort=65535}setModel(e,n){Te(this,e,n/100)}setView(e,n){this[e]=this.decimalToPercent(n)}onBalancingModeChange(e){const n=["maxUtilization","maxRatePerInstance","maxConnectionsPerInstance"];let t=[];switch(e){case"RATE":t=Be(n,"maxRatePerInstance");break;case"UTILIZATION":t=Be(n,"maxUtilization");break;case"CONNECTION":t=Be(n,"maxConnectionsPerInstance")}t.forEach(e=>delete this.command.loadBalancingPolicy[e])}getBalancingModes(){let e=[];if(Se(this,"command.backingData.filtered.loadBalancerIndex")){const n=this.command.backingData.filtered.loadBalancerIndex,t=this.command.loadBalancers,a=t.find(e=>"SSL"===ve(n[e],"loadBalancerType")),i=t.find(e=>"TCP"===ve(n[e],"loadBalancerType")),l=t.find(e=>"HTTP"===ve(n[e],"loadBalancerType"))||t.find(e=>"HTTP2"===ve(n[e],"loadBalancerType"))||t.find(e=>"GRPC"===ve(n[e],"loadBalancerType"));e=(a||i)&&l?["UTILIZATION"]:a||i?["CONNECTION","UTILIZATION"]:["RATE","UTILIZATION"]}return e.includes(ve(this.command,"loadBalancingPolicy.balancingMode"))||Te(this.command,"loadBalancingPolicy.balancingMode",e[0]),e}$onInit(){this.gceBackendServiceReader.listBackendServices("globalBackendService").then(e=>{this.globalBackendServices=e})}$onDestroy(){delete this.command.loadBalancingPolicy}addNamedPort(){ve(this.command,"loadBalancingPolicy.namedPorts")||Te(this.command,"loadBalancingPolicy.namedPorts",[]),this.command.loadBalancingPolicy.namedPorts.push({name:"",port:80})}removeNamedPort(e){this.command.loadBalancingPolicy.namedPorts.splice(e,1)}getPortNames(){const e=this.command.backingData.filtered.loadBalancerIndex,n=this.command.loadBalancers,t=this.command.loadBalancingPolicy.namedPorts.map(e=>e.name);return ge(n).flatMap(n=>((n,a)=>{switch(ve(e[a],"loadBalancerType")){case"SSL":case"TCP":case"GRPC":case"HTTP2":case"HTTP":{const i=ve(e[a],"backendServices"),l=n.filter(e=>i.includes(e.name)).map(e=>e.portName),c=Ge(l,t);return l.filter(e=>!c.includes(e))}default:return[]}})(this.globalBackendServices,n)).uniq().value()}decimalToPercent(e){return 0===e?0:e?Math.round(100*e):void 0}}fa.$inject=["gceBackendServiceReader"];const va={bindings:{command:"="},controller:fa,templateUrl:"google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html"},ba="spinnaker.gce.loadBalancingPolicy.selector.component";n(ba,[]).component("gceLoadBalancingPolicySelector",va),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html",'<ng-form name="loadBalancingPolicySubForm">\n <collapsible-section heading="Port Name Mapping" expanded="true" subsection="true">\n <div class="form-group">\n <div class="form-group row" ng-repeat="namedPort in $ctrl.command.loadBalancingPolicy.namedPorts">\n <div class="col-md-12">\n <div class="col-md-2 sm-label-right">\n <b>Name</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.portName"></help-field>\n </div>\n <div class="col-md-3">\n <ui-select ng-model="namedPort.name" class="form-control input-sm">\n <ui-select-match>{{ namedPort.name }}</ui-select-match>\n <ui-select-choices repeat="portName in $ctrl.getPortNames()">\n <span ng-bind-html="portName"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n <div class="col-md-2 sm-label-right">\n <b>Port</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.listeningPort"></help-field>\n </div>\n <div class="col-md-3">\n <input\n type="number"\n name="listeningPort"\n required\n class="form-control input-sm"\n ng-model="namedPort.port"\n max="{{ $ctrl.maxPort }}"\n min="1"\n />\n </div>\n <div class="col-md-2 sm-label-left">\n <span\n class="glyphicon glyphicon-trash"\n uib-tooltip="Remove Named Port"\n ng-click="$ctrl.removeNamedPort($index)"\n ></span>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.listeningPort.$error.min || loadBalancingPolicySubForm.listeningPort.$error.max\n "\n >\n Must be between 1 and {{ $ctrl.maxPort }}.\n </div>\n </div>\n </div>\n <div class="col-md-11">\n <button class="btn btn-block add-new" ng-click="$ctrl.addNamedPort()">\n <span class="glyphicon glyphicon-plus-sign"></span> Add Port Name Mapping\n </button>\n </div>\n </div>\n </collapsible-section>\n <collapsible-section heading="Capacity Metrics" expanded="true" subsection="true">\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Balancing Mode</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.balancingMode"></help-field>\n </div>\n <div class="col-md-3">\n <ui-select\n ng-model="$ctrl.command.loadBalancingPolicy.balancingMode"\n on-select="$ctrl.onBalancingModeChange($item)"\n class="form-control input-sm"\n required\n >\n <ui-select-match placeholder="Select...">{{ $select.selected }}</ui-select-match>\n <ui-select-choices repeat="balancingMode in $ctrl.getBalancingModes() | filter: $select.search">\n <span ng-bind-html="balancingMode"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n <div ng-switch on="$ctrl.command.loadBalancingPolicy.balancingMode">\n <div class="form-group" ng-switch-when="UTILIZATION">\n <div class="col-md-5 sm-label-right">\n <b>Max CPU utilization</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxUtilization"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxUtilization"\n required\n class="form-control input-sm"\n ng-model="$ctrl.maxUtilizationView"\n ng-init="$ctrl.setView(\'maxUtilizationView\', $ctrl.command.loadBalancingPolicy.maxUtilization)"\n ng-change="$ctrl.setModel(\'command.loadBalancingPolicy.maxUtilization\', $ctrl.maxUtilizationView)"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.maxUtilization.$error.min || loadBalancingPolicySubForm.maxUtilization.$error.max\n "\n >\n Must be between 0 and 100%.\n </div>\n </div>\n <div class="form-group" ng-switch-when="RATE">\n <div class="col-md-5 sm-label-right">\n <b>Max RPS per instance</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxRatePerInstance"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxRatePerInstance"\n required\n class="form-control input-sm"\n ng-model="$ctrl.command.loadBalancingPolicy.maxRatePerInstance"\n min="0"\n />\n </div>\n </div>\n <div class="col-md-4 error-message" ng-if="loadBalancingPolicySubForm.maxRatePerInstance.$error.min">\n Cannot be less than zero.\n </div>\n </div>\n <div class="form-group" ng-switch-when="CONNECTION">\n <div class="col-md-5 sm-label-right">\n <b>Max connections per instance</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="maxConnectionsPerInstance"\n required\n class="form-control input-sm"\n ng-model="$ctrl.command.loadBalancingPolicy.maxConnectionsPerInstance"\n min="0"\n />\n </div>\n </div>\n <div class="col-md-4 error-message" ng-if="loadBalancingPolicySubForm.maxConnectionsPerInstance.$error.min">\n Cannot be less than zero.\n </div>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-5 sm-label-right">\n <b>Capacity</b>\n <help-field key="gce.serverGroup.loadBalancingPolicy.capacityScaler"></help-field>\n </div>\n <div class="col-md-3">\n <div class="input-group">\n <input\n type="number"\n name="capacityScaler"\n required\n class="form-control input-sm"\n ng-model="$ctrl.capacityScalerView"\n ng-init="$ctrl.setView(\'capacityScalerView\', $ctrl.command.loadBalancingPolicy.capacityScaler)"\n ng-change="$ctrl.setModel(\'command.loadBalancingPolicy.capacityScaler\', $ctrl.capacityScalerView)"\n min="0"\n max="100"\n />\n <span class="input-group-addon">%</span>\n </div>\n </div>\n <div\n class="col-md-4 error-message"\n ng-if="\n loadBalancingPolicySubForm.capacityScaler.$error.min || loadBalancingPolicySubForm.capacityScaler.$error.max\n "\n >\n Must be between 0 and 100%.\n </div>\n </div>\n </collapsible-section>\n</ng-form>\n')}]);const ya="spinnaker.google.networkSelectField.directive";n(ya,[]).directive("gceNetworkSelectField",function(){return{restrict:"E",templateUrl:"google/src/networkSelectField.directive.html",scope:{networks:"=",component:"=",field:"@",account:"=",helpKey:"@",onChange:"&",labelColumns:"@",fieldColumns:"@"}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/networkSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">\n Network <help-field ng-if="helpKey" key="{{helpKey}}"></help-field>\n </div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!account">(Select an account)</div>\n <div class="col-md-{{fieldColumns || 7}}">\n <select class="form-control input-sm" ng-if="account" ng-model="component.network" ng-change="onChange()" required>\n <option ng-repeat="network in networks" value="{{network}}" ng-selected="component[field] === network">\n {{network}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const ka="spinnaker.google.subnet.subnetSelectField.directive";n(ka,[]).directive("gceSubnetSelectField",function(){return{restrict:"E",templateUrl:"google/src/subnet/subnetSelectField.directive.html",scope:{subnets:"=",subnetPlaceholder:"=",autoCreateSubnets:"=",component:"=",field:"@",account:"=",region:"=",onChange:"&",labelColumns:"@",helpKey:"@"}}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/subnet/subnetSelectField.directive.html",'<div class="form-group">\n <div class="col-md-{{labelColumns}} sm-label-right">\n Subnet\n <help-field key="{{helpKey}}"></help-field>\n </div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="subnetPlaceholder">{{subnetPlaceholder}}</div>\n <div class="col-md-{{fieldColumns || 7}}" ng-if="!subnetPlaceholder">\n <select\n class="form-control input-sm"\n ng-if="account"\n ng-model="component.subnet"\n ng-change="onChange()"\n ng-required="autoCreateSubnets === false"\n >\n <option ng-repeat="subnet in subnets" value="{{subnet}}" ng-selected="component[field] === subnet">\n {{subnet}}\n </option>\n </select>\n <p ng-if="readOnly" class="form-control-static">{{component[field]}}</p>\n </div>\n</div>\n')}]);const Sa="spinnaker.google.serverGroup.configure.wizard.basicSettings.controller";e.module(Sa,[xe,Ie,F,Jn,ya,ka]).controller("gceServerGroupBasicSettingsCtrl",["$scope","$controller","$uibModalStack","$state",function(n,t,a,i){const l=new De;l.pipe(Re(function(){return Ee(Ze.findImages({account:n.command.credentials,provider:n.command.selectedProvider,q:"*"}))})).subscribe(e=>{n.command.backingData.allImages=e}),this.accountUpdated=()=>{l.next()},this.selectImage=e=>{n.$apply(()=>{n.command.image=e})},e.extend(this,t("BasicSettingsMixin",{$scope:n,imageReader:Ze,$uibModalStack:a,$state:i})),this.stackPattern={test:function(e){return(n.command.viewState.templatingEnabled?/^([a-zA-Z0-9]*(\${.+})*)*$/:/^[a-zA-Z0-9]*$/).test(e)}},this.detailPattern={test:function(e){return(n.command.viewState.templatingEnabled?/^([a-zA-Z0-9-]*(\${.+})*)*$/:/^[a-zA-Z0-9-]*$/).test(e)}},this.getSubnetPlaceholder=()=>n.command.region?n.command.viewState.autoCreateSubnets?"(Subnet will be automatically selected)":null===n.command.viewState.autoCreateSubnets?"(Subnets not supported)":null:"(Select an account)",this.imageSources=["artifact","priorStage"],this.excludedImageArtifactTypes=U(O.CUSTOM_OBJECT),this.onImageArtifactEdited=e=>{n.$applyAsync(()=>{n.command.imageArtifactId=null,n.command.imageArtifact=e})},this.onImageArtifactSelected=e=>{this.onChangeImageArtifactId(e.id)},this.onChangeImageArtifactId=e=>{n.$applyAsync(()=>{n.command.imageArtifactId=e,n.command.imageArtifact=null})},this.onImageArtifactAccountSelected=e=>{n.$applyAsync(()=>{n.command.imageAccountName=e})};const c=new q(n);n.gceImageArtifact={showCreateArtifactForm:!1,delegate:c,controller:new V(c)}}]);const wa="spinnaker.deck.gce.tagSelector.component";n(wa,[Qt]).component("gceTagSelector",{bindings:{command:"=",securityGroupId:"="},templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/tagSelector.component.html",controller:["$scope","gceTagManager",function(e,n){this.securityGroup=n.securityGroupObjectsKeyedById[this.securityGroupId],this.onSelect=n.addTag,this.onRemove=n.removeTag,e.$on("uis:select",function(e){e.preventDefault()})}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/tagSelector.component.html",'<div class="row">\n <div class="col-md-4 sm-label-right">\n <b style="font-family: \'Source Sans 3\', sans-serif; font-size: 14px; color: #333"\n >Target Tags\n <help-field\n ng-if="!$ctrl.securityGroup.selectedTags.length"\n key="gce.serverGroup.securityGroups.targetTags"\n ></help-field>\n </b>\n </div>\n <div class="col-md-8">\n <ui-select\n multiple\n on-remove="$ctrl.onRemove($item)"\n on-select="$ctrl.onSelect($item)"\n nested\n ng-model="$ctrl.securityGroup.selectedTags"\n class="form-control input-sm"\n >\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="tag in $ctrl.securityGroup.tagsArray | filter: $select.search">\n <span ng-bind-html="tag | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Ca="spinnaker.deck.gce.tagSelectorGenerator.component";e.module(Ca,[wa,Qt]).directive("gceTagSelectorGenerator",["$compile","gceTagManager",function(n,t){return{restrict:"E",scope:{command:"=",securityGroupId:"="},link:function(a,i){const{securityGroupId:l}=a,c=t.securityGroupObjectsKeyedById[l];if(c&&c.tagsArray.length<2)return void c.tagsArray.forEach(e=>t.addTag(e));const r=n('<gce-tag-selector security-group-id="securityGroupId" command="command">\n </gce-tag-selector>')(a);e.element(i).closest(".ui-select-match-item").after(r)}}}]);const Ba="spinnaker.google.serverGroup.configure.wizard.securityGroups.selector.directive";n(Ba,[ea,Ca,Qt]).directive("gceServerGroupSecurityGroupSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/securityGroupSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupSecurityGroupsSelectorCtrl"}}).controller("gceServerGroupSecurityGroupsSelectorCtrl",["gceServerGroupConfigurationService","gceTagManager",function(e,n){this.getSecurityGroupRefreshTime=()=>d.get("securityGroups").getStats().ageMax,this.refreshSecurityGroups=()=>{this.refreshing=!0,e.refreshSecurityGroups(this.command).then(()=>{this.refreshing=!1})},this.onRemove=n.removeSecurityGroup}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroupSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b><firewall-label label="Firewalls"></firewall-label></b>\n </div>\n <div class="col-md-9">\n <ui-select\n ng-if="vm.command.backingData.filtered.securityGroups.length"\n multiple\n on-remove="vm.onRemove($item.id)"\n ng-model="vm.command.securityGroups"\n class="form-control input-sm"\n >\n <ui-select-match\n >{{$item.name}}\n <gce-tag-selector-generator security-group-id="$item.id" command="vm.command"> </gce-tag-selector-generator>\n </ui-select-match>\n <ui-select-choices\n repeat="securityGroup.id as securityGroup in vm.command.backingData.filtered.securityGroups | anyFieldFilter: {name: $select.search, id: $select.search}"\n >\n <span ng-bind-html="securityGroup.name | highlight: $select.search"></span>\n (<span ng-bind-html="securityGroup.id | highlight: $select.search"></span>)\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-9 col-md-offset-3 checkbox">\n <p>\n <label\n ><input type="checkbox" ng-model="vm.command.viewState.listImplicitSecurityGroups" /> Show Implicit\n <firewall-label label="Firewalls"></firewall-label>\n </label>\n <help-field key="gce.serverGroup.securityGroups.implicit"></help-field>\n </p>\n </div>\n</div>\n\n<div class="form-group" ng-if="vm.command.viewState.listImplicitSecurityGroups">\n <div class="col-md-3 sm-label-right">\n <b>Implicit <firewall-label label="Firewalls"></firewall-label></b>\n </div>\n <div class="col-md-9">\n <ul ng-if="vm.command.implicitSecurityGroups.length" style="margin-top: 10px; list-style-type: none">\n <li ng-repeat="securityGroup in vm.command.implicitSecurityGroups">{{securityGroup.name}}</li>\n </ul>\n <ul ng-if="!vm.command.implicitSecurityGroups.length" style="margin-top: 10px; list-style-type: none">\n <li>None</li>\n </ul>\n </div>\n</div>\n\n<div class="form-group small" style="margin-top: 20px">\n <div class="col-md-9 col-md-offset-3">\n <p>\n <span ng-if="refreshing"><span class="fa fa-sync-alt fa-spin"></span></span>\n <firewall-label label="Firewalls"></firewall-label>\n <span ng-if="!refreshing">last refreshed {{ vm.getSecurityGroupRefreshTime() | timestamp }}</span>\n <span ng-if="refreshing"> refreshing...</span>\n </p>\n <p>\n If you\'re not finding a <firewall-label label="firewall"></firewall-label> that was recently added,\n <a href ng-click="vm.refreshSecurityGroups()">click here</a> to refresh the list.\n </p>\n </div>\n</div>\n')}]);const Ta="spinnaker.google.serverGroup.configure.wizard.securityGroups.removed.directive";n(Ta,[]).directive("gceServerGroupSecurityGroupsRemoved",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/securityGroups/securityGroupsRemoved.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:"gceServerGroupSecurityGroupsRemovedCtrl"}}).controller("gceServerGroupSecurityGroupsRemovedCtrl",function(){this.acknowledgeSecurityGroupRemoval=()=>{this.command.viewState.dirty.securityGroups=null}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroupsRemoved.directive.html",'<div class="col-md-12" ng-if="vm.command.viewState.dirty.securityGroups">\n <div class="alert alert-warning">\n <p>\n <i class="fa fa-exclamation-triangle"></i> The following <firewall-label label="firewalls"></firewall-label> could\n not be found in the selected account/network and were removed:\n </p>\n <ul>\n <li ng-repeat="securityGroup in vm.command.viewState.dirty.securityGroups">{{securityGroup}}</li>\n </ul>\n <p class="text-right">\n <a class="btn btn-sm btn-default dirty-flag-dismiss" href ng-click="vm.acknowledgeSecurityGroupRemoval()">Okay</a>\n </p>\n </div>\n</div>\n')}]);const Ga="spinnaker.google.serverGroup.configure.wizard.capacity.regional.directive";e.module(Ga,[]).directive("gceRegionalSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/regionalSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:e.noop}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/regionalSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Regional</b></div>\n <div class="col-md-7 checkbox">\n <label\n ><input type="checkbox" ng-model="vm.command.regional" />\n Distribute instances across multiple zones\n </label>\n <label ng-if="vm.command.regional"\n ><input type="checkbox" ng-model="vm.command.selectZones" />\n Select zones explicitly\n </label>\n </div>\n</div>\n')}]);const Aa="spinnaker.google.serverGroup.configure.wizard.capacity.targetShape.directive";n(Aa,[]).directive("gceTargetShapeSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/targetShapeSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:t}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/targetShapeSelector.directive.html",'<div class="form-group">\n <div class="col-md-3 sm-label-right"><b>Target Shape</b></div>\n <div class="col-md-3">\n <ui-select ng-model="vm.command.distributionPolicy.targetShape" class="form-control input-sm" required>\n <ui-select-match placeholder="Select...">{{$select.selected}}</ui-select-match>\n <ui-select-choices\n repeat="targetShape in vm.command.backingData.distributionPolicyTargetShapes | filter: $select.search"\n >\n <span ng-bind-html="targetShape | highlight: $select.search"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const Pa="spinnaker.google.serverGroup.configure.wizard.capacity.zone.directive";e.module(Pa,[]).directive("gceZoneSelector",function(){return{restrict:"E",templateUrl:"google/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html",scope:{},bindToController:{command:"="},controllerAs:"vm",controller:e.noop}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/zones/zoneSelector.directive.html",'<div class="form-group" ng-if="!vm.command.regional">\n <div class="col-md-3 sm-label-right">Zone</div>\n <div class="col-md-7" ng-if="!vm.command.region">(Select a region)</div>\n <div class="col-md-7">\n <select\n class="form-control input-sm"\n ng-model="vm.command.zone"\n ng-options="zone for zone in vm.command.backingData.filtered.zones"\n ng-required="!vm.command.regional"\n ></select>\n </div>\n</div>\n\n<div class="form-group" ng-if="vm.command.regional && vm.command.selectZones">\n <div class="col-md-3 sm-label-right">Zones</div>\n <div class="col-md-7">\n <ui-select multiple ng-model="vm.command.distributionPolicy.zones" class="form-control input-sm">\n <ui-select-match>{{ $item }}</ui-select-match>\n <ui-select-choices repeat="zone as zone in vm.command.backingData.filtered.zones">\n <span>{{ zone }}</span>\n </ui-select-choices>\n </ui-select>\n </div>\n</div>\n')}]);const $a="spinnaker.serverGroup.configure.gce";n($a,[qt,_t,jt,ba,oa,ra,Je,Cn,ga,la,na,Ye,Wt,Zt,ea,ta,sa,da,ha,Sa,Ta,Ba,Ga,Aa,Pa]);const xa="spinnaker.deck.gce.serverGroup.hiddenMetadataKeys.value";n(xa,[]).value("gceServerGroupHiddenMetadataKeys",["load-balancer-names","global-load-balancer-names","backend-service-names","load-balancing-policy","select-zones","customUserData"]);const Ia="spinnaker.gce.serverGroupCommandBuilder.service";e.module(Ia,[j,Ke,Je,xa]).factory("gceServerGroupCommandBuilder",["$q","instanceTypeService","gceCustomInstanceBuilderService","gceServerGroupHiddenMetadataKeys","gceXpnNamingService",function(n,t,a,i,l){function c(e){const n=l.deriveProjectId(e.launchConfig.instanceTemplate),t=oe.get(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].network");return l.decorateXpnResourceIfNecessary(n,t)}function r(e){const n=l.deriveProjectId(e.launchConfig.instanceTemplate),t=oe.get(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].subnetwork");return l.decorateXpnResourceIfNecessary(n,t)}function o(e){return oe.has(e,"launchConfig.instanceTemplate.properties.networkInterfaces[0].accessConfigs")}function s(e){return(e.disks||[]).filter(e=>"local-ssd"===e.type)}function d(e){return(e.disks||[]).filter(e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-"))}function u(e,n){if(e.storage.localSSDSupported)s(n).length!==e.storage.count&&(n.viewState.overriddenStorageDescription=s(n).length+"×375");else{const t=d(n);(t.some(n=>n.sizeGb!==e.storage.size)||t.length!==e.storage.count)&&(n.viewState.overriddenStorageDescription=function(e){const n=d(e),t=new Map;return n.forEach(e=>{t.has(e.sizeGb)?t.set(e.sizeGb,t.get(e.sizeGb)+1):t.set(e.sizeGb,1)}),Array.from(t).sort(([e],[n])=>n-e).map(([e,n])=>n+"×"+e).join(", ")}(n))}}function g(e,n){const t=e.autoHealingPolicy;if(t){const e=t.healthCheckUrl?t.healthCheckUrl:t.healthCheck;if(e){const{healthCheckName:a,healthCheckKind:i}=Xt(e);n.autoHealingPolicy={healthCheck:a,healthCheckKind:i,healthCheckUrl:e,initialDelaySec:t.initialDelaySec}}const a=t.maxUnavailable;a&&(n.autoHealingPolicy.maxUnavailable=a,n.viewState.maxUnavailableMetric="number"==typeof a.percent?"percent":"fixed")}}function p(n,t){if(n){let a="",l=[];if(e.isArray(n)){const e=n.find(e=>"customUserData"===e.key);e&&(a=e.value,l=m(a),t.userData=a),n.forEach(function(e){oe.includes(l,e.key)||oe.includes(i,e.key)||(t.instanceMetadata[e.key]=e.value)})}else n.customUserData&&(a=n.customUserData,l=m(a),t.userData=a,n=oe.omit(n,l)),e.extend(t.instanceMetadata,oe.omit(n,i))}t.labels["spinnaker-moniker-sequence"]&&delete t.labels["spinnaker-moniker-sequence"]}function m(e){const n=[];return e.split(/\n|,/).forEach(function(e){const t=e.split("=")[0];n.push(t)}),n}function h(e,n){e&&e.items&&oe.map(e.items,function(e){n.tags.push({value:e})})}function f(e,n){e&&Object.assign(n.resourceManagerTags,e)}function v(e,n){e&&Object.assign(n.partnerMetadata,e)}function b(e,n){const t=(n=n||{}).account||Ue.defaults.account,a=n.region||Ue.defaults.region,i=n.zone||Ue.defaults.zone,l=!oe.has(e,"attributes.providerSettings.gce.associatePublicIpAddress")||e.attributes.providerSettings.gce.associatePublicIpAddress,c={application:e.name,credentials:t,region:a,zone:i,regional:!1,selectZones:!1,distributionPolicy:{zones:[]},network:"default",associatePublicIpAddress:l,canIpForward:!1,strategy:"",capacity:{min:0,max:0,desired:1},backendServiceMetadata:[],minCpuPlatform:"(Automatic)",disks:[{type:"pd-ssd",sizeGb:10},{type:"local-ssd",sizeGb:375}],imageSource:"priorStage",instanceMetadata:{},tags:[],labels:{},resourceManagerTags:{},partnerMetadata:{},enableSecureBoot:!1,enableVtpm:!1,enableIntegrityMonitoring:!1,preemptible:!1,automaticRestart:!0,onHostMaintenance:"MIGRATE",serviceAccountEmail:"default",authScopes:["cloud.useraccounts.readonly","devstorage.read_only","logging.write","monitoring.write"],enableTraffic:!0,cloudProvider:"gce",selectedProvider:"gce",availabilityZones:[],viewState:{instanceProfile:"custom",allImageSelection:null,useSimpleCapacity:!0,usePreferredZones:!0,listImplicitSecurityGroups:!1,mode:n.mode||"create",disableStrategySelection:!0,expectedArtifacts:[]}};return e.attributes&&e.attributes.platformHealthOnlyShowOverride&&e.attributes.platformHealthOnly&&(c.interestingHealthProviderNames=["Google"]),function(e,n,t){return y.listAccounts("gce").then(function(e){const a=oe.map(e,"name"),i=a[0],l=n&&a.includes(n);t.credentials=l?n:i||"my-account-name"})}(0,t,c).then(()=>c)}return{buildNewServerGroupCommand:b,buildNewServerGroupCommandForPipeline:function(e,t){const a=M.getExpectedArtifactsAvailableToStage(e,t);return n.when({viewState:{pipeline:t,expectedArtifacts:a,requiresTemplateSelection:!0,stage:e}})},buildServerGroupCommandFromExisting:function(i,l,s){s=s||"clone";const d=l.moniker,m={application:i.name,autoscalingPolicy:oe.cloneDeep(l.autoscalingPolicy),strategy:"",stack:d.stack,freeFormDetails:d.detail,credentials:l.account,loadBalancers:(b=l.asg,["load-balancer-names","global-load-balancer-names"].reduce((e,n)=>(b[n]&&(e=e.concat(b[n])),e),[])),loadBalancingPolicy:oe.cloneDeep(l.loadBalancingPolicy),backendServiceMetadata:l.asg["backend-service-names"],securityGroups:l.securityGroups,region:l.region,capacity:{min:l.asg.minSize,max:l.asg.maxSize,desired:l.asg.desiredCapacity},regional:l.regional,network:c(l),subnet:r(l),associatePublicIpAddress:o(l),canIpForward:l.canIpForward,minCpuPlatform:l.launchConfig.minCpuPlatform||"(Automatic)",instanceMetadata:{},tags:[],labels:{},resourceManagerTags:{},partnerMetadata:{},availabilityZones:[],enableSecureBoot:l.enableSecureBoot,enableVtpm:l.enableVtpm,enableIntegrityMonitoring:l.enableIntegrityMonitoring,enableTraffic:!0,cloudProvider:"gce",selectedProvider:"gce",distributionPolicy:{zones:l.distributionPolicy?l.distributionPolicy.zones:[],targetShape:l.distributionPolicy?l.distributionPolicy.targetShape:null},selectZones:l.selectZones,source:{account:l.account,region:l.region,serverGroupName:l.name,asgName:l.name},viewState:{allImageSelection:null,useSimpleCapacity:!l.autoscalingPolicy,usePreferredZones:!1,listImplicitSecurityGroups:!1,mode:s}};var b;if(m.regional||(m.zone=l.zones[0],m.source.zone=l.zones[0]),i.attributes&&i.attributes.platformHealthOnlyShowOverride&&i.attributes.platformHealthOnly&&(m.interestingHealthProviderNames=["Google"]),g(l,m),l.launchConfig){const i=l.launchConfig.instanceType;return e.extend(m,{instanceType:i}),oe.includes(i,"custom-")&&(m.viewState.customInstance=a.parseInstanceTypeString(i),m.viewState.instanceProfile="buildCustom"),m.viewState.imageId=l.launchConfig.imageId,function(e){return t.getCategories("gce").then(function(n){n.forEach(function(n){n.families.forEach(function(t){t.instanceTypes.forEach(function(t){t.name===e.instanceType&&(e.viewState.instanceProfile=n.type)})})})})}(m).then(function(){return function(e,n){e?(n.preemptible=e.preemptible,n.automaticRestart=e.automaticRestart,n.onHostMaintenance=e.onHostMaintenance):(n.preemptible=!1,n.automaticRestart=!0,n.onHostMaintenance="MIGRATE")}(l.launchConfig.instanceTemplate.properties.scheduling,m),p(l.launchConfig.instanceTemplate.properties.metadata.items,m),h(l.launchConfig.instanceTemplate.properties.tags,m),function(e,n){e&&(Object.assign(n.labels,e),n.labels["spinnaker-region"]&&delete n.labels["spinnaker-region"],n.labels["spinnaker-server-group"]&&delete n.labels["spinnaker-server-group"],n.labels["spinnaker-moniker-sequence"]&&delete n.labels["spinnaker-moniker-sequence"])}(l.instanceTemplateLabels,m),function(e,n){e&&e.length?(n.serviceAccountEmail=e[0].email,n.authScopes=oe.map(e[0].scopes,e=>e.replace("https://www.googleapis.com/auth/",""))):n.authScopes=[]}(l.launchConfig.instanceTemplate.properties.serviceAccounts,m),f(l.launchConfig.instanceTemplate.properties.resourceManagerTags,m),v(l.launchConfig.instanceTemplate.properties.partnerMetadata,m),function(e,a){const i=(e=e.map((e,n)=>0===n?{type:e.initializeParams.diskType,sizeGb:e.initializeParams.diskSizeGb}:{type:e.initializeParams.diskType,sizeGb:e.initializeParams.diskSizeGb,sourceImage:oe.last(oe.get(e,"initializeParams.sourceImage","").split("/"))||null})).filter(e=>"local-ssd"===e.type),l=e.filter(e=>e.type.startsWith("pd-")||e.type.startsWith("hyperdisk-"));return l.length?(a.disks=l.concat(i),t.getInstanceTypeDetails(a.selectedProvider,oe.includes(a.instanceType,"custom-")?"buildCustom":a.instanceType).then(e=>{a.viewState.instanceTypeDetails=e,u(e,a)})):(a.disks=[{type:"pd-ssd",sizeGb:10}].concat(i),n.when(null))}(l.launchConfig.instanceTemplate.properties.disks,m).then(function(){return m})})}return n.when(m)},buildServerGroupCommandFromPipeline:function(i,l,c,r){const o=oe.cloneDeep(l),m=Object.keys(o.availabilityZones)[0],y=o.zone,k=t.getCategoryForInstanceType("gce",o.instanceType),S={account:o.account,region:m,zone:y};return n.all([b(i,S),k]).then(function([i,l]){const b=M.getExpectedArtifactsAvailableToStage(c,r),y={pipeline:r,stage:c,instanceProfile:l,disableImageSelection:!0,expectedArtifacts:b,showImageSourceSelector:!0,useSimpleCapacity:!o.autoscalingPolicy,mode:"editPipeline",submitButtonLabel:"Done",customInstance:"buildCustom"===l?a.parseInstanceTypeString(o.instanceType):null,templatingEnabled:!0},k={region:m,credentials:o.account,enableTraffic:!o.disableTraffic,viewState:y};o.strategy=o.strategy||"";const S=e.extend({},i,o,k);return function(e){const a=d(e),i=s(e);return a.length?(e.disks=a.concat(i),t.getInstanceTypeDetails(e.selectedProvider,oe.includes(e.instanceType,"custom-")?"buildCustom":e.instanceType).then(n=>{e.viewState.instanceTypeDetails=n,u(n,e)})):(e.disks=[{type:"pd-ssd",sizeGb:10}].concat(i),n.when(null))}(S).then(function(){const e=S.instanceMetadata;var n;S.loadBalancers=(n=e,["load-balancer-names","global-load-balancer-names"].reduce((e,t)=>(n[t]&&(e=e.concat(n[t].split(","))),e),[])),S.backendServiceMetadata=e["backend-service-names"]?e["backend-service-names"].split(","):[],S.minCpuPlatform=o.minCpuPlatform||"(Automatic)",S.instanceMetadata={},p(e,S),g(o,S),function(e,n){n.enableSecureBoot=e.enableSecureBoot,n.enableVtpm=e.enableVtpm,n.enableIntegrityMonitoring=e.enableIntegrityMonitoring}(o,S);const t={items:S.tags};S.tags=[],h(t,S);f(S.resourceManagerTags,S);return v(S.partnerMetadata,S),S})})}}}]);const Ma="spinnaker.serverGroup.configure.gce.cloneServerGroup";e.module(Ma,[xe,Je,j,xa,Qt]).controller("gceCloneServerGroupCtrl",["$scope","$uibModalInstance","$q","$state","$log","serverGroupWriter","gceServerGroupConfigurationService","serverGroupCommand","application","title","gceCustomInstanceBuilderService","instanceTypeService","wizardSubFormValidation","gceServerGroupHiddenMetadataKeys","gceTagManager",function(n,t,a,i,l,c,r,o,s,d,u,g,p,h,f){function v(){if(n.$$destroyed)return;const e=n.taskMonitor.task.execution.stages.find(e=>"cloneServerGroup"===e.type);if(e&&e.context["deploy.server.groups"]){const t=e.context["deploy.server.groups"][n.command.region];if(t){const e={serverGroup:t,accountId:n.command.credentials,region:n.command.region,provider:"gce"};let a="^.^.^.clusters.serverGroup";i.includes("**.clusters.serverGroup")&&(a="^.serverGroup"),i.includes("**.clusters.cluster.serverGroup")&&(a="^.^.serverGroup"),i.includes("**.clusters")&&(a=".serverGroup"),i.go(a,e)}}}function b(){r.configureCommand(s,o).then(function(){n.state.loaded=!0,k(n.command.credentialsChanged(n.command)),k(n.command.regionalChanged(n.command)),k(n.command.regionChanged(n.command)),k(n.command.networkChanged(n.command)),k(n.command.zoneChanged(n.command)),k(n.command.customInstanceChanged(n.command)),r.configureSubnets(n.command),n.$watch("command.credentials",y(n.command.credentialsChanged)),n.$watch("command.regional",y(n.command.regionalChanged)),n.$watch("command.region",y(n.command.regionChanged)),n.$watch("command.network",y(n.command.networkChanged)),n.$watch("command.zone",y(n.command.zoneChanged)),n.$watch("command.viewState.instanceTypeDetails",function(e){n.command.viewState.initialized?e&&e.storage&&e.storage.defaultSettings&&(n.command.disks=e.storage.defaultSettings.disks,delete n.command.viewState.overriddenStorageDescription):n.command.viewState.initialized=!0}),n.$watch("command.selectZones",y(n.command.selectZonesChanged)),n.$watch("command.distributionPolicy.zones",y(n.command.selectZonesChanged)),n.$watch("command.viewState.customInstance",()=>{n.command.customInstanceChanged(n.command),function(){const e=n.command,t=e.regional?e.region:e.zone,{locationToInstanceTypesMap:a}=e.backingData.credentialsKeyedByAccount[e.credentials],i=oe.get(e,"viewState.customInstance.extendedMemory"),l=[oe.get(e,"viewState.customInstance.instanceFamily"),oe.get(e,"viewState.customInstance.vCpuCount"),oe.get(e,"viewState.customInstance.memory")];oe.every([...l,u.customInstanceChoicesAreValid(...l,t,a,i)])&&(e.instanceType=u.generateInstanceTypeString(...l,i),g.getInstanceTypeDetails(e.selectedProvider,"buildCustom").then(n=>{e.viewState.instanceTypeDetails=n}))}()},!0),p.config({scope:n,form:"form"}).register({page:"location",subForm:"basicSettings"}).register({page:"capacity",subForm:"capacitySubForm"}).register({page:"zones",subForm:"zonesSubForm"}).register({page:"load-balancers",subForm:"loadBalancerSubForm"}).register({page:"autohealing-policy",subForm:"autoHealingPolicySubForm"}).register({page:"autoscaling-policy",subForm:"autoScalingPolicySubForm"})}).catch(e=>{l.error("Error generating server group command: ",e)})}function y(e){return function(){k(e(n.command))}}function k(e){e.dirty.loadBalancers&&P.markDirty("load-balancers"),e.dirty.securityGroups&&P.markDirty("security-groups"),e.dirty.availabilityZones&&P.markDirty("capacity"),e.dirty.instanceType&&P.markDirty("instance-type")}n.pages={templateSelection:"google/src/serverGroup/configure/wizard/templateSelection/templateSelection.html",basicSettings:"google/src/serverGroup/configure/wizard/location/basicSettings.html",loadBalancers:"google/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html",securityGroups:"google/src/serverGroup/configure/wizard/securityGroups/securityGroups.html",instanceType:"google/src/serverGroup/configure/wizard/instanceType/instanceType.html",capacity:"google/src/serverGroup/configure/wizard/capacity/capacity.html",autoHealingPolicy:"google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicy.html",autoScalingPolicy:"google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicy.html",zones:"google/src/serverGroup/configure/wizard/capacity/zones.html",advancedSettings:"google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html"},n.firewallsLabel=m.get("Firewalls"),n.title=d,n.applicationName=s.name,n.application=s,n.command=o,n.state={loaded:!1,requiresTemplateSelection:!!o.viewState.requiresTemplateSelection},this.templateSelectionText={copied:["account, region, subnet, cluster name (stack, details)","load balancers",m.get("firewalls"),"instance type","all fields on the Advanced Settings page"],notCopied:[]},n.command.viewState.disableStrategySelection||this.templateSelectionText.notCopied.push("the deployment strategy (if any) used to deploy the most recent server group"),n.taskMonitor=new G({application:s,title:"Creating your server group",modalInstance:t,onTaskComplete:function(){s.serverGroups.refresh(),s.serverGroups.onNextRefresh(n,v)}}),this.isValid=function(){const e=n.command.selectZones&&oe.get(n,"command.distributionPolicy.zones.length")>=1,t=n.command.autoscalingPolicy;return n.command&&(n.command.viewState.disableImageSelection||n.command.image)&&n.command.application&&n.command.credentials&&n.command.instanceType&&n.command.region&&(n.command.regional||n.command.zone)&&null!==n.command.capacity.desired&&(!n.command.selectZones||e)&&n.form.$valid&&(!t||t&&(!se(t.cpuUtilization)||!se(t.customMetricUtilizations)||!se(t.loadBalancingUtilization)))&&P.isComplete()},this.showSubmitButton=function(){return P.allPagesVisited()},this.submit=function(){const a=function(e,n,t){let a={};oe.get(e,"length")>0&&(a=e.reduce((e,t)=>{const a=n[t];return"HTTP"===a.loadBalancerType?e["global-load-balancer-names"]=e["global-load-balancer-names"].concat(a.listeners.map(e=>e.name)):"INTERNAL_MANAGED"===a.loadBalancerType?e["load-balancer-names"]=e["load-balancer-names"].concat(a.listeners.map(e=>e.name)):"SSL"===a.loadBalancerType||"TCP"===a.loadBalancerType?e["global-load-balancer-names"].push(t):e["load-balancer-names"].push(t),e},{"load-balancer-names":[],"global-load-balancer-names":[]})),oe.isObject(t)&&Object.keys(t).length>0&&(a["backend-service-names"]=oe.reduce(t,(e,n)=>e.concat(n),[]));for(const e in a)0===a[e].length?delete a[e]:a[e]=oe.uniq(a[e]).toString();return a}(n.command.loadBalancers,n.command.backingData.filtered.loadBalancerIndex,n.command.backendServices),i=n.command.loadBalancers;n.command.loadBalancers=function(e,n){let t=[];n["load-balancer-names"]&&(t=t.concat(n["load-balancer-names"].split(",")));const a=oe.chain(e).filter({loadBalancerType:"SSL"}).map("name").intersection(n["global-load-balancer-names"]?n["global-load-balancer-names"].split(","):[]).value(),i=oe.chain(e).filter({loadBalancerType:"TCP"}).map("name").intersection(n["global-load-balancer-names"]?n["global-load-balancer-names"].split(","):[]).value();return t.concat(a).concat(i)}(n.command.backingData.filtered.loadBalancerIndex,a),"(Automatic)"===n.command.minCpuPlatform&&(n.command.minCpuPlatform=""),e.extend(n.command.instanceMetadata,a);const l=n.command.tags,r=[];if(n.command.tags.forEach(function(e){r.push(e.value)}),n.command.tags=r,n.command.targetSize=n.command.capacity.desired,n.command.autoscalingPolicy?(n.command.capacity.min=n.command.autoscalingPolicy.minNumReplicas,n.command.capacity.max=n.command.autoscalingPolicy.maxNumReplicas):(n.command.capacity.min=n.command.capacity.desired,n.command.capacity.max=n.command.capacity.desired),delete n.command.securityGroups,"editPipeline"===n.command.viewState.mode||"createPipeline"===n.command.viewState.mode)return t.close(n.command);n.taskMonitor.submit(function(){const t=c.cloneServerGroup(e.copy(n.command),s);return n.command.instanceMetadata=oe.omit(n.command.instanceMetadata,h),n.command.tags=l,n.command.loadBalancers=i,n.command.securityGroups=f.inferSecurityGroupIdsFromTags(n.command.tags),t})},this.onHealthCheckRefresh=function(){r.refreshHealthChecks(n.command)},this.onEnableAutoHealingChange=function(){n.command.overwriteAncestorAutoHealingPolicy="clone"===n.command.viewState.mode&&null!=n.command.autoHealingPolicy&&!1===n.command.enableAutoHealing},this.setAutoHealingPolicy=function(e){n.command.autoHealingPolicy=e},this.onEnableAutoScalingChange=function(){n.command.overwriteAncestorAutoScalingPolicy=null!=n.command.autoscalingPolicy&&!1===n.command.enableAutoScaling},this.setAutoScalingPolicy=function(e){n.command.autoscalingPolicy=e},this.cancel=function(){t.dismiss()},this.specialInstanceProfiles=new Set(["custom","buildCustom"]),n.command.setCustomInstanceViewState=e=>{n.$apply(()=>n.command.viewState.customInstance=e)},n.state.requiresTemplateSelection?n.state.loaded=!0:b(),this.templateSelected=()=>{n.state.requiresTemplateSelection=!1,b()},n.$on("$destroy",f.reset)}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/templateSelection/templateSelection.html",'<deploy-initializer\n cloud-provider="gce"\n command="command"\n application="application"\n parent-state="state"\n dismiss="ctrl.cancel()"\n template-selection-text="ctrl.templateSelectionText"\n on-template-selected="ctrl.templateSelected()"\n></deploy-initializer>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/location/basicSettings.html",'<div class="container-fluid form-horizontal" ng-controller="gceServerGroupBasicSettingsCtrl 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 component="command"\n field="credentials"\n accounts="command.backingData.accounts"\n provider="\'gce\'"\n on-change="basicSettingsCtrl.accountUpdated()"\n ></account-select-field>\n </div>\n </div>\n <gce-region-select-field\n label-columns="3"\n component="command"\n field="region"\n account="command.credentials"\n regions="command.backingData.filtered.regions"\n ></gce-region-select-field>\n <gce-network-select-field\n label-columns="3"\n component="command"\n field="network"\n account="command.credentials"\n networks="command.backingData.filtered.networks"\n ></gce-network-select-field>\n <gce-subnet-select-field\n label-columns="3"\n help-key="gce.serverGroup.subnet"\n component="command"\n field="subnet"\n account="command.credentials"\n region="command.region"\n subnets="command.backingData.filtered.subnets"\n subnet-placeholder="basicSettingsCtrl.getSubnetPlaceholder()"\n auto-create-subnets="command.viewState.autoCreateSubnets"\n >\n </gce-subnet-select-field>\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"\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>Stack can only contain letters and numbers.</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"\n ng-pattern="basicSettingsCtrl.detailPattern"\n name="details"\n ng-model="command.freeFormDetails"\n />\n </div>\n </div>\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>Detail can only contain letters, numbers, and dashes(-).</span>\n </div>\n </div>\n <div ng-if="command.viewState.showImageSourceSelector">\n <image-source-selector\n command="command"\n id-field="imageSource"\n image-sources="basicSettingsCtrl.imageSources"\n image-source-text="command.viewState.imageSourceText"\n help-field-key="gce.image.source"\n >\n </image-source-selector>\n <stage-artifact-selector-delegate\n ng-if="command.imageSource === \'artifact\'"\n artifact="command.imageArtifact"\n excluded-artifact-type-patterns="basicSettingsCtrl.excludedImageArtifactTypes"\n expected-artifact-id="command.imageArtifactId"\n field-columns="7"\n help-key="\'gce.image.artifact\'"\n label="\'Expected Artifact\'"\n on-artifact-edited="basicSettingsCtrl.onImageArtifactEdited"\n on-expected-artifact-selected="basicSettingsCtrl.onImageArtifactSelected"\n pipeline="command.viewState.pipeline"\n stage="command.viewState.stage"\n >\n </stage-artifact-selector-delegate>\n </div>\n <div class="form-group" ng-if="!command.viewState.disableImageSelection">\n <div class="col-md-3 sm-label-right">\n Image\n <help-field key="gce.serverGroup.imageName"></help-field>\n </div>\n <div class="col-md-7">\n <gce-image-select\n available-images="command.backingData.allImages"\n selected-image="command.image"\n select-image="basicSettingsCtrl.selectImage"\n ></gce-image-select>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Traffic <help-field key="gce.serverGroup.traffic"></help-field></div>\n <div class="col-md-9 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableTraffic" />\n Send client requests to new instances\n </label>\n </div>\n </div>\n <deployment-strategy-selector\n ng-if="!command.viewState.disableStrategySelection && command.selectedProvider"\n command="command"\n ></deployment-strategy-selector>\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 <task-reason command="command"></task-reason>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/loadBalancers/loadBalancers.html",'<ng-form name="loadBalancerSubForm">\n <div>\n <gce-server-group-load-balancer-selector command="command"></gce-server-group-load-balancer-selector>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/securityGroups/securityGroups.html",'<div class="row">\n <gce-server-group-security-groups-removed command="command"></gce-server-group-security-groups-removed>\n <gce-server-group-security-group-selector command="command"></gce-server-group-security-group-selector>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/instanceType/instanceType.html",'<div ng-show="!!((command.viewState.disableImageSelection || command.image) && (command.zone || command.regional))">\n <v2-instance-archetype-selector command="command"></v2-instance-archetype-selector>\n <div style="padding: 0 15px">\n <v2-instance-type-selector\n ng-if="command.viewState.instanceProfile && !ctrl.specialInstanceProfiles.has(command.viewState.instanceProfile)"\n command="command"\n ></v2-instance-type-selector>\n </div>\n</div>\n<h5 class="text-center" ng-if="!command.image && !command.viewState.disableImageSelection">Please select an image.</h5>\n<h5 class="text-center" ng-if="!(command.zone || command.regional)">Please select a zone.</h5>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/capacity.html",'<ng-form name="capacitySubForm">\n <div class="clearfix">\n <div class="row">\n <div class="col-md-12">\n <gce-server-group-simple-capacity-selector\n ng-if="command.viewState.useSimpleCapacity"\n command="command"\n ></gce-server-group-simple-capacity-selector>\n <gce-server-group-advanced-capacity-selector\n ng-if="!command.viewState.useSimpleCapacity"\n command="command"\n ></gce-server-group-advanced-capacity-selector>\n </div>\n </div>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoHealingPolicy/autoHealingPolicy.html",'<ng-form name="autoHealingPolicySubForm">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Enable Autohealing</b>\n </div>\n <div class="col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableAutoHealing" ng-change="ctrl.onEnableAutoHealingChange()" />\n </label>\n </div>\n </div>\n <gce-auto-healing-policy-selector\n ng-if="command.enableAutoHealing"\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="command.backingData.filtered.healthChecks"\n enabled="command.enableAutoHealing"\n auto-healing-policy="command.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicy.html",'<ng-form name="autoScalingPolicySubForm">\n <div class="form-group">\n <div class="col-md-3 sm-label-right">\n <b>Enable Autoscaling</b>\n </div>\n <div class="col-md-6 checkbox">\n <label>\n <input type="checkbox" ng-model="command.enableAutoScaling" ng-change="ctrl.onEnableAutoScalingChange()" />\n </label>\n </div>\n </div>\n <div>\n <gce-auto-scaling-policy-Selector-Component\n ng-if="command.enableAutoScaling"\n enabled="command.enableAutoScaling"\n auto-scaling-policy="command.autoscalingPolicy"\n set-auto-scaling-policy="ctrl.setAutoScalingPolicy(autoscalingPolicy)"\n ></gce-auto-scaling-policy-Selector-Component>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/capacity/zones.html",'<ng-form name="zonesSubForm">\n <div class="container-fluid form-horizontal">\n <gce-regional-selector command="command"></gce-regional-selector>\n <gce-zone-selector command="command"></gce-zone-selector>\n <gce-target-shape-selector command="command"></gce-target-shape-selector>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/advancedSettings/advancedSettings.html",'<div class="container-fluid form-horizontal">\n <gce-server-group-advanced-settings-selector command="command"></gce-server-group-advanced-settings-selector>\n <div class="form-group" ng-if="application.attributes.platformHealthOnlyShowOverride">\n <div class="col-md-5 sm-label-right"><b>Task Completion</b></div>\n <div class="col-md-6">\n <platform-health-override command="command" platform-health-type="\'Google\'"> </platform-health-override>\n </div>\n </div>\n</div>\n')}]);class za{static markDiskStateful(e,n,t){return T.executeTask({application:e,description:"Mark disk as stateful",job:[{cloudProvider:"gce",credentials:t.account,deviceName:n,region:t.region,serverGroupName:t.name,type:"setStatefulDisk"}]})}static statefullyUpdateBootDisk(e,n,t){return T.executeTask({application:e,description:"Statefully update boot disk image",job:[{bootImage:n,cloudProvider:"gce",credentials:t.account,region:t.region,serverGroupName:t.name,type:"statefullyUpdateBootImage"}]})}static isDiskStateful(e,n){return Se(n,["statefulPolicy","preservedState","disks",e])}static statefulMigsEnabled(){return Ue.feature.statefulMigsEnabled}}function Na({application:e,deviceName:n,serverGroup:t}){return za.isDiskStateful(n,t)?ce.createElement("span",null," (Marked as Stateful)"):ce.createElement("button",{className:"btn-link",onClick:()=>{h.confirm({account:t.account,askForReason:!0,buttonText:"Mark as stateful",header:`Really mark disk ${n} as stateful?`,submitMethod:()=>za.markDiskStateful(e.name,n,t),taskMonitorConfig:{application:e,title:"Marking disk as stateful"}})}},"Mark as Stateful")}function Da(e){return ce.createElement("button",{className:"btn-link",onClick:function(){const n={...e};X.show(Ea,n,{dialogClassName:"wizard-modal modal-lg"})}},"Statefully Update")}class Ea extends ce.Component{constructor(e){super(e),this.submit=e=>{this.state.taskMonitor.submit(()=>za.statefullyUpdateBootDisk(this.props.application.name,e.image,this.props.serverGroup))},this.state={availableImages:[],taskMonitor:new G({application:e.application,title:"Updating Boot Image",modalInstance:G.modalInstanceEmulation(()=>this.props.dismissModal())})}}componentDidMount(){Ze.findImages({account:this.props.serverGroup.account,provider:"gce",q:"*"}).then(e=>{this.setState({availableImages:e})})}render(){return ce.createElement(K,{closeModal:this.submit,dismissModal:this.props.dismissModal,heading:"Update Boot Disk Image",initialValues:{image:this.props.bootImage},submitButtonLabel:"Update Image",taskMonitor:this.state.taskMonitor,render:({formik:e,nextIdx:n,wizard:t})=>ce.createElement(W,{label:"Boot Image",wizard:t,order:n(),render:()=>ce.createElement(Z,{input:n=>ce.createElement("div",{className:"full-width",style:{height:"225px"}},ce.createElement(Xe,{availableImages:this.state.availableImages,selectedImage:n.value,selectImage:n=>e.setFieldValue("image",n)})),label:"Boot image name:",name:"image",required:!0})})})}}const Ra=class e extends ce.Component{render(){const{application:n,serverGroup:t}=this.props,a=ve(t,"launchConfig.instanceTemplate.properties.disks",[]),i=za.statefulMigsEnabled(),l=i&&a.some(e=>za.isDiskStateful(e.deviceName,t));return a.map(a=>a.boot?ce.createElement(ce.Fragment,{key:a.deviceName},ce.createElement("dt",null,"Boot Disk",l&&ce.createElement(Da,{application:n,bootImage:e.getDiskImageName(a),serverGroup:t})),ce.createElement("dd",null,e.getDiskTypeLabel(a)),ce.createElement("dd",null,e.getDiskImageLabel(a))):ce.createElement(ce.Fragment,{key:a.deviceName},ce.createElement("dt",null,"Disk",i&&ce.createElement(Na,{application:n,deviceName:a.deviceName,serverGroup:t})),ce.createElement("dd",null,e.getDiskTypeLabel(a)),ce.createElement("dd",null,e.getDiskImageLabel(a))))}};Ra.translateDiskType=e=>{const n=e.initializeParams.diskType;return"pd-ssd"===n?"Persistent SSD":"local-ssd"===n?"Local SSD":"Persistent Std"},Ra.getDiskTypeLabel=e=>`${Ra.translateDiskType(e)}: ${e.initializeParams.diskSizeGb}GB`,Ra.getDiskImageLabel=e=>`Image: ${Ra.getDiskImageName(e)}`,Ra.getDiskImageName=e=>Ae(ve(e,"initializeParams.sourceImage","").split("/"));let La=Ra;const Ha="spinnaker.gce.serverGroupDiskDescriptions";n(Ha,[]).component("gceServerGroupDiskDescriptions",re(i(La,"gceServerGroupDiskDescriptions"),["application","serverGroup"]));const Fa={maxScaledInReplicas:{fixed:null,percent:0},timeWindowSec:60};const Ua="spinnaker.gce.scaleInControls";n(Ua,[]).component("gceScaleInControls",re(i(function({policy:e,updatePolicy:n}){function t(t){if(Object.keys(t).length)n({...e,scaleInControl:t});else{const t=e;delete t.scaleInControl,n({...t})}}function a(){return Number.isInteger(e.scaleInControl.maxScaledInReplicas.percent)?"percent":"fixed"}return ce.createElement(l,{value:He},ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(r,{...e}),label:"Enable scale-in controls",onChange:e=>{t(e.target.checked?Fa:{})},value:!se(e.scaleInControl)})),!se(e.scaleInControl)&&ce.createElement(ce.Fragment,null,ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(Y,{...e,min:0,max:"percent"===a()?100:null}),label:"Max scaled-in replicas",onChange:n=>{t({...e.scaleInControl,maxScaledInReplicas:{[a()]:parseInt(n.target.value,10)}})},value:e.scaleInControl.maxScaledInReplicas[a()]}),ce.createElement(c,{input:e=>ce.createElement(J,{...e,clearable:!1,stringOptions:["percent","fixed"]}),label:"",onChange:n=>{t({...e.scaleInControl,maxScaledInReplicas:{[n.target.value]:e.scaleInControl.maxScaledInReplicas[a()]}})},value:a()})),ce.createElement("div",{className:"row"},ce.createElement(c,{input:e=>ce.createElement(Y,{...e,min:60,max:3600}),label:"Time window (seconds)",onChange:n=>{t({...e.scaleInControl,timeWindowSec:parseInt(n.target.value,10)})},value:e.scaleInControl.timeWindowSec}))))},"gceScaleInControls"),["policy","updatePolicy"]));class Oa{constructor(e){this.$uibModal=e}addAutoHealingPolicy(){this.$uibModal.open({templateUrl:"google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",controller:"gceUpsertAutoHealingPolicyModalCtrl",controllerAs:"ctrl",size:"md",resolve:{serverGroup:()=>this.serverGroup,application:()=>this.application}})}}Oa.$inject=["$uibModal"];const qa={bindings:{application:"<",serverGroup:"<"},template:'<a href ng-click="$ctrl.addAutoHealingPolicy()">Create new autohealing policy</a>',controller:Oa},_a="spinnaker.gce.addAutoHealingPolicyButton.component";n(_a,[]).component("gceAddAutoHealingPolicyButton",qa),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",'<div modal-page class="form-inline gce-auto-healing-policy-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} autohealing policy</h4>\n </div>\n <ng-form name="autoHealingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <gce-auto-healing-policy-selector\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="ctrl.healthChecks"\n enabled="true"\n label-columns="4"\n auto-healing-policy="ctrl.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoHealingPolicyUpsertModal.$valid"\n label="$ctrl.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);class Va{constructor(e,n){this.$uibModal=e,this.gceAutoscalingPolicyWriter=n}editPolicy(){this.$uibModal.open({templateUrl:"google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",controller:"gceUpsertAutoHealingPolicyModalCtrl",controllerAs:"ctrl",size:"md",resolve:{application:()=>this.application,serverGroup:()=>this.serverGroup}})}deletePolicy(){const e={application:this.application,title:`Deleting autohealing policy for ${this.serverGroup.name}`};h.confirm({header:`Really delete autohealer for ${this.serverGroup.name}?`,buttonText:"Delete autohealer",account:this.serverGroup.account,taskMonitorConfig:e,submitMethod:()=>this.gceAutoscalingPolicyWriter.deleteAutoHealingPolicy(this.application,this.serverGroup)})}}Va.$inject=["$uibModal","gceAutoscalingPolicyWriter"];const ja={bindings:{serverGroup:"<",application:"<"},template:'\n <dt>\n Health Check\n <help-field key="gce.serverGroup.autoHealing"></help-field>\n </dt>\n <dd>{{$ctrl.serverGroup.autoHealingPolicyHealthCheck}}</dd>\n <dt>\n Initial Delay\n <help-field key="gce.serverGroup.initialDelaySec"></help-field>\n </dt>\n <dd>{{$ctrl.serverGroup.initialDelaySec}} seconds</dd>\n <dt ng-if="$ctrl.serverGroup.maxUnavailable">\n Max Unavailable\n <help-field key="gce.serverGroup.maxUnavailable"></help-field>\n </dt>\n <dd ng-if="$ctrl.serverGroup.maxUnavailable">{{$ctrl.serverGroup.maxUnavailable}}</dd>\n <action-icons class="text-right"\n edit="$ctrl.editPolicy()"\n edit-info="Edit Policy"\n destroy="$ctrl.deletePolicy()"\n destroy-info="Delete Policy">\n </action-icons>',controller:Va},Ka="spinnaker.gce.autoHealingPolicyDetails.component";n(Ka,[]).component("gceAutoHealingPolicyDetails",ja),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoHealingPolicy/modal/upsertAutoHealingPolicy.modal.html",'<div modal-page class="form-inline gce-auto-healing-policy-modal">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} autohealing policy</h4>\n </div>\n <ng-form name="autoHealingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <gce-auto-healing-policy-selector\n on-health-check-refresh="ctrl.onHealthCheckRefresh()"\n set-auto-healing-policy="ctrl.setAutoHealingPolicy(autoHealingPolicy)"\n health-checks="ctrl.healthChecks"\n enabled="true"\n label-columns="4"\n auto-healing-policy="ctrl.autoHealingPolicy"\n ></gce-auto-healing-policy-selector>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoHealingPolicyUpsertModal.$valid"\n label="$ctrl.submitButtonLabel"\n submitting="ctrl.taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const Wa="spinnaker.gce.serverGroup.details.scalingPolicy.write.service";e.module(Wa,[]).factory("gceAutoscalingPolicyWriter",function(){return{upsertAutoscalingPolicy:function(n,t,a,i={}){const l={type:"upsertScalingPolicy",cloudProvider:t.type,credentials:t.account,region:t.region,serverGroupName:t.name,autoscalingPolicy:a};return e.extend(l,i),T.executeTask({application:n,description:"Upsert scaling policy "+t.name,job:[l]})},deleteAutoscalingPolicy:function(e,n){return T.executeTask({application:e,description:"Delete scaling policy "+n.name,job:[{type:"deleteScalingPolicy",cloudProvider:n.type,credentials:n.account,region:n.region,serverGroupName:n.name}]})},upsertAutoHealingPolicy:function(n,t,a,i={}){const l={type:"upsertScalingPolicy",cloudProvider:t.type,credentials:t.account,region:t.region,serverGroupName:t.name,autoHealingPolicy:a};return e.extend(l,i),T.executeTask({application:n,description:"Upsert autohealing policy "+t.name,job:[l]})},deleteAutoHealingPolicy:function(e,n){return T.executeTask({application:e,description:"Delete autohealing policy "+n.name,job:[{type:"deleteScalingPolicy",cloudProvider:n.type,credentials:n.account,region:n.region,serverGroupName:n.name,deleteAutoHealingPolicy:!0}]})}}});Le(".gce-auto-healing-policy-modal .form-group {\n width: 100%;\n margin-top: 10px;\n}\n.gce-auto-healing-policy-modal .input-group {\n width: 100%;\n}\n.gce-auto-healing-policy-modal .input-group .input-group-addon {\n width: 20%;\n}\n");class Za{constructor(e,n,t,a,i){this.$uibModalInstance=e,this.application=n,this.serverGroup=t,this.gceHealthCheckReader=a,this.gceAutoscalingPolicyWriter=i,this.initialize()}submit(){this.taskMonitor.submit(()=>this.gceAutoscalingPolicyWriter.upsertAutoHealingPolicy(this.application,this.serverGroup,this.autoHealingPolicy))}cancel(){this.$uibModalInstance.dismiss()}setAutoHealingPolicy(e){this.autoHealingPolicy=e}onHealthCheckRefresh(){this.gceHealthCheckReader.listHealthChecks().then(e=>{const n=e.filter(e=>e.account===this.serverGroup.account);this.healthChecks=Yt(n)})}initialize(){this.onHealthCheckRefresh(),this.action=this.serverGroup.autoHealingPolicy?"Edit":"New",this.isNew=!this.serverGroup.autoHealingPolicy,this.submitButtonLabel=this.isNew?"Create":"Update",this.isNew||(this.autoHealingPolicy=ke(this.serverGroup.autoHealingPolicy)),this.taskMonitor=new G({application:this.application,title:`${this.action} autohealing policy for ${this.serverGroup.name}`,modalInstance:this.$uibModalInstance})}}Za.$inject=["$uibModalInstance","application","serverGroup","gceHealthCheckReader","gceAutoscalingPolicyWriter"];const Xa="spinnaker.gce.upsertAutoHealingPolicy.modal.controller";n(Xa,[Ve,Wa]).controller("gceUpsertAutoHealingPolicyModalCtrl",Za);Le(".gce-scaling-policy-modal .error-message {\n margin-top: 4px;\n}\n.gce-scaling-policy-modal input.form-control,\n.gce-scaling-policy-modal select.form-control {\n margin-bottom: 2px;\n width: 100%;\n}\n.gce-scaling-policy-modal .collapsible-subsection .content-body {\n padding-left: 0;\n}\n");const Ya="spinnaker.deck.gce.upsertAutoscalingPolicy.modal.controller";n(Ya,[Wa,qt,_t,jt]).controller("gceUpsertAutoscalingPolicyModalCtrl",["policy","application","serverGroup","$uibModalInstance","gceAutoscalingPolicyWriter","$scope",function(e,n,t,a,i,l){[this.action,this.isNew]=e?["Edit",!1]:["New",!0],this.policy=ke(e||{}),this.cancel=a.dismiss,this.taskMonitor=new G({application:n,title:`${this.action} scaling policy for ${t.name}`,modalInstance:a}),this.save=()=>{this.taskMonitor.submit(()=>i.upsertAutoscalingPolicy(n,t,this.policy))},this.updatePolicy=e=>{l.$applyAsync(()=>{this.policy=e})}}]);const Ja="spinnaker.gce.serverGroup.details.scalingPolicy.addButton";n(Ja,[Ie,Ya]).component("gceAddAutoscalingPolicyButton",{bindings:{serverGroup:"=",application:"="},template:'<a href ng-click="$ctrl.addAutoscalingPolicy()">Create new scaling policy</a>',controller:["$uibModal",function(e){this.addAutoscalingPolicy=()=>{e.open({templateUrl:"google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",controller:"gceUpsertAutoscalingPolicyModalCtrl",controllerAs:"ctrl",size:"lg",resolve:{policy:()=>{},serverGroup:()=>this.serverGroup,application:()=>this.application}})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",'<div modal-page class="scaling-policy-modal gce-scaling-policy-modal form-inline">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} scaling policy</h4>\n </div>\n <ng-form name="autoscalingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <h4 class="section-heading">Basic Settings</h4>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="ctrl.policy" update-policy="ctrl.updatePolicy" />\n </div>\n <h4 class="section-heading">Metric Types</h4>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="ctrl.showNoMetricsWarning"\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h4 class="section-heading">Scaling Schedules</h4>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoscalingPolicyUpsertModal.$valid || ctrl.showNoMetricsWarning()"\n submitting="taskMonitor.submitting"\n on-click="ctrl.save()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const Qa="spinnaker.gce.instance.details.scalingPolicy.directive";n(Qa,[Ie,Wa,Ya,oa]).component("gceAutoscalingPolicy",{bindings:{policy:"=",application:"=",serverGroup:"="},templateUrl:"google/src/serverGroup/details/autoscalingPolicy/autoscalingPolicy.directive.html",controller:["$uibModal","gceAutoscalingPolicyWriter",function(e,n){var t=this;t.$onInit=function(){const a=t.policy;if(a.bases=[],a.cpuUtilization){const e={description:"CPU Usage",helpKey:"gce.serverGroup.autoscaling.targetCPUUsage"};a.cpuUtilization.utilizationTarget&&(e.targets=[Math.round(100*a.cpuUtilization.utilizationTarget)+"%"]),a.bases.push(e)}if(a.loadBalancingUtilization){const e={description:"HTTP Load Balancing Usage",helpKey:"gce.serverGroup.autoscaling.targetHTTPLoadBalancingUsage"};a.loadBalancingUtilization.utilizationTarget&&(e.targets=[Math.round(100*a.loadBalancingUtilization.utilizationTarget)+"%"]),a.bases.push(e)}if(a.customMetricUtilizations){const e={description:a.customMetricUtilizations.length>1?"Monitoring Metrics":"Monitoring Metric",helpKey:"gce.serverGroup.autoscaling.targetMetric"};a.customMetricUtilizations.length>0&&(e.targets=[],a.customMetricUtilizations.forEach(n=>{let t=n.metric+": "+n.utilizationTarget;"DELTA_PER_SECOND"===n.utilizationTargetType?t+="/sec":"DELTA_PER_MINUTE"===n.utilizationTargetType&&(t+="/min"),e.targets.push(t)})),a.bases.push(e)}t.scaleInControlsConfigured=a.scaleInControl&&a.scaleInControl.timeWindowSec&&a.scaleInControl.maxScaledInReplicas&&(a.scaleInControl.maxScaledInReplicas.percent||a.scaleInControl.maxScaledInReplicas.fixed),t.scaleInControlsConfigured&&(t.maxScaledInReplicasMessage=a.scaleInControl.maxScaledInReplicas.percent?`${a.scaleInControl.maxScaledInReplicas.percent}%`:`${a.scaleInControl.maxScaledInReplicas.fixed}`,t.timeWindowSecMessage=`${a.scaleInControl.timeWindowSec} seconds`),t.predictiveAutoscalingEnabled=Ue.feature.predictiveAutoscaling&&a.cpuUtilization&&a.cpuUtilization.predictiveMethod,t.editPolicy=()=>{e.open({templateUrl:"google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",controller:"gceUpsertAutoscalingPolicyModalCtrl",controllerAs:"ctrl",size:"lg",resolve:{policy:()=>t.policy,application:()=>t.application,serverGroup:()=>t.serverGroup}})},t.deletePolicy=()=>{const e={application:t.application,title:`Deleting autoscaler for ${t.serverGroup.name}`};h.confirm({header:`Really delete autoscaler for ${t.serverGroup.name}?`,buttonText:"Delete autoscaler",account:t.serverGroup.account,taskMonitorConfig:e,submitMethod:()=>n.deleteAutoscalingPolicy(t.application,t.serverGroup)})}}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/autoscalingPolicy.directive.html",'<dl class="horizontal-when-filters-collapsed" style="margin-bottom: 20px">\n <dt ng-repeat-start="basis in $ctrl.policy.bases">\n Target {{basis.description}}\n <help-field key="{{basis.helpKey}}"></help-field>\n </dt>\n <dd ng-repeat-end>\n <div ng-repeat="target in basis.targets">{{target}}</div>\n </dd>\n <dt ng-if="$ctrl.predictiveAutoscalingEnabled">Predictive Autoscaling</dt>\n <dd ng-if="$ctrl.predictiveAutoscalingEnabled">{{$ctrl.policy.cpuUtilization.predictiveMethod}}</dd>\n <dt>\n Min # VMs\n <help-field key="gce.serverGroup.autoscaling.minVMs"></help-field>\n </dt>\n <dd>{{$ctrl.policy.minNumReplicas}}</dd>\n <dt>\n Max # VMs\n <help-field key="gce.serverGroup.autoscaling.maxVMs"></help-field>\n </dt>\n <dd>{{$ctrl.policy.maxNumReplicas}}</dd>\n <dt>\n Cool-down Period\n <help-field key="gce.serverGroup.autoscaling.cooldown"></help-field>\n </dt>\n <dd>{{$ctrl.policy.coolDownPeriodSec}} sec</dd>\n <dt>\n Mode\n <help-field key="gce.serverGroup.autoscaling.mode"></help-field>\n </dt>\n <dd>{{$ctrl.policy.mode}}</dd>\n <dt ng-if="$ctrl.scaleInControlsEnabled">Scale-in Controls</dt>\n <dd ng-if="$ctrl.scaleInControlsEnabled">{{$ctrl.scaleInControlsConfigured ? \'Enabled\' : \'Disabled\'}}</dd>\n <dt ng-if="$ctrl.scaleInControlsConfigured">Max Scaled-in Replicas</dt>\n <dd ng-if="$ctrl.scaleInControlsConfigured">{{$ctrl.maxScaledInReplicasMessage}}</dd>\n <dt ng-if="$ctrl.scaleInControlsConfigured">Scale-in Time Window</dt>\n <dd ng-if="$ctrl.scaleInControlsConfigured">{{$ctrl.timeWindowSecMessage}}</dd>\n <dt ng-if="$ctrl.serverGroup.autoscalingMessages">Messages</dt>\n <dd ng-if="$ctrl.serverGroup.autoscalingMessages" ng-repeat="message in $ctrl.serverGroup.autoscalingMessages">\n {{message}}\n </dd>\n <action-icons\n class="text-right"\n edit="$ctrl.editPolicy()"\n edit-info="Edit Policy"\n destroy="$ctrl.deletePolicy()"\n destroy-info="Delete Policy"\n >\n </action-icons>\n</dl>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/autoscalingPolicy/modal/upsertAutoscalingPolicy.modal.html",'<div modal-page class="scaling-policy-modal gce-scaling-policy-modal form-inline">\n <task-monitor monitor="ctrl.taskMonitor"></task-monitor>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">{{ctrl.action}} scaling policy</h4>\n </div>\n <ng-form name="autoscalingPolicyUpsertModal">\n <div class="modal-body clearfix">\n <h4 class="section-heading">Basic Settings</h4>\n <div class="section-body">\n <gce-autoscaling-policy-basic-settings policy="ctrl.policy" update-policy="ctrl.updatePolicy" />\n </div>\n <h4 class="section-heading">Metric Types</h4>\n <gce-autoscaling-policy-metric-settings\n show-no-metrics-warning="ctrl.showNoMetricsWarning"\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n >\n </gce-autoscaling-policy-metric-settings>\n <h4 class="section-heading">Scaling Schedules</h4>\n <div class="section-body">\n <gce-autoscaling-policy-scaling-schedules\n policy="ctrl.policy"\n update-policy="ctrl.updatePolicy"\n ></gce-autoscaling-policy-scaling-schedules>\n </div>\n </div>\n </ng-form>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="!autoscalingPolicyUpsertModal.$valid || ctrl.showNoMetricsWarning()"\n submitting="taskMonitor.submitting"\n on-click="ctrl.save()"\n is-new="ctrl.isNew"\n ></submit-button>\n </div>\n</div>\n')}]);const ei="spinnaker.google.footer.directive";e.module(ei,[]).directive("gceFooter",function(){return{restrict:"E",templateUrl:"google/src/common/footer.directive.html",scope:{},bindToController:{action:"&",isValid:"&",cancel:"&",account:"=?",verification:"=?"},controllerAs:"vm",controller:e.noop}}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/common/footer.directive.html",'<div class="modal-footer">\n <user-verification account="vm.account" verification="vm.verification"></user-verification>\n <button type="submit" ng-click="vm.action()" style="display: none"></button>\n \x3c!-- Allows form submission via enter keypress--\x3e\n <button class="btn btn-default" ng-click="vm.cancel()">Cancel</button>\n <button type="submit" class="btn btn-primary" ng-click="vm.action()" ng-disabled="!vm.isValid()">Submit</button>\n</div>\n')}]);const ni="spinnaker.deck.gce.serverGroup.details.resizeAutoscalingPolicy.component";e.module(ni,[Wa]).component("gceResizeAutoscalingPolicy",{bindings:{serverGroup:"=",command:"=",formMethods:"=",application:"="},templateUrl:"google/src/serverGroup/details/resize/resizeAutoscalingPolicy.component.html",controller:["$scope","gceAutoscalingPolicyWriter",function(n,t){this.$onInit=()=>{const a=["newMinNumReplicas","newMaxNumReplicas"];a.forEach(e=>this.command[e]=null),e.extend(this.formMethods,{formIsValid:()=>oe.every([oe.chain(a).map(e=>null!==this.command[e]).every().value(),n.resizeAutoscalingPolicyForm.$valid]),submitMethod:()=>t.upsertAutoscalingPolicy(this.application,this.serverGroup,{minNumReplicas:this.command.newMinNumReplicas,maxNumReplicas:this.command.newMaxNumReplicas},{reason:this.command.reason,interestingHealthProviderNames:this.command.interestingHealthProviderNames})})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeAutoscalingPolicy.component.html",'<ng-form name="resizeAutoscalingPolicyForm">\n <div class="form-group">\n <div class="col-md-12">\n <p>\n Sets min and max instance counts for this server group\'s autoscaling policy.\n <help-field key="gce.serverGroup.resizeWithAutoscalingPolicy" placement="right"></help-field>\n </p>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-2 col-md-offset-3"><b>Min</b></div>\n <div class="col-md-2"><b>Max</b></div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Current</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.autoscalingPolicy.minNumReplicas"\n readonly\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.autoscalingPolicy.maxNumReplicas"\n readonly\n />\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-3 sm-label-right">Resize to</div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.newMinNumReplicas"\n min="0"\n required\n max="{{ $ctrl.command.newMaxNumReplicas }}"\n />\n </div>\n <div class="col-md-2">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.command.newMaxNumReplicas"\n required\n min="{{ $ctrl.command.newMinNumReplicas }}"\n />\n </div>\n </div>\n</ng-form>\n')}]);const ti="spinnaker.google.serverGroup.details.resize.capacity.component";e.module(ti,[Q]).component("gceResizeCapacity",{bindings:{command:"=",application:"=",serverGroup:"=",formMethods:"="},templateUrl:"google/src/serverGroup/details/resize/resizeCapacity.component.html",controller:["$scope","serverGroupWriter",function(n,t){this.$onInit=()=>{this.command.newSize=null,e.extend(this.formMethods,{formIsValid:()=>oe.every([null!==this.command.newSize,n.resizeCapacityForm.$valid]),submitMethod:()=>t.resizeServerGroup(this.serverGroup,this.application,{capacity:{min:this.command.newSize,max:this.command.newSize,desired:this.command.newSize},serverGroupName:this.serverGroup.name,targetSize:this.command.newSize,region:this.serverGroup.region,interestingHealthProviderNames:this.command.interestingHealthProviderNames,reason:this.command.reason})})}}]}),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeCapacity.component.html",'<ng-form name="resizeCapacityForm">\n <div>\n <div class="form-group">\n <div class="col-md-12">\n <p>Sets desired instance count for this server group.</p>\n </div>\n </div>\n <div class="form-group form-inline">\n <div class="col-md-3 sm-label-right">Current size</div>\n <div class="col-md-6">\n <input\n type="number"\n class="form-control input-sm"\n ng-model="$ctrl.serverGroup.asg.desiredCapacity"\n style="width: 60px"\n readonly\n />\n instance<span ng-if="$ctrl.currentSize.desired > 1">s</span>\n </div>\n </div>\n <div class="form-group form-inline">\n <div class="col-md-3 sm-label-right">Resize to</div>\n <div class="col-md-6">\n <input\n type="number"\n class="form-control input-sm highlight-pristine"\n ng-model="$ctrl.command.newSize"\n min="0"\n required\n style="width: 60px"\n />\n instances\n </div>\n </div>\n </div>\n</ng-form>\n')}]);const ai="spinnaker.google.serverGroup.details.resize.controller";n(ai,[ti,ni,ei]).controller("gceResizeServerGroupCtrl",["$scope","$uibModalInstance","application","serverGroup",function(e,n,t,a){e.serverGroup=a,e.application=t,e.verification={},e.command={},e.formMethods={},t&&t.attributes&&(t.attributes.platformHealthOnlyShowOverride&&t.attributes.platformHealthOnly&&(e.command.interestingHealthProviderNames=["Google"]),e.command.platformHealthOnlyShowOverride=t.attributes.platformHealthOnlyShowOverride),this.isValid=function(){return!!e.verification.verified&&e.formMethods.formIsValid()},e.taskMonitor=new G({application:t,title:"Resizing "+a.name,modalInstance:n}),this.resize=function(){this.submitting=!0,this.isValid()&&e.taskMonitor.submit(e.formMethods.submitMethod)},this.cancel=function(){n.dismiss()}}]);const ii="spinnaker.google.serverGroup.details.rollback.controller";n(ii,[Q,ei]).controller("gceRollbackServerGroupCtrl",["$scope","$uibModalInstance","serverGroupWriter","application","serverGroup","disabledServerGroups",function(e,n,t,a,i,l){e.serverGroup=i,e.disabledServerGroups=l.sort((e,n)=>n.name.localeCompare(e.name)),e.verification={},e.command={rollbackType:"EXPLICIT",rollbackContext:{rollbackServerGroupName:i.name}},a&&a.attributes&&(a.attributes.platformHealthOnlyShowOverride&&a.attributes.platformHealthOnly&&(e.command.interestingHealthProviderNames=["Google"]),e.command.platformHealthOnlyShowOverride=a.attributes.platformHealthOnlyShowOverride),this.isValid=function(){const n=e.command;return!!e.verification.verified&&void 0!==n.rollbackContext.restoreServerGroupName},e.taskMonitor=new G({application:a,title:"Rollback "+i.name,modalInstance:n}),this.rollback=function(){if(this.submitting=!0,!this.isValid())return;e.taskMonitor.submit(function(){return t.rollbackServerGroup(i,a,e.command)})},this.cancel=function(){n.dismiss()}}]);Le("gce-custom-instance-configurer .Select-menu-outer {\n z-index: 4;\n}\n");const li="spinnaker.serverGroup.details.gce.controller";e.module(li,[xe,Ia,Q,Ke,ai,ii,Qa,Ja]).controller("gceServerGroupDetailsCtrl",["$scope","$state","$templateCache","$interpolate","app","serverGroup","gceServerGroupCommandBuilder","$uibModal","serverGroupWriter","gceXpnNamingService",function(n,t,a,i,l,c,r,o,s,d){this.state={loading:!0},this.firewallsLabel=m.get("Firewalls"),this.application=l;const u=()=>{n.$$destroyed||t.go("^",{allowModalToStayOpen:!0},{location:"replace"})},g=()=>{this.state.loading=!1},p=()=>{const n=(()=>{let e=oe.find(l.serverGroups.data,e=>e.name===c.name&&e.account===c.accountId&&e.region===c.region);return e||l.loadBalancers.data.some(n=>{if(n.account===c.accountId&&n.region===c.region)return n.serverGroups.some(n=>{if(n.name===c.name)return e=n,!0})}),e})();return te.getServerGroup(l.name,c.accountId,c.region,c.name).then(t=>{if(g(),e.extend(t,n),t.account=c.accountId,this.serverGroup=t,oe.isEmpty(this.serverGroup))u();else{t.securityGroups&&(this.securityGroups=oe.chain(t.securityGroups).map(e=>oe.find(l.securityGroups.data,{accountName:c.accountId,region:"global",id:e})||oe.find(l.securityGroups.data,{accountName:c.accountId,region:"global",name:e})).compact().value()),this.serverGroup.zones.sort();const e=d.deriveProjectId(this.serverGroup.launchConfig.instanceTemplate);this.serverGroup.logsLink="https://console.developers.google.com/project/"+e+'/logs?advancedFilter=resource.type=(gce_instance_group_manager OR gce_instance OR gce_autoscaler)%0A"'+this.serverGroup.name+'"',this.serverGroup.network=T(e),G(e),A(),f(),v(),b(),y(),k(),S(),w(),P(),C()}},u)},f=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.metadata.items")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.metadata.items,n=oe.find(e,e=>"startup-script"===e.key);n&&(this.serverGroup.startupScript=n.value)}},v=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.scheduling")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.scheduling;this.serverGroup.availabilityPolicies={preemptibility:e.preemptible?"On":"Off",automaticRestart:e.automaticRestart?"On":"Off",onHostMaintenance:"MIGRATE"===e.onHostMaintenance?"Migrate":"Terminate"}}},b=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.shieldedVmConfig")){const e=this.serverGroup.launchConfig.instanceTemplate.properties.shieldedVmConfig;this.serverGroup.shieldedVmConfig={enableSecureBoot:e.enableSecureBoot?"On":"Off",enableVtpm:e.enableVtpm?"On":"Off",enableIntegrityMonitoring:e.enableIntegrityMonitoring?"On":"Off"}}},y=()=>{if(this.serverGroup.autoHealingPolicy){const e=this.serverGroup.autoHealingPolicy,n=e.healthCheck;this.serverGroup.autoHealingPolicyHealthCheck=n?oe.last(n.split("/")):null,this.serverGroup.initialDelaySec=e.initialDelaySec,e.maxUnavailable&&("number"==typeof e.maxUnavailable.percent?this.serverGroup.maxUnavailable=e.maxUnavailable.percent+"%":this.serverGroup.maxUnavailable=e.maxUnavailable.fixed+" instances")}},k=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.serviceAccounts")){if(this.serverGroup.launchConfig.instanceTemplate.properties.serviceAccounts.length){const e=this.serverGroup.launchConfig.instanceTemplate.properties.serviceAccounts[0];this.serverGroup.serviceAccountEmail=e.email,this.serverGroup.authScopes=oe.map(e.scopes,e=>e.replace("https://www.googleapis.com/auth/",""))}}},S=()=>{this.serverGroup.currentActions&&(this.serverGroup.currentActionsSummary=[],oe.forOwn(this.serverGroup.currentActions,(e,n)=>{"none"!==n&&e&&this.serverGroup.currentActionsSummary.push({action:oe.startCase(n),count:e})}),this.serverGroup.currentActionsSummary.length||delete this.serverGroup.currentActionsSummary)},w=()=>{if(oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.tags.items")&&this.securityGroups){const e={};this.serverGroup.launchConfig.instanceTemplate.properties.tags.items.forEach(n=>{const t=oe.filter(this.securityGroups,e=>oe.includes(e.targetTags,n)),a=oe.map(t,"name");if(!oe.isEmpty(a)){const t=a.length>1?"groups":"group";e[n]="This tag associates this server group with security "+t+" <em>"+a.join(", ")+"</em>."}}),this.serverGroup.launchConfig.instanceTemplate.properties.tags.helpMap=e}},C=()=>{oe.size(this.serverGroup.instanceTemplateLabels)||delete this.serverGroup.instanceTemplateLabels},T=e=>{const n=oe.get(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].network");return d.decorateXpnResourceIfNecessary(e,n)},G=e=>{B.listNetworksByProvider("gce").then(n=>{if(oe.chain(n).filter({account:this.serverGroup.account,id:this.serverGroup.network}).map("autoCreateSubnets").head().value())this.serverGroup.subnet="(Auto-select)";else{const n=oe.get(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].subnetwork");this.serverGroup.subnet=d.decorateXpnResourceIfNecessary(e,n)}})},A=()=>{this.serverGroup.associatePublicIPAddress=oe.has(this.serverGroup,"launchConfig.instanceTemplate.properties.networkInterfaces[0].accessConfigs")};p().then(()=>{n.$$destroyed||l.serverGroups.onRefresh(n,p)}),this.destroyServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Destroying "+e.name,onTaskComplete:()=>{t.includes("**.serverGroup",a)&&t.go("^")}},a={name:e.name,accountId:e.account,region:e.region},i={header:"Really destroy "+e.name+"?",buttonText:"Destroy "+e.name,account:e.account,taskMonitorConfig:n,submitMethod:n=>s.destroyServerGroup(e,l,n),askForReason:!0,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google"};ee.addDestroyWarningMessage(l,e,i),l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(i.interestingHealthProviderNames=["Google"]),h.confirm(i)},this.disableServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Disabling "+e.name},t={header:"Really disable "+e.name+"?",buttonText:"Disable "+e.name,account:e.account,taskMonitorConfig:n,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google",submitMethod:n=>s.disableServerGroup(e,l,n),askForReason:!0};ee.addDisableWarningMessage(l,e,t),l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=["Google"]),h.confirm(t)},this.enableServerGroup=()=>{const e=this.serverGroup,n={application:l,title:"Enabling "+e.name},t={header:"Really enable "+e.name+"?",buttonText:"Enable "+e.name,account:e.account,taskMonitorConfig:n,platformHealthOnlyShowOverride:l.attributes.platformHealthOnlyShowOverride,platformHealthType:"Google",submitMethod:n=>s.enableServerGroup(e,l,n),askForReason:!0};l.attributes.platformHealthOnlyShowOverride&&l.attributes.platformHealthOnly&&(t.interestingHealthProviderNames=["Google"]),h.confirm(t)},this.rollbackServerGroup=()=>{o.open({templateUrl:"google/src/serverGroup/details/rollback/rollbackServerGroup.html",controller:"gceRollbackServerGroupCtrl as ctrl",resolve:{serverGroup:()=>this.serverGroup,disabledServerGroups:()=>{const e=oe.find(l.clusters,{name:this.serverGroup.cluster,account:this.serverGroup.account});return oe.filter(e.serverGroups,{isDisabled:!0,region:this.serverGroup.region})},application:()=>l}})},this.resizeServerGroup=()=>{o.open({templateUrl:"google/src/serverGroup/details/resize/resizeServerGroup.html",controller:"gceResizeServerGroupCtrl as ctrl",resolve:{serverGroup:()=>this.serverGroup,application:()=>l}})},this.cloneServerGroup=e=>{o.open({templateUrl:"google/src/serverGroup/configure/wizard/serverGroupWizard.html",controller:"gceCloneServerGroupCtrl as ctrl",size:"lg",resolve:{title:()=>"Clone "+e.name,application:()=>l,serverGroup:()=>e,serverGroupCommand:()=>r.buildServerGroupCommandFromExisting(l,e)}})},this.showStartupScript=()=>{n.userDataModalTitle="Startup Script",n.serverGroup={name:this.serverGroup.name},n.userData=this.serverGroup.startupScript,o.open({templateUrl:ne.userData,scope:n})},this.buildJenkinsLink=()=>{if(this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.buildInfoUrl)return this.serverGroup.buildInfo.buildInfoUrl;if(this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.jenkins){const e=this.serverGroup.buildInfo.jenkins;return e.host+"job/"+e.name+"/"+e.number}return null},this.truncateCommitHash=()=>this.serverGroup&&this.serverGroup.buildInfo&&this.serverGroup.buildInfo.commit?this.serverGroup.buildInfo.commit.substring(0,8):null;const P=()=>{this.entityTagTargets=ae.buildClusterTargets(this.serverGroup)}}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/rollback/rollbackServerGroup.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Rollback {{serverGroup.name}}</h4>\n </div>\n <div class="modal-body confirmation-modal">\n <div class="row">\n <div class="col-sm-3 sm-label-right">Restore to</div>\n <div class="col-sm-6">\n <ui-select ng-model="command.rollbackContext.restoreServerGroupName" class="form-control input-sm">\n <ui-select-match placeholder="Select...">{{$select.selected.name}}</ui-select-match>\n <ui-select-choices repeat="serverGroup.name as serverGroup in disabledServerGroups">\n <span ng-bind-html="serverGroup.name"></span>\n </ui-select-choices>\n </ui-select>\n </div>\n </div>\n\n <div class="row" ng-if="command.platformHealthOnlyShowOverride">\n <div class="col-sm-10 col-sm-offset-1">\n <platform-health-override\n command="command"\n platform-health-type="\'Google\'"\n show-help-details="true"\n field-columns="12"\n >\n </platform-health-override>\n </div>\n </div>\n\n <task-reason command="command"></task-reason>\n </div>\n <gce-footer\n action="ctrl.rollback()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="serverGroup.account"\n verification="verification"\n ></gce-footer>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/resize/resizeServerGroup.html",'<div modal-page class="confirmation-modal">\n <task-monitor monitor="taskMonitor"></task-monitor>\n <form role="form" ng-if="!ctrl.submitting">\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Resize {{serverGroup.name}}</h4>\n </div>\n <div class="modal-body confirmation-modal">\n <div class="form-horizontal">\n <gce-resize-capacity\n ng-if="!serverGroup.autoscalingPolicy"\n server-group="serverGroup"\n form-methods="formMethods"\n application="application"\n command="command"\n ></gce-resize-capacity>\n <gce-resize-autoscaling-policy\n ng-if="serverGroup.autoscalingPolicy"\n server-group="serverGroup"\n form-methods="formMethods"\n application="application"\n command="command"\n ></gce-resize-autoscaling-policy>\n </div>\n <div class="row" ng-if="command.platformHealthOnlyShowOverride">\n <div class="col-sm-10 col-sm-offset-1">\n <platform-health-override command="command" platform-health-type="\'Google\'" show-help-details="true">\n </platform-health-override>\n </div>\n </div>\n <task-reason command="command"></task-reason>\n </div>\n <gce-footer\n action="ctrl.resize()"\n cancel="ctrl.cancel()"\n is-valid="ctrl.isValid()"\n account="serverGroup.account"\n verification="verification"\n ></gce-footer>\n </form>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal" novalidate>\n <div ng-if="state.requiresTemplateSelection">\n <ng-include src="pages.templateSelection"></ng-include>\n </div>\n <div ng-if="!state.loaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!state.requiresTemplateSelection">\n <v2-modal-wizard ng-show="state.loaded" heading="{{title}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="location" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancers" label="Load Balancers" mark-complete-on-view="false">\n <ng-include src="pages.loadBalancers"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="security-groups" label="{{firewallsLabel}}" done="true">\n <ng-include src="pages.securityGroups"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="instance-type" label="Instance Type" mark-complete-on-view="false">\n <ng-include src="pages.instanceType"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="capacity" label="Capacity" mark-complete-on-view="false">\n <ng-include src="pages.capacity"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autoscaling-policy" label="Autoscaling Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoScalingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autohealing-policy" label="Autohealing Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoHealingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="zones" label="Zones" mark-complete-on-view="false" done="true">\n <ng-include src="pages.zones"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advanced" label="Advanced Settings" mandatory="false" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="state.loaded">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="ctrl.showSubmitButton()"\n is-disabled="!ctrl.isValid() || taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="taskMonitor.submitting"\n on-click="ctrl.submit()"\n is-new="true"\n ></submit-button>\n </div>\n </div>\n</form>\n')}]);const ci="spinnaker.serverGroup.details.gce";n(ci,[li,_a,Xa,Ka]);const ri="spinnaker.gce.subnet.renderer";n(ri,[]).factory("gceSubnetRenderer",function(){let e;return B.listNetworksByProvider("gce").then(function(n){e=n}),{render:function(n){return n.subnet?n.subnet:oe.chain(e).filter({account:n.account,name:n.network}).map("autoCreateSubnets").head().value()?"(Auto-select)":"[none]"}}});const oi="spinnaker.gce.validation.applicationName";n(oi,[]).factory("gceApplicationNameValidator",function(){return{validate:function(e){const n=[],t=[];return e&&e.length&&(function(e,n){/^([a-zA-Z][a-zA-Z0-9]*)?$/.test(e)||n.push("The application name must begin with a letter and must contain only letters or digits. No special characters are allowed.")}(e,t),function(e,n,t){const a=63;if(e.length>a)t.push("The maximum length for an application in Google is 63 characters.");else{if(e.length>34)if(e.length>46)n.push(`You will not be able to create a Google load balancer for this application if the\n application's name is longer than 46 characters (currently: ${e.length}\n characters).`);else if(e.length>=44)n.push('With separators ("-"), you will not be able to include a stack and detail field for Google load balancers.');else{const t=44-e.length;n.push(`If you plan to include a stack or detail field for Google load balancers, you will only\n have ~${t} characters to do so.`)}if(e.length>41)if(e.length>53)n.push(`You will not be able to create a Google server group for this application if the\n application's name is longer than 53 characters (currently: ${e.length}\n characters).`);else if(e.length>=51)n.push('With separators ("-"), you will not be able to include a stack and detail field for Google server groups.');else{const t=51-e.length;n.push(`If you plan to include a stack or detail field for Google server groups, you will only\n have ~${t} characters to do so.`)}if(e.length>51)if(e.length>=61)n.push(`With separators ("-"), you will not be able to include a stack and detail field for Google ${m.get("firewalls")}.`);else{const t=61-e.length;n.push(`If you plan to include a stack or detail field for Google ${m.get("firewalls")}, you will only\n have ~${t} characters to do so.`)}}}(e,n,t)),{warnings:n,errors:t}}}}).run(["gceApplicationNameValidator",function(e){ie.registerValidator("gce",e)}]);Le('.cloud-provider-logo .icon-gce {\n -webkit-mask-image: url("data:image/svg+xml,%3C%3Fxml version%3D%221.0%22 encoding%3D%22UTF-8%22 standalone%3D%22no%22%3F%3E%3Csvg width%3D%2232px%22 height%3D%2232px%22 viewBox%3D%220 0 32 32%22 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 xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E %3C!-- Generator%3A Sketch 3.4.1 (15681) - http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch --%3E %3Ctitle%3Eshaded-32%2Fcompute-engine%3C%2Ftitle%3E %3Cdesc%3ECreated with Sketch.%3C%2Fdesc%3E %3Cdefs%3E%3C%2Fdefs%3E %3Cg id%3D%22Page-1%22 stroke%3D%22none%22 stroke-width%3D%221%22 fill%3D%22none%22 fill-rule%3D%22evenodd%22 sketch%3Atype%3D%22MSPage%22%3E %3Cg id%3D%22shaded-32%2Fcompute-engine%22 sketch%3Atype%3D%22MSArtboardGroup%22%3E %3Crect id%3D%22Container%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%220%22 width%3D%2232%22 height%3D%2232%22%3E%3C%2Frect%3E %3Cg id%3D%22shape%22 sketch%3Atype%3D%22MSLayerGroup%22 transform%3D%22translate(1.000000%2C 1.000000)%22%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Cpath d%3D%22M4%2C4 L4%2C25 L25%2C25 L25%2C4 L4%2C4 L4%2C4 Z M22%2C22 L7%2C22 L7%2C7 L22%2C7 L22%2C22 L22%2C22 Z%22 id%3D%22Shape%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2210%22 y%3D%2210%22 width%3D%229%22 height%3D%229%22%3E%3C%2Frect%3E %3Cpath d%3D%22M19%2C19 L14.5%2C19 L14.5%2C14.4998 L19%2C9.9997 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23424242%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Cpath d%3D%22M19%2C19 L10%2C19 L14.5%2C14.4998 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3C%2Fg%3E %3C%2Fg%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 standalone%3D%22no%22%3F%3E%3Csvg width%3D%2232px%22 height%3D%2232px%22 viewBox%3D%220 0 32 32%22 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 xmlns%3Asketch%3D%22http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch%2Fns%22%3E %3C!-- Generator%3A Sketch 3.4.1 (15681) - http%3A%2F%2Fwww.bohemiancoding.com%2Fsketch --%3E %3Ctitle%3Eshaded-32%2Fcompute-engine%3C%2Ftitle%3E %3Cdesc%3ECreated with Sketch.%3C%2Fdesc%3E %3Cdefs%3E%3C%2Fdefs%3E %3Cg id%3D%22Page-1%22 stroke%3D%22none%22 stroke-width%3D%221%22 fill%3D%22none%22 fill-rule%3D%22evenodd%22 sketch%3Atype%3D%22MSPage%22%3E %3Cg id%3D%22shaded-32%2Fcompute-engine%22 sketch%3Atype%3D%22MSArtboardGroup%22%3E %3Crect id%3D%22Container%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%220%22 width%3D%2232%22 height%3D%2232%22%3E%3C%2Frect%3E %3Cg id%3D%22shape%22 sketch%3Atype%3D%22MSLayerGroup%22 transform%3D%22translate(1.000000%2C 1.000000)%22%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%220%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%228%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2213%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2225%22 y%3D%2218%22 width%3D%224%22 height%3D%223%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%220%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2218%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2213%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%228%22 y%3D%2225%22 width%3D%223%22 height%3D%224%22%3E%3C%2Frect%3E %3Cpath d%3D%22M4%2C4 L4%2C25 L25%2C25 L25%2C4 L4%2C4 L4%2C4 Z M22%2C22 L7%2C22 L7%2C7 L22%2C7 L22%2C22 L22%2C22 Z%22 id%3D%22Shape%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Crect id%3D%22Rectangle-path%22 fill%3D%22%23757575%22 sketch%3Atype%3D%22MSShapeGroup%22 x%3D%2210%22 y%3D%2210%22 width%3D%229%22 height%3D%229%22%3E%3C%2Frect%3E %3Cpath d%3D%22M19%2C19 L14.5%2C19 L14.5%2C14.4998 L19%2C9.9997 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23424242%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3Cpath d%3D%22M19%2C19 L10%2C19 L14.5%2C14.4998 L19%2C19 Z%22 id%3D%22Shape%22 fill%3D%22%23616161%22 sketch%3Atype%3D%22MSShapeGroup%22%3E%3C%2Fpath%3E %3C%2Fg%3E %3C%2Fg%3E %3C%2Fg%3E%3C%2Fsvg%3E");\n background-color: #4285f4;\n}\n');const si="spinnaker.gce";n(si,[St,Zn,dn,it,ot,rn,Ha,Ua,Oe,ci,Ia,Ma,$a,Zt,Ct,Bt,Tt,Gt,At,Pt,$t,xt,It,Mt,zt,Nt,ln,cn,Qe,Qn,bt,et,Hn,Xn,tn,Ft,Dt,Et,Ot,Ut,ri,oi,je,Ke]).config(()=>{p.registerProvider("gce",{name:"Google",logo:{path:"gce.logo2ae85d8704b1d6b8.png"},cache:{configurer:"gceCacheConfigurer"},image:{reader:Ze},serverGroup:{transformer:"gceServerGroupTransformer",detailsTemplateUrl:"google/src/serverGroup/details/serverGroupDetails.html",detailsController:"gceServerGroupDetailsCtrl",cloneServerGroupTemplateUrl:"google/src/serverGroup/configure/wizard/serverGroupWizard.html",cloneServerGroupController:"gceCloneServerGroupCtrl",commandBuilder:"gceServerGroupCommandBuilder",configurationService:"gceServerGroupConfigurationService"},instance:{instanceTypeService:"gceInstanceTypeService",detailsTemplateUrl:"google/src/instance/details/instanceDetails.html",detailsController:"gceInstanceDetailsCtrl",multiInstanceTaskTransformer:"gceMultiInstanceTaskTransformer",customInstanceBuilderTemplateUrl:"google/src/serverGroup/configure/wizard/customInstance/customInstanceBuilder.html"},loadBalancer:{transformer:"gceLoadBalancerTransformer",setTransformer:"gceLoadBalancerSetTransformer",detailsTemplateUrl:"google/src/loadBalancer/details/loadBalancerDetails.html",detailsController:"gceLoadBalancerDetailsCtrl",createLoadBalancerTemplateUrl:"google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",createLoadBalancerController:"gceLoadBalancerChoiceCtrl",pipelineCreateLoadBalancerModal:un},securityGroup:{transformer:"gceSecurityGroupTransformer",reader:"gceSecurityGroupReader",detailsTemplateUrl:"google/src/securityGroup/details/securityGroupDetail.html",detailsController:"gceSecurityGroupDetailsCtrl",createSecurityGroupTemplateUrl:"google/src/securityGroup/configure/createSecurityGroup.html",createSecurityGroupController:"gceCreateSecurityGroupCtrl"},subnet:{renderer:"gceSubnetRenderer"},snapshotsEnabled:!0,applicationProviderFields:{templateUrl:"google/src/applicationProviderFields/gceFields.html"}})}),le.registerProvider("gce",["custom","redblack"]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/details/serverGroupDetails.html",'<div class="details-panel" ng-class="{ disabled: ctrl.serverGroup.isDisabled }">\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>\n {{ctrl.serverGroup.name}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n entity="ctrl.serverGroup"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="serverGroup"\n page-location="details"\n on-update="ctrl.application.serverGroups.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </h3>\n </div>\n <div>\n <div class="actions" ng-class="{ insights: ctrl.serverGroup.insightActions.length > 0 }">\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><a href ng-if="!ctrl.serverGroup.isDisabled" ng-click="ctrl.rollbackServerGroup()">Rollback</a></li>\n <li role="presentation" class="divider" ng-if="!ctrl.serverGroup.isDisabled"></li>\n <li><a href ng-click="ctrl.resizeServerGroup()">Resize</a></li>\n <li><a href ng-if="!ctrl.serverGroup.isDisabled" ng-click="ctrl.disableServerGroup()">Disable</a></li>\n <li>\n <a\n href\n ng-if="ctrl.serverGroup.isDisabled && (ctrl.serverGroup.loadBalancers.length || ctrl.serverGroup.discovery)"\n ng-click="ctrl.enableServerGroup()"\n >Enable</a\n >\n </li>\n <li><a href ng-click="ctrl.destroyServerGroup()">Destroy</a></li>\n <li><a href ng-click="ctrl.cloneServerGroup(ctrl.serverGroup)">Clone</a></li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="ctrl.serverGroup"\n application="ctrl.application"\n entity-type="serverGroup"\n owner-options="ctrl.entityTagTargets"\n on-update="ctrl.application.serverGroups.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n <div class="dropdown" ng-if="ctrl.serverGroup.insightActions.length > 0" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-default dropdown-toggle" uib-dropdown-toggle>\n Insight <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-repeat="action in ctrl.serverGroup.insightActions">\n <a target="_blank" href="{{action.url}}">{{action.label}}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!ctrl.state.loading">\n <h4 class="text-center" ng-if="ctrl.serverGroup.isDisabled">[SERVER GROUP IS DISABLED]</h4>\n <server-group-running-tasks-details\n server-group="ctrl.serverGroup"\n application="ctrl.application"\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.launchConfig.createdTime | timestamp}}</dd>\n <render-if-feature feature="entityTags">\n <entity-source metadata="ctrl.serverGroup.entityTags.creationMetadata"></entity-source>\n </render-if-feature>\n <dt>In</dt>\n <dd>\n <account-tag account="ctrl.serverGroup.account" pad="right"></account-tag>\n {{ctrl.serverGroup.region}}\n </dd>\n <dt>Network</dt>\n <dd>{{ctrl.serverGroup.network}}</dd>\n <dt ng-if="ctrl.serverGroup.subnet">Subnet</dt>\n <dd ng-if="ctrl.serverGroup.subnet">{{ctrl.serverGroup.subnet}}</dd>\n <dt>Zone{{ctrl.serverGroup.regional ? \'s\' : \'\'}}</dt>\n <dd>\n <ul>\n <li ng-repeat="zone in ctrl.serverGroup.zones">{{zone}}</li>\n </ul>\n </dd>\n <dt ng-if="ctrl.serverGroup.regional">Target Shape</dt>\n <dd ng-if="ctrl.serverGroup.regional">{{ctrl.serverGroup.distributionPolicy.targetShape}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Size" expanded="true">\n <dl class="dl-horizontal dl-narrow" ng-if="ctrl.serverGroup.asg.minSize === ctrl.serverGroup.asg.maxSize">\n <dt>Min/Max</dt>\n <dd>{{ctrl.serverGroup.asg.desiredCapacity}}</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.asg.minSize !== ctrl.serverGroup.asg.maxSize">\n <dt>Min</dt>\n <dd>{{ctrl.serverGroup.asg.minSize}}</dd>\n <dt>Desired</dt>\n <dd>{{ctrl.serverGroup.asg.desiredCapacity}}</dd>\n <dt>Max</dt>\n <dd>{{ctrl.serverGroup.asg.maxSize}}</dd>\n <dt>Current</dt>\n <dd>{{ctrl.serverGroup.instances.length}}</dd>\n </dl>\n <a href ng-click="ctrl.resizeServerGroup()">Resize Server Group</a>\n </collapsible-section>\n <collapsible-section heading="Current Actions" ng-if="ctrl.serverGroup.currentActionsSummary">\n <dl class="dl-horizontal dl-narrow">\n <dt ng-repeat-start="currentAction in ctrl.serverGroup.currentActionsSummary">{{currentAction.action}}</dt>\n <dd ng-repeat-end>{{currentAction.count}}</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="Launch Configuration">\n <dl class="horizontal-when-filters-collapsed">\n <dt>Name</dt>\n <dd>{{ctrl.serverGroup.launchConfig.launchConfigurationName}}</dd>\n <dt>Image</dt>\n <dd>{{ctrl.serverGroup.launchConfig.imageId}}</dd>\n <dt>Instance Type</dt>\n <dd>{{ctrl.serverGroup.launchConfig.instanceType | customInstanceFilter }}</dd>\n <dt>Minimum CPU Platform</dt>\n <dd>{{ctrl.serverGroup.launchConfig.minCpuPlatform || \'(Automatic)\'}}</dd>\n <gce-server-group-disk-descriptions application="ctrl.application" server-group="ctrl.serverGroup">\n </gce-server-group-disk-descriptions>\n <dt ng-if="ctrl.serverGroup.serviceAccountEmail">Service Account</dt>\n <dd ng-if="ctrl.serverGroup.serviceAccountEmail">{{ctrl.serverGroup.serviceAccountEmail}}</dd>\n <dt ng-if="ctrl.serverGroup.authScopes">Auth Scopes</dt>\n <dd ng-repeat="authScope in ctrl.serverGroup.authScopes">\n {{authScope}}\n <help-field key="gce.instance.authScopes.{{authScope}}"></help-field>\n </dd>\n <dt>Network</dt>\n <dd>{{ctrl.serverGroup.network}}</dd>\n <dt ng-if="ctrl.serverGroup.subnet">Subnet</dt>\n <dd ng-if="ctrl.serverGroup.subnet">{{ctrl.serverGroup.subnet}}</dd>\n <dt>Associate Public IP Address</dt>\n <dd>{{ctrl.serverGroup.associatePublicIPAddress}}</dd>\n <dt>Can IP Forward</dt>\n <dd>{{ctrl.serverGroup.canIpForward}}</dd>\n <dt>Startup Script</dt>\n <dd ng-if="ctrl.serverGroup.startupScript">\n <a href ng-click="ctrl.showStartupScript()">Show Startup Script</a>\n </dd>\n <dd ng-if="!ctrl.serverGroup.startupScript">[none]</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="{{ctrl.firewallsLabel}}">\n <ul>\n <li ng-repeat="securityGroup in ctrl.securityGroups | orderBy:\'name\'">\n <a\n ui-sref="^.firewallDetails({name: securityGroup.name, accountId: securityGroup.accountName, region: \'global\', provider: ctrl.serverGroup.type})"\n >\n {{securityGroup.name}}\n </a>\n </li>\n </ul>\n </collapsible-section>\n <collapsible-section heading="Autoscaling">\n <gce-add-autoscaling-policy-button\n ng-if="!ctrl.serverGroup.autoscalingPolicy"\n server-group="ctrl.serverGroup"\n application="ctrl.application"\n >\n </gce-add-autoscaling-policy-button>\n <gce-autoscaling-policy\n ng-repeat="policy in [ctrl.serverGroup.autoscalingPolicy]"\n ng-if="ctrl.serverGroup.autoscalingPolicy"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n policy="policy"\n >\n </gce-autoscaling-policy>\n </collapsible-section>\n <collapsible-section heading="AutoHealing">\n <gce-add-auto-healing-policy-button\n ng-if="!ctrl.serverGroup.autoHealingPolicyHealthCheck"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n ></gce-add-auto-healing-policy-button>\n <gce-auto-healing-policy-details\n ng-if="ctrl.serverGroup.autoHealingPolicyHealthCheck"\n application="ctrl.application"\n server-group="ctrl.serverGroup"\n ></gce-auto-healing-policy-details>\n </collapsible-section>\n <collapsible-section heading="Custom Metadata">\n <div ng-if="!ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items.length">\n No custom metadata associated with this server group\n </div>\n <dl ng-if="ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items.length">\n <dt ng-repeat-start="metadata in ctrl.serverGroup.launchConfig.instanceTemplate.properties.metadata.items">\n {{metadata.key}}\n <help-field key="gce.serverGroup.customMetadata.{{metadata.key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{metadata.value.length > 90 ? metadata.value.substring(0, 90) + "..." : metadata.value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Tags">\n <div ng-if="!ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items.length">\n No tags associated with this server group\n </div>\n <dl ng-if="ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items.length">\n <dd ng-repeat="tag in ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.items">\n {{tag}}\n <help-field\n content="{{ctrl.serverGroup.launchConfig.instanceTemplate.properties.tags.helpMap[tag]}}"\n ></help-field>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Labels">\n <div ng-if="!ctrl.serverGroup.instanceTemplateLabels">No labels associated with this server group</div>\n <dl ng-if="ctrl.serverGroup.instanceTemplateLabels">\n <dt ng-repeat-start="(key, value) in ctrl.serverGroup.instanceTemplateLabels">\n {{key}}\n <help-field key="gce.serverGroup.labels.{{key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Shielded VM Policies">\n <div ng-if="!ctrl.serverGroup.shieldedVmConfig">No Shielded VM policies associated with this server group</div>\n <dl ng-if="ctrl.serverGroup.shieldedVmConfig" class="horizontal-when-filters-collapsed">\n <dt>\n Secure Boot\n <help-field key="gce.serverGroup.shieldedVmSecureBoot"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableSecureBoot}}</dd>\n <dt>\n vTPM\n <help-field key="gce.serverGroup.shieldedVmVtpm"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableVtpm}}</dd>\n <dt>\n Integrity Monitoring\n <help-field key="gce.serverGroup.shieldedVmIntegrityMonitoring"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.shieldedVmConfig.enableIntegrityMonitoring}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Availability Policies">\n <div ng-if="!ctrl.serverGroup.availabilityPolicies">\n No availability policies associated with this server group\n </div>\n <div ng-if="ctrl.serverGroup.availabilityPolicies">\n <dl class="horizontal-when-filters-collapsed">\n <dt>\n Preemptibility\n <help-field key="gce.serverGroup.preemptibility"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.preemptibility}}</dd>\n <dt>\n Automatic Restart\n <help-field key="gce.serverGroup.automaticRestart"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.automaticRestart}}</dd>\n <dt>\n On Host Maintenance\n <help-field key="gce.serverGroup.onHostMaintenance"></help-field>\n </dt>\n <dd>{{ctrl.serverGroup.availabilityPolicies.onHostMaintenance}}</dd>\n </dl>\n </div>\n </collapsible-section>\n <collapsible-section heading="Package" ng-if="ctrl.serverGroup.buildInfo">\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Job</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">{{ctrl.serverGroup.buildInfo.jenkins.name}}</dd>\n <dt>Package</dt>\n <dd>{{ctrl.serverGroup.buildInfo.package_name}}</dd>\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Build</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">{{ctrl.serverGroup.buildInfo.jenkins.number}}</dd>\n <dt>Commit</dt>\n <dd>{{ctrl.truncateCommitHash()}}</dd>\n <dt>Version</dt>\n <dd>{{ctrl.serverGroup.buildInfo.version}}</dd>\n <dt ng-if="ctrl.serverGroup.buildInfo.jenkins">Build Link</dt>\n <dd ng-if="ctrl.serverGroup.buildInfo.jenkins">\n <a target="_blank" href="{{ctrl.buildJenkinsLink()}}">{{ctrl.buildJenkinsLink()}}</a>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Port Name Mapping" ng-if="ctrl.serverGroup.loadBalancingPolicy">\n <dl class="horizontal-when-filters-collapsed" ng-if="ctrl.serverGroup.loadBalancingPolicy.namedPorts.length">\n <dd ng-repeat="namedPort in ctrl.serverGroup.loadBalancingPolicy.namedPorts">\n {{ namedPort.name }} → {{ namedPort.port }}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Capacity Metrics" ng-if="ctrl.serverGroup.loadBalancingPolicy">\n <dl class="horizontal-when-filters-collapsed" ng-if="ctrl.serverGroup.loadBalancingPolicy.listeningPort">\n <dt>Balancing Mode</dt>\n <dd>{{ctrl.serverGroup.loadBalancingPolicy.balancingMode}}</dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance">Max RPS per instance</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance">\n {{ctrl.serverGroup.loadBalancingPolicy.maxRatePerInstance}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxUtilization">Max CPU Utilization</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxUtilization">\n {{ctrl.serverGroup.loadBalancingPolicy.maxUtilization | decimalToPercent}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance">Max connections per instance</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance">\n {{ctrl.serverGroup.loadBalancingPolicy.maxConnectionsPerInstance}}\n </dd>\n <dt ng-if="ctrl.serverGroup.loadBalancingPolicy.capacityScaler">Capacity</dt>\n <dd ng-if="ctrl.serverGroup.loadBalancingPolicy.capacityScaler">\n {{ctrl.serverGroup.loadBalancingPolicy.capacityScaler | decimalToPercent}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="ctrl.serverGroup.logsLink">\n <a href="{{ctrl.serverGroup.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="ctrl.serverGroup.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/serverGroup/configure/wizard/serverGroupWizard.html",'<form name="form" class="form-horizontal" novalidate>\n <div ng-if="state.requiresTemplateSelection">\n <ng-include src="pages.templateSelection"></ng-include>\n </div>\n <div ng-if="!state.loaded" style="height: 200px" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div ng-if="!state.requiresTemplateSelection">\n <v2-modal-wizard ng-show="state.loaded" heading="{{title}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="location" label="Basic Settings" mark-complete-on-view="false">\n <ng-include src="pages.basicSettings"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="load-balancers" label="Load Balancers" mark-complete-on-view="false">\n <ng-include src="pages.loadBalancers"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="security-groups" label="{{firewallsLabel}}" done="true">\n <ng-include src="pages.securityGroups"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="instance-type" label="Instance Type" mark-complete-on-view="false">\n <ng-include src="pages.instanceType"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="capacity" label="Capacity" mark-complete-on-view="false">\n <ng-include src="pages.capacity"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autoscaling-policy" label="Autoscaling Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoScalingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="autohealing-policy" label="Autohealing Policy" mark-complete-on-view="false">\n <ng-include src="pages.autoHealingPolicy"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="zones" label="Zones" mark-complete-on-view="false" done="true">\n <ng-include src="pages.zones"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="advanced" label="Advanced Settings" mandatory="false" done="true">\n <ng-include src="pages.advancedSettings"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer" ng-if="state.loaded">\n <button ng-disabled="taskMonitor.submitting" class="btn btn-default btn-cancel" ng-click="ctrl.cancel()">\n Cancel\n </button>\n <submit-button\n ng-if="ctrl.showSubmitButton()"\n is-disabled="!ctrl.isValid() || taskMonitor.submitting"\n label="command.viewState.submitButtonLabel"\n submitting="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("google/src/instance/details/instanceDetails.html",'<div class="details-panel">\n <div class="header">\n <instance-details-header\n health-state="instance.healthState"\n instance-id="instance ? instance.instanceId : instanceIdNotFound"\n loading="state.loading"\n standalone="state.standalone"\n ></instance-details-header>\n <div ng-if="!state.loading">\n <div class="actions" ng-class="{ insights: instance.insightActions.length > 0 }" ng-if="instance.placement">\n <div class="dropdown" uib-dropdown dropdown-append-to-body>\n <button\n type="button"\n class="btn btn-sm btn-primary dropdown-toggle"\n ng-disabled="disabled"\n uib-dropdown-toggle\n >\n Instance Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li>\n <a href ng-click="ctrl.enableInstanceInDiscovery()" ng-if="ctrl.canRegisterWithDiscovery()"\n >Enable in Discovery</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.disableInstanceInDiscovery()" ng-if="ctrl.hasHealthState(\'Discovery\', \'Up\')"\n >Disable in Discovery</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.registerInstanceWithLoadBalancer()" ng-if="ctrl.canRegisterWithLoadBalancer()"\n >Register with Load Balancer</a\n >\n </li>\n <li>\n <a href ng-click="ctrl.deregisterInstanceFromLoadBalancer()" ng-if="ctrl.canDeregisterFromLoadBalancer()"\n >Deregister from Load Balancer</a\n >\n </li>\n <li role="presentation" class="divider" ng-if="ctrl.showInstanceActionsDivider()"></li>\n <li><a href ng-click="ctrl.rebootInstance()">Reboot</a></li>\n <li><a href ng-click="ctrl.terminateInstance()">Terminate</a></li>\n <li>\n <a href ng-click="ctrl.terminateInstanceAndShrinkServerGroup()" ng-if="instance.serverGroup"\n >Terminate and Shrink Server Group</a\n >\n </li>\n </ul>\n </div>\n <div class="dropdown" ng-if="instance.insightActions.length > 0" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-default dropdown-toggle" uib-dropdown-toggle>\n Insight <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li ng-repeat="action in instance.insightActions">\n <a target="_blank" href="{{action.url}}">{{action.label}}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!state.loading && instance">\n <collapsible-section heading="Instance Information" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Launched</dt>\n <dd ng-if="instance.launchTime">{{instance.launchTime | timestamp}}</dd>\n <dd ng-if="!instance.launchTime">(Unknown)</dd>\n <dt>In</dt>\n <dd>\n <account-tag account="instance.account" pad="right"></account-tag>\n {{instance.placement.availabilityZone || \'(Unknown)\'}}\n </dd>\n <dt>Type</dt>\n <dd>{{instance.instanceType || \'(Unknown)\' | customInstanceFilter}}</dd>\n <dt>CPU Platform</dt>\n <dd>{{instance.cpuPlatform}}</dd>\n <dt ng-if="instance.serverGroup">Server Group</dt>\n <dd ng-if="instance.serverGroup">\n <a\n ui-sref="^.serverGroup({region: instance.region,\n accountId: instance.account,\n serverGroup: instance.serverGroup,\n provider: instance.provider})"\n >{{instance.serverGroup}}</a\n >\n </dd>\n <dt>Network</dt>\n <dd>{{instance.network || \'(Unknown)\'}}</dd>\n <dt ng-if="instance.subnet">Subnet</dt>\n <dd ng-if="instance.subnet">{{instance.subnet}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <p ng-if="instance.healthState !== \'Starting\' && !healthMetrics.length">\n No health metrics found for this instance\n </p>\n <p ng-if="instance.healthState === \'Starting\' && !healthMetrics.length">\n <span class="glyphicon glyphicon-Starting-triangle"></span> <strong>Starting</strong>\n </p>\n\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-repeat-start="metric in healthMetrics | orderBy: \'type\'">{{metric.type | robotToHuman}}</dt>\n <dd ng-repeat-end>\n <div ng-if="metric.type !== \'LoadBalancer\'">\n <span\n uib-tooltip="{{metric.state.toLowerCase() === \'down\' ? metric.description : \'\'}}"\n tooltip-placement="left"\n >\n <span class="glyphicon glyphicon-{{metric.state}}-triangle"></span>\n {{metric.state | robotToHuman}}\n </span>\n <span class="pad-left small">\n <a ng-if="metric.healthCheckUrl" target="_blank" href="{{metric.healthCheckUrl}}">Health Check</a>\n <span ng-if="metric.healthCheckUrl && metric.statusPageUrl"> | </span>\n <a ng-if="metric.statusPageUrl" target="_blank" href="{{metric.statusPageUrl}}">Status</a>\n </span>\n </div>\n <div\n ng-if="metric.type === \'LoadBalancer\' && metric.loadBalancers.length"\n ng-repeat="loadBalancer in metric.loadBalancers"\n >\n <instance-load-balancer-health load-balancer="loadBalancer"></instance-load-balancer-health>\n </div>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="DNS">\n <dl class="horizontal-when-filters-collapsed">\n <dt ng-if="instance.internalDnsName">Internal DNS Name</dt>\n <dd ng-if="instance.internalDnsName">\n <a href="http://{{instance.internalDnsName}}" target="_blank">{{instance.internalDnsName}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.internalDnsName"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n <dt ng-if="instance.internalIpAddress">Internal IP Address</dt>\n <dd ng-if="instance.internalIpAddress">\n <a href="http://{{instance.internalIpAddress}}" target="_blank">{{instance.internalIpAddress}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.internalIpAddress"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n <dt ng-if="instance.externalIpAddress">External IP Address</dt>\n <dd ng-if="instance.externalIpAddress">\n <a href="http://{{instance.externalIpAddress}}" target="_blank">{{instance.externalIpAddress}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.externalIpAddress"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="{{firewallsLabel}}">\n <ul>\n <li ng-repeat="securityGroup in instance.securityGroups | orderBy:\'groupName\'">\n <a\n ui-sref="^.firewallDetails({name:securityGroup.groupName, accountId: instance.account, region: \'global\', provider: instance.provider})"\n >\n {{securityGroup.groupName}}\n </a>\n </li>\n </ul>\n </collapsible-section>\n <collapsible-section heading="Custom Metadata">\n <div ng-if="!instance.metadata.items.length">No custom metadata associated with this instance</div>\n <dl ng-if="instance.metadata.items.length">\n <dt ng-repeat-start="metadata in instance.metadata.items">\n {{metadata.key}}\n <help-field key="gce.instance.customMetadata.{{metadata.key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{metadata.value.length > 90 ? metadata.value.substring(0, 90) + "..." : metadata.value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Tags">\n <div ng-if="!instance.tags.items.length">No tags associated with this instance</div>\n <dl ng-if="instance.tags.items.length">\n <dd ng-repeat="tag in instance.tags.items">\n {{tag}}\n <help-field content="{{instance.tags.helpMap[tag]}}"></help-field>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Labels">\n <div ng-if="!instance.labels">No labels associated with this instance</div>\n <dl ng-if="instance.labels">\n <dt ng-repeat-start="(key, value) in instance.labels">\n {{key}}\n <help-field key="gce.instance.labels.{{key}}"></help-field>\n </dt>\n <dd ng-repeat-end>{{value}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="SSH" ng-if="!instance.notFound">\n <dl>\n <dt ng-if="instance.sshLink">SSH into this instance</dt>\n <dd ng-if="instance.sshLink">\n <a href="{{instance.sshLink}}" target="_blank">SSH</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.sshLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="instance.logsLink">\n <a href="{{instance.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="instance.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n <li>\n <console-output-link instance="instance"></console-output-link>\n </li>\n </ul>\n </collapsible-section>\n <instance-links\n address="baseIpAddress"\n application="application"\n instance="instance"\n moniker="moniker"\n environment="environment"\n ></instance-links>\n </div>\n <div class="content" ng-if="!state.loading && !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("google/src/serverGroup/configure/wizard/customInstance/customInstanceBuilder.html",'<dirty-instance-type-notification command="instanceArchetypeCtrl.command"></dirty-instance-type-notification>\n<div class="form-group">\n <gce-custom-instance-configurer\n instance-family-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.instanceFamilyList"\n selected-instance-family="instanceArchetypeCtrl.command.viewState.customInstance.instanceFamily"\n v-cpu-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.vCpuList"\n selected-v-cpu-count="instanceArchetypeCtrl.command.viewState.customInstance.vCpuCount"\n memory-list="instanceArchetypeCtrl.command.backingData.customInstanceTypes.memoryList"\n selected-memory="instanceArchetypeCtrl.command.viewState.customInstance.memory"\n selected-extended-memory="instanceArchetypeCtrl.command.viewState.customInstance.extendedMemory"\n on-change="instanceArchetypeCtrl.command.setCustomInstanceViewState"\n ></gce-custom-instance-configurer>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/details/loadBalancerDetails.html",'<div class="details-panel">\n <div ng-if="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="!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 <span class="fa icon-sitemap"></span>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>\n {{loadBalancer.name}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n entity="loadBalancer"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="loadBalancer"\n page-location="details"\n on-update="ctrl.application.loadBalancers.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </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="!loadBalancer.instances.length">\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <li\n ng-if="loadBalancer.instances.length"\n class="disabled"\n uib-tooltip="You must detach all instances before you can delete this load balancer."\n >\n <a href ng-click="ctrl.deleteLoadBalancer()">Delete Load Balancer</a>\n </li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="loadBalancer"\n application="ctrl.application"\n entity-type="loadBalancer"\n on-update="ctrl.application.loadBalancers.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div ng-if="!state.loading" class="content">\n <collapsible-section heading="Load Balancer Details" expanded="true">\n <dl class="dl-horizontal dl-narrow">\n <dt>Created</dt>\n <dd>{{loadBalancer.elb.createdTime | timestamp}}</dd>\n <dt>In</dt>\n <dd><account-tag account="loadBalancer.account" pad="right"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{loadBalancer.region}}</dd>\n <dt>Type</dt>\n <dd>\n <gce-load-balancer-type load-balancer="loadBalancer"></gce-load-balancer-type>\n </dd>\n <dt ng-if="loadBalancer.network">Network</dt>\n <dd ng-if="loadBalancer.network">{{ctrl.getNetworkId(loadBalancer)}}</dd>\n <dt ng-if="loadBalancer.subnet">Subnet</dt>\n <dd ng-if="loadBalancer.subnet">{{ctrl.getSubnetId(loadBalancer)}}</dd>\n <dt ng-if="loadBalancer.serverGroups.length">Server Groups</dt>\n <dd ng-if="loadBalancer.serverGroups.length">\n <ul>\n <li ng-repeat="serverGroup in loadBalancer.serverGroups | orderBy: [\'isDisabled\', \'-name\']">\n <a\n ui-sref="^.serverGroup({region: serverGroup.region,\n accountId: serverGroup.account,\n serverGroup: serverGroup.name,\n provider: \'gce\'})"\n >\n {{serverGroup.name}}\n </a>\n </li>\n </ul>\n </dd>\n <div ng-if="!ctrl.isHttpLoadBalancer(loadBalancer) && loadBalancer.elb.dnsname">\n <dt>DNS Name</dt>\n <dd>\n <a target="_blank" href="{{loadBalancer.elb.dns.protocol}}//{{loadBalancer.elb.dns.dnsname}}"\n >{{loadBalancer.elb.dns.dnsname}}</a\n >\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="loadBalancer.elb.dns.dnsname"\n tool-tip="\'Copy DNS Name to clipboard\'"\n >\n </copy-to-clipboard>\n </dd>\n </div>\n <div ng-if="ctrl.loadBalancer.loadBalancerType === \'HTTP\'">\n <dt>DNS Names</dt>\n <dd>\n <ul>\n <li ng-repeat="dns in loadBalancer.elb.dns">\n <a target="_blank" href="{{dns.protocol}}//{{dns.dnsname}}">{{dns.dnsname}}</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="dns.dnsname"\n tool-tip="\'Copy DNS Name to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </dd>\n </div>\n <div ng-if="ctrl.isHttpLoadBalancer(loadBalancer)">\n <gce-host-and-path-rules-button\n default-service="loadBalancer.defaultService"\n load-balancer-name="loadBalancer.name"\n host-rules="loadBalancer.hostRules"\n >\n </gce-host-and-path-rules-button>\n </div>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Status" expanded="true">\n <health-counts class="pull-left" container="loadBalancer.instanceCounts"></health-counts>\n </collapsible-section>\n <collapsible-section heading="Listeners">\n <dl>\n <dt>\n Load Balancer → Instance\n <help-field\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.loadBalancerType === \'SSL\' || loadBalancer.loadBalancerType === \'TCP\'"\n key="gce.httpLoadBalancer.namedPort"\n ></help-field>\n </dt>\n <dd ng-repeat="listener in loadBalancer.elb.listenerDescriptions">\n {{listener.listener.protocol}}:{{listener.listener.loadBalancerPort}} →\n {{listener.listener.instanceProtocol}}:{{listener.listener.instancePort}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="Backend Services"\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.backendService"\n >\n <dl class="horizontal-when-filters-collapsed" ng-switch on="loadBalancer.loadBalancerType">\n <div ng-repeat="service in loadBalancer.elb.backendServices" ng-switch-when="HTTP">\n <hr ng-if="$index > 0" />\n <gce-backend-service-details backend-service="service"></gce-backend-service-details>\n </div>\n\n <gce-backend-service-details\n backend-service="loadBalancer.backendService"\n ng-switch-default\n ></gce-backend-service-details>\n </dl>\n </collapsible-section>\n <collapsible-section\n ng-if="ctrl.isHttpLoadBalancer(loadBalancer) || loadBalancer.healthCheck || loadBalancer.backendService.healthCheck"\n heading="Health Checks"\n >\n <dl class="horizontal-when-filters-collapsed" ng-switch on="loadBalancer.loadBalancerType">\n <gce-health-check ng-switch-when="NETWORK" health-check="loadBalancer.healthCheck"></gce-health-check>\n\n <div ng-switch-when="HTTP" ng-repeat="healthCheck in loadBalancer.elb.healthChecks">\n <hr ng-if="$index > 0" />\n <gce-health-check health-check="healthCheck"></gce-health-check>\n </div>\n\n <gce-health-check ng-switch-default health-check="loadBalancer.backendService.healthCheck"></gce-health-check>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="loadBalancer.logsLink">\n <a href="{{loadBalancer.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="loadBalancer.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html",'<div modal-page>\n <modal-close dismiss="$dismiss()"></modal-close>\n <div class="modal-header">\n <h4 class="modal-title">Load Balancer Type Selection</h4>\n </div>\n <div class="modal-body">\n <form class="form-horizontal">\n <div class="form-group">\n <div class="col-md-4 col-md-offset-1 sm-label-left">\n <b>Choose load balancer type:</b>\n </div>\n </div>\n <div class="form-group">\n <div class="col-md-6 col-md-offset-1">\n <ui-select ng-model="ctrl.choice" class="form-control input-sm">\n <ui-select-match placeholder="Select...">\n <span>{{$select.selected}}</span>\n </ui-select-match>\n <ui-select-choices repeat="choice in ctrl.choices">\n <span>{{choice}}<help-field key="gce.loadBalancerType.{{choice}}"></help-field></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-primary" ng-click="ctrl.choose(ctrl.choice)">\n <span>Create {{ ctrl.choice }} load balancer</span>\n <span class="glyphicon glyphicon-chevron-right"></span>\n </button>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/details/securityGroupDetail.html",'<div class="details-panel">\n <div class="header">\n <div class="close-button" ng-if="!state.standalone">\n <a class="btn btn-link" ui-sref="^">\n <span class="glyphicon glyphicon-remove"></span>\n </a>\n </div>\n <div ng-if="state.loading" class="horizontal center middle">\n <loading-spinner size="\'small\'"></loading-spinner>\n </div>\n <div class="header-text horizontal middle" ng-if="!state.loading">\n <span class="glyphicon glyphicon-transfer"></span>\n <h3 class="horizontal middle space-between flex-1" select-on-dbl-click>\n {{securityGroup.name || \'(not found)\'}}\n <render-if-feature feature="entityTags">\n <entity-notifications\n ng-if="!state.loading"\n entity="securityGroup"\n application="ctrl.application"\n placement="bottom"\n h-offset-percent="90%"\n entity-type="securityGroup"\n page-location="details"\n on-update="ctrl.application.securityGroups.refresh()"\n ></entity-notifications>\n </render-if-feature>\n </h3>\n </div>\n <div class="actions">\n <div ng-if="!securityGroup.id.includes(\'/\')" class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n <firewall-label label="Firewall"></firewall-label> Actions <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" uib-dropdown-menu role="menu">\n <li><a href ng-click="ctrl.editInboundRules()">Edit Inbound Rules</a></li>\n <li>\n <a href ng-click="ctrl.deleteSecurityGroup()">Delete <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <li>\n <a href ng-click="ctrl.cloneSecurityGroup()">Clone <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <render-if-feature feature="entityTags">\n <add-entity-tag-links\n component="securityGroup"\n application="ctrl.application"\n entity-type="securityGroup"\n on-update="ctrl.application.securityGroups.refresh"\n ></add-entity-tag-links>\n </render-if-feature>\n </ul>\n </div>\n <div ng-if="securityGroup.id.includes(\'/\')" class="dropdown" uib-dropdown dropdown-append-to-body>\n <button type="button" class="btn btn-sm btn-primary dropdown-toggle" uib-dropdown-toggle>\n <firewall-label label="Firewall"></firewall-label> Actions <span class="caret"></span>\n </button>\n <ul\n uib-tooltip="You cannot modify shared VPC host project firewall rules."\n class="dropdown-menu"\n uib-dropdown-menu\n role="menu"\n >\n <li class="disabled"><a>Edit Inbound Rules</a></li>\n <li class="disabled">\n <a>Delete <firewall-label label="Firewall"></firewall-label></a>\n </li>\n <li class="disabled">\n <a>Clone <firewall-label label="Firewall"></firewall-label></a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n <div class="content" ng-if="!state.loading">\n <collapsible-section heading="{{firewallLabel}} Details" expanded="true">\n <dl class="dl-horizontal dl-medium">\n <dt>ID</dt>\n <dd>{{securityGroup.id}}</dd>\n <dt>Account</dt>\n <dd><account-tag account="securityGroup.accountName"></account-tag></dd>\n <dt>Region</dt>\n <dd>{{securityGroup.region}}</dd>\n <dt>Network</dt>\n <dd>{{securityGroup.network}}</dd>\n <dt>Description</dt>\n <dd>{{securityGroup.description}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="IP Range Rules ({{securityGroup.sourceRanges.length || 0}})"\n expanded="{{!!(securityGroup.sourceRanges && securityGroup.sourceRanges.length)}}"\n >\n <div ng-if="!securityGroup.sourceRanges.length">None</div>\n\n <dl\n ng-class="insightCtrl.vm.filtersExpanded ? \'\' : \'dl-horizontal dl-medium\'"\n ng-repeat="sourceRange in securityGroup.sourceRanges"\n >\n <dt>IP Range</dt>\n <dd>{{sourceRange}}</dd>\n </dl>\n </collapsible-section>\n <collapsible-section\n heading="Protocol/Port Ranges ({{securityGroup.protocolPortRangeCount || 0}})"\n expanded="{{!!(securityGroup.protocolPortRangeCount)}}"\n >\n <div ng-if="!securityGroup.ipIngressRules.length">None</div>\n\n <dl\n ng-class="insightCtrl.vm.filtersExpanded ? \'\' : \'dl-horizontal dl-medium\'"\n ng-repeat="ipIngressRule in securityGroup.ipIngressRules"\n >\n <dt ng-if="ipIngressRule.portRanges.length === 0">Protocol</dt>\n <dd ng-if="ipIngressRule.portRanges.length === 0">{{ipIngressRule.protocol}}</dd>\n <dt\n ng-if="ipIngressRule.portRanges && ipIngressRule.portRanges.length === 1 && ipIngressRule.portRanges[0].startPort"\n >\n Port Range\n </dt>\n <dt\n ng-if="ipIngressRule.portRanges && ipIngressRule.portRanges.length > 1 && ipIngressRule.portRanges[0].startPort"\n >\n Port Ranges\n </dt>\n <dd ng-repeat="portRange in ipIngressRule.portRanges" ng-if="portRange.startPort && portRange.endPort">\n {{ipIngressRule.protocol}}: {{portRange.startPort}} → {{portRange.endPort}}\n </dd>\n </dl>\n </collapsible-section>\n <collapsible-section heading="Target Tags ({{securityGroup.targetTags.length || 0}})">\n <ul ng-if="securityGroup.targetTags.length">\n <li ng-repeat="tag in securityGroup.targetTags">\n {{tag}} <help-field content="{{ctrl.getTagHelpText(tag, \'target\')}}"></help-field>\n </li>\n </ul>\n <div ng-if="!securityGroup.targetTags.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Source Tags ({{securityGroup.sourceTags.length || 0}})">\n <ul ng-if="securityGroup.sourceTags.length">\n <li ng-repeat="tag in securityGroup.sourceTags">\n {{tag}} <help-field content="{{ctrl.getTagHelpText(tag, \'source\')}}"></help-field>\n </li>\n </ul>\n <div ng-if="!securityGroup.sourceTags.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Target Service Accounts ({{securityGroup.targetServiceAccounts.length || 0}})">\n <ul ng-if="securityGroup.targetServiceAccounts.length">\n <li ng-repeat="serviceAccount in securityGroup.targetServiceAccounts">{{serviceAccount}}</li>\n </ul>\n <div ng-if="!securityGroup.targetServiceAccounts.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Source Service Accounts ({{securityGroup.sourceServiceAccounts.length || 0}})">\n <ul ng-if="securityGroup.sourceServiceAccounts.length">\n <li ng-repeat="serviceAccount in securityGroup.sourceServiceAccounts">{{serviceAccount}}</li>\n </ul>\n <div ng-if="!securityGroup.sourceServiceAccounts.length">None</div>\n </collapsible-section>\n <collapsible-section heading="Logs">\n <ul>\n <li ng-if="securityGroup.logsLink">\n <a href="{{securityGroup.logsLink}}" target="_blank">Cloud Console Logs</a>\n <copy-to-clipboard\n class="copy-to-clipboard copy-to-clipboard-sm"\n text="securityGroup.logsLink"\n tool-tip="\'Copy to clipboard\'"\n >\n </copy-to-clipboard>\n </li>\n </ul>\n </collapsible-section>\n </div>\n</div>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/securityGroup/configure/createSecurityGroup.html",'<ng-form role="form" name="form" novalidate class="gce-security-group-wizard">\n <v2-modal-wizard heading="Create New {{firewallLabel}}" task-monitor="taskMonitor" dismiss="$dismiss()">\n <v2-wizard-page key="Location" label="Location">\n <ng-include src="pages.location"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Targets" label="Targets" done="true">\n <ng-include src="pages.targets"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Source Filters" label="Source Filters" done="true">\n <ng-include src="pages.sourceFilters"></ng-include>\n </v2-wizard-page>\n <v2-wizard-page key="Ingress" label="Ingress" done="true">\n <ng-include src="pages.ingress"></ng-include>\n </v2-wizard-page>\n </v2-modal-wizard>\n <div class="modal-footer">\n <button class="btn btn-default" ng-click="ctrl.cancel()">Cancel</button>\n <submit-button\n is-disabled="form.$invalid || !wizard.isComplete() || state.submitting || !ctrl.isValid()"\n submitting="state.submitting"\n on-click="ctrl.upsert()"\n is-new="isNew"\n ></submit-button>\n </div>\n</ng-form>\n')}]),window.angular.module("ng").run(["$templateCache",function(e){e.put("google/src/applicationProviderFields/gceFields.html",'<div class="form-group row">\n <div class="col-sm-3 sm-label-right">GCE Settings</div>\n <div class="col-sm-9 checkbox" style="margin-bottom: 0; margin-top: 5px">\n <label>\n <input\n type="checkbox"\n ng-init="$ctrl.initializeApplicationField(\'gce.associatePublicIpAddress\')"\n ng-model="$ctrl.application.providerSettings.gce.associatePublicIpAddress"\n />\n Associate Public IP Address <help-field key="gce.serverGroup.associatePublicIpAddress.providerField"></help-field>\n </label>\n </div>\n</div>\n')}]);export{si as GOOGLE_MODULE};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|