All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.spinnaker.clouddriver.google.deploy.ops.loadbalancer.UpsertGoogleInternalHttpLoadBalancerAtomicOperation Maven / Gradle / Ivy

package com.netflix.spinnaker.clouddriver.google.deploy.ops.loadbalancer;

import static com.netflix.spinnaker.clouddriver.google.deploy.GCEUtil.REGIONAL_LOAD_BALANCER_NAMES;
import static com.netflix.spinnaker.clouddriver.google.deploy.GCEUtil.REGION_BACKEND_SERVICE_NAMES;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.json.GenericJson;
import com.google.api.services.compute.Compute;
import com.google.api.services.compute.model.*;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.netflix.spinnaker.clouddriver.data.task.Task;
import com.netflix.spinnaker.clouddriver.data.task.TaskRepository;
import com.netflix.spinnaker.clouddriver.google.deploy.GCEUtil;
import com.netflix.spinnaker.clouddriver.google.deploy.GoogleOperationPoller;
import com.netflix.spinnaker.clouddriver.google.deploy.SafeRetry;
import com.netflix.spinnaker.clouddriver.google.deploy.description.BaseGoogleInstanceDescription;
import com.netflix.spinnaker.clouddriver.google.deploy.description.UpsertGoogleLoadBalancerDescription;
import com.netflix.spinnaker.clouddriver.google.deploy.exception.GoogleOperationException;
import com.netflix.spinnaker.clouddriver.google.model.GoogleHealthCheck;
import com.netflix.spinnaker.clouddriver.google.model.GoogleNetwork;
import com.netflix.spinnaker.clouddriver.google.model.GoogleSubnet;
import com.netflix.spinnaker.clouddriver.google.model.callbacks.Utils;
import com.netflix.spinnaker.clouddriver.google.model.loadbalancing.*;
import com.netflix.spinnaker.clouddriver.google.provider.view.GoogleNetworkProvider;
import com.netflix.spinnaker.clouddriver.google.provider.view.GoogleSubnetProvider;
import com.netflix.spinnaker.clouddriver.google.security.GoogleNamedAccountCredentials;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperation;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperationConverter;
import com.netflix.spinnaker.clouddriver.orchestration.AtomicOperationsRegistry;
import com.netflix.spinnaker.clouddriver.orchestration.OrchestrationProcessor;
import groovy.lang.Closure;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.groovy.runtime.StringGroovyMethods;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

