Class: Xolo::Server::Version
- Inherits:
-
Core::BaseClasses::Version
- Object
- Core::BaseClasses::Version
- Xolo::Server::Version
- Includes:
- Comparable, Helpers::JamfPro, Helpers::Log, Helpers::Notification, Helpers::TitleEditor, Mixins::Changelog, Mixins::VersionJamfAccess, Mixins::VersionTedAccess
- Defined in:
- lib/xolo/server/version.rb
Overview
Xolo Version/Patch as used on the Xolo Server
The code in this file mostly deals with the data on the Xolo server itself, and general methods for manipulating the version.
Code for interacting with the Title Editor and Jamf Pro are in the helpers and mixins.
NOTE be sure to only instantiate these using the server's 'instantiate_version' method, or else they might not have all the correct innards
Constant Summary collapse
- VERSIONS_DIRNAME =
On the server, xolo versions are represented by JSON files in the 'versions' directory of the title directory
So a title 'foobar' would have a directory (Xolo::Server::DATA_DIR)/titles/foobar/
In there will be a 'versions' dir containing json files for each version of the title.
'versions'- JAMF_PKG_NOTES_VERS_PH =
'XOLO-VERSION-HERE'- JAMF_PKG_NOTES_TITLE_PH =
'XOLO-TITLE-HERE'- JAMF_PKG_NOTES_PREFIX =
The 'Notes' of a jamf pkg are the Xolo Title Description, with this prepended
<<~ENDNOTES This package is maintained by 'xolo', to install version '#{JAMF_PKG_NOTES_VERS_PH}' of title '#{JAMF_PKG_NOTES_TITLE_PH}'. The description in Xolo is: ENDNOTES
- MAX_PKG_DELETION_THREADS =
10- STUB_PATCH_VERSION =
STUB PATCH
We create a fake 'stub' patch with all ted titles so that we can activate the title before any real version is added and also accept any EA/version_script, either manually or automatically
This version should never be available to any mac, and needs no patch policies or packages.
It should also never be deleted until the title itself is deleted.
'0.0.0x0'- STUB_PATCH_CAPABILITY_CRITERION_NAME =
machines that can install this version
'Operating System Version'- STUB_PATCH_CAPABILITY_CRITERION_OPERATOR =
'less than or equal'- STUB_PATCH_CAPABILITY_CRITERION_VALUE =
'10.0'- STUB_PATCH_COMPONENT_NAME =
machines that have this version installed
'Xolo Stub'- STUB_PATCH_COMPONENT_CRITERION_NAME =
'Application Title'- STUB_PATCH_COMPONENT_CRITERION_OPERATOR =
'is'- STUB_PATCH_COMPONENT_CRITERION_VALUE =
'XoloStub-DoesNotExist.app'
Constants included from Mixins::VersionJamfAccess
Mixins::VersionJamfAccess::JAMF_POLICY_NAME_AUTO_INSTALL_SFX, Mixins::VersionJamfAccess::JAMF_POLICY_NAME_AUTO_REINSTALL_SFX, Mixins::VersionJamfAccess::JAMF_POLICY_NAME_MANUAL_INSTALL_SFX, Mixins::VersionJamfAccess::JAMF_SMART_GROUP_NAME_INSTALLED_SFX, Mixins::VersionJamfAccess::PATCH_POL_GRACE_PERIOD_MINUTES, Mixins::VersionJamfAccess::PATCH_POL_GRACE_PERIOD_MSG, Mixins::VersionJamfAccess::PATCH_POL_GRACE_PERIOD_SUBJ, Mixins::VersionJamfAccess::PATCH_POL_SSVC_DEADLINE_DAYS, Mixins::VersionJamfAccess::PATCH_POL_SSVC_INSTALL_BTN_TXT, Mixins::VersionJamfAccess::PATCH_POL_SSVC_NOTIFICATION_TYPE, Mixins::VersionJamfAccess::PATCH_POL_SSVC_REMINDER_FREQ_HRS
Constants included from Mixins::Changelog
Mixins::Changelog::TITLE_CHANGELOG_FILENAME
Constants included from Helpers::Notification
Helpers::Notification::ALERT_TOOL_EMAIL_PREFIX, Helpers::Notification::DFT_EMAIL_FROM
Constants included from Helpers::JamfPro
Helpers::JamfPro::PATCH_REPORT_JPAPI_PAGE_SIZE, Helpers::JamfPro::PATCH_REPORT_UNKNOWN_VERSION
Instance Attribute Summary collapse
-
#changes_for_update ⇒ Hash
readonly
Also when applying updates, this will hold the changes being made: the differences between tne current attributs and the new_data_for_update We'll figure this out at the start of the update and can use it later to 1) avoid doing things we don't need to 2) log the changes in the change log at the very end.
-
#current_action ⇒ Symbol
The current action being taken on this title one of :creating, :updating, :deleting.
-
#jamf_auto_install_policy_name ⇒ Object
readonly
Jamf auto-install policies are named this.
-
#jamf_auto_reinstall_policy_name ⇒ Object
readonly
Jamf auto re-install policies are named this.
-
#jamf_installed_group_name ⇒ String
readonly
For each version there will be a smart group containing all macs that have that version of the title installed.
-
#jamf_manual_install_policy_name ⇒ Object
(also: #jamf_manual_install_trigger)
readonly
Jamf manual install policies are named this.
-
#jamf_obj_name_pfx ⇒ Object
readonly
Jamf object names start with this.
-
#jamf_patch_policy_name ⇒ Object
readonly
Jamf Patch Policy is named this.
-
#jamf_pkg_id ⇒ Object
readonly
The Jamf Package object has this jamf id.
-
#new_data_for_update ⇒ Object
readonly
when applying updates, the new data is stored here so it can be accessed by update-methods and compared to the current instanace values both for updating the title, and the versions.
-
#pkg_is_from_autopkg ⇒ Boolean
Is the pkg being processed now from an autopkg recipe?.
-
#server_app_instance ⇒ Object
The instance of Xolo::Server::App that instantiated this version object.
-
#ted_id_number ⇒ Object
The Windoo::Patch#patchId.
-
#title_object(refresh: false) ⇒ Xolo::Server::Title
This might have been set already if we were instantiated via our title.
Class Method Summary collapse
-
.add_version_via_subscription(title_object:, new_version:) ⇒ void
add a new version in response to a patch title update webhook event.
-
.all_versions(title) ⇒ Array<String>
A list of all known versions for a title, just the basenames of all the version files with the extension removed.
-
.data_dir(title, version) ⇒ Pathname
The the local directory containing various files specific to the given version of a title.
-
.data_file(title, version) ⇒ Pathname
The the local JSON file containing the current values for the given version of a title.
-
.in_ted?(patch_id, cnx:) ⇒ Boolean
Does the given patch exist in the Title Editor?.
-
.load(title, version) ⇒ Xolo::Server::Title
Instantiate from the local JSON file containing the current values for the given version of a title.
-
.locked?(title, version) ⇒ Boolean
Is a version locked for updates?.
-
.manifest_file(title, version) ⇒ Pathname
The the local xml plist file containing the .pkg manifest for the given version of a title.
-
.new_subscribed_version_data(title_object:, new_version:) ⇒ Hash
Gather the data needed to create a new xolo version from a subscription.
-
.pkg_deletion_pool ⇒ Queue
The package-deletion thread pool.
-
.pkg_deletion_pool_info ⇒ Hash
info about the current pkg deletion pool state, for the /state route.
-
.send_alerts_about_new_subscribed_version(title_object:, vobj:) ⇒ Object
Handle alerts for newly-created subscribed version.
-
.version_dir(title) ⇒ Pathname
The directory containing subdirectories for each version of a title.
-
.version_dirs(title) ⇒ Array<Pathname>
All version directories for a title.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
version comparison.
-
#admin ⇒ String
This can be manually set earlier in the request handling to use a non-standard admin username.
-
#create ⇒ void
Save a new version, adding to the local filesystem, Jamf Pro, and the Title Editor as needed This should be running in the context of #with_streaming.
-
#creating? ⇒ Boolean
Are we creating this version?.
-
#data_dir ⇒ Pathname
The data directory for this version.
-
#data_file ⇒ Pathname
The JSON data file for this version.
-
#delete(update_title: true, deleting_title: false) ⇒ void
Delete the version.
-
#deleting? ⇒ Boolean
Are we deleting this version?.
-
#deprecate ⇒ void
deprecate this version.
-
#excluded_groups_to_use(ttl_obj: nil) ⇒ Array<String>
The scope excluded groups to use in policies and patch policies for all versions of this title.
-
#handle_autopkg_during_create ⇒ Object
Do autopkg stuff during creation.
-
#initialize(data_hash) ⇒ Version
constructor
NOTE: be sure to only instantiate these using the servers 'instantiate_version' method, or else they might not have all the correct innards.
-
#jamf_cnx(refresh: false) ⇒ Jamf::Connection
A single Jamf Pro API connection to use for the life of this instance.
-
#lock ⇒ Object
Lock this version for updates.
-
#locked? ⇒ Boolean
Is this version locked for updates?.
-
#managed? ⇒ Boolean
or a managed title?.
-
#manifest_file ⇒ Pathname
The manifest plist file for this version.
-
#order_index ⇒ Integer
The index of this version in the title's reversed version_order array.
-
#pilot_groups_to_use ⇒ Array<String>
The scope target groups to use in policies and patch policies during pilot This is defined in each version, and inherited when new versions are created.
-
#progress(msg, log: :debug, alert: false) ⇒ void
Append a message to the progress stream file, optionally sending it also to the log.
-
#release(rollback:) ⇒ void
Release this version, possibly rolling back from a previously newer version This should only be called by the title.
-
#release_groups_to_use(ttl_obj: nil) ⇒ Array<String>
The scope target groups to use in policies and patch policies when the version is released This is defined in the title and applies to all versions.
-
#releasing? ⇒ Boolean
Are we releasing this version?.
-
#repair ⇒ Object
Repair this version.
-
#repairing? ⇒ Boolean
Are we repairing this version?.
-
#reset_to_pilot ⇒ void
Reset this version to 'pilot' status, since we are rolling back to a previous version.
-
#save_local_data ⇒ void
Save our current data out to our JSON data file This overwrites the existing data.
- #session ⇒ Hash
-
#skip ⇒ void
skip this version.
-
#subscribed? ⇒ Boolean
Is this version part of a subscribed title?.
-
#ted_cnx ⇒ Windoo::Connection
A single Title Editor connection to use for the life of this instance.
-
#ted_patch(refresh: false) ⇒ Windoo::Patch
TODO: maybe pass in an appropriate Windoo::SoftwareTitle, so we don't have to use refresh all the time to re-fetch, if we just re-fetched from elsewhere?.
-
#ted_title(refresh: false) ⇒ Windoo::SoftwareTitle
The Windoo::SoftwareTitle object that represents this version's title in the title editor.
-
#to_h ⇒ Object
Add more data to our hash.
-
#unlock ⇒ Object
Unlock this version for updates.
-
#update(new_data) ⇒ void
Update a this version, updating to the local filesystem, Jamf Pro, and the Title Editor as needed.
-
#update_local_instance_values ⇒ void
Update our instance attributes with any new data before saving the changes back out to the file system.
-
#updating? ⇒ Boolean
Are we updating this version?.
Methods included from Mixins::VersionTedAccess
#create_patch_in_ted, #delete_patch_from_ted, #enable_ted_patch, #get_patch_component_criteria_params, included, #repair_ted_patch, #set_app_component, #set_ea_component, #set_patch_capabilites, #set_patch_killapps, #set_ted_patch_component_criteria, #ted_patch_url, #update_patch_in_ted
Methods included from Mixins::VersionJamfAccess
#activate_managed_patch_version_in_jamf, #activate_patch_version_in_jamf, #activate_subscribed_patch_version_in_jamf, #assign_pkg_to_patch_in_jamf, #configure_jamf_auto_install_policy, #configure_jamf_auto_reinstall_policy, #configure_jamf_installed_group, #configure_jamf_manual_install_policy, #configure_jamf_patch_policy, #configure_patch_pol_deployment, #create_in_jamf, #create_jamf_auto_install_policy, #create_jamf_auto_reinstall_policy, #create_jamf_installed_group, #create_jamf_manual_install_policy, #create_jamf_package, #create_jamf_patch_policy, #delete_jamf_package, #delete_version_from_jamf, #deploy_via_mdm, #disable_policies_for_deprecation_or_skipping, #expand_groups_for_deploy, extended, included, #jamf_auto_install_policy, #jamf_auto_install_policy_exist?, #jamf_auto_install_policy_url, #jamf_auto_reinstall_policy, #jamf_auto_reinstall_policy_exist?, #jamf_auto_reinstall_policy_url, #jamf_gui_url, #jamf_installed_group, #jamf_installed_group_criteria, #jamf_installed_group_exist?, #jamf_installed_group_url, #jamf_manual_install_policy, #jamf_manual_install_policy_exist?, #jamf_manual_install_policy_url, #jamf_package, #jamf_package_exist?, #jamf_package_notes, #jamf_package_url, #jamf_patch_policy, #jamf_patch_policy_exist?, #jamf_patch_policy_url, #jamf_patch_version, #patch_report, #remove_exclusions_from_deploy, #remove_invalid_computers_for_deploy, #repair_jamf_auto_install_policy, #repair_jamf_auto_reinstall_policy, #repair_jamf_installed_group, #repair_jamf_manual_install_policy, #repair_jamf_package, #repair_jamf_patch_policy, #repair_jamf_version_objects, #reset_policies_to_pilot, #set_patch_policy_ssvc_icon, #set_policy_exclusions, #set_policy_pilot_groups, #set_policy_release_groups, #set_policy_to_all_targets, #update_excluded_groups, #update_jamf_package_notes, #update_jamf_patch_policy_patch_unknown, #update_jamf_pkg_min_os, #update_jamf_pkg_reboot, #update_patch_policy_deployment, #update_pilot_groups, #update_release_groups, #update_version_in_jamf
Methods included from Mixins::Changelog
#backup_changelog, backup_file_dir, #changelog, #changelog_backup_file, #changelog_file, #changelog_lock, changelog_locks, #delete_changelog, #hostname_from_ip, included, #log_change, #log_update_changes, #note_changes_for_update_and_log
Methods included from Helpers::Notification
#email_from, included, #send_alert, #send_email, #send_email_alert, #server_fqdn, #server_name
Methods included from Helpers::Log
included, #log_debug, #log_error, #log_fatal, #log_info, #log_unknown, #log_warn, #logger, #session_svr_obj_id
Methods included from Helpers::TitleEditor
Methods included from Helpers::JamfPro
extended, included, #jamf_gui_url, #jamf_obj_name_pfx_base, #jamf_xolo_category_id, #valid_forced_exclusion_group_name
Constructor Details
#initialize(data_hash) ⇒ Version
NOTE: be sure to only instantiate these using the servers 'instantiate_version' method, or else they might not have all the correct innards
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/xolo/server/version.rb', line 424 def initialize(data_hash) super # These attrs aren't defined in the ATTRIBUTES # and/or are not stored in the on-disk json file @ted_id_number ||= data_hash[:ted_id_number] @jamf_pkg_id ||= data_hash[:jamf_pkg_id] # and these can be generated now @jamf_obj_name_pfx = "#{jamf_obj_name_pfx_base}#{title}-#{version}" @jamf_pkg_name ||= @jamf_obj_name_pfx @jamf_installed_group_name = "#{jamf_obj_name_pfx}-#{JAMF_SMART_GROUP_NAME_INSTALLED_SFX}" @jamf_auto_install_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_AUTO_INSTALL_SFX}" @jamf_manual_install_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_MANUAL_INSTALL_SFX}" @jamf_auto_reinstall_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_AUTO_REINSTALL_SFX}" @jamf_patch_policy_name = @jamf_obj_name_pfx # we set @jamf_pkg_file when a pkg is uploaded # since we don't know until then if its a .pkg or .zip # It will be stored in the local data and reloaded as needed end |
Instance Attribute Details
#changes_for_update ⇒ Hash (readonly)
Also when applying updates, this will hold the changes being made: the differences between tne current attributs and the new_data_for_update We'll figure this out at the start of the update and can use it later to
- avoid doing things we don't need to
- log the changes in the change log at the very end
This is a Hash with keys of the attribute names that have changed the values are Hashes with keys of :old and :new e.g. { pilot_groups: { old: ['foo'], new: ['bar'] } }
408 409 410 |
# File 'lib/xolo/server/version.rb', line 408 def changes_for_update @changes_for_update end |
#current_action ⇒ Symbol
Returns The current action being taken on this title one of :creating, :updating, :deleting.
412 413 414 |
# File 'lib/xolo/server/version.rb', line 412 def current_action @current_action end |
#jamf_auto_install_policy_name ⇒ Object (readonly)
Jamf auto-install policies are named this
373 374 375 |
# File 'lib/xolo/server/version.rb', line 373 def jamf_auto_install_policy_name @jamf_auto_install_policy_name end |
#jamf_auto_reinstall_policy_name ⇒ Object (readonly)
Jamf auto re-install policies are named this
379 380 381 |
# File 'lib/xolo/server/version.rb', line 379 def jamf_auto_reinstall_policy_name @jamf_auto_reinstall_policy_name end |
#jamf_installed_group_name ⇒ String (readonly)
For each version there will be a smart group containing all macs that have that version of the title installed. The smart group will be named 'xolo-
It will be used as the target for the auto-reinstall
370 371 372 |
# File 'lib/xolo/server/version.rb', line 370 def jamf_installed_group_name @jamf_installed_group_name end |
#jamf_manual_install_policy_name ⇒ Object (readonly) Also known as: jamf_manual_install_trigger
Jamf manual install policies are named this
376 377 378 |
# File 'lib/xolo/server/version.rb', line 376 def jamf_manual_install_policy_name @jamf_manual_install_policy_name end |
#jamf_obj_name_pfx ⇒ Object (readonly)
Jamf object names start with this
361 362 363 |
# File 'lib/xolo/server/version.rb', line 361 def jamf_obj_name_pfx @jamf_obj_name_pfx end |
#jamf_patch_policy_name ⇒ Object (readonly)
Jamf Patch Policy is named this
385 386 387 |
# File 'lib/xolo/server/version.rb', line 385 def jamf_patch_policy_name @jamf_patch_policy_name end |
#jamf_pkg_id ⇒ Object (readonly)
The Jamf Package object has this jamf id
388 389 390 |
# File 'lib/xolo/server/version.rb', line 388 def jamf_pkg_id @jamf_pkg_id end |
#new_data_for_update ⇒ Object (readonly)
when applying updates, the new data is stored here so it can be accessed by update-methods and compared to the current instanace values both for updating the title, and the versions
394 395 396 |
# File 'lib/xolo/server/version.rb', line 394 def new_data_for_update @new_data_for_update end |
#pkg_is_from_autopkg ⇒ Boolean
Returns is the pkg being processed now from an autopkg recipe?.
415 416 417 |
# File 'lib/xolo/server/version.rb', line 415 def pkg_is_from_autopkg @pkg_is_from_autopkg end |
#server_app_instance ⇒ Object
The instance of Xolo::Server::App that instantiated this version object. This is how we access things that are available in routes and helpers, like the single Jamf and TEd connections for this App instance.
348 349 350 |
# File 'lib/xolo/server/version.rb', line 348 def server_app_instance @server_app_instance end |
#ted_id_number ⇒ Object
The Windoo::Patch#patchId
358 359 360 |
# File 'lib/xolo/server/version.rb', line 358 def ted_id_number @ted_id_number end |
#title_object(refresh: false) ⇒ Xolo::Server::Title
This might have been set already if we were instantiated via our title
615 616 617 618 |
# File 'lib/xolo/server/version.rb', line 615 def title_object(refresh: false) @title_object = nil if refresh @title_object ||= server_app_instance.instantiate_title title end |
Class Method Details
.add_version_via_subscription(title_object:, new_version:) ⇒ void
This method returns an undefined value.
add a new version in response to a patch title update webhook event. This doesn't upload a pkg - it just creates the version in Xolo, and then someone can upload a pkg to it via xadm or autopkg will do it if configured.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/xolo/server/version.rb', line 234 def self.add_version_via_subscription(title_object:, new_version:) title_object.log_info "Adding new version '#{new_version}' for subscribed title '#{title_object.title}'" vobj_data = new_subscribed_version_data(title_object: title_object, new_version: new_version) unless vobj_data msg = "Could not get patch version data from JPAPI for version '#{new_version}' of subscribed title '#{title_object.title}'. Cannot create new version in Xolo without this data. Aborting." title_object.log_error msg, alert: true return end # instantiate the version object title_object.log_debug "Instantiating version via subscription '#{new_version}' of title '#{title_object.title}' (#{title_object.class}) with data: #{vobj_data}" vobj = title_object.server_app_instance.instantiate_version( title: title_object, version: new_version, **vobj_data ) # create it in xolo vobj.create # tell someone send_alerts_about_new_subscribed_version(title_object: title_object, vobj: vobj) end |
.all_versions(title) ⇒ Array<String>
Returns A list of all known versions for a title, just the basenames of all the version files with the extension removed.
117 118 119 |
# File 'lib/xolo/server/version.rb', line 117 def self.all_versions(title) version_dirs(title).map { |c| c.basename.to_s } end |
.data_dir(title, version) ⇒ Pathname
The the local directory containing various files specific to the given version of a title
130 131 132 |
# File 'lib/xolo/server/version.rb', line 130 def self.data_dir(title, version) version_dir(title) + version end |
.data_file(title, version) ⇒ Pathname
The the local JSON file containing the current values for the given version of a title
143 144 145 |
# File 'lib/xolo/server/version.rb', line 143 def self.data_file(title, version) data_dir(title, version) + "#{version}.json" end |
.in_ted?(patch_id, cnx:) ⇒ Boolean
Returns Does the given patch exist in the Title Editor?.
182 183 184 |
# File 'lib/xolo/server/version.rb', line 182 def self.in_ted?(patch_id, cnx:) Windoo::Patch.all_ids(cnx: cnx).include? patch_id end |
.load(title, version) ⇒ Xolo::Server::Title
Instantiate from the local JSON file containing the current values for the given version of a title
NOTE: All instantiation should happen using the #instantiate_version method in the server app instance. Please don't call this method directly
173 174 175 176 |
# File 'lib/xolo/server/version.rb', line 173 def self.load(title, version) Xolo::Server.logger.debug "Loading version '#{version}' of title '#{title}' from file" new parse_json(data_file(title, version).read) end |
.locked?(title, version) ⇒ Boolean
Is a version locked for updates?
188 189 190 191 |
# File 'lib/xolo/server/version.rb', line 188 def self.locked?(title, version) curr_lock = Xolo::Server.object_locks.dig title, :versions, version curr_lock && curr_lock > Time.now end |
.manifest_file(title, version) ⇒ Pathname
The the local xml plist file containing the .pkg manifest for the given version of a title
156 157 158 |
# File 'lib/xolo/server/version.rb', line 156 def self.manifest_file(title, version) data_dir(title, version) + "#{version}.manifest.plist" end |
.new_subscribed_version_data(title_object:, new_version:) ⇒ Hash
Gather the data needed to create a new xolo version from a subscription
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/xolo/server/version.rb', line 264 def self.new_subscribed_version_data(title_object:, new_version:) patch_version_data = title_object.patch_versions(version: new_version).first # return nil if not found - will send error alert return unless patch_version_data title_object.log_debug "Got patch version data from JPAPI for version '#{new_version}': #{patch_version_data}" # put the data into a hash for creating a new version object vobj_data = { publish_date: Time.parse(patch_version_data[:releaseDate]), standalone: patch_version_data[:standalone], min_os: patch_version_data[:minimumOperatingSystem], reboot: patch_version_data[:rebootRequired], killapps: [] } # Killapps for subscribed titles? The API only shows app names without the .app, e.g. # "killApps": [ # { # "appName": "ChrislTestHelper" # }, # { # "appName": "Chrisl Test" # } # ] # Since we don't manage them, we'll just record them in the data like this... unless patch_version_data[:killApps].pix_empty? patch_version_data[:killApps].each do |ka| vobj_data[:killapps] << "#{ka[:appName]}.app;unknown.from.subscription" end end vobj_data end |
.pkg_deletion_pool ⇒ Queue
The package-deletion thread pool
the auto_terminate is false to prevents the threads from being daemonized, and running after the main thread exits. This is important because launchd jobs should never do that.
See https://ruby-concurrency.github.io/concurrent-ruby/master/file.thread_pools.html
202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/xolo/server/version.rb', line 202 def self.pkg_deletion_pool @pkg_deletion_pool ||= Concurrent::ThreadPoolExecutor.new( name: 'package-deletion', min_threads: 1, # start with 1 thread max_threads: MAX_PKG_DELETION_THREADS, # create at most 10 threads max_queue: 0, # no limit auto_terminate: false, # see method comments above idletime: 60 # seconds thread can remain idle before it is reclaimed, default is 60 # fallback_policy: :abort # the default is :abort, which will raise a # Concurrent::RejectedExecutionError exception and discard the task ) end |
.pkg_deletion_pool_info ⇒ Hash
info about the current pkg deletion pool state, for the /state route
219 220 221 222 223 224 |
# File 'lib/xolo/server/version.rb', line 219 def self.pkg_deletion_pool_info { threads: pkg_deletion_pool.length, queued_tasks: pkg_deletion_pool.queue_length } end |
.send_alerts_about_new_subscribed_version(title_object:, vobj:) ⇒ Object
Handle alerts for newly-created subscribed version
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/xolo/server/version.rb', line 301 def self.send_alerts_about_new_subscribed_version(title_object:, vobj:) title = title_object.title new_version = vobj.version # if here, and auto_release_delay is zero, release it immediately if title_object.autopkg_enabled? auto_release_delay = title_object.auto_release_delay.to_i # is auto_release_delay an integer? if title_object.auto_release_delay&.pix_integer? && !auto_release_delay.negative? how_soon = if auto_release_delay.zero? 'tonight' elsif auto_release_delay == 1 'tomorrow night' else "in #{auto_release_delayy} days" end msg = "New pilot for subscribed title '#{title}', version '#{new_version}' has been created in Xolo via subscription.\nIt will be released during nightly maintenance #{how_soon}" # nil, empty, 'none', or negative = no auto release else msg = "ACTION REQUIRED: New pilot for subscribed title '#{title}', version '#{new_version}' has been created in Xolo via subscription.\nRequires manual release." end # if not autopkg enabled, we need to tell someone to upload a pkg for this new version else # update general alert msg msg = "ACTION REQUIRED: New pilot for subscribed title '#{title}', version '#{new_version}' has been created in Xolo via subscription.\nPlease upload a .pkg for it ASAP using this command:\n xadm edit-version #{title} #{new_version} --pkg-to-upload /path/to/installer.pkg" # email to title contact vobj.server_app_instance.send_email to: title_object.contact_email, subject: 'Need manual upload of xolo pkg', msg: msg end # if title_object.autopkg_enabled? # send alert vobj.log_info msg, alert: true end |
.version_dir(title) ⇒ Pathname
Returns The directory containing subdirectories for each version of a title. They contain JSON and other files for the versions.
101 102 103 |
# File 'lib/xolo/server/version.rb', line 101 def self.version_dir(title) Xolo::Server::Title.title_dir(title) + VERSIONS_DIRNAME end |
.version_dirs(title) ⇒ Array<Pathname>
Returns All version directories for a title.
108 109 110 111 |
# File 'lib/xolo/server/version.rb', line 108 def self.version_dirs(title) vdir = version_dir(title) vdir.directory? ? vdir.children : [] end |
Instance Method Details
#<=>(other) ⇒ Object
version comparison
458 459 460 461 462 463 |
# File 'lib/xolo/server/version.rb', line 458 def <=>(other) raise Xolo::InvalidDataError, 'Cannot compare with other classes' unless other.is_a? Xolo::Server::Version raise Xolo::InvalidDataError, 'Cannot compare versions of different titles' unless other.title == title order_index <=> other.order_index end |
#admin ⇒ String
This can be manually set earlier in the request handling to use a non-standard admin username
594 595 596 |
# File 'lib/xolo/server/version.rb', line 594 def admin @admin ||= session[:admin] end |
#create ⇒ void
This method returns an undefined value.
Save a new version, adding to the local filesystem, Jamf Pro, and the Title Editor as needed This should be running in the context of #with_streaming
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 |
# File 'lib/xolo/server/version.rb', line 681 def create lock @current_action = :creating # NOTE: this for later in this method, # since uploads are processed in separate threads pkg_upload_given = !pkg_to_upload.pix_empty? self.creation_date = Time.now self.created_by = admin self.status = STATUS_PENDING log_debug "creation_date: #{creation_date}, created_by: #{created_by}" # save to file here so that we have something to delete if # the next couple steps fail progress 'Saving version data to Xolo server' save_local_data create_patch_in_ted unless subscribed? create_in_jamf self.status = STATUS_PILOT # save to file again now, because saving to TitleEd and Jamf will # add some data save_local_data # prepend our version to the version_order array of the title progress "Updating title version_order, prepending '#{version}'", log: :info title_object.prepend_version(version) log_change msg: 'Version Created' progress "Version '#{version}' of Title '#{title}' has been created in Xolo.", log: :info # all done unless we need to get a pkg via autopkg # pkg upload from xadm will happen in a separate process, # so we don't want to do it here in the create method # do we have an uploaded pkg? if pkg_upload_given progress "Pkg will be uploaded to xolo via xadm shortly, from path '#{pkg_to_upload}'", log: :info # if we have an autopkg recipe and dir, get the .pkg and upload it to Jamf elsif title_object.autopkg_enabled? handle_autopkg_during_create # otherwise tell someone we need a .pkg else msg = "No --pkg-to-upload given for version '#{version}' of title #{title}, and no autopkg recipe enabled. Please upload a pkg via xadm or enable autopkg for this title." # no alert when subscribed because a better alert mesg is sent from add_version_via_subscription subscribed? ? progress(msg, log: :warn) : progress(msg, log: :warn, alert: true) end server_app_instance.update_client_data ensure unlock end |
#creating? ⇒ Boolean
Returns Are we creating this version?.
475 476 477 |
# File 'lib/xolo/server/version.rb', line 475 def creating? current_action == :creating end |
#data_dir ⇒ Pathname
The data directory for this version
637 638 639 |
# File 'lib/xolo/server/version.rb', line 637 def data_dir self.class.data_dir title, version end |
#data_file ⇒ Pathname
The JSON data file for this version
644 645 646 |
# File 'lib/xolo/server/version.rb', line 644 def data_file self.class.data_file title, version end |
#delete(update_title: true, deleting_title: false) ⇒ void
This method returns an undefined value.
Delete the version
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 |
# File 'lib/xolo/server/version.rb', line 1037 def delete(update_title: true, deleting_title: false) lock @current_action = :deleting delete_version_from_jamf # NOTE: we no longer delete the patch from the Title Editor # unless the whole title is being deleted, because # patches may be needed for reporting purposes. # When the title is deleted, the title's delete method # will delete all patches for all versions. # If other situations arise where we need to delete # ted patches individually, set deleting_title to true. delete_patch_from_ted if deleting_title && managed? # remove from the title's list of versions progress 'Deleting version from title data on the Xolo server', log: :debug title_object.remove_version(version) if update_title # delete the local data progress 'Deleting version data from the Xolo server', log: :info data_dir.rmtree log_change msg: 'Version Deleted' progress "Version '#{version}' of Title '#{title}' has been deleted from Xolo.", log: :info server_app_instance.update_client_data unless deleting_title ensure unlock end |
#deleting? ⇒ Boolean
Returns Are we deleting this version?.
493 494 495 |
# File 'lib/xolo/server/version.rb', line 493 def deleting? current_action == :deleting end |
#deprecate ⇒ void
This method returns an undefined value.
deprecate this version
931 932 933 934 935 936 937 938 939 940 941 942 943 |
# File 'lib/xolo/server/version.rb', line 931 def deprecate lock progress "Deprecating older released version '#{version}'" disable_policies_for_deprecation_or_skipping :deprecated self.status = STATUS_DEPRECATED self.deprecation_date = Time.now self.deprecated_by = admin log_change msg: 'Version Deprecated' save_local_data ensure unlock end |
#excluded_groups_to_use(ttl_obj: nil) ⇒ Array<String>
The scope excluded groups to use in policies and patch policies for all versions of this title.
Excluded groups are defined in the title, applying to all versions, and may be augmented by:
- A smart group of computers NOT in the target_groups, if any target groups are defined.
- Xolo::Server.config.forced_exclusion, a group excluded from ALL of xolo, defined in the server config.
- The title's jamf_frozen_group_name, if it exists, containing computers that have been 'frozen' to a single version.
For initial install policies, the smart group of macs with any version installed (jamf_installed_group_name) "xolo-
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/xolo/server/version.rb', line 543 def excluded_groups_to_use(ttl_obj: nil) return @excluded_groups_to_use if @excluded_groups_to_use ttl_obj ||= title_object # always exclude the frozen static group # calling ttl_obj.jamf_frozen_group will create the group if needed @excluded_groups_to_use = [ttl_obj.jamf_frozen_group.name] log_debug "Added '#{ttl_obj.jamf_frozen_group_name}' to @excluded_groups_to_use" # get the excluded groups from the title @excluded_groups_to_use += ttl_obj.changes_for_update&.key?(:excluded_groups) ? ttl_obj.changes_for_update[:excluded_groups][:new] : ttl_obj.excluded_groups # add the jamf_target_groups_exclusion_group - it must have been created before now @excluded_groups_to_use << ttl_obj.jamf_target_groups_exclusion_group_name if ttl_obj.use_jamf_target_groups_exclusion_group? # always exclude Xolo::Server.config.forced_exclusion if defined @excluded_groups_to_use << valid_forced_exclusion_group_name if valid_forced_exclusion_group_name @excluded_groups_to_use.uniq! log_debug "Excluded groups to use: #{@excluded_groups_to_use.join ', '}" @excluded_groups_to_use end |
#handle_autopkg_during_create ⇒ Object
Do autopkg stuff during creation
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
# File 'lib/xolo/server/version.rb', line 746 def handle_autopkg_during_create return unless title_object.autopkg_enabled? pkg_src = title_object.run_autopkg_recipe if pkg_src.nil? msg = 'AutoPkg recipe is enabled for this title, but no pkg was found after running the recipe. Please check the AutoPkg recipe and the server log for details.' progress msg, log: :warn, alert: true return end oldest_allowed = Time.now - 1200 # 20 minutes ago if pkg_src.mtime < oldest_allowed msg = "AutoPkg recipe is enabled for this title, and a pkg was found after running the recipe, but it was last modified at #{pkg_src.mtime}, which is more than 20 minutes ago. To avoid accidentally uploading an old pkg, the server will not upload this pkg. Please check the AutoPkg recipe and the server log for details." progress msg, log: :warn, alert: true return end # this lets future code know that the pkg we're working with came from autopkg self.pkg_is_from_autopkg = true # Upload the pkg to Jamf, and associate it with this version server_app_instance.process_and_upload_autopkg_pkg(title, self, pkg_src) end |
#jamf_cnx(refresh: false) ⇒ Jamf::Connection
Returns a single Jamf Pro API connection to use for the life of this instance.
630 631 632 |
# File 'lib/xolo/server/version.rb', line 630 def jamf_cnx(refresh: false) server_app_instance.jamf_cnx refresh: refresh end |
#lock ⇒ Object
Lock this version for updates
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
# File 'lib/xolo/server/version.rb', line 1076 def lock raise Xolo::ServerError, 'Server is shutting down' if Xolo::Server.shutting_down? while locked? if (Time.now.to_i % 5).zero? log_debug "Method #{caller_locations.first.label} is waiting for update lock on Version '#{version}' of title '#{title}'..." end sleep 0.33 end Xolo::Server.object_locks[title] ||= { versions: {} } exp = Time.now + Xolo::Server::ObjectLocks::OBJECT_LOCK_LIMIT Xolo::Server.object_locks[title][:versions][version] = exp log_debug "Locked version '#{version}' of title '#{title}' for updates until #{exp}, by method #{caller_locations.first.label}" end |
#locked? ⇒ Boolean
Is this version locked for updates?
1070 1071 1072 |
# File 'lib/xolo/server/version.rb', line 1070 def locked? self.class.locked?(title, version) end |
#managed? ⇒ Boolean
or a managed title?
777 778 779 |
# File 'lib/xolo/server/version.rb', line 777 def managed? !subscribed? end |
#manifest_file ⇒ Pathname
The manifest plist file for this version
651 652 653 |
# File 'lib/xolo/server/version.rb', line 651 def manifest_file self.class.manifest_file title, version end |
#order_index ⇒ Integer
Returns The index of this version in the title's reversed version_order array. We reverse it because the version_order array holds the newest versions first, so the index of the newest version is 0, the next newest is 1, etc - we need the opposite of that.
469 470 471 |
# File 'lib/xolo/server/version.rb', line 469 def order_index title_object.version_order.reverse.index version end |
#pilot_groups_to_use ⇒ Array<String>
The scope target groups to use in policies and patch policies during pilot This is defined in each version, and inherited when new versions are created.
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/xolo/server/version.rb', line 508 def pilot_groups_to_use return @pilot_groups_to_use if @pilot_groups_to_use # any defined in the version override any in the title @pilot_groups_to_use = changes_for_update&.key?(:pilot_groups) ? changes_for_update[:pilot_groups][:new] : pilot_groups return @pilot_groups_to_use unless @pilot_groups_to_use.empty? # if none defined in the version, look in the title @pilot_groups_to_use = if title_object.changes_for_update&.key?(:pilot_groups) title_object.changes_for_update[:pilot_groups][:new] else title_object.pilot_groups end end |
#progress(msg, log: :debug, alert: false) ⇒ void
This method returns an undefined value.
Append a message to the progress stream file, optionally sending it also to the log
608 609 610 |
# File 'lib/xolo/server/version.rb', line 608 def progress(msg, log: :debug, alert: false) server_app_instance.progress msg, log: log, alert: alert end |
#release(rollback:) ⇒ void
This method returns an undefined value.
Release this version, possibly rolling back from a previously newer version This should only be called by the title. The initial 'release' action starts in the title, and then calls this method on the version to do the version-specific release steps.
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
# File 'lib/xolo/server/version.rb', line 887 def release(rollback:) lock @current_action = :releasing # set scope targets of auto-install policy to release-groups msg = "Jamf: Version '#{version}': Setting scope targets of auto-install policy to release_groups: #{release_groups_to_use.join(', ')}" progress msg, log: :info pol = jamf_auto_install_policy set_policy_release_groups pol pol.enable pol.save # set scope targets of patch policy to all (in patch pols, 'all' means 'all eligible') msg = "Jamf: Version '#{version}': Setting scope targets of patch policy to all eligible computers" progress msg, log: :info ppol = jamf_patch_policy ppol.enable ppol.scope.set_all_targets # if rollback, make sure the patch policy is set to 'allow downgrade' if rollback msg = "Jamf: Version '#{version}': Setting patch policy to allow downgrade" progress msg, log: :info ppol.allow_downgrade = true else ppol.allow_downgrade = false end ppol.save # change status to 'released' self.status = STATUS_RELEASED self.release_date = Time.now self.released_by = admin chg_msg = rollback ? 'Version Released - Rolled Back' : 'Version Released' log_change msg: chg_msg save_local_data ensure unlock end |
#release_groups_to_use(ttl_obj: nil) ⇒ Array<String>
The scope target groups to use in policies and patch policies when the version is released This is defined in the title and applies to all versions.
576 577 578 579 580 581 |
# File 'lib/xolo/server/version.rb', line 576 def release_groups_to_use(ttl_obj: nil) return @release_groups_to_use if @release_groups_to_use ttl_obj ||= title_object @release_groups_to_use = ttl_obj.changes_for_update&.key?(:release_groups) ? ttl_obj.changes_for_update[:release_groups][:new] : ttl_obj.release_groups end |
#releasing? ⇒ Boolean
Returns Are we releasing this version?.
499 500 501 |
# File 'lib/xolo/server/version.rb', line 499 def releasing? current_action == :releasing end |
#repair ⇒ Object
Repair this version. Look at the Title Editor patch object, and ensure it's correct based on the local data file.
- version order
- min os
- max os
- standalone
- reboot
- release date
- killapps
- component criteria
- component name '
'
- component name '
- capability criteria
- enabled
Then look at the various Jamf objects pertaining to this version, and ensure they are correct
- package object 'xolo-
- ' - filename 'xolo-
- .pkg' - description
- os limitations
- filename 'xolo-
- auto install policy 'xolo-
- -auto-install' - manual install policy 'xolo-
- -manual-install' - patch policy 'xolo-
- '
Then look at the xolo metadata, and fix whatever is needed
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 |
# File 'lib/xolo/server/version.rb', line 847 def repair lock @current_action = :repairing log_change msg: "Repairing version '#{version}'" progress "Starting repair of version '#{version}' of title '#{title}'", log: :debug repair_ted_patch repair_jamf_version_objects # If there's a reupload, but no original, make the orig the same as the re unless upload_date if reupload_date && reuploaded_by new_date = reupload_date new_by = reuploaded_by else new_date = Time.parse '2025-02-15' new_by = 'buzzlightyear' end progress "Fixing original upload date: #{new_date}, by: #{new_by}", log: :debug self.upload_date = new_date self.uploaded_by = new_by end save_local_data # no need to update client data now if we're being repaired along with our title return if title_object.current_action == :repairing server_app_instance.update_client_data ensure unlock end |
#repairing? ⇒ Boolean
Returns Are we repairing this version?.
487 488 489 |
# File 'lib/xolo/server/version.rb', line 487 def repairing? current_action == :repairing end |
#reset_to_pilot ⇒ void
This method returns an undefined value.
Reset this version to 'pilot' status, since we are rolling back to a previous version
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 |
# File 'lib/xolo/server/version.rb', line 967 def reset_to_pilot return if status == STATUS_PILOT lock progress "Resetting version '#{version}' to pilot status due to rollback of an older version" reset_policies_to_pilot self.status = STATUS_PILOT self.skipped_date = nil self.skipped_by = nil self.deprecation_date = nil self.deprecated_by = nil log_change msg: 'Version Reset to Pilot' save_local_data ensure unlock end |
#save_local_data ⇒ void
This method returns an undefined value.
Save our current data out to our JSON data file This overwrites the existing data.
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 |
# File 'lib/xolo/server/version.rb', line 1015 def save_local_data data_dir.mkpath self.modification_date = Time.now self.modified_by = admin log_debug "Version '#{version}' of Title '#{title}' noting modification by #{modified_by}" file = data_file log_debug "Saving local version data to: #{file}" file.pix_atomic_write to_json end |
#session ⇒ Hash
585 586 587 588 |
# File 'lib/xolo/server/version.rb', line 585 def session server_app_instance&.session || {} # @session ||= {} end |
#skip ⇒ void
This method returns an undefined value.
skip this version
949 950 951 952 953 954 955 956 957 958 959 960 |
# File 'lib/xolo/server/version.rb', line 949 def skip lock progress "Skipping unreleased version '#{version}'" disable_policies_for_deprecation_or_skipping :skipped self.status = STATUS_SKIPPED self.skipped_date = Time.now self.skipped_by = admin log_change msg: 'Version Skipped' save_local_data ensure unlock end |
#subscribed? ⇒ Boolean
Is this version part of a subscribed title?
772 773 774 |
# File 'lib/xolo/server/version.rb', line 772 def subscribed? title_object.subscribed? end |
#ted_cnx ⇒ Windoo::Connection
Returns a single Title Editor connection to use for the life of this instance.
623 624 625 |
# File 'lib/xolo/server/version.rb', line 623 def ted_cnx server_app_instance.ted_cnx end |
#ted_patch(refresh: false) ⇒ Windoo::Patch
TODO: maybe pass in an appropriate Windoo::SoftwareTitle, so we don't have to use refresh all the time to re-fetch, if we just re-fetched from elsewhere?
662 663 664 665 |
# File 'lib/xolo/server/version.rb', line 662 def ted_patch(refresh: false) @ted_patch = nil if refresh @ted_patch ||= ted_title(refresh: refresh).patches.patch(version) end |
#ted_title(refresh: false) ⇒ Windoo::SoftwareTitle
Returns The Windoo::SoftwareTitle object that represents this version's title in the title editor.
670 671 672 673 |
# File 'lib/xolo/server/version.rb', line 670 def ted_title(refresh: false) @ted_title = nil if refresh @ted_title ||= Windoo::SoftwareTitle.fetch id: title, cnx: ted_cnx end |
#to_h ⇒ Object
Add more data to our hash
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 |
# File 'lib/xolo/server/version.rb', line 1104 def to_h hash = super # These attrs aren't defined in the ATTRIBUTES # but we want them in the hash and/or JSON hash[:jamf_pkg_id] = jamf_pkg_id hash[:ted_id_number] = ted_id_number hash[:pilot_groups_to_use] = pilot_groups_to_use hash[:release_groups_to_use] = release_groups_to_use hash.sort.to_h end |
#unlock ⇒ Object
Unlock this version for updates
1094 1095 1096 1097 1098 1099 1100 |
# File 'lib/xolo/server/version.rb', line 1094 def unlock curr_lock = Xolo::Server.object_locks.dig title, :versions, version return unless curr_lock Xolo::Server.object_locks[title][:versions].delete version log_debug "Unlocked version '#{version}' of title '#{title}' for updates" end |
#update(new_data) ⇒ void
This method returns an undefined value.
Update a this version, updating to the local filesystem, Jamf Pro, and the Title Editor as needed
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 |
# File 'lib/xolo/server/version.rb', line 787 def update(new_data) lock @current_action = :updating @new_data_for_update = new_data @changes_for_update = note_changes_for_update_and_log if @changes_for_update.pix_empty? progress 'No changes to make', log: :info return end log_info "Updating version '#{version}' of title '#{title}' for admin '#{admin}'" # changelog - log the changes now, and # if there is an error, we'll log that too # saying the above changes were not completed and to # look at the server log for details. log_update_changes # update ted before jamf update_patch_in_ted update_version_in_jamf update_local_instance_values save_local_data server_app_instance.update_client_data # new pkg uploads happen in a separate process rescue => e log_change msg: "ERROR: The update failed and the changes didn't all go through!\n#{e.class}: #{e.}\nSee server log for details." # re-raise for proper error handling in the server app raise ensure unlock end |
#update_local_instance_values ⇒ void
This method returns an undefined value.
Update our instance attributes with any new data before saving the changes back out to the file system
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 |
# File 'lib/xolo/server/version.rb', line 988 def update_local_instance_values # update instance data with new data before writing out to the filesystem. # Do this last so that the instance values can be compared to # new_data_for_update in the steps above. # Also, those steps might have updated some server-specific attributes # which will be saved to the file system as well. ATTRIBUTES.each do |attr, deets| # make sure these are updated elsewhere if needed, # e.g. modification data. next if deets[:read_only] next unless deets[:cli] new_val = new_data_for_update[attr] old_val = send(attr) next if new_val == old_val log_debug "Updating Xolo Version attribute '#{attr}': '#{old_val}' -> '#{new_val}'" send "#{attr}=", new_val end # update any other server-specific attributes here... end |
#updating? ⇒ Boolean
Returns Are we updating this version?.
481 482 483 |
# File 'lib/xolo/server/version.rb', line 481 def updating? current_action == :updating end |