-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merged in SHIBUI-1707-a (pull request #458)
SHIBUI-1707-a Beacon module implementation Approved-by: Jonathan Johnson
- Loading branch information
Showing
15 changed files
with
375 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
allprojects { | ||
group = 'edu.internet2.tap.beacon' | ||
version = '1.0.0-SNAPSHOT' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import org.springframework.boot.gradle.plugin.SpringBootPlugin | ||
|
||
plugins { | ||
id 'org.springframework.boot' version '2.0.0.RELEASE' apply false | ||
id 'io.spring.dependency-management' version '1.0.6.RELEASE' | ||
} | ||
|
||
apply plugin: 'groovy' | ||
sourceCompatibility = 1.8 | ||
targetCompatibility = 1.8 | ||
|
||
repositories { | ||
jcenter() | ||
} | ||
|
||
dependencyManagement { | ||
imports { | ||
mavenBom SpringBootPlugin.BOM_COORDINATES | ||
} | ||
} | ||
|
||
dependencies { | ||
testCompile "org.springframework.boot:spring-boot-starter-test" | ||
testCompile "org.spockframework:spock-core:1.1-groovy-2.4" | ||
testCompile "org.spockframework:spock-spring:1.1-groovy-2.4" | ||
|
||
testCompile 'org.junit.jupiter:junit-jupiter-api:5.5.2' | ||
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.5.2' | ||
} | ||
|
||
jar { | ||
archiveName = "beacon-core-${version}.jar" | ||
} |
22 changes: 22 additions & 0 deletions
22
beacon/core/src/main/java/edu/internet2/tap/beacon/Beacon.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package edu.internet2.tap.beacon; | ||
|
||
/** | ||
* Exposes expected names of environment variables holding beacon config data. | ||
*/ | ||
public final class Beacon { | ||
|
||
private Beacon() { | ||
} | ||
|
||
public static final String LOG_HOST = "LOGHOST"; | ||
|
||
public static final String LOG_PORT = "LOGPORT"; | ||
|
||
public static final String IMAGE = "IMAGE"; | ||
|
||
public static final String VERSION = "VERSION"; | ||
|
||
public static final String TIERVERSION = "TIERVERSION"; | ||
|
||
public static final String MAINTAINER = "MAINTAINER"; | ||
} |
10 changes: 10 additions & 0 deletions
10
beacon/core/src/main/java/edu/internet2/tap/beacon/BeaconPublisher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package edu.internet2.tap.beacon; | ||
|
||
/** | ||
* Simple SPI allowing implementations to publish to beacon service utilizing Runnable API | ||
* so that publishing code could run in separate threads of execution. | ||
* | ||
* @author Dmitriy Kopylenko | ||
*/ | ||
public interface BeaconPublisher extends Runnable { | ||
} |
90 changes: 90 additions & 0 deletions
90
beacon/core/src/main/java/edu/internet2/tap/beacon/DefaultBeaconPublisher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package edu.internet2.tap.beacon; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.net.HttpURLConnection; | ||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
import static edu.internet2.tap.beacon.Beacon.IMAGE; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_HOST; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_PORT; | ||
import static edu.internet2.tap.beacon.Beacon.MAINTAINER; | ||
import static edu.internet2.tap.beacon.Beacon.TIERVERSION; | ||
import static edu.internet2.tap.beacon.Beacon.VERSION; | ||
|
||
/** | ||
* Default implementation that knows the details about payload structure with its data and beacon endpoint details | ||
* gathered by upstream components and passed to this implementation at object construction site. | ||
* | ||
* @author Dmitriy Kopylenko | ||
*/ | ||
public class DefaultBeaconPublisher implements BeaconPublisher { | ||
|
||
private final URL endpointUrl; | ||
|
||
private final String jsonPayload; | ||
|
||
public DefaultBeaconPublisher(Map<String, String> beaconDetails) { | ||
//Do data validation checks here. If any of the necessary beacon data not available here, throw a Runtime exception | ||
if (beaconDetails == null) { | ||
throw new IllegalArgumentException("beaconDetails Map must not be null"); | ||
} | ||
if (beaconDetails.get(LOG_HOST) == null | ||
|| beaconDetails.get(LOG_PORT) == null | ||
|| beaconDetails.get(IMAGE) == null | ||
|| beaconDetails.get(VERSION) == null | ||
|| beaconDetails.get(TIERVERSION) == null | ||
|| beaconDetails.get(MAINTAINER) == null) { | ||
throw new IllegalArgumentException("Not all the necessary beacon data is available to be able to publish to beacon"); | ||
} | ||
try { | ||
this.endpointUrl = new URL(String.format("http://%s:%s", beaconDetails.get(LOG_HOST), beaconDetails.get(LOG_PORT))); | ||
} catch (MalformedURLException ex) { | ||
throw new IllegalArgumentException(ex.getMessage()); | ||
} | ||
|
||
Map<String, String> dataToPublish = new HashMap<>(); | ||
dataToPublish.put("msgType", "TIERBEACON"); | ||
dataToPublish.put("msgName", "TIER"); | ||
dataToPublish.put("msgVersion", "1.0"); | ||
dataToPublish.put("tbProduct", beaconDetails.get(IMAGE)); | ||
dataToPublish.put("tbProductVersion", beaconDetails.get(VERSION)); | ||
dataToPublish.put("tbTIERRelease", beaconDetails.get(TIERVERSION)); | ||
dataToPublish.put("tbMaintainer", beaconDetails.get(MAINTAINER)); | ||
|
||
//Create JSON payload without any 3-rd party library | ||
this.jsonPayload = "{" + dataToPublish.entrySet().stream() | ||
.map(e -> "\"" + e.getKey() + "\"" + ":\"" + e.getValue() + "\"") | ||
.collect(Collectors.joining(", ")) + "}"; | ||
} | ||
|
||
@Override | ||
public void run() { | ||
try { | ||
HttpURLConnection con = (HttpURLConnection) this.endpointUrl.openConnection(); | ||
con.setRequestMethod("POST"); | ||
con.setRequestProperty("Content-Type", "application/json; utf-8"); | ||
con.setRequestProperty("Accept", "application/json"); | ||
con.setDoOutput(true); | ||
try(OutputStream os = con.getOutputStream()){ | ||
byte[] input = jsonPayload.getBytes("utf-8"); | ||
os.write(input, 0, input.length); | ||
} | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
//getters used in unit tests and calling components for debugging purposes | ||
public String getEndpointUri() { | ||
return endpointUrl.toString(); | ||
} | ||
|
||
public String getJsonPayload() { | ||
return jsonPayload; | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
beacon/core/src/test/groovy/edu/internet2/tap/beacon/DefaultBeaconPublisherTests.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package edu.internet2.tap.beacon | ||
|
||
import spock.lang.Specification | ||
import sun.security.x509.OtherName | ||
|
||
class DefaultBeaconPublisherTests extends Specification { | ||
|
||
def "DefaultBeaconPublisher invariants are enforced during object creation - null Map is passed"() { | ||
when: | ||
new DefaultBeaconPublisher(null) | ||
|
||
then: | ||
thrown IllegalArgumentException | ||
|
||
} | ||
|
||
def "DefaultBeaconPublisher invariants are enforced during object creation - empty Map is passed"() { | ||
when: | ||
new DefaultBeaconPublisher([:]) | ||
|
||
then: | ||
thrown IllegalArgumentException | ||
} | ||
|
||
def "DefaultBeaconPublisher invariants are enforced during object creation - valid Beacon data Map is passed"() { | ||
when: | ||
def expectedJsonPaylaod = """{"msgType":"TIERBEACON", "tbMaintainer":"unittest_maintainer", "msgName":"TIER", "tbProduct":"image", "msgVersion":"1.0", "tbProductVersion":"v1", "tbTIERRelease":"tv1"}""" | ||
|
||
def configuredBeaconData = [LOGHOST : 'collector.testbed.tier.internet2.edu', | ||
LOGPORT : '5001', | ||
IMAGE : 'image', | ||
VERSION : 'v1', | ||
TIERVERSION: 'tv1', | ||
MAINTAINER : 'unittest_maintainer'] | ||
def p = new DefaultBeaconPublisher(configuredBeaconData) | ||
println p.jsonPayload | ||
|
||
then: | ||
noExceptionThrown() | ||
p.endpointUri == 'http://collector.testbed.tier.internet2.edu:5001' | ||
p.jsonPayload == expectedJsonPaylaod | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import org.springframework.boot.gradle.plugin.SpringBootPlugin | ||
|
||
plugins { | ||
id 'org.springframework.boot' version '2.0.0.RELEASE' apply false | ||
id 'io.spring.dependency-management' version '1.0.6.RELEASE' | ||
} | ||
|
||
apply plugin: 'java' | ||
sourceCompatibility = 1.8 | ||
targetCompatibility = 1.8 | ||
|
||
repositories { | ||
jcenter() | ||
} | ||
|
||
jar { | ||
archiveName = "beacon-spring-${version}.jar" | ||
} | ||
|
||
dependencyManagement { | ||
imports { | ||
mavenBom SpringBootPlugin.BOM_COORDINATES | ||
} | ||
} | ||
|
||
dependencies { | ||
compile project(':beacon:core') | ||
compile "org.springframework.boot:spring-boot-starter" | ||
} |
61 changes: 61 additions & 0 deletions
61
...g/src/main/java/edu/internet2/tap/beacon/configuration/BeaconPublishingConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package edu.internet2.tap.beacon.configuration; | ||
|
||
import edu.internet2.tap.beacon.DefaultBeaconPublisher; | ||
import edu.internet2.tap.beacon.configuration.condition.ConditionalOnBeaconEnvironmentVariablesPresent; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.env.Environment; | ||
import org.springframework.scheduling.annotation.Async; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import static edu.internet2.tap.beacon.Beacon.IMAGE; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_HOST; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_PORT; | ||
import static edu.internet2.tap.beacon.Beacon.MAINTAINER; | ||
import static edu.internet2.tap.beacon.Beacon.TIERVERSION; | ||
import static edu.internet2.tap.beacon.Beacon.VERSION; | ||
|
||
@Configuration | ||
@ConditionalOnProperty(name = "shibui.beacon-enabled", havingValue = "true") | ||
public class BeaconPublishingConfiguration { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(BeaconPublishingConfiguration.class); | ||
|
||
@Bean | ||
@ConditionalOnBeaconEnvironmentVariablesPresent | ||
public BeaconPublishingTask beaconPublisher(Environment env) { | ||
logger.debug("Creating BeaconPublishingTask..."); | ||
Map<String, String> beaconData = new HashMap<>(); | ||
beaconData.put(LOG_HOST, env.getProperty(LOG_HOST)); | ||
beaconData.put(LOG_PORT, env.getProperty(LOG_PORT)); | ||
beaconData.put(IMAGE, env.getProperty(IMAGE)); | ||
beaconData.put(VERSION, env.getProperty(VERSION)); | ||
beaconData.put(TIERVERSION, env.getProperty(TIERVERSION)); | ||
beaconData.put(MAINTAINER, env.getProperty(MAINTAINER)); | ||
return new BeaconPublishingTask(new DefaultBeaconPublisher(beaconData)); | ||
} | ||
|
||
public static class BeaconPublishingTask { | ||
private DefaultBeaconPublisher beaconPublisher; | ||
|
||
public BeaconPublishingTask(DefaultBeaconPublisher beaconPublisher) { | ||
this.beaconPublisher = beaconPublisher; | ||
} | ||
|
||
//Cron is based on the spec defined here: https://spaces.at.internet2.edu/display/TWGH/TIER+Instrumentation+-+The+TIER+Beacon | ||
@Scheduled(cron = "0 ${random.int[0,59]} ${random.int[0,3]} ? * *}") | ||
@Async | ||
void publish() { | ||
logger.debug("Publishing payload: {} to beacon endpoint: {}", | ||
beaconPublisher.getJsonPayload(), | ||
beaconPublisher.getEndpointUri()); | ||
beaconPublisher.run(); | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...edu/internet2/tap/beacon/configuration/condition/BeaconEnvironmentVariablesCondition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package edu.internet2.tap.beacon.configuration.condition; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; | ||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; | ||
import org.springframework.context.annotation.Condition; | ||
import org.springframework.context.annotation.ConditionContext; | ||
import org.springframework.core.env.Environment; | ||
import org.springframework.core.type.AnnotatedTypeMetadata; | ||
|
||
import static edu.internet2.tap.beacon.Beacon.IMAGE; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_HOST; | ||
import static edu.internet2.tap.beacon.Beacon.LOG_PORT; | ||
import static edu.internet2.tap.beacon.Beacon.MAINTAINER; | ||
import static edu.internet2.tap.beacon.Beacon.TIERVERSION; | ||
import static edu.internet2.tap.beacon.Beacon.VERSION; | ||
|
||
/** | ||
* {@link Condition} that checks for required beacon environment variables. | ||
* | ||
* @author Dmitriy Kopylenko | ||
* @see ConditionalOnBeaconEnvironmentVariablesPresent | ||
*/ | ||
public class BeaconEnvironmentVariablesCondition extends SpringBootCondition { | ||
|
||
private static final String MATCHED_MSG = "Beacon properties are present. Beacon activation condition is matched."; | ||
|
||
private static final String NOT_MATCHED_MSG = "Beacon properties are not present. Beacon activation condition is not matched."; | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(BeaconEnvironmentVariablesCondition.class); | ||
|
||
@Override | ||
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { | ||
Environment env = context.getEnvironment(); | ||
if (env.containsProperty(LOG_HOST) | ||
&& env.containsProperty(LOG_PORT) | ||
&& env.containsProperty(IMAGE) | ||
&& env.containsProperty(VERSION) | ||
&& env.containsProperty(TIERVERSION) | ||
&& env.containsProperty(MAINTAINER)) { | ||
logger.debug(MATCHED_MSG); | ||
return ConditionOutcome.match(MATCHED_MSG); | ||
} | ||
logger.debug(NOT_MATCHED_MSG); | ||
return ConditionOutcome.noMatch(NOT_MATCHED_MSG); | ||
} | ||
} |
Oops, something went wrong.