Skip to content

Add support for lockable resources#940

Closed
cdleonard wants to merge 8 commits into
pycontribs:masterfrom
cdleonard:lockable-resources
Closed

Add support for lockable resources#940
cdleonard wants to merge 8 commits into
pycontribs:masterfrom
cdleonard:lockable-resources

Conversation

@cdleonard

Copy link
Copy Markdown
Contributor

This adds a new class for managing lockable resources from python. It includes some helpful context managers so you can do stuff like this:

    with jenkins.lockable_resources.reservation_by_label("aaa") as r:
        print(f"locked resource {r.locked_resource_name}")

The intended use case is hardware reservation systems backed by jenkins. There is no CLI.

I also found a similar implementation as external package: https://gitlab.com/alexandre-perrin1/jenkins-lockable-resources. It seems unmaintained, I was unable to install due to dependency conflicts and such.

This PR contains full systest support so the CI will test the code against a running jenkins instance. Hopefully this makes it easy to maintain.

@cdleonard cdleonard force-pushed the lockable-resources branch 4 times, most recently from a9c0059 to 4a16129 Compare July 24, 2025 12:23
@cdleonard

Copy link
Copy Markdown
Contributor Author

This went through several force-pushes as I added more features and fix linting issues.

Current version is stable

Comment thread jenkinsapi/jenkins.py Outdated
res = opener.open(request)
Requester.AUTH_COOKIE = res.cookie

_lockable_resources: Optional[LockableResources] = None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you help me understand usage here?
Instead of just using a normal property method

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is meant to prevent creating multiple instance of the LockableResources class for each Jenkins instance - because why would you want that? But looking at how other entities are handled it is seems that only for Jobs a single instance is held inside Jenkins.jobs_container.

For consistency it makes sense to switch to a get_lockable_resources() method.

@cdleonard cdleonard Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Switched to a get_lockable_resources() method

return f"Lockable Resources @ {self.baseurl}"

def get_jenkins_obj(self) -> "Jenkins":
return self.jenkins

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why not use just obj.jenkins?

@cdleonard cdleonard Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Most other JenkinsBase subclasses do this. The base JenkinsBase.get_jenkins_obj raises NotImplementedError so implementing this seems to be required.

Make JenkinsBase should just have a jenkins: Jenkins member? But that would be an unrelated refactor.

Comment thread jenkinsapi/lockable_resources.py Outdated

@property
def _requester(self) -> Requester:
return self.jenkins.requester

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Don't think we need this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed


def poll(self, tree=None) -> None:
super().poll(tree)
self._data_dict = None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why set this to none?

@cdleonard cdleonard Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's cache invalidation.

The super().poll() call will update self._data and self._data_dict is generated based on self._data upon request (inside the data_dict getter method).

@cdleonard

Copy link
Copy Markdown
Contributor Author

Rebased addressing review comments and integrating a some other changes:

  • Rewrite RetryConfig so that it is reusable.
  • Turn ResourceSelector into ABC
  • Add custom ResourceReservationTimeoutError class
  • Add LockedResourceReservation.is_active, with tests
  • Remove unused LockedResourceReservation.locked field
  • Improve documentation

The RetryConfig class is very basic, the purpose of this abstraction is to allow more complex implementations - for example based on tenacity. I wanted to avoid adding dependencies.

@cdleonard

Copy link
Copy Markdown
Contributor Author

rebased to fix codacy

Copilot AI review requested due to automatic review settings January 29, 2026 06:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@cdleonard

Copy link
Copy Markdown
Contributor Author

@clintonsteiner I see you merged trunk into this branch, but codacy now fails.

Should I go fix it? It's been a while since I posted the PR but I'm still interested in getting this merged, and also #939. I believe I already addressed all of your comments.

@clintonsteiner

Copy link
Copy Markdown
Collaborator

Merged this in due to a conflict on another branch, but left your commits, and name intact on the work

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.

3 participants