Skip to content

Commit 59afa45

Browse files
committed
Cache network list and availability info
1 parent d8efc2d commit 59afa45

3 files changed

Lines changed: 55 additions & 10 deletions

File tree

plugin/src/main/java/jenkins/plugins/openstack/compute/auth/AbstractOpenstackCredential.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package jenkins.plugins.openstack.compute.auth;
22

33
import com.cloudbees.plugins.credentials.CredentialsScope;
4+
import com.cloudbees.plugins.credentials.common.PasswordCredentials;
45
import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
56
import hudson.ExtensionPoint;
67

@@ -11,5 +12,6 @@ public abstract class AbstractOpenstackCredential extends BaseStandardCredential
1112

1213
public AbstractOpenstackCredential(@CheckForNull CredentialsScope scope, @CheckForNull String id, @CheckForNull String description) {
1314
super(scope, id, description);
15+
assert this instanceof PasswordCredentials;
1416
}
1517
}

plugin/src/main/java/jenkins/plugins/openstack/compute/internal/Openstack.java

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ public class Openstack {
130130
// Store the OS session token so clients can be created from it per all threads using this.
131131
private final ClientProvider clientProvider;
132132

133+
private static final @Nonnull Cache<Openstack, List<? extends Network>> networksCache
134+
= Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build()
135+
;
136+
137+
// The time to cache here is questionable as the information can get outdated based on activity out of our reach.
138+
// Caching this for few seconds will smooth spikes provisioning many VMs at the time, although it might cause some
139+
// of provisioning attempts to fail in case the number fo the VMs is larger than number of free IPs in the pool with most free IPs.
140+
private static final @Nonnull Cache<Openstack, List<? extends NetworkIPAvailability>> networkIpAvailabilityCache
141+
= Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build()
142+
;
143+
133144
private Openstack(@Nonnull String endPointUrl, boolean ignoreSsl, @Nonnull OpenstackCredential auth, @CheckForNull String region) {
134145

135146
final IOSClientBuilder<? extends OSClient<?>, ?> builder = auth.getBuilder(endPointUrl);
@@ -150,7 +161,7 @@ private Openstack(@Nonnull String endPointUrl, boolean ignoreSsl, @Nonnull Opens
150161
debug("Openstack client created for \"{0}\", \"{1}\".", auth.toString(), region);
151162
}
152163

153-
/*exposed for testing*/
164+
@VisibleForTesting
154165
public Openstack(@Nonnull final OSClient<?> client) {
155166
this.clientProvider = new ClientProvider() {
156167
@Override public @Nonnull OSClient<?> get() {
@@ -178,8 +189,11 @@ public Openstack(@Nonnull final OSClient<?> client) {
178189
}
179190

180191
@VisibleForTesting
181-
public @Nonnull List<? extends Network> _listNetworks() {
182-
return clientProvider.get().networking().network().list();
192+
public @Nonnull List<? extends Network> _listNetworks() {
193+
return Objects.requireNonNull(networksCache.get(
194+
this,
195+
(os) -> os.clientProvider.get().networking().network().list()
196+
));
183197
}
184198

185199
/**
@@ -223,7 +237,7 @@ public Openstack(@Nonnull final OSClient<?> client) {
223237

224238
List<String> declaredIds = declaredNetworks.values().stream().map(Network::getId).collect(Collectors.toList());
225239

226-
List<? extends NetworkIPAvailability> networkIPAvailabilities = clientProvider.get().networking().networkIPAvailability().get();
240+
List<? extends NetworkIPAvailability> networkIPAvailabilities = getNetworkIPAvailability();
227241

228242
return networkIPAvailabilities.stream().filter( // Those we care for
229243
n -> declaredIds.contains(n.getNetworkId())
@@ -233,6 +247,13 @@ public Openstack(@Nonnull final OSClient<?> client) {
233247
));
234248
}
235249

250+
public List<? extends NetworkIPAvailability> getNetworkIPAvailability() {
251+
return Objects.requireNonNull(networkIpAvailabilityCache.get(
252+
this,
253+
(os) -> os.clientProvider.get().networking().networkIPAvailability().get()
254+
));
255+
}
256+
236257
/**
237258
* Finds all {@link Image}s.
238259
*
@@ -868,11 +889,6 @@ public static abstract class FactoryEP implements ExtensionPoint {
868889
public static @Nonnull Openstack get(
869890
@Nonnull final String endPointUrl, final boolean ignoreSsl, @Nonnull final OpenstackCredential auth, @CheckForNull final String region
870891
) throws FormValidation {
871-
final String fingerprint = Util.getDigestOf(endPointUrl + '\n'
872-
+ ignoreSsl + '\n'
873-
+ auth.toString() + '\n'
874-
+ (auth instanceof PasswordCredentials ? ((PasswordCredentials) auth).getPassword().getEncryptedValue() + '\n' : "")
875-
+ region);
876892
final FactoryEP ep = ExtensionList.lookup(FactoryEP.class).get(0);
877893
final Function<String, Openstack> cacheMissFunction = (String unused) -> {
878894
try {
@@ -882,6 +898,7 @@ public static abstract class FactoryEP implements ExtensionPoint {
882898
}
883899
};
884900

901+
final String fingerprint = getCloudConnectionFingerprint(endPointUrl, ignoreSsl, auth, region);
885902
try {
886903
// cacheMissFunction is guaranteed to return nonnull
887904
return Objects.requireNonNull(ep.cache.get(fingerprint, cacheMissFunction));
@@ -913,6 +930,23 @@ public static abstract class FactoryEP implements ExtensionPoint {
913930
}
914931
}
915932

933+
/**
934+
* Get a string unique per cloud connection.
935+
*
936+
* This is used as caching identifier.
937+
*/
938+
@Nonnull
939+
private static String getCloudConnectionFingerprint(
940+
@Nonnull String endPointUrl, boolean ignoreSsl, @Nonnull OpenstackCredential auth, @CheckForNull String region
941+
) {
942+
String pwd = auth instanceof PasswordCredentials
943+
? ((PasswordCredentials) auth).getPassword().getEncryptedValue()
944+
: ""
945+
;
946+
String plain = String.join("::", endPointUrl, Boolean.toString(ignoreSsl), auth.toString(), pwd, region);
947+
return Util.getDigestOf(plain);
948+
}
949+
916950
@Extension
917951
public static final class Factory extends FactoryEP {
918952
public @Nonnull Openstack getOpenstack(@Nonnull String endPointUrl, boolean ignoreSsl, @Nonnull OpenstackCredential auth, @CheckForNull String region) throws FormValidation {

plugin/src/test/java/jenkins/plugins/openstack/PluginTestRule.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey;
44
import com.cloudbees.plugins.credentials.CredentialsScope;
55
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
6+
import com.cloudbees.plugins.credentials.common.PasswordCredentials;
67
import com.google.common.collect.ImmutableList;
78
import com.google.common.collect.ImmutableList.Builder;
89

@@ -27,6 +28,7 @@
2728
import hudson.slaves.OfflineCause;
2829
import hudson.util.FormValidation;
2930
import hudson.util.ProcessTree;
31+
import hudson.util.Secret;
3032
import hudson.util.StreamTaskListener;
3133
import jenkins.model.Jenkins;
3234
import jenkins.plugins.openstack.compute.JCloudsCleanupThread;
@@ -150,8 +152,9 @@ public static NodeProperty<Node> mkNodeProperty(int number) {
150152
}
151153
}
152154

153-
public static class DummyOpenstackCredentials extends AbstractOpenstackCredential {
155+
public static class DummyOpenstackCredentials extends AbstractOpenstackCredential implements PasswordCredentials {
154156
private static final long serialVersionUID = -6458476198187349017L;
157+
public static final Secret SECRET = Secret.fromString("");
155158

156159
private DummyOpenstackCredentials() {
157160
super(CredentialsScope.SYSTEM, "my-id", "testCredentials");
@@ -161,6 +164,12 @@ private DummyOpenstackCredentials() {
161164
public @Nonnull IOSClientBuilder<? extends OSClient<?>, ?> getBuilder(String endPointUrl) {
162165
throw new AuthenticationException(getClass().getSimpleName() + " can not be use to create client", -1);
163166
}
167+
168+
@Nonnull
169+
@Override
170+
public Secret getPassword() {
171+
return SECRET;
172+
}
164173
}
165174

166175
public String dummyCredentials() {

0 commit comments

Comments
 (0)