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

[JENKINS-31801] Add Pipeline throttle(category) step #46

Merged
merged 20 commits into from May 19, 2017

Conversation

abayer
Copy link
Member

@abayer abayer commented Mar 8, 2017

JENKINS-31801

  • POC
  • Getting to finalized implementation
  • Snippet generator support
  • Tests!

Note that yes, this depends on workflow-api - I need that for FlowExecution and friends. And I had to remove the existing guava:18.0 test dependency due to that violently conflicting with StepContext. I haven't actually run tests yet, let alone written new ones, but I wanted to throw this up for initial feedback ASAP.

cc @reviewbybees esp @jglick for Pipeline sanity review and @oleg-nenashev for Throttle sanity review

@@ -44,10 +44,10 @@ THE SOFTWARE.
</licenses>

<properties>
<jenkins.version>1.609.3</jenkins.version>
<jenkins.version>1.642.3</jenkins.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Copy link
Member

Choose a reason for hiding this comment

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

delete

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup yup - at one point, I was experimenting with stuff that did need newer versions of other dependencies and bumped things, but I'll roll this back now.

pom.xml Outdated
<compileSource>1.6</compileSource>
<compileTarget>1.6</compileTarget>
<compileSource>1.7</compileSource>
<compileTarget>1.7</compileTarget>
Copy link
Member

Choose a reason for hiding this comment

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

delete

Copy link
Member

Choose a reason for hiding this comment

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

java.level should be used instead

pom.xml Outdated
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<compileSource>1.6</compileSource>
<compileTarget>1.6</compileTarget>
<compileSource>1.7</compileSource>
Copy link
Member

Choose a reason for hiding this comment

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

delete

Copy link
Member Author

Choose a reason for hiding this comment

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

Brought these back now that we're depending on 1.642.3 again.

Copy link
Member

Choose a reason for hiding this comment

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

Use java.level, Luke

Copy link
Member Author

Choose a reason for hiding this comment

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

Yessir. =)

@@ -229,29 +243,82 @@ public boolean isThrottleMatrixConfigurations() {
assert category != null && !category.equals("");
List<Queue.Task> categoryTasks = new ArrayList<Queue.Task>();
Collection<ThrottleJobProperty> properties;
DescriptorImpl descriptor = Jenkins.getActiveInstance().getDescriptorByType(DescriptorImpl.class);
DescriptorImpl descriptor = Jenkins.getActiveInstance().getDescriptorByType(DescriptorImpl.class);
Copy link
Member

Choose a reason for hiding this comment

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

Please avoid gratuitous whitespace changes. Configure your editor to not make them.

// No run found, so remove the throttle.
descriptor.removeThrottledPipelineForCategory(currentPipeline.getKey(), category, null);
} else if (!(flowNodeRun instanceof FlowExecutionOwner.Executable)) {
// If for some reason we've somehow ended up with a non-pipeline job, remove the throttle.
Copy link
Member

Choose a reason for hiding this comment

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

Why not just index by FlowExecutionOwner to begin with?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is there an easy way to go from some easily serializable identifier for FlowExecutionOwner to an actual instance of FlowExecutionOwner, like with Run.fromExternalizableId?

Copy link
Member

Choose a reason for hiding this comment

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

No, it is just Serializable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah. Still not sure I want to actually save that large a chunk of data when I can just do the round-tripping...

return new ThrottleStepExecution(this, context);
}

}
Copy link
Member

Choose a reason for hiding this comment

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

No descriptor?

Copy link
Member Author

Choose a reason for hiding this comment

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

whoops.

