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

Enable auto ssl keystore reload #156

Merged
merged 17 commits into from
Nov 15, 2019
Merged

Conversation

@ghost
Copy link

ghost commented Nov 5, 2019

It looks like @yanglei99 hasn't signed our Contributor License Agreement, yet.

The purpose of a CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen licence.
Wikipedia

You can read and sign our full Contributor License Agreement here.

Once you've signed reply with [clabot:check] to prove it.

Appreciation of efforts,

clabot

log.info("WatchKey not recognized");
continue;
}
log.info("Watch Key notified");
Copy link
Contributor

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

Copy link
Member Author

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);
Copy link
Contributor

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);
Copy link
Contributor

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;
Copy link
Contributor

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?

Copy link
Member Author

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

Copy link
Contributor

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.

Copy link
Member Author

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) {
Copy link
Contributor

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?

Copy link
Member Author

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;
Copy link
Contributor

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.

Copy link
Member Author

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 {
Copy link
Member

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?

Copy link
Member Author

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) {
Copy link
Member

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?

Copy link
Member Author

@yanglei99 yanglei99 Nov 6, 2019

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

Copy link
Member

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.
Copy link
Member

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?

Copy link
Member Author

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) {
Copy link
Member

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?

Copy link
Member Author

@yanglei99 yanglei99 Nov 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yanglei99
Copy link
Member Author

[clabot:check]

@ghost
Copy link

ghost commented Nov 6, 2019

@confluentinc It looks like @yanglei99 just signed our Contributor License Agreement. 👍

Always at your service,

clabot

Copy link
Contributor

@norwood norwood left a 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());
Copy link
Member

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();
Copy link
Member

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));
Copy link
Member

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.

Copy link
Member Author

@yanglei99 yanglei99 Nov 14, 2019

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?

Copy link
Member

@xvrl xvrl left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants