From f5cc661e8f8b155302f69a37547c3b09affc1b7e Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Thu, 22 Oct 2020 17:23:21 -0300 Subject: [PATCH 1/7] Add basic service discovery client --- .../BaselineOfSuperluminal.class.st | 20 ++++-- ...nifestSuperluminalRESTfulAPITests.class.st | 8 --- .../FixedServiceDiscoveryClientTest.class.st | 64 +++++++++++++++++++ .../NullServiceDiscoveryClientTest.class.st | 22 +++++++ .../package.st | 1 + .../FixedServiceDiscoveryClient.class.st | 37 +++++++++++ .../NullServiceDiscoveryClient.class.st | 11 ++++ .../ServiceDiscoveryClient.class.st | 11 ++++ .../Superluminal-Service-Discovery/package.st | 1 + 9 files changed, 162 insertions(+), 13 deletions(-) delete mode 100644 source/Superluminal-RESTfulAPI-Tests/ManifestSuperluminalRESTfulAPITests.class.st create mode 100644 source/Superluminal-Service-Discovery-Tests/FixedServiceDiscoveryClientTest.class.st create mode 100644 source/Superluminal-Service-Discovery-Tests/NullServiceDiscoveryClientTest.class.st create mode 100644 source/Superluminal-Service-Discovery-Tests/package.st create mode 100644 source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st create mode 100644 source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st create mode 100644 source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st create mode 100644 source/Superluminal-Service-Discovery/package.st diff --git a/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st b/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st index 76c8527..6dbf71a 100644 --- a/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st +++ b/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st @@ -48,21 +48,31 @@ BaselineOfSuperluminal >> setUpDependencies: spec [ BaselineOfSuperluminal >> setUpPackages: spec [ spec - package: 'Superluminal-Model' with: [ spec requires: #('Hyperspace-Deployment' 'NeoJSON-Core') ]; + package: 'Superluminal-Model' + with: [ spec requires: #( 'Hyperspace-Deployment' 'NeoJSON-Core' ) ]; group: 'Deployment' with: 'Superluminal-Model'. spec package: 'Superluminal-RESTfulAPI' - with: [ spec requires: #('Superluminal-Model' 'ObjectPool-Core') ]; + with: [ spec requires: #( 'Superluminal-Model' 'ObjectPool-Core' ) ]; group: 'Deployment' with: 'Superluminal-RESTfulAPI'. + spec + package: 'Superluminal-Service-Discovery'; + group: 'Deployment' with: 'Superluminal-Service-Discovery'. + spec package: 'Superluminal-Model-Tests' - with: [ spec requires: #('Superluminal-Model' 'Hyperspace-SUnit') ]; + with: [ spec requires: #( 'Superluminal-Model' 'Hyperspace-SUnit' ) ]; group: 'Tests' with: 'Superluminal-Model-Tests'. spec package: 'Superluminal-RESTfulAPI-Tests' - with: [ spec requires: #('Superluminal-RESTfulAPI' 'Hyperspace-SUnit') ]; - group: 'Tests' with: 'Superluminal-RESTfulAPI-Tests' + with: [ spec requires: #( 'Superluminal-RESTfulAPI' 'Hyperspace-SUnit' ) ]; + group: 'Tests' with: 'Superluminal-RESTfulAPI-Tests'. + + spec + package: 'Superluminal-Service-Discovery-Tests' + with: [ spec requires: #( 'Superluminal-Service-Discovery' 'Hyperspace-SUnit' ) ]; + group: 'Tests' with: 'Superluminal-Service-Discovery-Tests' ] diff --git a/source/Superluminal-RESTfulAPI-Tests/ManifestSuperluminalRESTfulAPITests.class.st b/source/Superluminal-RESTfulAPI-Tests/ManifestSuperluminalRESTfulAPITests.class.st deleted file mode 100644 index dd311e6..0000000 --- a/source/Superluminal-RESTfulAPI-Tests/ManifestSuperluminalRESTfulAPITests.class.st +++ /dev/null @@ -1,8 +0,0 @@ -" -I store metadata for this package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser -" -Class { - #name : #ManifestSuperluminalRESTfulAPITests, - #superclass : #PackageManifest, - #category : #'Superluminal-RESTfulAPI-Tests-Manifest' -} diff --git a/source/Superluminal-Service-Discovery-Tests/FixedServiceDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/FixedServiceDiscoveryClientTest.class.st new file mode 100644 index 0000000..7cae164 --- /dev/null +++ b/source/Superluminal-Service-Discovery-Tests/FixedServiceDiscoveryClientTest.class.st @@ -0,0 +1,64 @@ +" +A FixedServiceDiscoveryClientTest is a test class for testing the behavior of FixedServiceDiscoveryClient +" +Class { + #name : #FixedServiceDiscoveryClientTest, + #superclass : #TestCase, + #category : #'Superluminal-Service-Discovery-Tests' +} + +{ #category : #tests } +FixedServiceDiscoveryClientTest >> testClientChaining [ + + | discoveryClient fallbackClient | + + fallbackClient := FixedServiceDiscoveryClient basedOn: + { #'google-finance' -> 'https://api.finance.google.com/' asUrl } + asDictionary. + + discoveryClient := FixedServiceDiscoveryClient basedOn: + { #reuters -> 'api.reuters.com' asUrl } asDictionary + chainedWith: fallbackClient. + + discoveryClient withLocationOfService: #'google-finance' + do: [ :location | self assertUrl: location equals: 'https://api.finance.google.com' ] + ifUnable: [ self fail ]. + + discoveryClient withLocationOfService: #reuters + do: [ :location | self assertUrl: location equals: 'api.reuters.com' ] + ifUnable: [ self fail ] +] + +{ #category : #tests } +FixedServiceDiscoveryClientTest >> testUnableToFindLocation [ + + | discoveryClient locationWasFound | + + discoveryClient := FixedServiceDiscoveryClient basedOn: Dictionary new. + + locationWasFound := true. + + discoveryClient withLocationOfService: #'google-finance' + do: [ :location | self fail ] + ifUnable: [ locationWasFound := false ]. + + self deny: locationWasFound +] + +{ #category : #tests } +FixedServiceDiscoveryClientTest >> testWithLocationOfDoIfUnable [ + + | discoveryClient | + + discoveryClient := FixedServiceDiscoveryClient basedOn: { + #'google-finance' -> 'https://api.finance.google.com/' asUrl. + #reuters -> 'api.reuters.com' asUrl } asDictionary. + + discoveryClient withLocationOfService: #'google-finance' + do: [ :location | self assertUrl: location equals: 'https://api.finance.google.com' ] + ifUnable: [ self fail ]. + + discoveryClient withLocationOfService: #reuters + do: [ :location | self assertUrl: location equals: 'api.reuters.com' ] + ifUnable: [ self fail ] +] diff --git a/source/Superluminal-Service-Discovery-Tests/NullServiceDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/NullServiceDiscoveryClientTest.class.st new file mode 100644 index 0000000..9bbb408 --- /dev/null +++ b/source/Superluminal-Service-Discovery-Tests/NullServiceDiscoveryClientTest.class.st @@ -0,0 +1,22 @@ +" +A NullServiceDiscoveryClientTest is a test class for testing the behavior of NullServiceDiscoveryClient +" +Class { + #name : #NullServiceDiscoveryClientTest, + #superclass : #TestCase, + #category : #'Superluminal-Service-Discovery-Tests' +} + +{ #category : #test } +NullServiceDiscoveryClientTest >> testWithLocationOfServiceDoIfUnable [ + + | serviceLookupWasUnsuccesfull | + serviceLookupWasUnsuccesfull := false. + + NullServiceDiscoveryClient new + withLocationOfService: #google + do: [ :location | self fail ] + ifUnable: [ serviceLookupWasUnsuccesfull := true ]. + + self assert: serviceLookupWasUnsuccesfull +] diff --git a/source/Superluminal-Service-Discovery-Tests/package.st b/source/Superluminal-Service-Discovery-Tests/package.st new file mode 100644 index 0000000..b9a5375 --- /dev/null +++ b/source/Superluminal-Service-Discovery-Tests/package.st @@ -0,0 +1 @@ +Package { #name : #'Superluminal-Service-Discovery-Tests' } diff --git a/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st new file mode 100644 index 0000000..d1da947 --- /dev/null +++ b/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st @@ -0,0 +1,37 @@ +Class { + #name : #FixedServiceDiscoveryClient, + #superclass : #ServiceDiscoveryClient, + #instVars : [ + 'mappings', + 'fallbackClient' + ], + #category : #'Superluminal-Service-Discovery' +} + +{ #category : #'instance creation' } +FixedServiceDiscoveryClient class >> basedOn: aDictionary [ + + ^ self basedOn: aDictionary chainedWith: NullServiceDiscoveryClient new +] + +{ #category : #'instance creation' } +FixedServiceDiscoveryClient class >> basedOn: aDictionary chainedWith: aServiceDiscoveryClient [ + + ^ self new initializeOn: aDictionary chainedWith: aServiceDiscoveryClient +] + +{ #category : #initialization } +FixedServiceDiscoveryClient >> initializeOn: aDictionary chainedWith: aServiceDiscoveryClient [ + + mappings := aDictionary. + fallbackClient := aServiceDiscoveryClient +] + +{ #category : #accessing } +FixedServiceDiscoveryClient >> withLocationOfService: aServiceName do: foundBlock ifUnable: failBlock [ + + ^ mappings at: aServiceName asSymbol + ifPresent: foundBlock + ifAbsent: [ + fallbackClient withLocationOfService: aServiceName do: foundBlock ifUnable: failBlock ] +] diff --git a/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st new file mode 100644 index 0000000..23f7da6 --- /dev/null +++ b/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st @@ -0,0 +1,11 @@ +Class { + #name : #NullServiceDiscoveryClient, + #superclass : #ServiceDiscoveryClient, + #category : #'Superluminal-Service-Discovery' +} + +{ #category : #accessing } +NullServiceDiscoveryClient >> withLocationOfService: serviceName do: foundBlock ifUnable: failBlock [ + + ^ failBlock value +] diff --git a/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st new file mode 100644 index 0000000..a136181 --- /dev/null +++ b/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st @@ -0,0 +1,11 @@ +Class { + #name : #ServiceDiscoveryClient, + #superclass : #Object, + #category : #'Superluminal-Service-Discovery' +} + +{ #category : #accessing } +ServiceDiscoveryClient >> withLocationOfService: serviceName do: foundBlock ifUnable: failBlock [ + + self subclassResponsibility +] diff --git a/source/Superluminal-Service-Discovery/package.st b/source/Superluminal-Service-Discovery/package.st new file mode 100644 index 0000000..e4bfec7 --- /dev/null +++ b/source/Superluminal-Service-Discovery/package.st @@ -0,0 +1 @@ +Package { #name : #'Superluminal-Service-Discovery' } From 7173d80549d4ffc50ad26bd72724f6475237577c Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Wed, 28 Oct 2020 08:04:51 -0300 Subject: [PATCH 2/7] Add service discovery client using Consul API --- ...ntHttpAPIBasedDiscoveryClientTest.class.st | 81 +++++++ .../StubbedConsulAgentHealthHttpAPI.class.st | 223 ++++++++++++++++++ ...lAgentHttpAPIBasedDiscoveryClient.class.st | 84 +++++++ .../FixedServiceDiscoveryClient.class.st | 5 + .../NullServiceDiscoveryClient.class.st | 3 + .../ServiceDiscoveryClient.class.st | 5 + 6 files changed, 401 insertions(+) create mode 100644 source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st create mode 100644 source/Superluminal-Service-Discovery-Tests/StubbedConsulAgentHealthHttpAPI.class.st create mode 100644 source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st diff --git a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st new file mode 100644 index 0000000..25d1cd2 --- /dev/null +++ b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st @@ -0,0 +1,81 @@ +" +A ConsulAgentHttpAPIBasedDiscoveryClientTest is a test class for testing the behavior of ConsulAgentHttpAPIBasedDiscoveryClient +" +Class { + #name : #ConsulAgentHttpAPIBasedDiscoveryClientTest, + #superclass : #TestCase, + #instVars : [ + 'consulAgent' + ], + #category : #'Superluminal-Service-Discovery-Tests' +} + +{ #category : #accessing } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> consulAgentLocation [ + + ^ 'http://localhost' asUrl port: self port +] + +{ #category : #accessing } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> port [ + + ^ 9998 +] + +{ #category : #running } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> setUp [ + + super setUp. + consulAgent := StubbedConsulAgentHealthHttpAPI + configuredBy: { + #port -> self port. + #serverUrl -> self consulAgentLocation. + #debugMode -> true } + on: self. + consulAgent start +] + +{ #category : #running } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> tearDown [ + + consulAgent stop. + super tearDown +] + +{ #category : #tests } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> testFoundLocation [ + + | discoveryClient locationWasFound | + + discoveryClient := ConsulAgentHttpAPIBasedDiscoveryClient queryingAgentOn: self consulAgentLocation. + + locationWasFound := false. + + discoveryClient + withLocationOfService: #mns + do: [ :location | + locationWasFound := true. + self assertUrl: location equals: ( ZnUrl new + host: '01649d6c4b01'; + port: 60666 ) + ] + ifUnable: [ self fail ]. + + self assert: locationWasFound +] + +{ #category : #tests } +ConsulAgentHttpAPIBasedDiscoveryClientTest >> testUnableToFindLocation [ + + | discoveryClient locationWasFound | + + discoveryClient := ConsulAgentHttpAPIBasedDiscoveryClient queryingAgentOn: self consulAgentLocation. + + locationWasFound := true. + + discoveryClient withLocationOfService: #'google-finance' + do: [ :location | self fail ] + ifUnable: [ locationWasFound := false ]. + + self deny: locationWasFound +] diff --git a/source/Superluminal-Service-Discovery-Tests/StubbedConsulAgentHealthHttpAPI.class.st b/source/Superluminal-Service-Discovery-Tests/StubbedConsulAgentHealthHttpAPI.class.st new file mode 100644 index 0000000..25f9f0b --- /dev/null +++ b/source/Superluminal-Service-Discovery-Tests/StubbedConsulAgentHealthHttpAPI.class.st @@ -0,0 +1,223 @@ +" +I'm a test double stubbing the health HTTP API of the Consul Agent. +" +Class { + #name : #StubbedConsulAgentHealthHttpAPI, + #superclass : #Object, + #instVars : [ + 'registeredServices', + 'asserter', + 'server' + ], + #category : #'Superluminal-Service-Discovery-Tests' +} + +{ #category : #'instance creation' } +StubbedConsulAgentHealthHttpAPI class >> configuredBy: configuration on: aTestAsserter [ + + ^ self new initializeConfiguredBy: configuration on: aTestAsserter +] + +{ #category : #initialization } +StubbedConsulAgentHealthHttpAPI >> configureTeapotServerWith: configuration [ + + server := Teapot configure: configuration. + server GET: + 'v1/health/service/' + -> [ :request | self handleServiceHealthLookup: request ] +] + +{ #category : #private } +StubbedConsulAgentHealthHttpAPI >> handleServiceHealthLookup: httpRequest [ + + | serviceName json | + + asserter assert: httpRequest method equals: #GET. + serviceName := httpRequest at: #serviceName. + json := serviceName = 'mns' then: [ self stubbedBodyResponse ] otherwise: [ '[]' ]. + ^ TeaResponse ok + body: json; + headerName: 'Content-Type' value: ZnMimeType applicationJson asString; + headerName: 'X-Consul-Effective-Consistency' value: 'leader'; + headerName: 'X-Consul-Index' value: 23; + headerName: 'X-Consul-Knownleader' value: 'true'; + headerName: 'X-Consul-Lastcontact' value: 0; + yourself +] + +{ #category : #initialization } +StubbedConsulAgentHealthHttpAPI >> initializeConfiguredBy: configuration on: aTestAsserter [ + + registeredServices := OrderedCollection new. + asserter := aTestAsserter. + self configureTeapotServerWith: configuration +] + +{ #category : #controlling } +StubbedConsulAgentHealthHttpAPI >> start [ + + server start +] + +{ #category : #controlling } +StubbedConsulAgentHealthHttpAPI >> stop [ + + server stop +] + +{ #category : #private } +StubbedConsulAgentHealthHttpAPI >> stubbedBodyResponse [ + + ^'[ + { + "Node": { + "ID": "ebc2d7c6-9909-ad1a-43d2-5e003202ca31", + "Node": "93b450bf7c73", + "Address": "127.0.0.1", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "127.0.0.1", + "lan_ipv4": "127.0.0.1", + "wan": "127.0.0.1", + "wan_ipv4": "127.0.0.1" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 11, + "ModifyIndex": 12 + }, + "Service": { + "ID": "mns-01649d6c4b01", + "Service": "mns", + "Tags": [ + "metrics-provider" + ], + "Address": "01649d6c4b01", + "Meta": {}, + "Port": 60666, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 22, + "ModifyIndex": 22 + }, + "Checks": [ + { + "Node": "93b450bf7c73", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Definition": {}, + "CreateIndex": 11, + "ModifyIndex": 11 + }, + { + "Node": "93b450bf7c73", + "CheckID": "service:mns-01649d6c4b01", + "Name": "health-check", + "Status": "passing", + "Notes": "", + "Output": "HTTP POST http://01649d6c4b01:60666/operations/health-check: 200 OK Output: {\"status\":\"PASS\"}", + "ServiceID": "mns-01649d6c4b01", + "ServiceName": "mns", + "ServiceTags": [ + "metrics-provider" + ], + "Type": "http", + "Definition": {}, + "CreateIndex": 22, + "ModifyIndex": 26 + } + ] + }, + { + "Node": { + "ID": "ebc2d7c6-9909-ad1a-43d2-5e003202ca31", + "Node": "93b450bf7c73", + "Address": "127.0.0.1", + "Datacenter": "dc1", + "TaggedAddresses": { + "lan": "127.0.0.1", + "lan_ipv4": "127.0.0.1", + "wan": "127.0.0.1", + "wan_ipv4": "127.0.0.1" + }, + "Meta": { + "consul-network-segment": "" + }, + "CreateIndex": 11, + "ModifyIndex": 12 + }, + "Service": { + "ID": "mns-94e2ce29d72a", + "Service": "mns", + "Tags": [ + "metrics-provider" + ], + "Address": "94e2ce29d72a", + "Meta": {}, + "Port": 60666, + "Weights": { + "Passing": 1, + "Warning": 1 + }, + "EnableTagOverride": false, + "Proxy": { + "MeshGateway": {}, + "Expose": {} + }, + "Connect": {}, + "CreateIndex": 14, + "ModifyIndex": 14 + }, + "Checks": [ + { + "Node": "93b450bf7c73", + "CheckID": "serfHealth", + "Name": "Serf Health Status", + "Status": "passing", + "Notes": "", + "Output": "Agent alive and reachable", + "ServiceID": "", + "ServiceName": "", + "ServiceTags": [], + "Type": "", + "Definition": {}, + "CreateIndex": 11, + "ModifyIndex": 11 + }, + { + "Node": "93b450bf7c73", + "CheckID": "service:mns-94e2ce29d72a", + "Name": "health-check", + "Status": "critical", + "Notes": "", + "Output": "Post \"http://94e2ce29d72a:60666/operations/health-check\": dial tcp: lookup 94e2ce29d72a on 127.0.0.11:53: no such host", + "ServiceID": "mns-94e2ce29d72a", + "ServiceName": "mns", + "ServiceTags": [ + "metrics-provider" + ], + "Type": "http", + "Definition": {}, + "CreateIndex": 14, + "ModifyIndex": 38 + } + ] + } +]' +] diff --git a/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st new file mode 100644 index 0000000..bbd52b4 --- /dev/null +++ b/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st @@ -0,0 +1,84 @@ +" +I'm a service discovery client using the Consul HTTP API to get a list of service locations. + +I will use the [Health](https://www.consul.io/api-docs/health) endpoint to obtain the list of healthy services. +I can be chained with another service discovery client as a fallback when a service is not found in my list. +" +Class { + #name : #ConsulAgentHttpAPIBasedDiscoveryClient, + #superclass : #ServiceDiscoveryClient, + #instVars : [ + 'httpClient', + 'agentLocation', + 'fallbackClient', + 'schema' + ], + #category : #'Superluminal-Service-Discovery' +} + +{ #category : #'Instance creation' } +ConsulAgentHttpAPIBasedDiscoveryClient class >> queryingAgentOn: aConsulAgentLocation [ + + ^ self queryingAgentOn: aConsulAgentLocation chainedWith: NullServiceDiscoveryClient new +] + +{ #category : #'instance creation' } +ConsulAgentHttpAPIBasedDiscoveryClient class >> queryingAgentOn: aConsulAgentLocation chainedWith: aServiceDiscoveryClient [ + + ^ self new initializeQueryingAgentOn: aConsulAgentLocation chainedWith: aServiceDiscoveryClient +] + +{ #category : #initialization } +ConsulAgentHttpAPIBasedDiscoveryClient >> initializeQueryingAgentOn: aConsulAgentLocation chainedWith: aServiceDiscoveryClient [ + + agentLocation := aConsulAgentLocation. + fallbackClient := aServiceDiscoveryClient. + httpClient := ZnClient new. + httpClient enforceHttpSuccess: true +] + +{ #category : #private } +ConsulAgentHttpAPIBasedDiscoveryClient >> requestToDiscover: serviceName [ + + ^ HttpRequest get: agentLocation / 'v1' / 'health' / 'service' / serviceName + configuredUsing: [ :request | + ( request queryString: [ :queryString | + queryString fieldNamed: 'filter' pairedTo: 'Checks.Status == passing' ] ) + + ( request headers setAcceptTo: ZnMimeType applicationJson ) + ] +] + +{ #category : #accessing } +ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfService: serviceName do: foundBlock ifUnable: failBlock [ + + ^ Retry + value: [ + | request serviceHealthList | + + request := self requestToDiscover: serviceName. + serviceHealthList := NeoJSONObject fromString: ( request applyOn: httpClient ) contents. + self withLocationOfServiceOn: serviceHealthList do: failBlock ifUnable: foundBlock + ] + configuredBy: [ :retry | + retry + upTo: 2; + on: NetworkError , ZnHttpUnsuccessful + ] +] + +{ #category : #private } +ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfServiceOn: serviceHealthList do: failBlock ifUnable: foundBlock [ + + "The API endpoint only filters the service instances failing all healthchecks, + so we need to filter them here anyway" + + ^ serviceHealthList + detect: [ :health | health Checks allSatisfy: [ :check | check Status = 'passing' ] ] + ifFound: [ :health | + | service | + + service := health Service. + foundBlock value: ( ( ZnUrl new host: service Address ) port: service Port ) + ] + ifNone: failBlock +] diff --git a/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st index d1da947..5e8f105 100644 --- a/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st +++ b/source/Superluminal-Service-Discovery/FixedServiceDiscoveryClient.class.st @@ -1,3 +1,8 @@ +" +I'm a service discovery client using a fixed list of service locations. + +I'm usefull for testing purposes or when transitioning clients not using the service discovery mechanics. I can be chained with another service discovery client as a fallback when a service is not found in my list. +" Class { #name : #FixedServiceDiscoveryClient, #superclass : #ServiceDiscoveryClient, diff --git a/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st index 23f7da6..774bced 100644 --- a/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st +++ b/source/Superluminal-Service-Discovery/NullServiceDiscoveryClient.class.st @@ -1,3 +1,6 @@ +" +I'm a service discovery client that will always fail when looking up a service by its name. +" Class { #name : #NullServiceDiscoveryClient, #superclass : #ServiceDiscoveryClient, diff --git a/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st index a136181..af12507 100644 --- a/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st +++ b/source/Superluminal-Service-Discovery/ServiceDiscoveryClient.class.st @@ -1,3 +1,8 @@ +" +I'm the abstract class for service discovery clients. + +A service discovery client is responsible for providing the known location of a service given its name. +" Class { #name : #ServiceDiscoveryClient, #superclass : #Object, From a3d8347016e8d051880b5860320c655d76f4aed9 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Wed, 28 Oct 2020 08:49:46 -0300 Subject: [PATCH 3/7] Make tearDown more robust --- .../ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st index 25d1cd2..e346ef6 100644 --- a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st +++ b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st @@ -38,7 +38,7 @@ ConsulAgentHttpAPIBasedDiscoveryClientTest >> setUp [ { #category : #running } ConsulAgentHttpAPIBasedDiscoveryClientTest >> tearDown [ - consulAgent stop. + consulAgent ifNotNil: #stop. super tearDown ] From 7efe2f2d5b8de261aa5044eb66578ef366aaf820 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Wed, 28 Oct 2020 09:21:26 -0300 Subject: [PATCH 4/7] Add missing dependency on test package --- .../BaselineOfSuperluminal/BaselineOfSuperluminal.class.st | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st b/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st index 6dbf71a..8904837 100644 --- a/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st +++ b/source/BaselineOfSuperluminal/BaselineOfSuperluminal.class.st @@ -42,6 +42,9 @@ BaselineOfSuperluminal >> setUpDependencies: spec [ baseline: 'ObjectPool' with: [ spec repository: 'github://pharo-ide/ObjectPool:v1.0.1' ]; project: 'ObjectPool-Core' copyFrom: 'ObjectPool' with: [ spec loads: 'Core' ]. + spec + baseline: 'Teapot' with: [ spec repository: 'github://zeroflag/Teapot:v2.6.0/source' ]; + project: 'Teapot-Deployment' copyFrom: 'Teapot' with: [ spec loads: 'Deployment' ]. ] { #category : #baselines } @@ -73,6 +76,6 @@ BaselineOfSuperluminal >> setUpPackages: spec [ spec package: 'Superluminal-Service-Discovery-Tests' - with: [ spec requires: #( 'Superluminal-Service-Discovery' 'Hyperspace-SUnit' ) ]; + with: [ spec requires: #( 'Superluminal-Service-Discovery' 'Hyperspace-SUnit' 'Teapot-Deployment') ]; group: 'Tests' with: 'Superluminal-Service-Discovery-Tests' ] From 8781171875d94832629c331826acb8ee8ada0075 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Wed, 28 Oct 2020 11:46:50 -0300 Subject: [PATCH 5/7] Remove unused variable. Use fallback client. --- .../ConsulAgentHttpAPIBasedDiscoveryClient.class.st | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st b/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st index bbd52b4..1c834aa 100644 --- a/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st +++ b/source/Superluminal-Service-Discovery/ConsulAgentHttpAPIBasedDiscoveryClient.class.st @@ -10,8 +10,7 @@ Class { #instVars : [ 'httpClient', 'agentLocation', - 'fallbackClient', - 'schema' + 'fallbackClient' ], #category : #'Superluminal-Service-Discovery' } @@ -57,7 +56,10 @@ ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfService: serviceName do: request := self requestToDiscover: serviceName. serviceHealthList := NeoJSONObject fromString: ( request applyOn: httpClient ) contents. - self withLocationOfServiceOn: serviceHealthList do: failBlock ifUnable: foundBlock + self withLocationOfServiceOn: serviceHealthList + do: foundBlock + ifUnable: [ + fallbackClient withLocationOfService: serviceName do: foundBlock ifUnable: failBlock ] ] configuredBy: [ :retry | retry @@ -67,7 +69,7 @@ ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfService: serviceName do: ] { #category : #private } -ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfServiceOn: serviceHealthList do: failBlock ifUnable: foundBlock [ +ConsulAgentHttpAPIBasedDiscoveryClient >> withLocationOfServiceOn: serviceHealthList do: foundBlock ifUnable: failBlock [ "The API endpoint only filters the service instances failing all healthchecks, so we need to filter them here anyway" From 3baa08773d5b069fbfc01cd35602bb0628043312 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Thu, 29 Oct 2020 12:30:14 -0300 Subject: [PATCH 6/7] Check for port availability --- .../ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st index e346ef6..bb5ed0c 100644 --- a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st +++ b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st @@ -19,6 +19,10 @@ ConsulAgentHttpAPIBasedDiscoveryClientTest >> consulAgentLocation [ { #category : #accessing } ConsulAgentHttpAPIBasedDiscoveryClientTest >> port [ + | check | + + check := PortAvailabilityChecker on: 9998. + check checkIfAvailableDo: [ ] ifNot: [ self fail ]. ^ 9998 ] From 1675ebcfba9f5e58e9b59fcc5f6917241b2ba8b7 Mon Sep 17 00:00:00 2001 From: Gabriel Omar Cotelli Date: Thu, 29 Oct 2020 12:59:57 -0300 Subject: [PATCH 7/7] Use a different port --- .../ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st index bb5ed0c..43a288f 100644 --- a/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st +++ b/source/Superluminal-Service-Discovery-Tests/ConsulAgentHttpAPIBasedDiscoveryClientTest.class.st @@ -18,12 +18,8 @@ ConsulAgentHttpAPIBasedDiscoveryClientTest >> consulAgentLocation [ { #category : #accessing } ConsulAgentHttpAPIBasedDiscoveryClientTest >> port [ - - | check | - - check := PortAvailabilityChecker on: 9998. - check checkIfAvailableDo: [ ] ifNot: [ self fail ]. - ^ 9998 + + ^ 41663 ] { #category : #running }