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

[FIXED JENKINS-54770] Prevent multiple instances of KubernetesClient #397

Merged
merged 17 commits into from
Nov 23, 2018

Conversation

fbelzunc
Copy link
Contributor

The current implementation tries to avoid the creation of a new KubernetesClient each time a connection is requested.

The implementation avoids the KubernetesClient to be recreated when saving the configuration in the case that any change was performed at Cloud level.

CC @escoem @Vlatombe @carlossg

Copy link

@ydubreuil ydubreuil left a comment

Choose a reason for hiding this comment

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

This looks good to me after comments are addressed

CACHE_TTL = Integer.getInteger("org.csanchez.jenkins.plugins.kubernetes.clients.cacheTtl", 60);
clients = CacheBuilder.newBuilder()
.maximumSize(CACHE_SIZE)
.expireAfterWrite(CACHE_TTL, TimeUnit.MINUTES)

Choose a reason for hiding this comment

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

Could you add a listener on the cache with removalListener() to close all connections hold by a client when the client is evicted from the cache? The listener would basically just call KubernetesDefaultClient.close()

see https://google.github.io/guava/releases/16.0/api/docs/com/google/common/cache/CacheBuilder.html#removalListener(com.google.common.cache.RemovalListener) and https://github.com/fabric8io/kubernetes-client/blob/master/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/BaseClient.java#L76

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was address by the next commit

final Client c = clients.getIfPresent(cloudName);
if (c != null && validity == c.getValidity()) {
return c.getClient();
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

now we are closing connections in the listener, probably we should close also if c!=null && validity != c.getValidity() before creating a new KubernetesClient

Copy link
Contributor

Choose a reason for hiding this comment

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

it would be good to close existing client, because onRemoval could be called later in time

https://google.github.io/guava/releases/23.0/api/docs/com/google/common/cache/RemovalListener.html#onRemoval-com.google.common.cache.RemovalNotification-

Notifies the listener that a removal occurred at some point in the past.

Copy link
Contributor

@carlossg carlossg left a comment

Choose a reason for hiding this comment

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

Added some comments, looks almost ready

Thanks!

.maximumSize(CACHE_SIZE)
.expireAfterWrite(CACHE_TTL, TimeUnit.MINUTES)
.removalListener((RemovalListener<String, Client>) removalNotification -> {
removalNotification.getValue().getClient().close();
Copy link
Contributor

Choose a reason for hiding this comment

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

see https://google.github.io/guava/releases/23.0/api/docs/com/google/common/cache/RemovalNotification.html

A notification of the removal of a single entry. The key and/or value may be null if they were already garbage collected.

so you need to check for null

final Client c = clients.getIfPresent(cloudName);
if (c != null && validity == c.getValidity()) {
return c.getClient();
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

it would be good to close existing client, because onRemoval could be called later in time

https://google.github.io/guava/releases/23.0/api/docs/com/google/common/cache/RemovalListener.html#onRemoval-com.google.common.cache.RemovalNotification-

Notifies the listener that a removal occurred at some point in the past.

@@ -46,6 +46,7 @@
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

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

why deprecate it if you are still using it in a new method?

Copy link
Contributor

Choose a reason for hiding this comment

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

Because we don't want to have a public class creating KubernetesClients from outside the (protected) Provider. In the future, we will at least reduce the visibility of the factoryAdapter.

Copy link
Member

Choose a reason for hiding this comment

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

In this case, you should add @Restricted(NoExternalUse.class) to prevent any actual usage outside the plugin, and also add a short comment to motivate the deprecation to potential users.


LOGGER.log(Level.FINE, "Building connection to Kubernetes {0} URL {1} namespace {2}",
new String[] { getDisplayName(), serverUrl, namespace });
client = new KubernetesFactoryAdapter(serverUrl, namespace, serverCertificate, credentialsId, skipTlsVerify,
connectTimeout, readTimeout, maxRequestsPerHost).createClient();
KubernetesClient client = KubernetesClientProvider.createClient(name, serverUrl, namespace, serverCertificate, credentialsId, skipTlsVerify,
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT I would just use KubernetesClientProvider.createClient(this) instead of passing 8 parameters


static KubernetesClient createClient(final String cloudName, final String serviceAddress, final String namespace, @CheckForNull final String caCertData,
@CheckForNull final String credentials, final boolean skipTlsVerify, final int connectTimeout, final int readTimeout, final int maxRequestsPerHost) throws NoSuchAlgorithmException, UnrecoverableKeyException,
KeyStoreException, IOException, CertificateEncodingException, ExecutionException {
Copy link
Member

Choose a reason for hiding this comment

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

ExecutionException is not used afaics

@@ -46,6 +46,7 @@
/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

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

In this case, you should add @Restricted(NoExternalUse.class) to prevent any actual usage outside the plugin, and also add a short comment to motivate the deprecation to potential users.

@@ -421,12 +421,12 @@ public void setPodRetention(PodRetention podRetention) {
*/
@SuppressFBWarnings({ "IS2_INCONSISTENT_SYNC", "DC_DOUBLECHECK" })
public KubernetesClient connect() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException,
IOException, CertificateEncodingException {
IOException, CertificateEncodingException, ExecutionException {
Copy link
Member

@Vlatombe Vlatombe Nov 23, 2018

Choose a reason for hiding this comment

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

ExecutionException is not required (here, and in other references across the PR)

@carlossg carlossg changed the title [FIXED JENKINS-54770] Manage KubernetesClient [FIXED JENKINS-54770] Prevent multiple instances of KubernetesClient Nov 23, 2018
@@ -643,6 +646,7 @@ public FormValidation doTestConnection(@QueryParameter String name, @QueryParame

// test listing pods
client.pods().list();
client.close();
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

@Vlatombe Vlatombe left a comment

Choose a reason for hiding this comment

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

lgtm

@carlossg carlossg merged commit 656b807 into jenkinsci:master Nov 23, 2018
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.

5 participants