diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/.travis.yml b/.travis.yml index 68009c3..8c59bcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,15 @@ language: smalltalk sudo: false +services: + - docker os: - linux smalltalk: - Pharo64-8.0 - Pharo64-7.0 - Pharo32-7.0 +script: + - smalltalkci + - export TRAVIS_BRANCH + - export TRAVIS_PULL_REQUEST_BRANCH + - ./compose-test.sh diff --git a/api-tests/Dockerfile b/api-tests/Dockerfile new file mode 100644 index 0000000..43599d0 --- /dev/null +++ b/api-tests/Dockerfile @@ -0,0 +1,35 @@ +# Stage 1: Load the project +FROM basmalltalk/pharo:7.0-image AS loader +ARG BRANCH_NAME=release-candidate +COPY load-project.st ./ +RUN pharo Pharo.image load-project.st --save --quit + +# Stage 2: Copy the resulting Pharo.image with our project loaded +# into a new docker image with just the vm +FROM basmalltalk/pharo:7.0 + +USER root + +RUN apt-get update \ + && apt-get --assume-yes --no-install-recommends install curl \ + && apt-get clean \ + && rm --recursive --force /var/lib/apt/lists/* /tmp/* /var/tmp/* + +WORKDIR /opt/Stargate-Consul-Example-API +COPY start.sh ./ +COPY health-check.sh ./ +COPY --from=loader /opt/pharo/Pharo.image ./ +COPY --from=loader /opt/pharo/Pharo.changes ./ +COPY --from=loader /opt/pharo/Pharo*.sources ./ + +RUN mkdir logs \ + && chmod a+x start.sh \ + && chmod a+x health-check.sh \ + && chown --recursive pharo:pharo /opt/Stargate-Consul-Example-API + +USER pharo +EXPOSE 8080 + +HEALTHCHECK CMD /opt/Stargate-Consul-Example-API/health-check.sh + +CMD ["./start.sh"] diff --git a/api-tests/docker-compose.yml b/api-tests/docker-compose.yml new file mode 100644 index 0000000..9fc0102 --- /dev/null +++ b/api-tests/docker-compose.yml @@ -0,0 +1,25 @@ +version: "3.7" +services: + api: + build: + context: ./ + args: + - BRANCH_NAME + ports: + - "8080:8080" + environment: + PUBLIC_URL: http://api:8080 + OPERATIONS_SECRET: API-tests + HEALTH_CHECK_TOKEN: eyJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6ImV4ZWN1dGU6aGVhbHRoLWNoZWNrIn0.FQgIMpaHZX_7yPWRNC5HAsmlYWkNYu0pdNrSil3ECRc + APPLICATION_CONTROL_TOKEN: eyJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6ImV4ZWN1dGU6YXBwbGljYXRpb24tY29udHJvbCJ9.CCMnq-6mlX-ZoSC2OfwdBOVn5DILmahP58hfXowjqFk + CONSUL_AGENT_LOCATION: http://consul-agent:8500 + depends_on: + - consul-agent + consul-agent: + image: consul:1.6 + ports: + - "8500:8500" + - "8600:8600" + - "8300:8300" + environment: + CONSUL_BIND_INTERFACE: eth0 diff --git a/api-tests/health-check.sh b/api-tests/health-check.sh new file mode 100755 index 0000000..bf54ffa --- /dev/null +++ b/api-tests/health-check.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +curl --fail --request POST \ + --header "Authorization: Bearer $HEALTH_CHECK_TOKEN" \ + --header "Accept: application/vnd.stargate.health-check.summary+json;version=1.0.0" \ + http://localhost:"${PORT:-8080}"/operations/health-check || exit 1 diff --git a/api-tests/load-project.st b/api-tests/load-project.st new file mode 100644 index 0000000..d53cf9d --- /dev/null +++ b/api-tests/load-project.st @@ -0,0 +1,9 @@ +| branchName | + +branchName := Smalltalk os environment at: 'BRANCH_NAME' ifAbsent: [Error signal: 'BRANCH_NAME environment variable not set']. +branchName ifEmpty: [ Error signal: 'BRANCH_NAME environment variable value is empty']. + +Metacello new + baseline: 'StargateConsul'; + repository: ('github://ba-st/Stargate-Consul:<1s>/source' expandMacrosWith: branchName); + load: 'Examples'. diff --git a/api-tests/start.sh b/api-tests/start.sh new file mode 100755 index 0000000..256e7f6 --- /dev/null +++ b/api-tests/start.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# SIGTERM-handler +termination_handler() { + echo 'SIGTERM was received, stopping the API' + curl --silent --fail --request POST \ + --header "Authorization: Bearer $APPLICATION_CONTROL_TOKEN" \ + --header "Content-Type:application/json" \ + --header "Accept: application/json" \ + --data '{"jsonrpc": "2.0" ,"method": "shutdown"}' \ + http://localhost:"${PORT:-8080}"/operations/application-control + exit 143; # 128 + 15 -- SIGTERM +} + +trap 'kill ${!}; termination_handler' SIGTERM + +/opt/pharo/pharo \ + /opt/Stargate-Consul-Example-API/Pharo.image \ + stargate-consul-example \ + "${PORT:+--port=$PORT}" \ + "${PUBLIC_URL:+--public-URL=$PUBLIC_URL}" \ + "${OPERATIONS_SECRET:+--operations-secret=$OPERATIONS_SECRET}" \ + "${CONSUL_AGENT_LOCATION:+--consul-agent-location=$CONSUL_AGENT_LOCATION}" & + +# wait forever +while true +do + tail -f /dev/null & wait ${!} +done diff --git a/compose-test.sh b/compose-test.sh new file mode 100755 index 0000000..61644ff --- /dev/null +++ b/compose-test.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e + +if [[ -z "${TRAVIS_PULL_REQUEST_BRANCH}" ]]; then + echo "BRANCH_NAME=${TRAVIS_BRANCH}" > .env +else + echo "BRANCH_NAME=${TRAVIS_PULL_REQUEST_BRANCH}" > .env +fi +docker-compose -f api-tests/docker-compose.yml up -d +sleep 10 +curl --fail http://localhost:8080/echo/hello +curl --fail http://localhost:8500/v1/agent/services +curl --fail http://localhost:8500/v1/health/checks/echo +docker-compose -f api-tests/docker-compose.yml down diff --git a/source/BaselineOfStargateConsul/BaselineOfStargateConsul.class.st b/source/BaselineOfStargateConsul/BaselineOfStargateConsul.class.st index 5f6a090..37c80ea 100644 --- a/source/BaselineOfStargateConsul/BaselineOfStargateConsul.class.st +++ b/source/BaselineOfStargateConsul/BaselineOfStargateConsul.class.st @@ -16,7 +16,7 @@ BaselineOfStargateConsul >> baseline: spec [ spec group: 'CI' with: 'Tests'; group: 'Tools' with: #('Stargate-Tools'); - group: 'Development' with: #('Tests' 'Tools') + group: 'Development' with: #('Tests' 'Tools' 'Examples') ] ] @@ -32,18 +32,35 @@ BaselineOfStargateConsul >> setUpDependencies: spec [ spec baseline: 'Stargate' with: [ spec repository: 'github://ba-st/Stargate:v4/source' ]; project: 'Stargate-Core' copyFrom: 'Stargate' with: [ spec loads: 'Core' ]; + project: 'Stargate-HealthCheck-Deployment' + copyFrom: 'Stargate' + with: [ spec loads: 'HealthCheck' ]; project: 'Stargate-SUnit' copyFrom: 'Stargate' with: [ spec loads: 'Dependent-SUnit-Extensions' ]; - project: 'Stargate-Tools' copyFrom: 'Stargate' with: [ spec loads: 'Tools' ] + project: 'Stargate-Tools' copyFrom: 'Stargate' with: [ spec loads: 'Tools' ]. + + spec + baseline: 'Launchpad' with: [ spec repository: 'github://ba-st/Launchpad:v2/source' ]; + project: 'Launchpad-Deployment' copyFrom: 'Launchpad' with: [ spec loads: 'Deployment' ] ] { #category : #baselines } BaselineOfStargateConsul >> setUpPackages: spec [ spec - package: 'Stargate-Consul' with: [ spec requires: #('Stargate-Core') ]; + package: 'Stargate-Consul' with: [ spec requires: 'Stargate-Core' ]; group: 'Deployment' with: 'Stargate-Consul'. spec package: 'Stargate-Consul-Tests' with: [ spec requires: #('Stargate-Consul' 'Stargate-SUnit') ]; - group: 'Tests' with: 'Stargate-Consul-Tests' + group: 'Tests' with: 'Stargate-Consul-Tests'. + + spec + package: 'Stargate-Consul-Examples' + with: [ spec requires: #('Stargate-Consul' 'Launchpad-Deployment' 'Stargate-HealthCheck-Deployment') ]; + group: 'Examples' with: 'Stargate-Consul-Examples'. + + spec + package: 'Stargate-Consul-Examples-Tests' + with: [ spec requires: #('Stargate-Consul-Examples' 'Stargate-SUnit') ]; + group: 'Tests' with: 'Stargate-Consul-Examples-Tests' ] diff --git a/source/Stargate-Consul-Examples-Tests/StargateConsulEchoRESTfulControllerTest.class.st b/source/Stargate-Consul-Examples-Tests/StargateConsulEchoRESTfulControllerTest.class.st new file mode 100644 index 0000000..d3fc316 --- /dev/null +++ b/source/Stargate-Consul-Examples-Tests/StargateConsulEchoRESTfulControllerTest.class.st @@ -0,0 +1,31 @@ +Class { + #name : #StargateConsulEchoRESTfulControllerTest, + #superclass : #SingleResourceRESTfulControllerTest, + #category : #'Stargate-Consul-Examples-Tests' +} + +{ #category : #'private - support' } +StargateConsulEchoRESTfulControllerTest >> baseUrl [ + + ^ 'http://api.example.com' asUrl +] + +{ #category : #running } +StargateConsulEchoRESTfulControllerTest >> setUpResourceController [ + + resourceController := StargateConsulEchoRESTfulController new +] + +{ #category : #tests } +StargateConsulEchoRESTfulControllerTest >> testMessageText [ + + | response | + + response := resourceController + messageTextBasedOn: ( self requestToGETResourceIdentifiedBy: 'Hello' accepting: ZnMimeType textPlain ) + within: self newHttpRequestContext. + + self + assert: response isSuccess; + assert: response contents equals: 'HELLO' +] diff --git a/source/Stargate-Consul-Examples-Tests/StargateConsulExampleLauncherTest.class.st b/source/Stargate-Consul-Examples-Tests/StargateConsulExampleLauncherTest.class.st new file mode 100644 index 0000000..efc4774 --- /dev/null +++ b/source/Stargate-Consul-Examples-Tests/StargateConsulExampleLauncherTest.class.st @@ -0,0 +1,30 @@ +Class { + #name : #StargateConsulExampleLauncherTest, + #superclass : #TestCase, + #category : #'Stargate-Consul-Examples-Tests' +} + +{ #category : #tests } +StargateConsulExampleLauncherTest >> testActivate [ + + | launcher | + + [ self + shouldnt: [ launcher := StargateConsulExampleLauncher new. + launcher + commandLine: + ( CommandLineArguments + withArguments: #('--public-URL=http://localhost' '--consul-agent-location=http://consul:8500' '--debug-mode') ); + disableConsulServiceDiscoveryPlugin; + activate + ] + raise: Exit + ] + ensure: [ launcher stop ] +] + +{ #category : #tests } +StargateConsulExampleLauncherTest >> testDescription [ + + self assert: StargateConsulExampleLauncher description equals: 'I provide a RESTful API over HTTP' +] diff --git a/source/Stargate-Consul-Examples-Tests/package.st b/source/Stargate-Consul-Examples-Tests/package.st new file mode 100644 index 0000000..95c3518 --- /dev/null +++ b/source/Stargate-Consul-Examples-Tests/package.st @@ -0,0 +1 @@ +Package { #name : #'Stargate-Consul-Examples-Tests' } diff --git a/source/Stargate-Consul-Examples/StargateConsulEchoRESTfulController.class.st b/source/Stargate-Consul-Examples/StargateConsulEchoRESTfulController.class.st new file mode 100644 index 0000000..82952ec --- /dev/null +++ b/source/Stargate-Consul-Examples/StargateConsulEchoRESTfulController.class.st @@ -0,0 +1,49 @@ +Class { + #name : #StargateConsulEchoRESTfulController, + #superclass : #SingleResourceRESTfulController, + #instVars : [ + 'requestHandler' + ], + #category : #'Stargate-Consul-Examples' +} + +{ #category : #routes } +StargateConsulEchoRESTfulController >> declareGetMessageRoute [ + + ^ RouteSpecification + handling: #GET + at: self identifierTemplate + evaluating: [ :httpRequest :requestContext | self messageTextBasedOn: httpRequest within: requestContext ] +] + +{ #category : #initialization } +StargateConsulEchoRESTfulController >> initialize [ + + super initialize. + requestHandler := RESTfulRequestHandlerBuilder new + handling: 'echo' extractingIdentifierWith: [ :httpRequest | self identifierIn: httpRequest ]; + whenResponding: ZnMimeType textPlain encodeApplying: [ :resource | resource ]; + createEntityTagHashingEncodedResource; + build +] + +{ #category : #API } +StargateConsulEchoRESTfulController >> messageTextBasedOn: httpRequest within: requestContext [ + + ^ requestHandler + from: httpRequest + within: requestContext + get: [ :message | message asString asUppercase ] +] + +{ #category : #private } +StargateConsulEchoRESTfulController >> requestHandler [ + + ^ requestHandler +] + +{ #category : #private } +StargateConsulEchoRESTfulController >> typeIdConstraint [ + + ^ IsObject +] diff --git a/source/Stargate-Consul-Examples/StargateConsulExampleLauncher.class.st b/source/Stargate-Consul-Examples/StargateConsulExampleLauncher.class.st new file mode 100644 index 0000000..9a4cb4b --- /dev/null +++ b/source/Stargate-Consul-Examples/StargateConsulExampleLauncher.class.st @@ -0,0 +1,149 @@ +Class { + #name : #StargateConsulExampleLauncher, + #superclass : #ApplicationStarterCommandLineHandler, + #instVars : [ + 'api', + 'consulServiceDiscoveryIsEnabled' + ], + #category : #'Stargate-Consul-Examples' +} + +{ #category : #accessing } +StargateConsulExampleLauncher class >> commandName [ + + ^ 'stargate-consul-example' +] + +{ #category : #accessing } +StargateConsulExampleLauncher class >> description [ + + ^ 'I provide a RESTful API over HTTP' +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher class >> logPrefix [ + + ^ 'stargate-consul-example' +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> apiConfiguration [ + + ^ Array + with: #serverUrl -> self publicURL + with: #port -> ( self configuration at: 'port' ) + with: #debugMode -> self isDebugModeEnabled + with: #operations -> self operationsConfiguration +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> authAlgorithm [ + + ^ JWAHMACSHA256 parameterValue +] + +{ #category : #'private - activation' } +StargateConsulExampleLauncher >> basicActivate [ + + api := HTTPBasedRESTfulAPI + configuredBy: self apiConfiguration + installing: {StargateConsulEchoRESTfulController new}. + + CurrentLogger value logAsInfo: 'Installing API' during: [ api install ]. + self isDebugModeEnabled + ifFalse: [ api on: Error addErrorHandler: [ :error | self class dumpStackAndReport: error ] ]. + CurrentLogger value logAsInfo: 'Starting API' during: [ api start ] +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> configurationDefinition [ + + ^ Array + with: ( MandatoryArgument named: 'public-URL' convertingWith: #asUrl ) + with: ( OptionalArgument named: 'port' defaultingTo: 8080 convertingWith: #asNumber ) + with: + ( OptionalArgument named: 'operations-secret' defaultingTo: 'SECRET' convertingWith: #asByteArray ) + asSensitive + with: ( MandatoryArgument named: 'consul-agent-location' convertingWith: #asUrl ) +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> consulServiceDiscoveryConfiguration [ + + ^ Dictionary new + at: #enabled put: consulServiceDiscoveryIsEnabled; + at: #definitions + put: + {( ConsulServiceDefinitionBuilder new + addCheck: + ( ConsulAgentHTTPBasedCheck + named: 'health-check' + executing: #POST + against: self publicURL / 'operations' asUrl / HealthCheckPlugin endpoint + withHeaders: + { #accept -> 'application/vnd.stargate.health-check.summary+json;version=1.0.0'. + #authorization -> ( 'Bearer <1s>' expandMacrosWith: self healthCheckToken )} + every: 10 seconds + timeoutAfter: 1 minute ); + buildNamed: 'echo' )}; + at: #consulAgentLocation put: ( self configuration at: 'consul-agent-location' ); + yourself +] + +{ #category : #configuring } +StargateConsulExampleLauncher >> disableConsulServiceDiscoveryPlugin [ + + consulServiceDiscoveryIsEnabled := false +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> healthCheckToken [ + + | jws | + + jws := JsonWebSignature new. + jws algorithmName: self authAlgorithm. + jws + payload: + ( JWTClaimsSet new + permissions: #('execute:health-check'); + yourself ). + jws key: self operationsSecret. + ^ jws compactSerialized +] + +{ #category : #initialization } +StargateConsulExampleLauncher >> initialize [ + + super initialize. + consulServiceDiscoveryIsEnabled := true +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> operationsConfiguration [ + + ^ Dictionary new + at: #authSchema put: 'jwt'; + at: #authAlgorithm put: self authAlgorithm; + at: #authSecret put: self operationsSecret; + at: ConsulServiceDiscoveryPlugin endpoint put: self consulServiceDiscoveryConfiguration; + yourself +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> operationsSecret [ + + ^ self configuration at: 'operations-secret' +] + +{ #category : #'private - accessing' } +StargateConsulExampleLauncher >> publicURL [ + + ^ self configuration at: 'public-URL' +] + +{ #category : #'private - activation' } +StargateConsulExampleLauncher >> stop [ + + api ifNotNil: [ :theAPI | theAPI stop ] +] diff --git a/source/Stargate-Consul-Examples/package.st b/source/Stargate-Consul-Examples/package.st new file mode 100644 index 0000000..a333e82 --- /dev/null +++ b/source/Stargate-Consul-Examples/package.st @@ -0,0 +1 @@ +Package { #name : #'Stargate-Consul-Examples' } diff --git a/source/Stargate-Consul-Tests/ConsulAgentHTTPBasedCheckTest.class.st b/source/Stargate-Consul-Tests/ConsulAgentHTTPBasedCheckTest.class.st index 6146f00..83727d8 100644 --- a/source/Stargate-Consul-Tests/ConsulAgentHTTPBasedCheckTest.class.st +++ b/source/Stargate-Consul-Tests/ConsulAgentHTTPBasedCheckTest.class.st @@ -49,7 +49,7 @@ ConsulAgentHTTPBasedCheckTest >> testAsJSONWhenHeadersArePresent [ self assert: json Name equals: 'HTTP check'; assert: json Method equals: 'POST'; - assert: json Header accept equals: 'application/json'; + assert: json Header accept equals: #('application/json'); assert: json HTTP equals: 'http://api.example.com/'; assert: json Timeout equals: '1m30s'; assert: json Interval equals: '10s' diff --git a/source/Stargate-Consul-Tests/FakeConsulAgentAPI.class.st b/source/Stargate-Consul-Tests/FakeConsulAgentAPI.class.st index 55404e8..405c4b4 100644 --- a/source/Stargate-Consul-Tests/FakeConsulAgentAPI.class.st +++ b/source/Stargate-Consul-Tests/FakeConsulAgentAPI.class.st @@ -23,9 +23,9 @@ FakeConsulAgentAPI >> configureTeapotServerWith: configuration [ server := Teapot configure: configuration. server - PUT: '/agent/service/register' -> [ :request | self handleServiceRegistration: request ]; + PUT: '/v1/agent/service/register' -> [ :request | self handleServiceRegistration: request ]; PUT: - '/agent/service/deregister/' + '/v1/agent/service/deregister/' -> [ :request | self handleServiceDeregistration: request ] ] diff --git a/source/Stargate-Consul/ConsulAgentHTTPBasedCheck.class.st b/source/Stargate-Consul/ConsulAgentHTTPBasedCheck.class.st index 489c7d4..2b0f661 100644 --- a/source/Stargate-Consul/ConsulAgentHTTPBasedCheck.class.st +++ b/source/Stargate-Consul/ConsulAgentHTTPBasedCheck.class.st @@ -50,13 +50,30 @@ ConsulAgentHTTPBasedCheck >> asDictionary [ yourself ] +{ #category : #initialization } +ConsulAgentHTTPBasedCheck >> initializeHeadersBasedOn: aHeaderCollection [ + + "Consul expects an array of strings as values for the headers. " + + headers := Dictionary new. + aHeaderCollection asDictionary + keysAndValuesDo: [ :headerName :headerValue | + headers + at: headerName + put: + ( headerValue isArray + ifTrue: [ headerValue ] + ifFalse: [ Array with: headerValue ] ) + ] +] + { #category : #initialization } ConsulAgentHTTPBasedCheck >> initializeNamed: aName executing: anHttpMethod against: anUrl withHeaders: aHeaderCollection every: aDuration timeoutAfter: timeoutDuration [ name := aName. method := anHttpMethod. url := anUrl. - headers := aHeaderCollection asDictionary. + self initializeHeadersBasedOn: aHeaderCollection. invokationInterval := aDuration. timeout := timeoutDuration ] diff --git a/source/Stargate-Consul/ConsulServiceDiscoveryPlugin.class.st b/source/Stargate-Consul/ConsulServiceDiscoveryPlugin.class.st index f92b734..6e2e4d0 100644 --- a/source/Stargate-Consul/ConsulServiceDiscoveryPlugin.class.st +++ b/source/Stargate-Consul/ConsulServiceDiscoveryPlugin.class.st @@ -53,6 +53,18 @@ ConsulServiceDiscoveryPlugin class >> reportingLifecycleOfAll: aServiceDefinitio ^ self new initializeReportingLifecycleOfAll: aServiceDefinitionCollection toAgentOn: aConsulAPIUrl ] +{ #category : #private } +ConsulServiceDiscoveryPlugin >> baseAPILocation [ + + ^ consulAgentLocation / 'v1/agent/service' +] + +{ #category : #private } +ConsulServiceDiscoveryPlugin >> connectivityErrors [ + + ^ ZnHttpUnsuccessful , NetworkError +] + { #category : #private } ConsulServiceDiscoveryPlugin >> deregistrationUrlFor: serviceDefinition [ @@ -60,7 +72,7 @@ ConsulServiceDiscoveryPlugin >> deregistrationUrlFor: serviceDefinition [ serviceId := serviceDefinition at: #ID ifAbsent: [ serviceDefinition Name ]. - ^ consulAgentLocation / ( 'agent/service/deregister/<1s>' expandMacrosWith: serviceId ) + ^ self baseAPILocation / ( 'deregister/<1s>' expandMacrosWith: serviceId ) ] { #category : #configuring } @@ -79,22 +91,52 @@ ConsulServiceDiscoveryPlugin >> startOn: teapotServer [ serviceDefinitions do: [ :serviceDefinition | - ZnClient new - beOneShot; - enforceHttpSuccess; - put: consulAgentLocation / 'agent/service/register' - contents: ( NeoJSONWriter toString: serviceDefinition ) + self + try: [ :client | + client + put: self baseAPILocation / 'register' + contents: ( NeoJSONWriter toString: serviceDefinition ) + ] + retryCount: 3 ] ] { #category : #controlling } ConsulServiceDiscoveryPlugin >> stop [ - serviceDefinitions + [ serviceDefinitions do: [ :serviceDefinition | - ZnClient new - beOneShot; - enforceHttpSuccess; - put: ( self deregistrationUrlFor: serviceDefinition ) contents: '' + self + try: [ :client | client put: ( self deregistrationUrlFor: serviceDefinition ) contents: '' ] + retryCount: 3 + ] + ] + on: self connectivityErrors + do: [ :error | "Since we are already stopping the API, just ignore it. + Consul will figure out eventually that this service is dead." + error return ] +] + +{ #category : #private } +ConsulServiceDiscoveryPlugin >> try: aBlock retryCount: count [ + + | client | + + client := ZnClient new. + client + beOneShot; + enforceHttpSuccess: true. + count = 1 + ifTrue: [ aBlock value: client ] + ifFalse: [ [ aBlock value: client ] + on: self connectivityErrors + do: [ :error | + CurrentLogger value + logAsError: + ( 'Consul Agent HTTP request failed: <1s>, retry count: <2p>' + expandMacrosWith: error messageText + with: count ). + self try: aBlock retryCount: count - 1 + ] ] ]