Skip to content

Commit

Permalink
feat(provider/google): Support UHCs in L7 load balancers.
Browse files Browse the repository at this point in the history
  • Loading branch information
jtk54 committed Oct 25, 2017
1 parent 9f48227 commit baa2901
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,18 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
String urlMapName = httpLoadBalancer?.urlMapName ?: httpLoadBalancerName // An L7 load balancer is identified by its UrlMap name in Google Cloud Console.

// Get all the existing infrastructure.
Set<HttpHealthCheck> existingHealthChecks = timeExecute(

// Look up the legacy health checks so we can do the work to transition smoothly to the UHCs.
Set<HttpHealthCheck> legacyHealthChecks = timeExecute(
compute.httpHealthChecks().list(project),
"compute.httpHealthChecks.list",
TAG_SCOPE, SCOPE_GLOBAL)
.getItems() as Set
Set<HealthCheck> existingHealthChecks = timeExecute(
compute.healthChecks().list(project),
"compute.healthChecks.list",
TAG_SCOPE, SCOPE_GLOBAL)
.getItems() as Set
Set<BackendService> existingServices = timeExecute(
compute.backendServices().list(project),
"compute.backendServices.list",
Expand Down Expand Up @@ -198,7 +205,7 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
}
}

// HttpHealthChecks
// HealthChecks
if (healthChecksFromDescription.size() != healthChecksFromDescription.unique(false) { it.name }.size()) {
throw new GoogleOperationException("Duplicate health checks with different attributes in the description. " +
"Please specify one object per named health check.")
Expand All @@ -210,12 +217,7 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
def existingHealthCheck = existingHealthChecks.find { it.name == healthCheckName }
if (existingHealthCheck) {
healthCheckExistsSet.add(healthCheck.name)
if (healthCheck.port != existingHealthCheck.getPort() ||
healthCheck.requestPath != existingHealthCheck.getRequestPath() ||
healthCheck.checkIntervalSec != existingHealthCheck.getCheckIntervalSec() ||
healthCheck.healthyThreshold != existingHealthCheck.getHealthyThreshold() ||
healthCheck.unhealthyThreshold != existingHealthCheck.getUnhealthyThreshold() ||
healthCheck.timeoutSec != existingHealthCheck.getTimeoutSec()) {
if (GCEUtil.healthCheckShouldBeUpdated(existingHealthCheck, healthCheck)) {
healthCheckNeedsUpdatedSet.add(healthCheck.name)
}
}
Expand All @@ -227,6 +229,7 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
"Please specify one object per named backend service.")
}

List<String> legacyHealthCheckNames = legacyHealthChecks*.name
backendServicesFromDescription.each { GoogleBackendService backendService ->
String backendServiceName = backendService.name

Expand All @@ -235,12 +238,13 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
serviceExistsSet.add(backendService.name)

Boolean differentHealthChecks = existingService.getHealthChecks().collect { GCEUtil.getLocalName(it) } != [backendService.healthCheck.name]
Boolean updateFromLegacyHealthCheck = legacyHealthCheckNames.contains(backendService.healthCheck.name)
Boolean differentSessionAffinity = GoogleSessionAffinity.valueOf(existingService.getSessionAffinity()) != backendService.sessionAffinity
Boolean differentSessionCookieTtl = existingService.getAffinityCookieTtlSec() != backendService.affinityCookieTtlSec
Boolean differentCDN = existingService.getEnableCDN() != backendService.enableCDN
Boolean differentPortName = existingService.getPortName() != backendService.portName
Boolean differentConnectionDraining = existingService.getConnectionDraining()?.getDrainingTimeoutSec() != backendService?.connectionDrainingTimeoutSec
if (differentHealthChecks || differentSessionAffinity || differentSessionCookieTtl || differentCDN || differentPortName || differentConnectionDraining) {
if (differentHealthChecks || differentSessionAffinity || differentSessionCookieTtl || differentCDN || differentPortName || differentConnectionDraining || updateFromLegacyHealthCheck) {
serviceNeedsUpdatedSet.add(backendService.name)
}
}
Expand All @@ -255,37 +259,21 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc

if (!healthCheckExistsSet.contains(healthCheck.name)) {
task.updateStatus BASE_PHASE, "Creating health check $healthCheckName..."
HttpHealthCheck newHealthCheck = new HttpHealthCheck(
name: healthCheckName,
port: healthCheck.port,
requestPath: healthCheck.requestPath,
checkIntervalSec: healthCheck.checkIntervalSec,
healthyThreshold: healthCheck.healthyThreshold,
unhealthyThreshold: healthCheck.unhealthyThreshold,
timeoutSec: healthCheck.timeoutSec,
)
HealthCheck newHealthCheck = GCEUtil.createNewHealthCheck(healthCheck)
def insertHealthCheckOperation = timeExecute(
compute.httpHealthChecks().insert(project, newHealthCheck),
"compute.httpHealthChecks.insert",
compute.healthChecks().insert(project, newHealthCheck),
"compute.healthChecks.insert",
TAG_SCOPE, SCOPE_GLOBAL)
googleOperationPoller.waitForGlobalOperation(compute, project, insertHealthCheckOperation.getName(),
null, task, "health check " + healthCheckName, BASE_PHASE)
} else if (healthCheckExistsSet.contains(healthCheck.name) &&
healthCheckNeedsUpdatedSet.contains(healthCheck.name)) {
task.updateStatus BASE_PHASE, "Updating health check $healthCheckName..."
def hcToUpdate = existingHealthChecks.find { it.name == healthCheckName }
hcToUpdate.with {
name = healthCheckName
port = healthCheck.port
requestPath = healthCheck.requestPath
checkIntervalSec = healthCheck.checkIntervalSec
healthyThreshold = healthCheck.healthyThreshold
unhealthyThreshold = healthCheck.unhealthyThreshold
timeoutSec = healthCheck.timeoutSec
}
GCEUtil.updateExistingHealthCheck(hcToUpdate, healthCheck)
def updateHealthCheckOperation = timeExecute(
compute.httpHealthChecks().update(project, healthCheckName, hcToUpdate),
"compute.httpHealthChecks.update",
compute.healthChecks().update(project, healthCheckName, hcToUpdate),
"compute.healthChecks.update",
TAG_SCOPE, SCOPE_GLOBAL)
googleOperationPoller.waitForGlobalOperation(compute, project, updateHealthCheckOperation.getName(),
null, task, "health check $healthCheckName", BASE_PHASE)
Expand All @@ -303,7 +291,7 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
name: backendServiceName,
portName: backendService.portName ?: GoogleHttpLoadBalancingPolicy.HTTP_DEFAULT_PORT_NAME,
connectionDraining: new ConnectionDraining().setDrainingTimeoutSec(backendService.connectionDrainingTimeoutSec),
healthChecks: [GCEUtil.buildHttpHealthCheckUrl(project, backendService.healthCheck.name)],
healthChecks: [GCEUtil.buildHealthCheckUrl(project, backendService.healthCheck.name)],
sessionAffinity: sessionAffinity,
enableCDN: backendService.enableCDN,
affinityCookieTtlSec: backendService.affinityCookieTtlSec
Expand All @@ -322,7 +310,7 @@ class UpsertGoogleHttpLoadBalancerAtomicOperation extends UpsertGoogleLoadBalanc
def hcName = backendService.healthCheck.name
bsToUpdate.portName = backendService.portName ?: GoogleHttpLoadBalancingPolicy.HTTP_DEFAULT_PORT_NAME
bsToUpdate.connectionDraining = new ConnectionDraining().setDrainingTimeoutSec(backendService.connectionDrainingTimeoutSec)
bsToUpdate.healthChecks = [GCEUtil.buildHttpHealthCheckUrl(project, hcName)]
bsToUpdate.healthChecks = [GCEUtil.buildHealthCheckUrl(project, hcName)]
bsToUpdate.sessionAffinity = sessionAffinity
bsToUpdate.enableCDN = backendService.enableCDN
bsToUpdate.affinityCookieTtlSec = backendService.affinityCookieTtlSec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class UpsertGoogleSslLoadBalancerAtomicOperation extends UpsertGoogleLoadBalance
task,
[400, 403, 412],
[],
[action: "insert", phase: BASE_PHASE, operation: "copmute.healthChecks.insert", (TAG_SCOPE): SCOPE_GLOBAL],
[action: "insert", phase: BASE_PHASE, operation: "compute.healthChecks.insert", (TAG_SCOPE): SCOPE_GLOBAL],
registry
)
} else if (existingHealthCheck && needToUpdateHealthCheck) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,20 +382,35 @@ class GoogleHttpLoadBalancerCachingAgent extends AbstractGoogleLoadBalancerCachi

backendService.healthChecks?.each { String healthCheckURL ->
def healthCheckName = Utils.getLocalName(healthCheckURL)
if (isHttps) {
def healthCheckCallback = new HttpsHealthCheckCallback(
def healthCheckType = Utils.getHealthCheckType(healthCheckURL)
switch (healthCheckType) {
case "httpHealthChecks":
def healthCheckCallback = new HttpHealthCheckCallback(
subject: googleLoadBalancer.name,
failedSubjects: failedLoadBalancers,
googleBackendServices: backendServicesToUpdate
)
compute.httpsHealthChecks().get(project, healthCheckName).queue(httpHealthCheckRequest, healthCheckCallback)
} else {
def healthCheckCallback = new HttpHealthCheckCallback(
)
compute.httpHealthChecks().get(project, healthCheckName).queue(httpHealthCheckRequest, healthCheckCallback)
break
case "httpsHealthChecks":
def healthCheckCallback = new HttpsHealthCheckCallback(
subject: googleLoadBalancer.name,
failedSubjects: failedLoadBalancers,
googleBackendServices: backendServicesToUpdate
)
compute.httpHealthChecks().get(project, healthCheckName).queue(httpHealthCheckRequest, healthCheckCallback)
)
compute.httpsHealthChecks().get(project, healthCheckName).queue(httpHealthCheckRequest, healthCheckCallback)
break
case "healthChecks":
def healthCheckCallback = new HealthCheckCallback(
subject: googleLoadBalancer.name,
failedSubjects: failedLoadBalancers,
googleBackendServices: backendServicesToUpdate
)
compute.healthChecks().get(project, healthCheckName).queue(httpHealthCheckRequest, healthCheckCallback)
break
default:
log.warn("Unknown health check type for health check named: ${healthCheckName}. Not queueing any batch requests.")
break
}
}
}
Expand Down Expand Up @@ -441,6 +456,50 @@ class GoogleHttpLoadBalancerCachingAgent extends AbstractGoogleLoadBalancerCachi
}
}

class HealthCheckCallback<HealthCheck> extends JsonBatchCallback<HealthCheck> implements FailedSubjectChronicler {
List<GoogleBackendService> googleBackendServices

@Override
void onSuccess(HealthCheck healthCheck, HttpHeaders responseHeaders) throws IOException {
def port = null
def hcType = null
def requestPath = null
if (healthCheck.tcpHealthCheck) {
port = healthCheck.tcpHealthCheck.port
hcType = GoogleHealthCheck.HealthCheckType.TCP
} else if (healthCheck.sslHealthCheck) {
port = healthCheck.sslHealthCheck.port
hcType = GoogleHealthCheck.HealthCheckType.SSL
} else if (healthCheck.httpHealthCheck) {
port = healthCheck.httpHealthCheck.port
requestPath = healthCheck.httpHealthCheck.requestPath
hcType = GoogleHealthCheck.HealthCheckType.HTTP
} else if (healthCheck.httpsHealthCheck) {
port = healthCheck.httpsHealthCheck.port
requestPath = healthCheck.httpsHealthCheck.requestPath
hcType = GoogleHealthCheck.HealthCheckType.HTTPS
} else if (healthCheck.udpHealthCheck) {
port = healthCheck.udpHealthCheck.port
hcType = GoogleHealthCheck.HealthCheckType.UDP
}

if (port && hcType) {
googleBackendServices?.each { googleBackendService ->
googleBackendService.healthCheck = new GoogleHealthCheck(
name: healthCheck.name,
healthCheckType: hcType,
port: port,
requestPath: requestPath ?: "",
checkIntervalSec: healthCheck.checkIntervalSec,
timeoutSec: healthCheck.timeoutSec,
unhealthyThreshold: healthCheck.unhealthyThreshold,
healthyThreshold: healthCheck.healthyThreshold,
)
}
}
}
}

class GroupHealthCallback<BackendServiceGroupHealth> extends JsonBatchCallback<BackendServiceGroupHealth> implements FailedSubjectChronicler {
GoogleHttpLoadBalancer googleLoadBalancer

Expand Down
Loading

0 comments on commit baa2901

Please sign in to comment.