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

Pin the last Jenkins package which uses sysvinit scripts. #119

Merged
merged 4 commits into from
Apr 25, 2024

Conversation

nuclearsandwich
Copy link
Collaborator

Earlier this year the packaged versions of Jenkins migrated to systemd units from sysvinit scripts. The current versions of the Jenkins chef cookbook and this cookbook both expect Jenkins to run using sysvinit and make configuration changes using the associated configuration files which are not used by the systemd service.

As a result, newer versions of the Jenkins will not initialize correctly with this cookbook.

Until the upstream Jenkins cookbook is updated to use systemd or we stop using the upstream cookbook entirely, we can't move forward from this Jenkins version.

It is possible for an already-configured farm to continue working after an upgrade and Jenkins cannot be safely downgraded so this version pin is a breaking change and additional work to protect from an unintended downgrade may be warranted.

@nuclearsandwich
Copy link
Collaborator Author

#120 fixes the problem with the repo CI scripts. I may rebase this PR if that merges first.

Comment on lines +23 to +24
# Last version supporting Java 8
#default['jenkins']['master']['version'] = '2.346.1'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a bit of a carry on from where this change originated (#117) but I'm interested in leaving it in as a signifier for other build farm maintainers. I expect that a PR and release between now and Java 11 support will raise our "high water level" to 2.346.1.

# However Jenkins cannot be safely downgraded and some configurations of
# Jenkins will continue to function once initially configured even after the
# systemd switch.
# To protect users from unintentional downgrades we're going to do something really ugly here.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The rest of this gets rough. I'm very sorry but also a little proud.

# To protect users from unintentional downgrades we're going to do something really ugly here.
package 'jenkins' do
action :lock
only_if { -> do
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The { opens the Ruby block that chef will eval at convergence time and then within it I immediately start a lambda literal with -> do (affectionately known as the "stabby lambda" back in my day) creating a block within a block.

I need to do this in order to use early returns in the code below since using return or break in a block literal will result in a LocalJumpError. I'll call the lambda as soon as I close it passing the result to the outer block.

only_if { -> do
jenkins_info = `dpkg -s jenkins`
if $?.exitstatus != 0
return false
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If Jenkins isn't installed the command above will fail and we can proceed nothing to worry about.


version_line = jenkins_info.lines.select{|line| line =~ /^Version: /}.first
if version_line.nil?
return false
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not actually sure that this code is reachable, I can't think of a situation where the command above would exit successfully but there wouldn't be any version info from it.

I may decide to take this out or turn it into a hard error because it's "wrong"

Copy link
Member

Choose a reason for hiding this comment

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

Eh, you're parsing output from a command line tool. I think it's totally reasonable to have a protection like this.

Not that you should support the scenario, but I could see something like localization resulting in nil here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I could see something like localization resulting in nil here.

Yeah, I think you're right and on consideration if we get here we should make the user stop and figure out how and what to do about it. 9fb0a78

Comment on lines +59 to +63
if version_components[0].to_i > 2 or version_components[1].to_i >= 332
node.run_state[:jenkins_package_version_lock] = version_components.join('.')
Chef::Log.warn("Chef detected this Jenkins version: #{node.run_state[:jenkins_package_version_lock]}")
return true
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Earlier attempts at this change tried to just override the node attribute within this block but due to the compile-then-converge nature of chef the attribute value has already been read by the recipe I'm trying to change before this converge-time code runs so instead I put a sigil value in the run_state and get nasty later.

end
# Jenkins is installed but is older than the maximum and can be upgraded.
return false
end.call }
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is calling the lambda literal and closing the only_if block value argument. I'm not happy this is necessary but it's the cleanest way I could phrase the code block within and that is what mattered to me.

Comment on lines 69 to 80
ruby_block 'prevent jenkins downgrade' do
block do
if node.run_state[:jenkins_package_version_lock]
if node['jenkins']['master']['version'] != node.run_state[:jenkins_package_version_lock]
Chef::Log.fatal("Before this cookbook continues. please set the node['jenkins']['master']['version'] attribute to #{node.run_state[:jenkins_package_version_lock]} or this cookbook will attempt to downgrade your Jenkins version.")
Chef::Log.fatal("See https://github.com/ros-infrastructure/cookbook-ros-buildfarm/issues/121 for more information")
raise
end
end
end
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By the time we know that we want to, we can't affect the attribute value used by the jenkins cookbook. If we did nothing here, the user would actually get a failure within the Jenkins cookbook when they try to downgrade a package marked for holding. But if we let that happen we risk them not knowing why that is happening and potentially releasing the hold.
So we'll check our sigil value and instead recommend they set the jenkins version that they are currently on which will keep them at their current state until we can make the necessary changes in this cookbook (and the upstream Jenkins one) to unblock further updates.

This is still suboptimal because anyone who is provisioning from scratch using an existing config will experience failures unless they revert back to our new default version for the first provisioning.

Copy link
Member

@cottsay cottsay left a comment

Choose a reason for hiding this comment

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

Minor comments, but looks good.

Disclaimer: IANAR (I Am Not A Rubist)


version_line = jenkins_info.lines.select{|line| line =~ /^Version: /}.first
if version_line.nil?
return false
Copy link
Member

Choose a reason for hiding this comment

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

Eh, you're parsing output from a command line tool. I think it's totally reasonable to have a protection like this.

Not that you should support the scenario, but I could see something like localization resulting in nil here.

# Transform "Version: 2.319.1\n" to ["2", "319", "1"]
version_components = version_line.chomp.split(": ")[1].split(".")
# The first systemd versions are 2.335 (weekly) and 2.332.1 (LTS)
if version_components[0].to_i > 2 or version_components[1].to_i >= 332
Copy link
Member

Choose a reason for hiding this comment

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

Could this be cleaned up with the "spaceship operator"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

<=> implements three-way comparison for a class. I don't actually think it helps us unless we parse these version components into an actual class and implement the component level comparisons. Although you could be seeing something tidy that I'm not.

The logic here is complex enough that I was optimizing for plain readability rather than conciseness. I suppose that if we had a Comparable version class here we would be able to write

version_installed = Version(version_line.chomp.split(": ")[1].split("."))
if version >= Version([2,332,1])

recipes/jenkins.rb Outdated Show resolved Hide resolved
nuclearsandwich and others added 4 commits April 25, 2024 10:31
This version of the Jenkins cookbook updates the keys used to
authenticate the jenkins apt repository.
I don't think this code is reachable in normal circumstances, Scott
pointed out that it could be a result of a change in CLI output or
localization differences in output. With that in mind, it makes more
sense to error in these cases since if the package is installed but at an unknowable version we should stop and make the user figure it out.
@nuclearsandwich nuclearsandwich force-pushed the supported-jenkins-version branch from 9fb0a78 to be95d8d Compare April 25, 2024 17:32
@nuclearsandwich nuclearsandwich merged commit eb00797 into latest Apr 25, 2024
4 checks passed
@nuclearsandwich nuclearsandwich deleted the supported-jenkins-version branch April 25, 2024 17:40
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.

2 participants