-
Notifications
You must be signed in to change notification settings - Fork 210
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
Enable auto ssl keystore reload #156
Conversation
It looks like @yanglei99 hasn't signed our Contributor License Agreement, yet.
You can read and sign our full Contributor License Agreement here. Once you've signed reply with Appreciation of efforts, clabot |
log.info("WatchKey not recognized"); | ||
continue; | ||
} | ||
log.info("Watch Key notified"); |
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.
may want many of these .info
to be .debug
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.
Changed most of the info to debug. But want to keep this notified...
break; | ||
} | ||
} catch (java.io.IOException e) { | ||
log.info("Hit error process the change event", e); |
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.
and this should probably be warn instead of info.
try { | ||
callback.run(); | ||
} catch (Exception e) { | ||
log.error("Error while reloading cert", e); |
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.
feels weird for this to specifically mention cert reloading, but the class is written to be generic
public static final String SSL_KEYSTORE_RELOAD_CONFIG = "ssl.keystore.reload"; | ||
protected static final String SSL_KEYSTORE_RELOAD_DOC = | ||
"Enable auto reload of ssl keystore"; | ||
protected static final boolean SSL_KEYSTORE_RELOAD_DEFAULT = false; |
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.
this seems like a great addition. why do you want this default false?
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.
Backward compatible? As current behavior is not auto-reload... I do not know if there are cases (like on prem), we do not want to auto-reload... I can change it to true if we do not see potential issues
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.
aren't your changes backwards compatible?
i mean, i guess not if people are relying on the ability to change the keystore file and have nothing happen. but that seems like a crazy thing to do in the first place.
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.
revised, thanks!
log.info("Watch Key notified"); | ||
for (WatchEvent<?> event : key.pollEvents()) { | ||
WatchEvent.Kind<?> kind = event.kind(); | ||
if (kind != StandardWatchEventKinds.ENTRY_MODIFY) { |
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.
would this miss a delete followed by a create?
since we already know this.file
, is there anything to be gained by doing all these checks anyhow? can we not just jump straight to callback.run()
and lfg?
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.
As we only listen to MODIFY, the only thing I need to check is OVERFLOW – Indicates that events might have been lost or discarded. You do not have to register for the OVERFLOW event to receive it.
. I can make it specific.
} | ||
// reset key | ||
if (!key.reset()) { | ||
break; |
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.
this seems to be killing the watcher, so probably a good place for a .info("Ending my watch")
too.
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.
Changed.
import java.util.concurrent.Future; | ||
|
||
// reference https://gist.github.com/danielflower/f54c2fe42d32356301c68860a4ab21ed | ||
public class FileWatcher implements Runnable { |
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.
what do we use in ce-kafka to auto-reload ssl certs, can we reuse some of the code there?
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.
I checked with @aashishkohli , she mentioned Kafka is using library from open source kafka repo... I will let her add more input.
break; | ||
} | ||
} | ||
} catch (InterruptedException e) { |
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.
what if any other kind of exception happens, wouldn't that kill the watcher thread and prevent future reloads?
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.
We caught other exceptions in the loop so that we can carry on with the watch. InterruptedException is the one we do want to catch for thread interruption on watchService.take()
so we can stop the work
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.
we should still have a catch for any unexpected runtime exceptions, otherwise the thread will stop
} | ||
|
||
/** | ||
* Starts watching a file and the given path and calls the callback when it is changed. |
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.
what do we mean by "file and the given path", do we mean a file or a directory?
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.
revised the comment
FileWatcher.onFileChange(keystorePath, () -> | ||
sslContextFactory.reload(scf -> log.info("Reloaded SSL cert"))); | ||
log.info("Enabled SSL cert auto reload: " + keystorePath); | ||
} catch (java.io.IOException e) { |
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.
do we know when this might fail?
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.
The IOException can be thrown for both creating a watch service and register
[clabot:check] |
@confluentinc It looks like @yanglei99 just signed our Contributor License Agreement. 👍 Always at your service, clabot |
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.
lfg if xavier gives it the old 👍
continue; | ||
} | ||
WatchEvent<Path> ev = (WatchEvent<Path>)event; | ||
Path changed = this.file.getParent().resolve(ev.context()); |
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.
nit, we don't seem to be consistent about file
vs. this.file
. Typically the distinction is only needed if there is ambiguity between a local variable and a member variable
public static void onFileChange(Path file, Callback callback) throws IOException { | ||
log.info("Configure watch file change: " + file); | ||
FileWatcher fileWatcher = new FileWatcher(file, callback); | ||
ExecutorService executor = Executors.newSingleThreadExecutor(); |
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.
let's make sure this is a daemon thread, so it doesn't prevent the service from shutting down. Then you can also remove the shutdownhook
FileWatcher fileWatcher = new FileWatcher(file, callback); | ||
ExecutorService executor = Executors.newSingleThreadExecutor(); | ||
Future<?> future = executor.submit(fileWatcher); | ||
Runtime.getRuntime().addShutdownHook(new Thread(executor::shutdownNow)); |
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.
shutdown hook is not necessary if you make this a daemon thread.
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.
FileWatcher is a Runnable... it is using newSingleThreadExecutor to run it... which uses a worker thread... https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html#newSingleThreadExecutor()
What benefit we get out of a deamon thread?
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.
two things: let's make sure the background thread is a daemon thread, and make sure we catch unchecked exceptions in the polling loop. We can merge once that's addressed.
https://confluentinc.atlassian.net/browse/MCM-462
Here is the one pager: https://confluentinc.atlassian.net/wiki/spaces/MCM/pages/938970260/One+Pager+Auto+Reload+SSL+Certificate+for+Http+Endpoints#OnePager:AutoReloadSSLCertificateforHttpEndpoints-CPComponentsusingrest-util
Here is the related logic in kafka ( using WatchService)
https://github.com/confluentinc/ce-kafka/blob/cc608b2d0856e9eee0b0f0faef280a3572f8e20d/ce-broker-plugins/src/main/java/io/confluent/kafka/multitenant/PhysicalClusterMetadata.java#L508
Here is the end-end test for kafka-api:
https://github.com/confluentinc/cc-spec-kafka/pull/54