if (parentInfo != null) {
// Make sure we record the node used for the parent throttle step if it exists.
Computer computer = getContext().get(Computer.class);
if (computer != null && computer.getNode() != null) {
Copy link
Member

Choose a reason for hiding this comment

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

Return Node from getRequiredContext and skip both the null check and the getNode conversion.

Copy link
Member Author

Choose a reason for hiding this comment

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

Aaaaah, for some reason I didn't think that was possible. Win!


@Override
public void stop(Throwable cause) throws Exception {
if (body != null)
Copy link
Member

Choose a reason for hiding this comment

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

This method can be left blank—will not be called for a block-scoped step anyway, unless it is doing something between returning from start and the BodyInvoker.start.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okiedokie.


body = getContext().newBodyInvoker()
.withContext(info)
.withCallback(BodyExecutionCallback.wrap(getContext()))
Copy link
Member

Choose a reason for hiding this comment

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

I think you meant to use TailCall and call removeThrottledPipelineForCategory.

Copy link
Member Author

Choose a reason for hiding this comment

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

Aaaah, you're right. Thanks.

ThrottleMatrixProjectOptions.DisplayName=Additional options for Matrix projects

ThrottleJobProperty.DescriptorImpl.NoSuchCategory=Requested category "{0}" does not exist, so cannot throttle.
Copy link
Member

Choose a reason for hiding this comment

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

trailing newline

@abayer
Copy link
Member Author

abayer commented Mar 10, 2017

@jglick Did a fairly drastic rewrite here - I think I got out of the blocking-CPS-VM issues, and am tracking FlowNodes of the start nodes for the step, but I'm not sure if I'm actually on the right path. =)

@jetersen
Copy link
Member

I would be happy to test this! 👍

@jetersen
Copy link
Member

jetersen commented Mar 13, 2017

Would this be a correct usage, see test stage?
I might rewrite it to scripted, to get parallel with failfast!

Sorry for sick indentation but such is life 🍎

pipeline {
    agent none
    options {
        buildDiscarder(logRotator(numToKeepStr:'20'))
    }
    stages {
        stage('Build') {
            agent { label 'ios' }
            steps {
                withCredentials([string(credentialsId: 'keychain', variable: 'PASSWORD')]) {
                    sh "security list-keychains -s ~/Library/Keychains/login.keychain"
                    sh "security unlock-keychain -p ${PASSWORD} ~/Library/Keychains/login.keychain"
                }
                wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm', 'defaultFg': 1, 'defaultBg': 2]) {
                    withCredentials([
                        string(credentialsId: 'fastlaneMatch', variable: 'MATCH_PASSWORD'),
                        string(credentialsId: 'fastlanePassword', variable: 'FASTLANE_PASSWORD')])
                    {
                        sh 'fastlane build'
                    }
                }
            }
        }
        stage('Test') {
            agent none
            steps {
                throttle('ios-simulator') {
                    node('ios') {
                        lock("ios-simulator-${env.NODE_NAME}") { // Do I even need lock?
                            withCredentials([string(credentialsId: 'keychain', variable: 'PASSWORD')]) {
                                sh "security list-keychains -s ~/Library/Keychains/login.keychain"
                                sh "security unlock-keychain -p ${PASSWORD} ~/Library/Keychains/login.keychain"
                            }
                            retry(5) {
                                wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm', 'defaultFg': 1, 'defaultBg': 2]) {
                                    withCredentials([
                                        string(credentialsId: 'fastlaneMatch', variable: 'MATCH_PASSWORD'),
                                        string(credentialsId: 'fastlanePassword', variable: 'FASTLANE_PASSWORD')])
                                    {
                                        sh 'fastlane test'
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Needed to bump to newer dependency versions, most notably to get
PlaceholderTask.getNode(). Still a work in progress, mind you.
@@ -26,7 +26,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.6</version>
<version>2.22</version>
Copy link
Member Author

Choose a reason for hiding this comment

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

2.23 led to errors due to a TestExtension usage in workflow-support's tests, so I bumped back down to 2.22 for now at least.

Copy link
Member

Choose a reason for hiding this comment

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

Harmless, just stack traces. workflow-support itself should be bumped up.

@@ -44,10 +44,10 @@ THE SOFTWARE.
</licenses>

<properties>
<jenkins.version>1.609.3</jenkins.version>
<jenkins.version>1.642.3</jenkins.version>
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 mentioned in the commit message, we needed to bump dependency versions to get the latest durable-task-step for PlaceholderTask.getNode().

// Handle multi-configuration filters
if (!shouldBeThrottled(task, tjp)) {
if (!shouldBeThrottled(task, tjp) && pipelineCategories.isEmpty()) {
Copy link
Member Author

Choose a reason for hiding this comment

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

So re: this and later similar ugliness - I plan to rewrite ThrottleQueueTaskDispatcher pretty much completely before I'm done here, or at least rationalize it a lot better. This was me wedging things together until they worked, with clean up planned for afterwards.

" node('first-agent') {\n" +
" semaphore 'wait-first-job'\n" +
" }\n" +
"}\n", 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.

Soooo - as I mentioned a few lines up, I'm getting script security exceptions on this when the sandbox is enabled and I can't figure out why, so for the moment, disabling script security until someone (probably @jglick!) tells me what I'm doing wrong to cause those errors to show up. =)

" node('" + label + "') {\n" +
" semaphore 'wait-" + jobName + "-job'\n" +
" }\n" +
"}\n", 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.

Soooo - as I mentioned a few lines up, I'm getting script security exceptions on this when the sandbox is enabled and I can't figure out why, so for the moment, disabling script security until someone (probably @jglick!) tells me what I'm doing wrong to cause those errors to show up. =)

@ghost
Copy link

ghost commented Mar 13, 2017

This pull request originates from a CloudBees employee. At CloudBees, we require that all pull requests be reviewed by other CloudBees employees before we seek to have the change accepted. If you want to learn more about our process please see this explanation.

@jglick jglick self-assigned this Mar 13, 2017
@jetersen
Copy link
Member

jetersen commented Mar 14, 2017

This supersedes #34 ?

@jetersen
Copy link
Member

Just checking if agent { label } + throttle with work same as agent none + throttle + node(label)

Are these two pipeline equal since throttle might run before reserving the executor?

pipeline {
    agent none
    stages {
        stage('test') {
            agent { label 'ios' }
            steps {
                throttle('ios-simulator') {
                    sh 'do stuff'
                }
            }
        }
    }
}
pipeline {
    agent none
    stages {
        stage('test') {
            agent none
            steps {
                throttle('ios-simulator') {
                    node('ios') {
                        sh 'do stuff'
                    }
                }
            }
        }
    }
}

@jetersen
Copy link
Member

jetersen commented Mar 17, 2017

Would it be possible to have agent label and throttle work so that we can have post steps that have file path context.

I would perhaps suggest this syntax but I know it requires work on the pipeline model definition instead.
This properly requires a JIRA issue, but just wondering if its at all possible :)

pipeline {
    agent none
    stages {
        stage('test') {
            agent {
                label 'ios'
                throttle 'ios-simulator'
            }
            steps {
                sh 'do stuff'
            }
        }
        post {
            always {
                junit '**/*.junit'
            }
        }
    }
}

This one below does not work:

pipeline {
    agent none
    stages {
        stage('test') {
            agent { label 'ios' }
            steps {
                throttle('ios-simulator') {
                    sh 'do stuff'
                }
            }
        }
        post {
            always {
                junit '**/*.junit'
            }
        }
    }
}

Neither does this:

pipeline {
    agent none
    stages {
        stage('test') {
            agent none
            steps {
                throttle('ios-simulator') {
                    node('ios') {
                        sh 'do stuff'
                    }
                }
            }
        }
        post {
            always {
                junit '**/*.junit'
            }
        }
    }
}

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

Looks good so far, but I do not feel this is a final implementation

pom.xml Outdated
<compileSource>1.6</compileSource>
<compileTarget>1.6</compileTarget>
<compileSource>1.7</compileSource>
<compileTarget>1.7</compileTarget>
Copy link
Member

Choose a reason for hiding this comment

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

java.level should be used instead

pom.xml Outdated
</parent>

<artifactId>throttle-concurrents</artifactId>
<packaging>hpi</packaging>
<name>Jenkins Throttle Concurrent Builds Plug-in</name>
<version>1.9.1-SNAPSHOT</version>
<version>2.0-SNAPSHOT</version>
Copy link
Member

Choose a reason for hiding this comment

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

I would advice going through the beta- or -rc for this change. The potential impact is too high.

Copy link
Member Author

Choose a reason for hiding this comment

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

+1.

pom.xml Outdated
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<compileSource>1.6</compileSource>
<compileTarget>1.6</compileTarget>
<compileSource>1.7</compileSource>
Copy link
Member

Choose a reason for hiding this comment

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

Use java.level, Luke

<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-durable-task-step</artifactId>
<version>2.8</version>
Copy link
Member

Choose a reason for hiding this comment

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

Is there a need to have it as required dependency? It pulls a significant number of Pipeline plugins into (like Pipeline Support & Co)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes - we need ExecutorStepExecution.class for checking against, etc.

Copy link
Member

Choose a reason for hiding this comment

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

You do not use this class from what I see

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, that may have gone away. Fixing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, it's still there - specifically ExecutorStepExecution.PlaceholderTask in ThrottleQueueTaskDispatcher.

this.step = step;
}

public String getCategory() {
Copy link
Member

Choose a reason for hiding this comment

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

Nonnull I'd guess

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup.

}

@Nonnull
public String getCategory() {
Copy link
Member

Choose a reason for hiding this comment

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

Only one category? I would expect an opportunity to define multiple categories like I can do for Job Properties

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'd rather keep it simple and wait to see what the demand is like for more complex.

Copy link

Choose a reason for hiding this comment

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

First of all thanks for this Pullrequest I was waiting for it for quite some time :)
I would really like to be able to throttle multiple categories as this is our usual scenario in our freestyle jobs.


@Override
public void stop(Throwable cause) throws Exception {

Copy link
Member

Choose a reason for hiding this comment

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

Should it also call removeThrottledPipelineForCategory ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Apparently, from Jesse's earlier comments, no - stop doesn't have relevance here.

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define"
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="${%Category}" field="category">
Copy link
Member

Choose a reason for hiding this comment

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

I would expect multiple categories

Copy link
Member Author

Choose a reason for hiding this comment

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

See above.

@@ -25,6 +26,7 @@

import static org.assertj.core.api.Assertions.assertThat;

@Ignore("Depends on a newer version of Guava than can be used with Pipeline")
Copy link
Member

Choose a reason for hiding this comment

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

It is a pretty important test suite. I do not feel that it is really required to have such declarative syntax here, so maybe it worth just rewriting it

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 agree. I'll tackle that either here or in a follow-up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, on second thought, this is basically covered by ThrottleStepTest, isn't it?

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 take that back - did some experimenting and there is something weird once you've got enough Pipeline tasks in queue. Working on it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Aaaah, queue.getPendingItems(). Fixed.

}

@Override
public boolean start() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

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

Maybe makes sense to contribute some information to the build log using the BuildListener from the context

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point.

@abayer
Copy link
Member Author

abayer commented Apr 7, 2017

Check that, think I hit the deadlock in testing. =)

Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

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

Only have a fuzzy idea what this is doing, so pobably not going to be a good reviewer.

@@ -26,7 +26,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.6</version>
<version>2.22</version>
Copy link
Member

Choose a reason for hiding this comment

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

Harmless, just stack traces. workflow-support itself should be bumped up.

if (origTask instanceof PlaceholderTask) {
PlaceholderTask task = (PlaceholderTask)origTask;
try {
FlowNode firstThrottle = firstThrottleStartNode(task.getNode());
Copy link
Member

Choose a reason for hiding this comment

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

This really deserves a proper API so you can get rid of the workflow-durable-task-step dependency. It was never intended as an API plugin.

// Check if this is a *different* throttling node.
StepDescriptor desc = ((StepNode) enclosing).getDescriptor();
if (desc != null && desc.getClass().equals(ThrottleStep.DescriptorImpl.class)) {
return enclosing;
Copy link
Member

Choose a reason for hiding this comment

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

Oof. @svanoort?

import java.util.Set;
import java.util.StringTokenizer;

public class ThrottleStep extends Step implements Serializable {
Copy link
Member

Choose a reason for hiding this comment

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

If you just have the one field, probably better to inline that into the execution state.

@abayer
Copy link
Member Author

abayer commented Apr 20, 2017

@oleg-nenashev So are we good to go here? I defer to you for merging and releasing.

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

Just minor comments so far. Will test it manually this week

<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-durable-task-step</artifactId>
<version>2.8</version>
Copy link
Member

Choose a reason for hiding this comment

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

You do not use this class from what I see


StringTokenizer tokenizer = new StringTokenizer(categories, ",");
while (tokenizer.hasMoreTokens()) {
catList.add(tokenizer.nextToken().trim());
Copy link
Member

Choose a reason for hiding this comment

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

If I understand correctly, it is going to inject empty strings. Which may become a real problem if somebody uses something like cat_a,${myvar},cat_c expression

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure what you mean?

Copy link
Member

Choose a reason for hiding this comment

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

I mean strings like throttle("cat_a,,cat_b"), which may come to this method

Copy link
Member Author

Choose a reason for hiding this comment

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

Aaaaaah. Changing to discard empty.

return Collections.singleton(TaskListener.class);
}

public FormValidation doCheckCategoryName(@QueryParameter String value) {
Copy link
Member

Choose a reason for hiding this comment

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

Restricted ? not sure if RequirePost is mandatory

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question. This doesn't actually expose any meaningful information - all you can do is tell whether there's a category with a given name, not anything about the category, etc. I think it's harmless, but defer to others.


@Override
public String getDisplayName() {
return "Throttle execution of node blocks within this body";
Copy link
Member

Choose a reason for hiding this comment

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

Nice2have: make it localizable

Copy link
Member Author

Choose a reason for hiding this comment

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

Reasonable.

firstThrottle.getId());
}
} catch (IOException | InterruptedException e) {
return new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

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

No logging? 🐜

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

return new ArrayList<>();
}
}
return new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

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

Nice2have: assert : 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.

? We're calling this method for all tasks, so an assert would cause problems.

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

The success path behavior looks good. Though 🐛 from me according to the UX testing

Documentation:

  • The feature is not documented. I am working on Wiki => GitHub migration in a parallel PR to make it possible

Configuration:

  • No validation of the categories field in the snippet generator, no context help about available categories
  • No built-n Documentation for the "Categories" field in the Snipper generator. 🐛 according to the criteria we have IIRC
  • Multiple categories are being passed as string, not as a list/array. It complicates management of categories in the code

Behavior:

  • If the declared category does not exist, it is just ignored. It is not safe if a user plans to throttle a special resource (like my favorite FPGAs) and makes a typo in Pipeline. I would expect the script to fail if there is a non-existent category. Smells like a 🐛 to me
  • Behavior for duplicated definitions is not clear. One may expect throttle('A,A,A') to acquire 3 items from the category pool A to run. I would add an explicit disclaimer that it is not how it is supposed to work
  • "Throttle Concurrent Builds" Job Property appears in the UI, but does nothing for both modes from what I see. It may confuse plugin users, hence 🐛. The JobProperty should be hidden IMHO, it is not a part of MVP to have its support for Pipeline

@abayer
Copy link
Member Author

abayer commented Apr 28, 2017

@oleg-nenashev Thanks for testing it out!

Snippet generator stuff definitely needs to be updated - I'll get that done.

Multiple categories are being passed as string, not as a list/array. It complicates management of categories in the code

So this is an interesting question. I've found surprisingly few examples of steps taking lists, so I went conservative. I'll play around with this to see if I can get a good list or array approach.

If the declared category does not exist, it is just ignored.

I wasn't sure what the best behavior would be in this case. I opted for a "first, do no harm" approach, but am fine with switching to fail.

Behavior for duplicated definitions is not clear.

Huh. I literally never thought about that. So you're thinking logging something when there are duplicate categories?

"Throttle Concurrent Builds" Job Property appears in the UI, but does nothing for both modes from what I see.

This isn't a change introduced by this PR - I think I did that a while back. It would, I think, apply to the full Pipeline execution, but yeah, it's pretty useless. I just worry that someone out there may be using it and would get screwed if we dropped it.

@oleg-nenashev
Copy link
Member

I wasn't sure what the best behavior would be in this case. I opted for a "first, do no harm" approach, but am fine with switching to fail.

Yeah, this is my approach as well. I just expect less harm if the job fails than if it gets executed incorrectly. Better fail than undefined behavior sorry :)

So you're thinking logging something when there are duplicate categories?

Or I can probably just add note to README in #47.

This isn't a change introduced by this PR - I think I did that a while back. It would, I think, apply to the full Pipeline execution, but yeah, it's pretty useless. I just worry that someone out there may be using it and would get screwed if we dropped it.

I will check if it actually works. If yes, likely we need to polish UI a bit to prevent confusion of users. Not sure how exactly

@abayer
Copy link
Member Author

abayer commented May 18, 2017

@oleg-nenashev Ok, I think I've responded to everything but the job property thing (which is a separate matter entirely).

@oleg-nenashev
Copy link
Member

@abayer After some consideration, I still feel bad about the TCB JobProperty UX. Would it be possible to add a disclaimer to its built-in help at least?

@abayer
Copy link
Member Author

abayer commented May 18, 2017

@oleg-nenashev I...guess? I'd almost rather just make it not show up and say "screw it" to anyone who's for some reason somehow using the job property with a Pipeline currently.

@abayer
Copy link
Member Author

abayer commented May 18, 2017

Also, I'm not the one who added that support in the first place, as far as I can tell. =)

@oleg-nenashev
Copy link
Member

@abayer

I'd almost rather just make it not show up and say "screw it" to anyone who's for some reason somehow using the job property with a Pipeline currently.

It is 2.0, so probably it is a valid option. The possible confusion is too high since the behavior is not documented at all. And the plugin explicitly says Pipeline is not supported. Just add incompatibleSince to pom.xml in such case

@Casz @jenkinsci/code-reviewers If you use Global config for TCB, please let us know

@oleg-nenashev
Copy link
Member

The Pipeline support has been added by @dsavenko in #32 . And yeah, both me and @abayer approved it. So I would rather vote for leaving as is and documenting

@abayer
Copy link
Member Author

abayer commented May 18, 2017

@oleg-nenashev Then frankly, I think documenting it is a separate issue, isn't it?

@oleg-nenashev
Copy link
Member

oleg-nenashev commented May 18, 2017

@abayer

Then frankly, I think documenting it is a separate issue, isn't it?

think some basic built-in docs should be included in this PR, because the new functionality leads to confusion with the existing one. A couple of sentences in JobProperty is enough. Regarding the full documentation, I will add it in #47 . Does it sound like a deal?

@abayer
Copy link
Member Author

abayer commented May 18, 2017

Deal.

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

Needs some manual testing of UI, but the code looks good to me. 🐝

@@ -80,6 +63,14 @@ public boolean takesImplicitBlockArgument() {
public FormValidation doCheckCategoryName(@QueryParameter String value) {
return ThrottleJobProperty.fetchDescriptor().doCheckCategoryName(value);
}

public List<ThrottleJobProperty.ThrottleCategory> getCategories() {
return ThrottleJobProperty.fetchDescriptor().getCategories();
Copy link
Member

Choose a reason for hiding this comment

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

Wrap to the unmodifyableList?

@oleg-nenashev oleg-nenashev self-assigned this May 18, 2017
@oleg-nenashev
Copy link
Member

@reviewbybees done
I will test it tomorrow & merge it if everything is fine

@oleg-nenashev
Copy link
Member

All my comments have been addressed. I have created https://issues.jenkins-ci.org/browse/JENKINS-44373 for better diagnosability, but it is no a blocker for the merge. @abayer should I squash it?

@oleg-nenashev oleg-nenashev merged commit 0bbd079 into master May 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants