Skip to content
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(registration): registration checks and re-registration flow #86

Merged
merged 6 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions src/main/java/io/cryostat/agent/Agent.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,10 @@ public static void main(String[] args) {
evt -> {
switch (evt.state) {
case REGISTERED:
harvester.start();
break;
case UNREGISTERED:
harvester.stop();
break;
case REFRESHED:
break;
case PUBLISHED:
log.info("Registration state: {}", evt.state);
break;
default:
log.error("Unknown registration state: {}", evt.state);
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/cryostat/agent/ConfigModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ public abstract class ConfigModule {
"cryostat.agent.registration.prefer-jmx";
public static final String CRYOSTAT_AGENT_REGISTRATION_RETRY_MS =
"cryostat.agent.registration.retry-ms";
public static final String CRYOSTAT_AGENT_REGISTRATION_CHECK_MS =
"cryostat.agent.registration.check-ms";
public static final String CRYOSTAT_AGENT_EXIT_SIGNALS = "cryostat.agent.exit.signals";
public static final String CRYOSTAT_AGENT_EXIT_DEREGISTRATION_TIMEOUT_MS =
"cryostat.agent.exit.deregistration.timeout-ms";
Expand Down Expand Up @@ -224,6 +226,13 @@ public static int provideCryostatAgentRegistrationRetryMs(SmallRyeConfig config)
return config.getValue(CRYOSTAT_AGENT_REGISTRATION_RETRY_MS, int.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_REGISTRATION_CHECK_MS)
public static int provideCryostatAgentRegistrationCheckMs(SmallRyeConfig config) {
return config.getValue(CRYOSTAT_AGENT_REGISTRATION_CHECK_MS, int.class);
}

@Provides
@Singleton
@Named(CRYOSTAT_AGENT_HARVESTER_PERIOD_MS)
Expand Down
115 changes: 78 additions & 37 deletions src/main/java/io/cryostat/agent/CryostatClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
Expand Down Expand Up @@ -116,12 +117,29 @@ public class CryostatClient {
log.info("Using Cryostat baseuri {}", baseUri);
}

public CompletableFuture<Boolean> checkRegistration(PluginInfo pluginInfo) {
if (!pluginInfo.isInitialized()) {
return CompletableFuture.completedFuture(false);
}
HttpGet req =
new HttpGet(
baseUri.resolve(
DISCOVERY_API_PATH
+ "/"
+ pluginInfo.getId()
+ "?token="
+ pluginInfo.getToken()));
log.info("{}", req);
return supply(req, (res) -> logResponse(req, res)).thenApply(this::isOkStatus);
}

public CompletableFuture<PluginInfo> register(PluginInfo pluginInfo, URI callback) {
try {
RegistrationInfo registrationInfo =
new RegistrationInfo(
pluginInfo.getId(), realm, callback, pluginInfo.getToken());
HttpPost req = new HttpPost(baseUri.resolve(DISCOVERY_API_PATH));
log.info("{}", req);
req.setEntity(
new StringEntity(
mapper.writeValueAsString(registrationInfo),
Expand Down Expand Up @@ -153,46 +171,61 @@ public CompletableFuture<PluginInfo> register(PluginInfo pluginInfo, URI callbac
}
}

public CompletableFuture<Integer> submitCredentials(Credentials credentials) {
synchronized (credentials) {
HttpPost req = new HttpPost(baseUri.resolve(CREDENTIALS_API_PATH));
MultipartEntityBuilder entityBuilder =
MultipartEntityBuilder.create()
.addPart(
FormBodyPartBuilder.create(
"username",
new StringBody(
credentials.user(),
ContentType.TEXT_PLAIN))
.build())
.addPart(
FormBodyPartBuilder.create(
"password",
new InputStreamBody(
new ByteArrayInputStream(
credentials.pass()),
ContentType.TEXT_PLAIN))
.build())
.addPart(
FormBodyPartBuilder.create(
"matchExpression",
new StringBody(
String.format(
"target.jvmId == \"%s\"",
this.jvmId),
ContentType.TEXT_PLAIN))
.build());
req.setEntity(entityBuilder.build());
return supply(req, (res) -> logResponse(req, res))
.thenApply(res -> assertOkStatus(req, res))
.thenApply(res -> res.getFirstHeader(HttpHeaders.LOCATION).getValue())
.thenApply(res -> res.substring(res.lastIndexOf('/') + 1, res.length()))
.thenApply(Integer::valueOf);
public CompletableFuture<Integer> submitCredentialsIfRequired(
int prevId, Credentials credentials) {
if (prevId < 0) {
return submitCredentials(credentials);
}
HttpGet req = new HttpGet(baseUri.resolve(CREDENTIALS_API_PATH + "/" + prevId));
log.info("{}", req);
return supply(req, (res) -> logResponse(req, res))
.thenApply(this::isOkStatus)
.thenCompose(
exists -> {
if (exists) {
return CompletableFuture.completedFuture(prevId);
}
return submitCredentials(credentials);
});
}

private CompletableFuture<Integer> submitCredentials(Credentials credentials) {
HttpPost req = new HttpPost(baseUri.resolve(CREDENTIALS_API_PATH));
MultipartEntityBuilder entityBuilder =
MultipartEntityBuilder.create()
.addPart(
FormBodyPartBuilder.create(
"username",
new StringBody(
credentials.user(), ContentType.TEXT_PLAIN))
.build())
.addPart(
FormBodyPartBuilder.create(
"password",
new InputStreamBody(
new ByteArrayInputStream(
credentials.pass()),
ContentType.TEXT_PLAIN))
.build())
.addPart(
FormBodyPartBuilder.create(
"matchExpression",
new StringBody(
selfMatchExpression(),
ContentType.TEXT_PLAIN))
.build());
log.info("{}", req);
req.setEntity(entityBuilder.build());
return supply(req, (res) -> logResponse(req, res))
.thenApply(res -> assertOkStatus(req, res))
.thenApply(res -> res.getFirstHeader(HttpHeaders.LOCATION).getValue())
.thenApply(res -> res.substring(res.lastIndexOf('/') + 1, res.length()))
.thenApply(Integer::valueOf);
}

public CompletableFuture<Void> deleteCredentials(int id) {
HttpDelete req = new HttpDelete(baseUri.resolve(CREDENTIALS_API_PATH + "/" + id));
log.info("{}", req);
return supply(req, (res) -> logResponse(req, res))
.thenApply(res -> assertOkStatus(req, res))
.thenApply(res -> null);
Expand Down Expand Up @@ -320,10 +353,18 @@ private CountingInputStream getRecordingInputStream(Path filePath) throws IOExce
return new CountingInputStream(new BufferedInputStream(Files.newInputStream(filePath)));
}

private String selfMatchExpression() {
return String.format("target.jvmId == \"%s\"", this.jvmId);
}

private boolean isOkStatus(HttpResponse res) {
int sc = res.getStatusLine().getStatusCode();
return 200 <= sc && sc < 300;
}

private HttpResponse assertOkStatus(HttpRequestBase req, HttpResponse res) {
int sc = res.getStatusLine().getStatusCode();
boolean isOk = 200 <= sc && sc < 300;
if (!isOk) {
if (!isOkStatus(res)) {
URI uri = req.getURI();
log.error("Non-OK response ({}) on HTTP API {}", sc, uri);
try {
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/io/cryostat/agent/Harvester.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,32 @@ class Harvester implements FlightRecorderListener {
String template,
int maxFiles,
RecordingSettings exitSettings,
CryostatClient client) {
CryostatClient client,
Registration registration) {
this.executor = executor;
this.period = period;
this.template = template;
this.maxFiles = maxFiles;
this.exitSettings = exitSettings;
this.client = client;

registration.addRegistrationListener(
evt -> {
switch (evt.state) {
case REGISTERED:
break;
case UNREGISTERED:
executor.submit(this::stop);
break;
case REFRESHED:
break;
case PUBLISHED:
executor.submit(this::start);
break;
default:
break;
}
});
}

public void start() {
Expand Down
17 changes: 11 additions & 6 deletions src/main/java/io/cryostat/agent/MainModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ public static WebServer provideWebServer(
ScheduledExecutorService executor,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_HOST) String host,
@Named(ConfigModule.CRYOSTAT_AGENT_WEBSERVER_PORT) int port,
Lazy<Registration> registration) {
return new WebServer(remoteContexts, cryostat, executor, host, port, registration);
Lazy<Registration> registration,
@Named(ConfigModule.CRYOSTAT_AGENT_REGISTRATION_RETRY_MS) int registrationRetryMs) {
return new WebServer(
remoteContexts, cryostat, executor, host, port, registration, registrationRetryMs);
}

@Provides
Expand Down Expand Up @@ -212,7 +214,8 @@ public static Registration provideRegistration(
@Named(ConfigModule.CRYOSTAT_AGENT_HOSTNAME) String hostname,
@Named(ConfigModule.CRYOSTAT_AGENT_REGISTRATION_PREFER_JMX) boolean preferJmx,
@Named(ConfigModule.CRYOSTAT_AGENT_APP_JMX_PORT) int jmxPort,
@Named(ConfigModule.CRYOSTAT_AGENT_REGISTRATION_RETRY_MS) int registrationRetryMs) {
@Named(ConfigModule.CRYOSTAT_AGENT_REGISTRATION_RETRY_MS) int registrationRetryMs,
@Named(ConfigModule.CRYOSTAT_AGENT_REGISTRATION_CHECK_MS) int registrationCheckMs) {
return new Registration(
executor,
cryostat,
Expand All @@ -224,7 +227,8 @@ public static Registration provideRegistration(
hostname,
preferJmx,
jmxPort,
registrationRetryMs);
registrationRetryMs,
registrationCheckMs);
}

@Provides
Expand All @@ -236,11 +240,12 @@ public static Harvester provideHarvester(
@Named(ConfigModule.CRYOSTAT_AGENT_HARVESTER_MAX_FILES) int maxFiles,
@Named(ConfigModule.CRYOSTAT_AGENT_HARVESTER_EXIT_MAX_AGE_MS) long maxAge,
@Named(ConfigModule.CRYOSTAT_AGENT_HARVESTER_EXIT_MAX_SIZE_B) long maxSize,
CryostatClient client) {
CryostatClient client,
Registration registration) {
RecordingSettings settings = new RecordingSettings();
settings.maxAge = maxAge;
settings.maxSize = maxSize;
return new Harvester(executor, period, template, maxFiles, settings, client);
return new Harvester(executor, period, template, maxFiles, settings, client, registration);
}

@Provides
Expand Down
Loading