Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.netflix.spinnaker.clouddriver.google.deploy.ops.loadbalancer.UpsertGoogleInternalLoadBalancerAtomicOperation.groovy Maven / Gradle / Ivy
/*
* Copyright 2016 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.spinnaker.clouddriver.google.deploy.ops.loadbalancer
import com.google.api.services.compute.model.BackendService
import com.google.api.services.compute.model.ForwardingRule
import com.google.api.services.compute.model.HealthCheck
import com.google.api.services.compute.model.Operation
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.UpsertGoogleLoadBalancerDescription
import com.netflix.spinnaker.clouddriver.google.deploy.exception.GoogleOperationException
import com.netflix.spinnaker.clouddriver.google.deploy.ops.GoogleAtomicOperation
import com.netflix.spinnaker.clouddriver.google.model.GoogleHealthCheck
import com.netflix.spinnaker.clouddriver.google.model.loadbalancing.GoogleSessionAffinity
import com.netflix.spinnaker.clouddriver.google.provider.view.GoogleNetworkProvider
import com.netflix.spinnaker.clouddriver.google.provider.view.GoogleSubnetProvider
import org.springframework.beans.factory.annotation.Autowired
class UpsertGoogleInternalLoadBalancerAtomicOperation extends GoogleAtomicOperation {
private static final String BASE_PHASE = "UPSERT_INTERNAL_LOAD_BALANCER"
@Autowired
SafeRetry safeRetry
private static Task getTask() {
TaskRepository.threadLocalTask.get()
}
@Autowired
private GoogleOperationPoller googleOperationPoller
@Autowired
private GoogleNetworkProvider googleNetworkProvider
@Autowired
private GoogleSubnetProvider googleSubnetProvider
private final UpsertGoogleLoadBalancerDescription description
UpsertGoogleInternalLoadBalancerAtomicOperation() {}
UpsertGoogleInternalLoadBalancerAtomicOperation(UpsertGoogleLoadBalancerDescription description) {
this.description = description
}
/**
* curl -X POST -H "Content-Type: application/json" -d '[ { "upsertLoadBalancer": { "region": "us-central1", "ports": ["80"], "ipProtocol": "TCP", "credentials" : "my-account-name", "loadBalancerName" : "testlb", "backendService": {"name": "default-backend-service", "backends": [], "healthCheck": {"name": "basic-check", "port": 80, "checkIntervalSec": 1, "timeoutSec": 1, "healthyThreshold": 1, "unhealthyThreshold": 1, "healthCheckType": "TCP"}}, "network": "default", "subnet": "default-4464150e11ecaace", "loadBalancerType": "INTERNAL"}} ]' localhost:7002/gce/ops
*
* @param priorOutputs
* @return
*/
@Override
Map operate(List priorOutputs) {
task.updateStatus BASE_PHASE, "Initializing upsert of load balancer $description.loadBalancerName " +
"in $description.region..."
if (!description.credentials) {
throw new IllegalArgumentException("Unable to resolve credentials for Google account '${description.accountName}'.")
}
def compute = description.credentials.compute
def project = description.credentials.project
def region = description.region
GoogleHealthCheck descriptionHealthCheck = description.backendService.healthCheck
String backendServiceName = description.backendService.name
String healthCheckName = descriptionHealthCheck.name
GoogleHealthCheck.HealthCheckType healthCheckType = descriptionHealthCheck.healthCheckType
// Set some default values that will be useful when doing comparisons.
description.ipProtocol = description.ipProtocol ?: Constants.DEFAULT_IP_PROTOCOL
ForwardingRule existingForwardingRule
BackendService existingBackendService
HealthCheck existingHealthCheck // Could be any one of Http, Https, Ssl, or Tcp.
// We first devise a plan by setting all of these flags.
boolean needToUpdateForwardingRule = false
boolean needToUpdateBackendService = false
boolean needToUpdateHealthCheck = false
// Check if there already exists a forwarding rule with the requested name.
existingForwardingRule = GCEUtil.queryRegionalForwardingRule(project, description.loadBalancerName, compute, task, BASE_PHASE, this)
if (existingForwardingRule && (description.region != GCEUtil.getLocalName(existingForwardingRule.region))) {
throw new GoogleOperationException("There is already a load balancer named " +
"$description.loadBalancerName (in region ${GCEUtil.getLocalName(existingForwardingRule.region)}). " +
"Please specify a different name.")
} else if (existingForwardingRule && (description.region == GCEUtil.getLocalName(existingForwardingRule.region))) {
needToUpdateForwardingRule = description.ports != existingForwardingRule.getPorts()
}
existingBackendService = safeRetry.doRetry(
{ timeExecute(
compute.regionBackendServices().get(project, region, backendServiceName),
"compute.regionBackendServices.get",
TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) },
"Region backend service $backendServiceName",
task,
[400, 403, 412],
[404],
[action: "get", phase: BASE_PHASE, operation: "compute.regionBackendServices.get", (TAG_SCOPE): SCOPE_REGIONAL, (TAG_REGION): region],
registry
) as BackendService
if (existingBackendService) {
Boolean differentHealthChecks = existingBackendService.getHealthChecks().collect { GCEUtil.getLocalName(it) } != [healthCheckName]
Boolean differentSessionAffinity = GoogleSessionAffinity.valueOf(existingBackendService.getSessionAffinity()) != description.backendService.sessionAffinity
if (differentHealthChecks || differentSessionAffinity || existingBackendService.protocol != description.ipProtocol) {
needToUpdateBackendService = true
}
}
// Note: ILBs only use HealthCheck objects, _not_ Http(s)HealthChecks. The actual check (i.e. Ssl, Tcp, Http(s))
// is nested in a field inside the HealthCheck object. This is different from all previous ways we used health checks,
// and uses a separate endpoint from Http(s)HealthChecks.
existingHealthCheck = safeRetry.doRetry(
{ timeExecute(
compute.healthChecks().get(project, healthCheckName),
"compute.healthChecks.get",
TAG_SCOPE, SCOPE_GLOBAL) },
"Health check $healthCheckName",
task,
[400, 403, 412],
[404],
[action: "get", phase: BASE_PHASE, operation: "compute.healthChecks.get", (TAG_SCOPE): SCOPE_GLOBAL],
registry
) as HealthCheck
needToUpdateHealthCheck = existingHealthCheck && GCEUtil.healthCheckShouldBeUpdated(existingHealthCheck, descriptionHealthCheck)
// Now we start phase 2 of our plan -- upsert all the components.
def healthCheckOp = null
if (!existingHealthCheck) {
task.updateStatus BASE_PHASE, "Creating health check $healthCheckName..."
def newHealthCheck = GCEUtil.createNewHealthCheck(descriptionHealthCheck)
healthCheckOp = safeRetry.doRetry(
{ timeExecute(
compute.healthChecks().insert(project, newHealthCheck as HealthCheck),
"compute.healthChecks.insert",
TAG_SCOPE, SCOPE_GLOBAL) },
"Health check $healthCheckName",
task,
[400, 403, 412],
[],
[action: "insert", phase: BASE_PHASE, operation: "compute.healthChecks.insert", (TAG_SCOPE): SCOPE_GLOBAL],
registry
)
} else if (existingHealthCheck && needToUpdateHealthCheck) {
task.updateStatus BASE_PHASE, "Updating health check $healthCheckName..."
GCEUtil.updateExistingHealthCheck(existingHealthCheck, descriptionHealthCheck)
healthCheckOp = safeRetry.doRetry(
{ timeExecute(
compute.healthChecks().update(project, healthCheckName, existingHealthCheck as HealthCheck),
"compute.healthChecks.update",
TAG_SCOPE, SCOPE_GLOBAL) },
"Health check $healthCheckName",
task,
[400, 403, 412],
[],
[action: "update", phase: BASE_PHASE, operation: "compute.healthChecks.update", (TAG_SCOPE): SCOPE_GLOBAL],
registry
)
}
if (healthCheckOp) {
googleOperationPoller.waitForGlobalOperation(compute, project, healthCheckOp.getName(),
null, task, "health check " + healthCheckName, BASE_PHASE)
}
def backendServiceOp = null
if (!existingBackendService) {
task.updateStatus BASE_PHASE, "Creating backend service ${description.backendService.name}..."
BackendService bs = new BackendService(
name: backendServiceName,
healthChecks: [GCEUtil.buildHealthCheckUrl(project, healthCheckName)],
sessionAffinity: description.backendService.sessionAffinity ?: 'NONE',
loadBalancingScheme: 'INTERNAL',
protocol: description.ipProtocol
)
backendServiceOp = safeRetry.doRetry(
{ timeExecute(
compute.regionBackendServices().insert(project, region, bs),
"compute.regionBackendServices.insert",
TAG_SCOPE, SCOPE_GLOBAL) },
"Backend service $description.backendService.name",
task,
[400, 403, 412],
[],
[action: "insert", phase: BASE_PHASE, operation: "compute.regionBackendServices.insert", (TAG_SCOPE): SCOPE_GLOBAL],
registry
)
} else if (existingBackendService && needToUpdateBackendService) {
task.updateStatus BASE_PHASE, "Upating backend service ${description.backendService.name}..."
existingBackendService.healthChecks = [GCEUtil.buildHealthCheckUrl(project, healthCheckName)]
existingBackendService.sessionAffinity = description.backendService.sessionAffinity ?: 'NONE'
existingBackendService.loadBalancingScheme = 'INTERNAL'
existingBackendService.protocol = description.ipProtocol
backendServiceOp = safeRetry.doRetry(
{ timeExecute(
compute.regionBackendServices().update(project, region, existingBackendService.getName(), existingBackendService),
"compute.regionBackendServices.update",
TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) },
"Backend service $description.backendService.name",
task,
[400, 403, 412],
[],
[action: "Update", phase: BASE_PHASE, operation: "compute.regionBackendServices.update", (TAG_SCOPE): SCOPE_REGIONAL, (TAG_REGION): region],
registry
)
}
if (backendServiceOp) {
googleOperationPoller.waitForRegionalOperation(compute, project, region, backendServiceOp.getName(),
null, task, "backend service " + healthCheckName, BASE_PHASE)
}
def network = GCEUtil.queryNetwork(description.accountName, description.network, task, BASE_PHASE, googleNetworkProvider)
def subnet = GCEUtil.querySubnet(description.accountName, region, description.subnet, task, BASE_PHASE, googleSubnetProvider)
if (!existingForwardingRule) {
def forwardingRule = new ForwardingRule(
name: description.loadBalancerName,
loadBalancingScheme: 'INTERNAL',
backendService: GCEUtil.buildRegionBackendServiceUrl(project, region, description.backendService.name),
IPProtocol: description.ipProtocol,
IPAddress: description.ipAddress,
network: network.selfLink,
subnetwork: subnet.selfLink,
ports: description.ports
)
insertRegionalForwardingRule(compute, project, region, forwardingRule)
} else if (existingForwardingRule && needToUpdateForwardingRule) {
def updatedForwardingRule = new ForwardingRule(
name: description.loadBalancerName,
loadBalancingScheme: 'INTERNAL',
backendService: GCEUtil.buildRegionBackendServiceUrl(project, region, description.backendService.name),
IPProtocol: description.ipProtocol,
IPAddress: description.ipAddress,
network: network.selfLink,
subnetwork: subnet.selfLink,
ports: description.ports
)
deleteRegionalForwardingRule(compute, project, region, existingForwardingRule.getName())
insertRegionalForwardingRule(compute, project, region, updatedForwardingRule)
}
// Delete extraneous listeners.
description.listenersToDelete?.each { String forwardingRuleName ->
deleteRegionalForwardingRule(compute, project, region, forwardingRuleName)
}
task.updateStatus BASE_PHASE, "Done upserting load balancer $description.loadBalancerName in $region."
return [loadBalancers: [(region): [name: description.loadBalancerName]]]
}
private void deleteRegionalForwardingRule(compute, String project, String region, String forwardingRuleName) {
task.updateStatus BASE_PHASE, "Deleting listener ${forwardingRuleName}..."
Operation deleteForwardingRuleOp = safeRetry.doRetry(
{
timeExecute(
compute.forwardingRules().delete(project, region, forwardingRuleName),
"compute.forwardingRules.delete",
TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) },
"Regional forwarding rule $forwardingRuleName",
task,
[400, 412],
[404],
[action: "delete", phase: BASE_PHASE, operation: "compute.forwardingRules.delete", (TAG_SCOPE): SCOPE_REGIONAL, (TAG_REGION): region],
registry
) as Operation
if (deleteForwardingRuleOp) {
googleOperationPoller.waitForRegionalOperation(compute, project, region, deleteForwardingRuleOp.getName(),
30, task, "Regional forwarding rule $forwardingRuleName", BASE_PHASE)
}
}
private void insertRegionalForwardingRule(compute, String project, String region, forwardingRule) {
task.updateStatus BASE_PHASE, "Creating forwarding rule $description.loadBalancerName..."
Operation forwardingRuleOp = safeRetry.doRetry(
{ timeExecute(
compute.forwardingRules().insert(project, region, forwardingRule),
"compute.forwardingRules.insert",
TAG_SCOPE, SCOPE_REGIONAL, TAG_REGION, region) },
"Regional forwarding rule ${description.loadBalancerName}",
task,
[400, 403, 412],
[],
[action: "insert", phase: BASE_PHASE, operation: "compute.forwardingRules.insert", (TAG_SCOPE): SCOPE_GLOBAL],
registry
) as Operation
// 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, task, "forwarding rule " + description.loadBalancerName, BASE_PHASE)
}
}