-
Notifications
You must be signed in to change notification settings - Fork 690
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
[JENKINS-25106] Support minimumNoInstances #343
Conversation
I would like some tips on how to properly test this. Any way to mock calls against ec2 api? |
9cbdfaa
to
4303fef
Compare
Finished the PR now. Should work fine! |
@thoulen sorry to ping you, I just saw you had some recent commits so I guess you're one of the maintainers. Do you think you could have a look at this PR? |
@@ -751,7 +793,11 @@ private static AmazonWebServicesCredentials getCredentials(@Nullable String cred | |||
|
|||
private AmazonEC2 reconnectToEc2() throws IOException { | |||
synchronized(this) { | |||
connection = connect(createCredentialsProvider(), getEc2EndpointUrl()); | |||
if (AmazonEC2Cloud.isTestMode()) { |
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 am not maintainer or owner of this plugin. Just happen to see this and adding my review comments.
I am not a big fan of mixing mock code in original code. Some suggestions here https://8thlight.com/blog/dariusz-pasciak/2014/12/19/dont-mix-test-code-with-production-code.html
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.
Well, no, generally you don't want that, but as you can see in the code this methodology is already used, and frankly I'm not sure Jenkins supports injecting and/or replacing (test) classes in a running Jenkins instance.
That blog post deals mostly with unit testing.
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 clean up this test mode stuff, but I'd be happy seeing it done in a separate PR since it's both pervasive already in this plugin, and fixing it here would make the diff gigantic.
@@ -784,12 +830,22 @@ public AmazonEC2 connect() throws AmazonClientException { | |||
* @return {@link AmazonEC2} client | |||
*/ | |||
public static AmazonEC2 connect(AWSCredentialsProvider credentialsProvider, URL endpoint) { | |||
if (AmazonEC2Cloud.isTestMode()) { |
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.
Same comment like above abt mixing test code and production code
awsCredentialsProvider = credentialsProvider; | ||
AmazonEC2 client = new AmazonEC2Client(credentialsProvider, createClientConfiguration(endpoint.getHost())); | ||
client.setEndpoint(endpoint.toString()); | ||
return client; | ||
} | ||
|
||
public synchronized static AmazonEC2 mockConnect(AWSCredentialsProvider credentialsProvider, URL endpoint, EC2PrivateKey privateKey) { |
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.
- Test code should be either package-private or annotated with
@Restricted(NoExternalUse.class)
. - This does not seem to be a good way to introduce test code here anyways, though restricting its visibility will make it simpler to refactor.
@jvz maybe that refactoring you mentioned is actually necessary before this is really testable. Perhaps some stuff can be made to use DI instead. As I understand it, Jenkins uses Guice. I've never used Guice specifically, but using any DI it should be possible to have a named injection, and perhaps that name can be read from a JVM parameter (or a default). Thus, when running tests we could just always pass a certain parameter that would ensure test (mock) classes would be loaded instead of real implementations. That way, test classes can remain firmly as test code and stay out of main source. Thoughts on that? |
Any class that's annotated with |
I found this: https://stackoverflow.com/questions/7651980/google-guice-and-varying-injections-at-runtime (second answer) Using that logic it might be possible to have a custom provider do something like:
Obviously with proper catches for ClassNotFound and such, but anyway, that way you can easily load a different implementation if running in test mode. |
2b30515
to
92c33a2
Compare
Merged in changes from #379, refactored based on those changes, and squashed commits. |
I.e, this depends on #379 being approved and merged. |
92c33a2
to
8a95d81
Compare
Hi #379 has been merged! We where looking into implementing this feature so this is a +1 from us. |
@jvz Do you think you can have a look at this again now, if you have time? :) @naxhh If you would try building and running it locally I think it would be great. If the maintainers get reports of successful real-world usage I'm sure they will feel safer accepting the PR. Also, real-world is usually better at exposing bugs than unit tests :) |
return instance; | ||
} | ||
|
||
EC2OndemandSlave createOnDemandSlave(String name, String instanceId, String description, String remoteFS, int numExecutors, String labelString, Node.Mode mode, String initScript, String tmpDir, List<? extends NodeProperty<?>> nodeProperties, String remoteAdmin, String jvmopts, boolean stopOnTerminate, String idleTerminationMinutes, String publicDNS, String privateDNS, List<EC2Tag> tags, String cloudName, boolean useDedicatedTenancy, int launchTimeout, AMITypeData amiType, ConnectionStrategy connectionStrategy, int maxTotalUses) |
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 it make more sense to use some sort of config class object here? Having more than a couple method parameters in Java gets really hard to deal with.
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.
Yeah I suppose. I just went with the lowest effort change to just retain the current parameters.
But how about a config object with a builder pattern that throws an exception for missing parameters?
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.
That sounds like a great way to approach it!
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 pushed a change here.
I decided against throwing Exceptions since I don't really know if some of those parameters should in fact be nullable.
+ "9ontJin0nlHPk+AOmV8xt3yYD+wPAJy5MjUco7tS4Ix6bmvxcpZi2ZcHT1GwkiIzgKWE\n" | ||
+ "-----END RSA PRIVATE KEY-----"); | ||
private static EC2PrivateKey getPrivateKey() { | ||
return new EC2PrivateKey(TestConstants.PRIVATE_KEY); |
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.
Not really in scope here, but this key could be replaced with a KeyPairGenerator
instance that creates a new key for each test run.
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.
Ah, I was not aware of the existence of that. :)
Want me to implement it in this PR despite being out of scope?
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.
That's up to you. I've updated a test in jsch-plugin a while ago to do this. There are similar tests in ssh-credentials-plugin that hard code the keys, too. This only came to my attention before due to some sort of static analysis complaining about embedding private keys in the source code.
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've managed to solve this locally for most tests.
However, the tests in EC2PrivateKeyTest specifically are a bit cumbersome because they rely on comparison against a hard-coded key fingerprint.
I could of course calculate the fingerprint of the generated key, but that generation is basically what the test already tests. So I'd more or less be replicating the production code in the test, and then testing whether my test replication functions the same as the production code.
I could of course revert the EC2PrivateKeyTest.java
and retain the hard-coded key there, and just use the generated key for the rest of the tests, but then we'll still have the hard-coded key.
Suggestion?
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.
Sounds like you'd be better off using the hard-coded key in that specific test at least.
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 pushed a change with that implemented now!
I also squashed the commits.
Looks like this is coming along nicely. @fcojfernandez can you or a teammate review? |
By the way, DevOps World/Jenkins World is going on this week, so review might be delayed. |
a737bd8
to
7f2c638
Compare
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.
Looking real good
@@ -170,7 +174,7 @@ | |||
public SlaveTemplate(String ami, String zone, SpotConfiguration spotConfig, String securityGroups, String remoteFS, | |||
InstanceType type, boolean ebsOptimized, String labelString, Node.Mode mode, String description, String initScript, | |||
String tmpDir, String userData, String numExecutors, String remoteAdmin, AMITypeData amiType, String jvmopts, | |||
boolean stopOnTerminate, String subnetId, List<EC2Tag> tags, String idleTerminationMinutes, | |||
boolean stopOnTerminate, String subnetId, List<EC2Tag> tags, String idleTerminationMinutes, String minimumNumberOfInstancesStr, |
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.
Could you fix this bit @erikhakansson
public class MinimumInstanceChecker { | ||
|
||
public static int countCurrentNumberOfSlaves(@Nonnull SlaveTemplate slaveTemplate) { | ||
return (int) Arrays.stream(Jenkins.get().getComputers()).filter(computer -> { |
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: I'd prefer if it was 4 spaces like the rest of the project
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 agree. Just the default settings from my IDE that came through. Fixed!
Relaunching the CI |
Seems like unrelated errors on CI side. But for some reason it doesn't rebuild when I close and open |
I can't figure this out. The build obviously works, but on Windows the CI fails to clean up the workspace after. |
Fixes #JENKINS-25106
Co-Authored-By: Francisco Fernández <31063239+fcojfernandez@users.noreply.github.com>
04b6848
to
e538322
Compare
Ok I'm even more confused now. I rebased against master and added a commit disabling my new tests, just to see what might cause the workdir to be undeletable. Now CI says
|
I created a new PR (#401) with the exact same changes and it builds (although it's currently stalled at a later step, waiting for a new slave). Should I just close this and continue on the other one? |
Yeah, let's continue on the new PR. It seems as though this branch's git history might have caused an issue in CI. |
Yeah seems that way. I'm closing this. |
I had a spelling mistake in the branch name anyway :) |
PR not finished yet. Just publishing to allow commenting on it.