public class UpsertGoogleInternalHttpLoadBalancerAtomicOperation
    extends UpsertGoogleLoadBalancerAtomicOperation {
  private static Task getTask() {
    return TaskRepository.threadLocalTask.get();
  }

  private static final Logger log = LoggerFactory.getLogger(GoogleInternalHttpLoadBalancer.class);
  private static final String BASE_PHASE = "UPSERT_INTERNAL_HTTP_LOAD_BALANCER";
  private static final String PATH_MATCHER_PREFIX = "pm";
  public static final String TARGET_HTTP_PROXY_NAME_PREFIX = "target-http-proxy";
  public static final String TARGET_HTTPS_PROXY_NAME_PREFIX = "target-https-proxy";
  @Autowired private GoogleOperationPoller googleOperationPoller;
  @Autowired private AtomicOperationsRegistry atomicOperationsRegistry;
  @Autowired private GoogleNetworkProvider googleNetworkProvider;
  @Autowired private GoogleSubnetProvider googleSubnetProvider;
  @Autowired private OrchestrationProcessor orchestrationProcessor;
  @Autowired private SafeRetry safeRetry;
  private final UpsertGoogleLoadBalancerDescription description;

  public UpsertGoogleInternalHttpLoadBalancerAtomicOperation(
      UpsertGoogleLoadBalancerDescription description) {
    this.description = description;
  }

  /**
   * minimal command: curl -v -X POST -H "Content-Type: application/json" -d '[{
   * "upsertLoadBalancer": {"credentials": "my-google-account", "loadBalancerType":
   * "INTERNAL_MANAGED", "loadBalancerName": "internal-http-create", "portRange": "80",
   * "backendServiceDiff": [], "defaultService": {"name": "default-backend-service", "backends": [],
   * "healthCheck": {"name": "basic-check", "requestPath": "/", "port": 80, "checkIntervalSec": 1,
   * "timeoutSec": 1, "healthyThreshold": 1, "unhealthyThreshold": 1}}, "certificate": "",
   * "hostRules": [] }}]' localhost:7002/gce/ops
   *
   * 

full command: curl -v -X POST -H "Content-Type: application/json" -d '[{ * "upsertLoadBalancer": {"credentials": "my-google-account", "loadBalancerType": * "INTERNAL_MANAGED", "loadBalancerName": "internal-http-create", "portRange": "80", * "backendServiceDiff": [], "defaultService": {"name": "default-backend-service", "backends": [], * "healthCheck": {"name": "basic-check", "requestPath": "/", "port": 80, "checkIntervalSec": 1, * "timeoutSec": 1, "healthyThreshold": 1, "unhealthyThreshold": 1}}, "certificate": "", * "hostRules": [{"hostPatterns": ["host1.com", "host2.com"], "pathMatcher": {"pathRules": * [{"paths": ["/path", "/path2/more"], "backendService": {"name": "backend-service", "backends": * [], "healthCheck": {"name": "health-check", "requestPath": "/", "port": 80, "checkIntervalSec": * 1, "timeoutSec": 1, "healthyThreshold": 1, "unhealthyThreshold": 1}}}], "defaultService": * {"name": "pm-backend-service", "backends": [], "healthCheck": {"name": "derp-check", * "requestPath": "/", "port": 80, "checkIntervalSec": 1, "timeoutSec": 1, "healthyThreshold": 1, * "unhealthyThreshold": 1}}}}]}}]' localhost:7002/gce/ops * * @param description * @param priorOutputs * @return */ @Override public Map operate(List priorOutputs) { GoogleNetwork network = GCEUtil.queryNetwork( description.getAccountName(), description.getNetwork(), getTask(), BASE_PHASE, googleNetworkProvider); GoogleSubnet subnet = GCEUtil.querySubnet( description.getAccountName(), description.getRegion(), description.getSubnet(), getTask(), BASE_PHASE, googleSubnetProvider); GoogleInternalHttpLoadBalancer internalHttpLoadBalancer = new GoogleInternalHttpLoadBalancer(); internalHttpLoadBalancer.setName(description.getLoadBalancerName()); internalHttpLoadBalancer.setUrlMapName(description.getUrlMapName()); internalHttpLoadBalancer.setDefaultService(description.getDefaultService()); internalHttpLoadBalancer.setHostRules( description.getHostRules() != null ? description.getHostRules() : new ArrayList<>()); internalHttpLoadBalancer.setCertificate(description.getCertificate()); internalHttpLoadBalancer.setIpAddress(description.getIpAddress()); internalHttpLoadBalancer.setIpProtocol(description.getIpProtocol()); internalHttpLoadBalancer.setNetwork(network.getSelfLink()); internalHttpLoadBalancer.setSubnet(subnet.getSelfLink()); internalHttpLoadBalancer.setPortRange(description.getPortRange()); String internalHttpLoadBalancerName = internalHttpLoadBalancer.getName(); getTask() .updateStatus( BASE_PHASE, "Initializing upsert of Internal HTTP load balancer " + internalHttpLoadBalancerName + "..."); if (description.getCredentials() == null) { throw new IllegalArgumentException( "Unable to resolve credentials for Google account '" + description.getAccountName() + "'."); } Compute compute = description.getCredentials().getCompute(); String project = description.getCredentials().getProject(); String region = description.getRegion(); // Step 0: Set up state to formulate a plan for creating or updating the L7 LB. Set healthCheckExistsSet = new HashSet<>(); Set healthCheckNeedsUpdatedSet = new HashSet<>(); Set serviceExistsSet = new HashSet<>(); Set serviceNeedsUpdatedSet = new HashSet<>(); boolean urlMapExists; boolean targetProxyExists = false; boolean targetProxyNeedsUpdated = false; boolean forwardingRuleExists; // The following are unique on object equality, not just name. This lets us check if a // service/hc exists or // needs updated by _name_ later. List backendServicesFromDescription = ImmutableSet.copyOf( Utils.getBackendServicesFromInternalHttpLoadBalancerView( internalHttpLoadBalancer.getView())) .asList(); List healthChecksFromDescription = backendServicesFromDescription.stream() .map(GoogleBackendService::getHealthCheck) .distinct() .collect(toList()); final String name = internalHttpLoadBalancer.getUrlMapName(); String urlMapName = name != null ? name : internalHttpLoadBalancerName; // An L7 load balancer is identified by its UrlMap name // in Google Cloud Console. // Get all the existing infrastructure. // Look up the legacy health checks so we can do the work to transition smoothly to the UHCs. try { List existingHealthChecks = timeExecute( compute.regionHealthChecks().list(project, region), "compute.regionHealthChecks.list", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) .getItems(); List existingServices = timeExecute( compute.regionBackendServices().list(project, region), "compute.regionBackendServices.list", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) .getItems(); UrlMap existingUrlMap = null; try { existingUrlMap = timeExecute( compute.regionUrlMaps().get(project, region, urlMapName), "compute.regionUrlMaps.get", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); } catch (GoogleJsonResponseException e) { // 404 is thrown if the url map doesn't exist. Any other exception needs to be propagated. if (e.getStatusCode() != 404) { throw e; } } // Determine if the infrastructure in the description exists already. // If it does, check and see if we need to update it from the description. // UrlMap urlMapExists = existingUrlMap != null; // ForwardingRule ForwardingRule existingRule = null; try { existingRule = timeExecute( compute.forwardingRules().get(project, region, internalHttpLoadBalancerName), "compute.forwardingRules.get", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); } catch (GoogleJsonResponseException e) { if (e.getStatusCode() != 404) { throw e; } } forwardingRuleExists = existingRule != null; // TargetProxy GenericJson existingProxy = null; if (forwardingRuleExists) { String targetProxyName = GCEUtil.getLocalName(existingRule.getTarget()); switch (Utils.getTargetProxyType(existingRule.getTarget())) { case HTTP: existingProxy = timeExecute( compute.regionTargetHttpProxies().get(project, region, targetProxyName), "compute.regionTargetHttpProxies.get", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); break; case HTTPS: existingProxy = timeExecute( compute.regionTargetHttpsProxies().get(project, region, targetProxyName), "compute.regionTargetHttpsProxies.get", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); if (!StringGroovyMethods.asBoolean(internalHttpLoadBalancer.getCertificate())) { throw new IllegalArgumentException( internalHttpLoadBalancerName + " is an Https load balancer, but the upsert description does not contain a certificate."); } targetProxyNeedsUpdated = !GCEUtil.getLocalName( ((TargetHttpsProxy) existingProxy).getSslCertificates().get(0)) .equals( GCEUtil.getLocalName( GCEUtil.buildCertificateUrl( project, internalHttpLoadBalancer.getCertificate()))); break; default: log.warn("Unexpected target proxy type for " + targetProxyName + "."); break; } targetProxyExists = existingProxy != null; if (targetProxyExists && !GCEUtil.getLocalName((String) existingProxy.get("urlMap")) .equals(description.getUrlMapName())) { throw new IllegalStateException( "Listener with name " + existingRule.getName() + " already exists and points to url map: " + GCEUtil.getLocalName((String) existingProxy.get("urlMap")) + "," + " which is different from the description url map: " + description.getUrlMapName() + "."); } } // HealthChecks if (healthChecksFromDescription.size() != healthChecksFromDescription.stream() .map(GoogleHealthCheck::getName) .distinct() .count()) { throw new GoogleOperationException( "Duplicate health checks with different attributes in the description. Please specify one object per named health check."); } for (GoogleHealthCheck healthCheck : healthChecksFromDescription) { String healthCheckName = healthCheck.getName(); existingHealthChecks.stream() .filter(e -> e.getName().equals(healthCheckName)) .findFirst() .ifPresent( existingHealthCheck -> { healthCheckExistsSet.add(healthCheck.getName()); if (GCEUtil.healthCheckShouldBeUpdated(existingHealthCheck, healthCheck)) { healthCheckNeedsUpdatedSet.add(healthCheck.getName()); } }); } // BackendServices if (backendServicesFromDescription.size() != backendServicesFromDescription.stream() .map(GoogleBackendService::getName) .distinct() .count()) { throw new GoogleOperationException( "Duplicate backend services with different attributes in the description. Please specify one object per named backend service."); } for (GoogleBackendService backendService : backendServicesFromDescription) { final String backendServiceName = backendService.getName(); existingServices.stream() .filter(e -> e.getName().equals(backendServiceName)) .findFirst() .ifPresent( existingService -> { serviceExistsSet.add(backendService.getName()); Set existingHcs = existingService.getHealthChecks() == null ? new HashSet<>() : existingService.getHealthChecks().stream() .map(GCEUtil::getLocalName) .collect(toSet()); Boolean differentHealthChecks = Sets.difference( existingHcs, ImmutableSet.of(backendService.getHealthCheck().getName())) .size() > 0; Boolean differentSessionAffinity = !GoogleSessionAffinity.valueOf(existingService.getSessionAffinity()) .equals(backendService.getSessionAffinity()); Boolean differentSessionCookieTtl = !Objects.equals( existingService.getAffinityCookieTtlSec(), backendService.getAffinityCookieTtlSec()); Boolean differentPortName = !Objects.equals(existingService.getPortName(), backendService.getPortName()); Integer drainingSec = existingService.getConnectionDraining() == null ? 0 : existingService.getConnectionDraining().getDrainingTimeoutSec(); Boolean differentConnectionDraining = !Objects.equals( drainingSec, backendService.getConnectionDrainingTimeoutSec()); if (differentHealthChecks || differentSessionAffinity || differentSessionCookieTtl || differentPortName || differentConnectionDraining) { serviceNeedsUpdatedSet.add(backendService.getName()); } }); } // Step 1: If there are no existing components in GCE, insert the new L7 components. // If something exists and needs updated, update it. Else do nothing. // HealthChecks for (GoogleHealthCheck healthCheck : healthChecksFromDescription) { String healthCheckName = healthCheck.getName(); if (!healthCheckExistsSet.contains(healthCheck.getName())) { getTask() .updateStatus( BASE_PHASE, "Creating health check " + healthCheckName + " in " + region + "..."); HealthCheck newHealthCheck = GCEUtil.createNewHealthCheck(healthCheck); Operation insertHealthCheckOperation = timeExecute( compute.regionHealthChecks().insert(project, region, newHealthCheck), "compute.regionHealthChecks.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, insertHealthCheckOperation.getName(), null, getTask(), "region health check " + healthCheckName, BASE_PHASE); } else if (healthCheckExistsSet.contains(healthCheck.getName()) && healthCheckNeedsUpdatedSet.contains(healthCheck.getName())) { getTask().updateStatus(BASE_PHASE, "Updating health check " + healthCheckName + "..."); HealthCheck hcToUpdate = existingHealthChecks.stream() .filter(hc -> hc.getName().equals(healthCheckName)) .findFirst() .get(); GCEUtil.updateExistingHealthCheck(hcToUpdate, healthCheck); Operation updateHealthCheckOperation = timeExecute( compute.regionHealthChecks().update(project, region, healthCheckName, hcToUpdate), "compute.regionHealthChecks.update", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, updateHealthCheckOperation.getName(), null, getTask(), "region health check " + healthCheckName, BASE_PHASE); } } // BackendServices for (GoogleBackendService backendService : backendServicesFromDescription) { String backendServiceName = backendService.getName(); String sessionAffinity = backendService.getSessionAffinity() != null ? backendService.getSessionAffinity().toString() : "NONE"; if (!serviceExistsSet.contains(backendService.getName())) { getTask() .updateStatus( BASE_PHASE, "Creating backend service " + backendServiceName + " in " + region + "..."); BackendService service = new BackendService(); BackendService bs = service.setName(backendServiceName); service.setLoadBalancingScheme("INTERNAL_MANAGED"); service.setPortName( backendService.getPortName() != null ? backendService.getPortName() : GoogleHttpLoadBalancingPolicy.HTTP_DEFAULT_PORT_NAME); service.setConnectionDraining( new ConnectionDraining() .setDrainingTimeoutSec(backendService.getConnectionDrainingTimeoutSec())); service.setHealthChecks( Arrays.asList( GCEUtil.buildRegionalHealthCheckUrl( project, region, backendService.getHealthCheck().getName()))); service.setSessionAffinity(sessionAffinity); service.setAffinityCookieTtlSec(backendService.getAffinityCookieTtlSec()); Operation insertBackendServiceOperation = timeExecute( compute.regionBackendServices().insert(project, region, bs), "compute.regionBackendServices.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, insertBackendServiceOperation.getName(), null, getTask(), "region backend service " + backendServiceName, BASE_PHASE); } else if (serviceExistsSet.contains(backendService.getName())) { // Update the actual backend service if necessary. if (serviceNeedsUpdatedSet.contains(backendService.getName())) { getTask() .updateStatus( BASE_PHASE, "Updating backend service " + backendServiceName + " in " + region + "..."); BackendService bsToUpdate = existingServices.stream() .filter(s -> s.getName().equals(backendServiceName)) .findFirst() .get(); String hcName = backendService.getHealthCheck().getName(); bsToUpdate.setPortName( backendService.getPortName() != null ? backendService.getPortName() : GoogleHttpLoadBalancingPolicy.HTTP_DEFAULT_PORT_NAME); bsToUpdate.setConnectionDraining( new ConnectionDraining() .setDrainingTimeoutSec(backendService.getConnectionDrainingTimeoutSec())); bsToUpdate.setHealthChecks( Arrays.asList(GCEUtil.buildRegionalHealthCheckUrl(project, region, hcName))); bsToUpdate.setSessionAffinity(sessionAffinity); bsToUpdate.setAffinityCookieTtlSec(backendService.getAffinityCookieTtlSec()); Operation updateServiceOperation = timeExecute( compute .regionBackendServices() .update(project, region, backendServiceName, bsToUpdate), "compute.regionBackendServices.update", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, updateServiceOperation.getName(), null, getTask(), "region backend service " + backendServiceName, BASE_PHASE); } fixBackendMetadata( compute, description.getCredentials(), project, getAtomicOperationsRegistry(), getOrchestrationProcessor(), description.getLoadBalancerName(), backendService); } } if (description.getBackendServiceDiff() != null) { for (GoogleBackendService backendService : description.getBackendServiceDiff()) { fixBackendMetadata( compute, description.getCredentials(), project, getAtomicOperationsRegistry(), getOrchestrationProcessor(), description.getLoadBalancerName(), backendService); } } // UrlMap String urlMapUrl = null; if (!urlMapExists) { getTask() .updateStatus(BASE_PHASE, "Creating URL map " + urlMapName + " in " + region + "..."); UrlMap newUrlMap = new UrlMap(); newUrlMap.setName(urlMapName); newUrlMap.setHostRules(new ArrayList<>()); newUrlMap.setPathMatchers(new ArrayList<>()); newUrlMap.setDefaultService( GCEUtil.buildRegionBackendServiceUrl( project, region, internalHttpLoadBalancer.getDefaultService().getName())); for (GoogleHostRule hostRule : internalHttpLoadBalancer.getHostRules()) { String pathMatcherName = PATH_MATCHER_PREFIX + "-" + UUID.randomUUID().toString(); GooglePathMatcher pathMatcher = hostRule.getPathMatcher(); PathMatcher matcher = new PathMatcher(); matcher.setDefaultService( GCEUtil.buildRegionBackendServiceUrl( project, region, pathMatcher.getDefaultService().getName())); matcher.setPathRules( pathMatcher.getPathRules().stream() .map( p -> { PathRule rule = new PathRule(); rule.setPaths(p.getPaths()); rule.setService( GCEUtil.buildRegionBackendServiceUrl( project, region, p.getBackendService().getName())); return rule; }) .collect(toList())); newUrlMap.getPathMatchers().add(matcher); HostRule rule = new HostRule(); rule.setHosts(hostRule.getHostPatterns()); rule.setPathMatcher(pathMatcherName); newUrlMap.getHostRules().add(rule); } Operation insertUrlMapOperation = timeExecute( compute.regionUrlMaps().insert(project, region, newUrlMap), "compute.regionUrlMaps.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, insertUrlMapOperation.getName(), null, getTask(), "region url map " + urlMapName, BASE_PHASE); urlMapUrl = insertUrlMapOperation.getTargetLink(); } else if (urlMapExists) { getTask() .updateStatus(BASE_PHASE, "Updating URL map " + urlMapName + " in " + region + "..."); existingUrlMap.setDefaultService( GCEUtil.buildRegionBackendServiceUrl( project, region, internalHttpLoadBalancer.getDefaultService().getName())); existingUrlMap.setPathMatchers(new ArrayList<>()); existingUrlMap.setHostRules(new ArrayList<>()); for (GoogleHostRule hostRule : internalHttpLoadBalancer.getHostRules()) { String pathMatcherName = PATH_MATCHER_PREFIX + "-" + UUID.randomUUID().toString(); GooglePathMatcher pathMatcher = hostRule.getPathMatcher(); PathMatcher matcher = new com.google.api.services.compute.model.PathMatcher(); matcher.setName(pathMatcherName); matcher.setDefaultService( GCEUtil.buildRegionBackendServiceUrl( project, region, pathMatcher.getDefaultService().getName())); matcher.setPathRules( pathMatcher.getPathRules().stream() .map( p -> { PathRule rule = new PathRule(); rule.setService( GCEUtil.buildRegionBackendServiceUrl( project, region, p.getBackendService().getName())); rule.setPaths(p.getPaths()); return rule; }) .collect(toList())); existingUrlMap.getPathMatchers().add(matcher); HostRule rule = new HostRule(); rule.setHosts(hostRule.getHostPatterns()); existingUrlMap.getHostRules().add(rule.setPathMatcher(pathMatcherName)); } Operation updateUrlMapOperation = timeExecute( compute.regionUrlMaps().update(project, region, urlMapName, existingUrlMap), "compute.regionUrlMaps.update", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, updateUrlMapOperation.getName(), null, getTask(), "region url map " + urlMapName, BASE_PHASE); urlMapUrl = updateUrlMapOperation.getTargetLink(); } else { urlMapUrl = existingUrlMap.getSelfLink(); } // TargetProxy String targetProxyName; Object targetProxy; Operation insertTargetProxyOperation; String targetProxyUrl = null; if (!targetProxyExists) { if (!StringUtils.isEmpty(internalHttpLoadBalancer.getCertificate())) { targetProxyName = internalHttpLoadBalancerName + "-" + TARGET_HTTPS_PROXY_NAME_PREFIX; getTask() .updateStatus( BASE_PHASE, "Creating target proxy " + targetProxyName + " in " + region + "..."); TargetHttpsProxy proxy = new TargetHttpsProxy(); proxy.setSslCertificates( Arrays.asList( GCEUtil.buildCertificateUrl(project, internalHttpLoadBalancer.getCertificate()))); proxy.setUrlMap(urlMapUrl); proxy.setName(targetProxyName); targetProxy = proxy; insertTargetProxyOperation = timeExecute( compute .regionTargetHttpsProxies() .insert(project, region, (TargetHttpsProxy) targetProxy), "compute.regionTargetHttpsProxies.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); } else { targetProxyName = internalHttpLoadBalancerName + "-" + TARGET_HTTP_PROXY_NAME_PREFIX; getTask() .updateStatus( BASE_PHASE, "Creating target proxy " + targetProxyName + " in " + region + "..."); TargetHttpProxy proxy = new TargetHttpProxy(); proxy.setName(targetProxyName); proxy.setUrlMap(urlMapUrl); targetProxy = proxy; insertTargetProxyOperation = timeExecute( compute .regionTargetHttpProxies() .insert(project, region, (TargetHttpProxy) targetProxy), "compute.regionTargetHttpProxies.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); } googleOperationPoller.waitForRegionalOperation( compute, project, region, insertTargetProxyOperation.getName(), null, getTask(), "region target proxy " + targetProxyName, BASE_PHASE); targetProxyUrl = insertTargetProxyOperation.getTargetLink(); } else if (targetProxyExists && targetProxyNeedsUpdated) { GoogleTargetProxyType proxyType = Utils.getTargetProxyType((String) existingProxy.get("selfLink")); switch (proxyType) { case HTTP: break; case HTTPS: targetProxyName = internalHttpLoadBalancerName + "-" + TARGET_HTTPS_PROXY_NAME_PREFIX; getTask() .updateStatus( BASE_PHASE, "Updating target proxy " + targetProxyName + " in " + region + "..."); RegionTargetHttpsProxiesSetSslCertificatesRequest request = new RegionTargetHttpsProxiesSetSslCertificatesRequest(); RegionTargetHttpsProxiesSetSslCertificatesRequest setSslReq = request.setSslCertificates( Arrays.asList( GCEUtil.buildRegionalCertificateUrl( project, region, internalHttpLoadBalancer.getCertificate()))); Operation sslCertOp = timeExecute( compute .regionTargetHttpsProxies() .setSslCertificates(project, region, targetProxyName, setSslReq), "compute.regionTargetHttpsProxies.setSslCertificates", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, sslCertOp.getName(), null, getTask(), "set ssl cert " + internalHttpLoadBalancer.getCertificate(), BASE_PHASE); UrlMapReference reference = new UrlMapReference(); UrlMapReference urlMapRef = reference.setUrlMap(urlMapUrl); Operation setUrlMapOp = timeExecute( compute .regionTargetHttpsProxies() .setUrlMap(project, region, targetProxyName, urlMapRef), "compute.regionTargetHttpsProxies.setUrlMap", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); googleOperationPoller.waitForRegionalOperation( compute, project, region, setUrlMapOp.getName(), null, getTask(), "set urlMap " + urlMapUrl + " for target proxy " + targetProxyName, BASE_PHASE); targetProxyUrl = setUrlMapOp.getTargetLink(); break; default: throw new IllegalStateException( "Updating Internal Http load balancer " + internalHttpLoadBalancerName + " in " + region + " failed. Could not update target proxy; Illegal target proxy type " + proxyType + "."); } } else { targetProxyUrl = (String) existingProxy.get("selfLink"); } // ForwardingRule if (!forwardingRuleExists) { getTask() .updateStatus( BASE_PHASE, "Creating internal forwarding rule " + internalHttpLoadBalancerName + " in " + region + "..."); ForwardingRule rule = new ForwardingRule(); rule.setName(internalHttpLoadBalancerName); rule.setLoadBalancingScheme("INTERNAL_MANAGED"); rule.setIPAddress(internalHttpLoadBalancer.getIpAddress()); rule.setIPProtocol(internalHttpLoadBalancer.getIpProtocol()); rule.setNetwork(internalHttpLoadBalancer.getNetwork()); rule.setSubnetwork(internalHttpLoadBalancer.getSubnet()); rule.setPortRange( StringGroovyMethods.asBoolean(internalHttpLoadBalancer.getCertificate()) ? "443" : internalHttpLoadBalancer.getPortRange()); rule.setTarget(targetProxyUrl); Operation forwardingRuleOp = safeRetry.doRetry( new Closure(this, this) { @Override public Operation call() { try { return timeExecute( compute.forwardingRules().insert(project, region, rule), "compute.forwardingRules.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region); } catch (IOException e) { throw new UncheckedIOException(e); } } }, "forwarding rule " + description.getLoadBalancerName(), getTask(), Arrays.asList(400, 403, 412), new ArrayList<>(), ImmutableMap.of( "action", "insert", "phase", BASE_PHASE, "operation", "compute.forwardingRules.insert", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region), getRegistry()); // Orca's orchestration for upserting a Google load balancer does not contain a task // to wait for the state of the platform to show that a load balancer was created (for good // reason, // that would be a complicated operation). Instead, Orca waits for Clouddriver to execute // this operation // and do a force cache refresh. We should wait for the whole load balancer to be created in // the platform // before we exit this upsert operation, so we wait for the forwarding rule to be created // before continuing // so we _know_ the state of the platform when we do a force cache refresh. googleOperationPoller.waitForRegionalOperation( compute, project, region, forwardingRuleOp.getName(), null, getTask(), "forwarding rule " + internalHttpLoadBalancerName, BASE_PHASE); } // NOTE: there is no update for forwarding rules because we support adding/deleting multiple // listeners in the frontend. // Rotating or changing certificates updates the targetProxy only, so the forwarding rule // doesn't need to change. // Delete extraneous listeners. if (description.getListenersToDelete() != null) { for (String forwardingRuleName : description.getListenersToDelete()) { getTask() .updateStatus( BASE_PHASE, "Deleting listener " + forwardingRuleName + " in " + region + "..."); GCEUtil.deleteRegionalListener( compute, project, region, forwardingRuleName, BASE_PHASE, getSafeRetry(), UpsertGoogleInternalHttpLoadBalancerAtomicOperation.this); } } getTask() .updateStatus( BASE_PHASE, "Done upserting Internal HTTP load balancer " + internalHttpLoadBalancerName + " in " + region); Map lb = new HashMap<>(1); lb.put("name", internalHttpLoadBalancerName); Map> regionToLb = new HashMap<>(1); regionToLb.put("region", lb); Map>> lbs = new HashMap<>(1); lbs.put("loadBalancers", regionToLb); return lbs; } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Update each instance template on all the server groups in the backend service to reflect being * added to the new load balancer. * * @param compute * @param credentials * @param project * @param loadBalancerName * @param backendService */ private void fixBackendMetadata( Compute compute, GoogleNamedAccountCredentials credentials, String project, AtomicOperationsRegistry atomicOperationsRegistry, OrchestrationProcessor orchestrationProcessor, String loadBalancerName, GoogleBackendService backendService) { try { for (GoogleLoadBalancedBackend backend : backendService.getBackends()) { String groupName = Utils.getLocalName(backend.getServerGroupUrl()); String groupRegion = Utils.getRegionFromGroupUrl(backend.getServerGroupUrl()); String templateUrl = null; switch (Utils.determineServerGroupType(backend.getServerGroupUrl())) { case REGIONAL: templateUrl = timeExecute( compute.regionInstanceGroupManagers().get(project, groupRegion, groupName), "compute.regionInstanceGroupManagers.get", TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, groupRegion) .getInstanceTemplate(); break; case ZONAL: String groupZone = Utils.getZoneFromGroupUrl(backend.getServerGroupUrl()); templateUrl = timeExecute( compute.instanceGroupManagers().get(project, groupZone, groupName), "compute.instanceGroupManagers.get", TAG_SCOPE, SCOPE_ZONAL, TAG_ZONE, groupZone) .getInstanceTemplate(); break; default: throw new IllegalStateException( "Server group referenced by " + backend.getServerGroupUrl() + " has illegal type."); } InstanceTemplate template = timeExecute( compute.instanceTemplates().get(project, Utils.getLocalName(templateUrl)), "compute.instancesTemplates.get", TAG_SCOPE, SCOPE_GLOBAL); BaseGoogleInstanceDescription instanceDescription = GCEUtil.buildInstanceDescriptionFromTemplate(project, template); Map templateOpMap = new HashMap<>(15); templateOpMap.put("image", instanceDescription.getImage()); templateOpMap.put("instanceType", instanceDescription.getInstanceType()); templateOpMap.put("credentials", credentials.getName()); templateOpMap.put("disks", instanceDescription.getDisks()); templateOpMap.put("instanceMetadata", instanceDescription.getInstanceMetadata()); templateOpMap.put("tags", instanceDescription.getTags()); templateOpMap.put("network", instanceDescription.getNetwork()); templateOpMap.put("subnet", instanceDescription.getSubnet()); templateOpMap.put("serviceAccountEmail", instanceDescription.getServiceAccountEmail()); templateOpMap.put("authScopes", instanceDescription.getAuthScopes()); templateOpMap.put("preemptible", instanceDescription.getPreemptible()); templateOpMap.put("automaticRestart", instanceDescription.getAutomaticRestart()); templateOpMap.put("onHostMaintenance", instanceDescription.getOnHostMaintenance()); templateOpMap.put("region", groupRegion); templateOpMap.put("serverGroupName", groupName); if (StringGroovyMethods.asBoolean(instanceDescription.getMinCpuPlatform())) { templateOpMap.put("minCpuPlatform", instanceDescription.getMinCpuPlatform()); } if (templateOpMap.containsKey("instanceMetadata")) { Map instanceMetadata = (Map) templateOpMap.get("instanceMetadata"); String regionLbStr = instanceMetadata.get(REGIONAL_LOAD_BALANCER_NAMES); List regionalLbs = regionLbStr != null ? new ArrayList<>(Arrays.asList(regionLbStr.split(","))) : new ArrayList<>(); regionalLbs.add(loadBalancerName); instanceMetadata.put( REGIONAL_LOAD_BALANCER_NAMES, regionalLbs.stream().distinct().collect(Collectors.joining(","))); String backendsStr = instanceMetadata.get(REGION_BACKEND_SERVICE_NAMES); List bsNames = backendsStr != null ? new ArrayList<>(Arrays.asList(backendsStr.split(","))) : new ArrayList<>(); bsNames.add(backendService.getName()); instanceMetadata.put( REGION_BACKEND_SERVICE_NAMES, bsNames.stream().distinct().collect(Collectors.joining(","))); } else { Map instanceMetadata = new HashMap<>(2); instanceMetadata.put(REGIONAL_LOAD_BALANCER_NAMES, loadBalancerName); instanceMetadata.put(REGION_BACKEND_SERVICE_NAMES, backendService.getName()); templateOpMap.put("instanceMetadata", instanceMetadata); } AtomicOperationConverter converter = atomicOperationsRegistry.getAtomicOperationConverter( "modifyGoogleServerGroupInstanceTemplateDescription", "gce"); AtomicOperation templateOp = converter.convertOperation(templateOpMap); orchestrationProcessor.process( "gce", new ArrayList<>(Arrays.asList(templateOp)), UUID.randomUUID().toString()); } } catch (IOException e) { throw new UncheckedIOException(e); } } public AtomicOperationsRegistry getAtomicOperationsRegistry() { return atomicOperationsRegistry; } public void setAtomicOperationsRegistry(AtomicOperationsRegistry atomicOperationsRegistry) { this.atomicOperationsRegistry = atomicOperationsRegistry; } public OrchestrationProcessor getOrchestrationProcessor() { return orchestrationProcessor; } public void setOrchestrationProcessor(OrchestrationProcessor orchestrationProcessor) { this.orchestrationProcessor = orchestrationProcessor; } public SafeRetry getSafeRetry() { return safeRetry; } public void setSafeRetry(SafeRetry safeRetry) { this.safeRetry = safeRetry; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy