-
Notifications
You must be signed in to change notification settings - Fork 809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(webhooks): Construct Webhook status check URL honoring the origin… #3187
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.JsonParseException | |
import com.fasterxml.jackson.databind.JsonMappingException | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.jayway.jsonpath.JsonPath | ||
import com.jayway.jsonpath.PathNotFoundException | ||
import com.netflix.spinnaker.orca.ExecutionStatus | ||
import com.netflix.spinnaker.orca.RetryableTask | ||
import com.netflix.spinnaker.orca.TaskResult | ||
|
@@ -32,13 +31,19 @@ import com.netflix.spinnaker.orca.webhook.service.WebhookService | |
import groovy.util.logging.Slf4j | ||
import org.apache.http.HttpHeaders | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.http.ResponseEntity | ||
import org.springframework.stereotype.Component | ||
import org.springframework.web.client.HttpStatusCodeException | ||
|
||
import java.util.regex.Matcher | ||
import java.util.regex.Pattern | ||
|
||
@Slf4j | ||
@Component | ||
class CreateWebhookTask implements RetryableTask { | ||
|
||
private static final Pattern URL_SCHEME = Pattern.compile("(.*)://(.*)") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was trying to see if there are ways to make the |
||
|
||
long backoffPeriod = 10000 | ||
long timeout = 300000 | ||
|
||
|
@@ -119,24 +124,16 @@ class CreateWebhookTask implements RetryableTask { | |
|
||
if (statusCode.is2xxSuccessful() || statusCode.is3xxRedirection()) { | ||
if (stageData.waitForCompletion) { | ||
def statusUrl = null | ||
def statusUrlResolution = stageData.statusUrlResolution | ||
switch (statusUrlResolution) { | ||
case WebhookProperties.StatusUrlResolution.getMethod: | ||
statusUrl = stageData.url | ||
break | ||
case WebhookProperties.StatusUrlResolution.locationHeader: | ||
statusUrl = response.headers.getFirst(HttpHeaders.LOCATION) | ||
break | ||
case WebhookProperties.StatusUrlResolution.webhookResponse: | ||
try { | ||
statusUrl = JsonPath.compile(stageData.statusUrlJsonPath as String).read(response.body) | ||
} catch (PathNotFoundException e) { | ||
outputs.webhook << [error: e.message] | ||
return TaskResult.builder(ExecutionStatus.TERMINAL).context(outputs).build() | ||
} | ||
String statusUrl | ||
try { | ||
statusUrl = determineWebHookStatusCheckUrl(response, stageData) | ||
} catch (Exception e) { | ||
outputs.webhook << [error: 'Exception while resolving status check URL: ' + e.message] | ||
log.error('Exception received while determining status check url', e) | ||
return TaskResult.builder(ExecutionStatus.TERMINAL).context(outputs).build() | ||
} | ||
if (!statusUrl || !(statusUrl instanceof String)) { | ||
|
||
if (!statusUrl) { | ||
outputs.webhook << [ | ||
error : "The status URL couldn't be resolved, but 'Wait for completion' was checked", | ||
statusEndpoint: statusUrl | ||
|
@@ -162,4 +159,41 @@ class CreateWebhookTask implements RetryableTask { | |
return TaskResult.builder(ExecutionStatus.TERMINAL).context(outputs).build() | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Below method adjusts the status check URL if the original webhook domain and status check URL domain given back matches. |
||
private String determineWebHookStatusCheckUrl(ResponseEntity response, WebhookStage.StageData stageData) { | ||
|
||
String statusCheckUrl | ||
|
||
switch (stageData.statusUrlResolution) { | ||
case WebhookProperties.StatusUrlResolution.getMethod: | ||
statusCheckUrl = stageData.url | ||
break | ||
case WebhookProperties.StatusUrlResolution.locationHeader: | ||
statusCheckUrl = response.headers.getFirst(HttpHeaders.LOCATION) | ||
break | ||
case WebhookProperties.StatusUrlResolution.webhookResponse: | ||
statusCheckUrl = JsonPath.compile(stageData.statusUrlJsonPath as String).read(response.body) | ||
break | ||
} | ||
log.info('Web hook status check url as resolved: {}', statusCheckUrl) | ||
|
||
// Preserve the protocol scheme of original webhook that was called, when calling for status check of a webhook. | ||
if (statusCheckUrl != stageData.url) { | ||
Matcher statusUrlMatcher = URL_SCHEME.matcher(statusCheckUrl) | ||
URI statusCheckUri = URI.create(statusCheckUrl).normalize() | ||
String statusCheckHost = statusCheckUri.getHost() | ||
|
||
URI webHookUri = URI.create(stageData.url).normalize() | ||
String webHookHost = webHookUri.getHost() | ||
if (webHookHost == statusCheckHost && | ||
webHookUri.getScheme() != statusCheckUri.getScheme() && statusUrlMatcher.find()) { | ||
// Same hosts keep the original protocol scheme of the webhook that was originally set. | ||
statusCheckUrl = webHookUri.getScheme() + '://' + statusUrlMatcher.group(2) | ||
log.info('Adjusted Web hook status check url: {}', statusCheckUrl) | ||
} | ||
} | ||
|
||
return statusCheckUrl | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,7 +160,6 @@ class MonitorWebhookTask implements OverridableTimeoutRetryableTask { | |
return TaskResult.builder(statusMap[result.toString().toUpperCase()]).context(responsePayload).build() | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed unnecessary statement. |
||
stage.context | ||
return TaskResult.builder(ExecutionStatus.RUNNING).context(response ? responsePayload : originalResponse).build() | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method expects a URL to be provided so adding it though Groovy doesn't complain about it during compilation time