Class: D3::Package
- Inherits:
-
JSS::Package
- Object
- JSS::Package
- D3::Package
- Defined in:
- lib/d3/package.rb,
lib/d3/package/mixins.rb,
lib/d3/package/aliases.rb,
lib/d3/package/getters.rb,
lib/d3/package/setters.rb,
lib/d3/package/validate.rb,
lib/d3/package/constants.rb,
lib/d3/package/questions.rb,
lib/d3/package/attributes.rb,
lib/d3/package/constructor.rb,
lib/d3/package/class_methods.rb,
lib/d3/package/client_actions.rb,
lib/d3/package/server_actions.rb,
lib/d3/package/class_variables.rb,
lib/d3/package/private_methods.rb
Overview
Package - A JSS::Package that can be installed and maintained by d3
Defined Under Namespace
Modules: Validate
Constant Summary collapse
- P_TABLE =
A short name for the d3 packages table definition
D3::Database::PACKAGE_TABLE
- P_FIELDS =
A short name for the basename table field definitions
P_TABLE[:field_definitions]
- SCRIPT_TYPES =
the possible types of scripts associated with a d3 pkg
[:pre_install, :post_install, :pre_remove, :post_remove]
- DFT_EXPIRATION_DAYS =
the default expiration is to never expire
0
- PKG_TYPES =
These are the kinds of pkgs we can deal wth note that :pkg means all flavors, including .mpkg’s
[:pkg, :dmg]
- PKG_CONTENTS_TABLE =
d3 allows indexing, so here’s the MySQL table that holds the package indices
"package_contents"
- PKG_RE =
This regular expression matches valid (m)pkg filenames
/\.m?pkg$/
- DEFAULT_STATUS =
default status is :pilot
:pilot
- @@package_data =
this holds the data from the D3::Database::Packages table in the db See D3::Package.package_data
nil
- @@all_packages =
This holds all D3::Package objects See D3::Package.all
nil
- @@filenames =
this holds all the pkg filesnames known to the jss
nil
Instance Attribute Summary collapse
-
#added_by ⇒ String?
readonly
The login name of the admin who added this packge to d3.
-
#added_date ⇒ Time
readonly
When was this package was added to d3.
-
#admin ⇒ String
included
from Basename
readonly
Who’s uploading, releasing, installing, or archiving this thing?.
-
#apple_receipt_data ⇒ Array<Hash>
readonly
The apple receipt data for the items installed by this pkg.
-
#auto_groups ⇒ Array
A list of JSS::ComputerGroup names whose members get this package installed automatically.
-
#basename ⇒ String
included
from Basename
readonly
The basname of the thing installed.
-
#excluded_groups ⇒ Array
A list of JSS::ComputerGroup names for whose members this package is not available without force.
-
#expiration ⇒ Integer
included
from Basename
readonly
The days of disuse before an expirable edition expires.
-
#expiration_paths ⇒ String
included
from Basename
readonly
The path to the executable that needs come to the foreground to prevent expiration.
-
#id ⇒ Integer
included
from Basename
readonly
The JSS id of this package.
-
#in_d3 ⇒ Boolean
readonly
Does this pkg exist in the d3 pkg table?.
-
#package_type ⇒ Symbol
included
from Basename
readonly
Is this package a .dmg or .pkg?.
-
#post_install_script_id ⇒ Integer?
(also: #post_install_id)
The JSS::Script id of the post-install script, if any.
-
#post_remove_script_id ⇒ Integer?
(also: #post_remove_id)
The JSS::Script id of the post-remove script, if any.
-
#pre_install_script_id ⇒ Integer?
(also: #pre_install_id)
The JSS::Script id of the pre-install script, if any.
-
#pre_remove_script_id ⇒ Integer?
(also: #pre_remove_id)
The JSS::Script id of the pre-remove script, if any.
-
#prohibiting_processes ⇒ Array<String>
included
from Basename
readonly
An array of Strings for matching to the output lines of ‘/bin/ps -A -c -o comm’.
-
#release_date ⇒ Time?
readonly
When was this package made live in d3.
-
#released_by ⇒ String?
readonly
The login name of the admin who made it live.
-
#remove_first ⇒ Boolean
(also: #remove_first?)
Should any currently installed versions of this basename be uninstalled (if possible) before installing this package?.
-
#revision ⇒ Integer
included
from Basename
readonly
The d3 release number of the thing installed.
-
#standard ⇒ Boolean
(also: #standard?)
readonly
Does this pkg get installed automatically on all non-exluded clients?.
-
#status ⇒ Symbol
readonly
The current status of the pkg in d3.
-
#status ⇒ Symbol
included
from Basename
readonly
Whats the d3 status of this package? One of the values of D3::Basename::STATUSES.
-
#version ⇒ String
included
from Basename
readonly
The version of the thing installed.
Class Method Summary collapse
-
.all_basenames(refresh = false) ⇒ Array<String>
An Array of basenames known to d3.
-
.all_editions(refresh = false) ⇒ Array<String>
An Array of editions known to d3.
-
.all_filenames(refresh = false) ⇒ Hash{Integer: String}
A Hash of all packages filenames keyed by pkg id These are looked up via a DB query because otherwise we’d have to instantiate a Package object for every package which is way too slow.
-
.all_ids(refresh = false, api: JSS.api) ⇒ Array<Integer>
An Array of all packages ids known to d3.
-
.all_names(refresh = false, api: JSS.api) ⇒ Array<String>
An Array of all packages names known to d3.
-
.auto_install_ids_for_group(group, refresh = false) ⇒ Array<Integer>
An array of ids for all pkgs that are auto-installed for a given computer group.
-
.basenames_to_live_ids(refresh = false) ⇒ Hash{String => Integer}
A Hash mapping all basenames to their currently live jss id.
-
.check_computer_groups(groups) ⇒ Array<String>
Check for existence of one or more computer groups in the JSS, raise an exception if any group doesn’t exist.
-
.deprecated_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “deprecated” packages.
-
.exclude_ids_for_group(group, refresh = false) ⇒ Array<Integer>
An array of ids for all pkgs that are exclude for a given computer group.
-
.find_package(search_term, type = :pkg) ⇒ D3::Package?
Get a single D3::Package by using a search term.
-
.ids_for_basename(basename, refresh = false) ⇒ Array
An Array of ids for all pkgs with a given basename.
-
.ids_to_editions(refresh = false) ⇒ Hash{Integer => String}
A Hash mapping package ids to editions in d3.
-
.import(ident, args) ⇒ D3::Package
Import an existing JSS::Package into d3.
-
.live_basenames(refresh = false) ⇒ Array
An Array of basenames that have live packages.
-
.live_data(refresh = false) ⇒ Hash{Integer=>Hash}
A Hash of Package Data for all live packages.
-
.live_ids(refresh = false) ⇒ Array<String>
An Array of all packages ids that are live.
-
.live_names(refresh = false) ⇒ Array<String>
An Array of all packages names that are live.
-
.missing_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “missing” packages.
-
.most_recent_package_for(basename) ⇒ D3::Package?
Get the most recent package on the server for a given basename.
-
.package_data(refresh = false, status = :all) ⇒ Hash<Hash>
Raw(ish) SQL data for all d3 packages as a Hash of Hashes.
-
.packages_for_script(script, script_type = nil, refresh = false) ⇒ Array<Integer>
An Array of pkg ids for all pkgs that use a given script, optionally limiting to those pkgs that use the script for a given purpose.
-
.pilot_data(refresh = false) ⇒ Hash<Hash>
A Hash of Package Data for all “pilot” packages.
-
.receipt_data_from_bundle_pkg(pkg_path) ⇒ Object
givin a Pathname to a bundle pkg, return an array of hashes with data for all the pkg rcpts hat will be installed each has includes at least :apple_pkg_id, :version, and :installed_kb.
-
.receipt_data_from_flat_pkg(pkg_path) ⇒ Object
Givinn a Pathname to a flat package, return an array of hashes with data for all the pkg rcpts hat will be installed each has includes at least :apple_pkg_id, :version, and :installed_kb.
-
.receipt_data_from_pkg(pkg_path) ⇒ Array<Hash>
Givin a Pathname to a package, return an array of hashes with data for all the pkg rcpts that will be installed each hash includes at least :apple_pkg_id, :version, and :installed_kb.
-
.receipt_data_from_xml(xml_file_path, pkg_path = nil) ⇒ Object
Parse an xml file (like a .dist, Distribution, or PackageInfo file) and find all pkg-ref or pkg-info elements to extract their pkg ids and other data, or locate and recurse on any sub-pkgs.
-
.scripts(refresh = false) ⇒ Hash{Integer => Hash{Integer => Array<Symbol>}}
A Hash of Hashes of all scripts used by d3 packages.
-
.skipped_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “skipped” packages.
-
.statuses_by(identifier, refresh = false) ⇒ Hash{String,Integer => Symbol] the statuses of the packages
A Hash of package identifiers (id’s, names, editions) as keys, to the status of the package (symbols).
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
included
from Basename
Use comparable to give sortability and equality.
-
#add_auto_groups(groupnames) ⇒ void
Add one or more groups the to list of auto_groups.
-
#add_excluded_groups(groupnames) ⇒ void
Add one or more groups the to list of excluded_groups.
-
#add_expiration_path(path) ⇒ Object
Add a path to expiration_paths The paths are those recorded in d3RepoMan’s timestamp plists.
-
#admin=(new_val = @admin) ⇒ void
Set the login name of the admin who’s doing something with this pkg.
-
#auto_clean(admin) ⇒ void
Perform any auto_cleanup, if the config says we should.
-
#basename=(new_val = @basename) ⇒ Object
Set the basename of this package.
-
#basename_exist?(name) ⇒ Boolean
included
from Validate
Check the existence of a basename in d3.
- #bom ⇒ Object
-
#check_cpu ⇒ Object
Check if this machine is OK wrt to the processor limitations Raise an exception if not.
-
#check_for_deprecated ⇒ Object
Check that we’re not installing a deprecated pkg, and raise an exception if we are.
-
#check_for_exclusions ⇒ Object
Check if this machine is in an excluded group.
-
#check_for_newer_version ⇒ Object
Check that there’s not a newer version of this thing alreay installed Raise an exception if so.
-
#check_for_skipped ⇒ Object
Check that we’re not trying to install a skipped pkg, and raise an exception if we are.
-
#check_oses ⇒ Object
Check if this machine is OK wrt to the os limitations Raise an exception if not.
-
#create ⇒ Integer
Create this package in the JSS if needed, and in d3.
-
#created? ⇒ Boolean
Is this pkg in on the server?.
-
#delete(keep_in_jss: false, keep_scripts: false, admin: @admin, rwpw: nil) ⇒ Array<String>
Delete this package from d3, possibly leaving it in the JSS.
-
#deleted? ⇒ Boolean
included
from Basename
Is the status :deleted?.
-
#deprecated? ⇒ Boolean
Is this pkg in pilot? (saved, but never made live).
-
#edition ⇒ String
included
from Basename
While several packages can have the same basename, the combination of basename, version, and revision (called the ‘edition’) must be unique among the d3 packages.
-
#edition_exist?(edition) ⇒ Boolean
included
from Validate
Check the existence of an edition in d3.
-
#expiration=(new_val = @expiration) ⇒ void
Set the expiration period for all installs of this pkg.
-
#expiration_paths=(new_val = @expiration_paths) ⇒ void
Set the expiration paths for this pkg.
-
#expiration_paths_match?(other_exp_paths) ⇒ Boolean
included
from Basename
Does a given array of pathnames have the same elements as This is generally used to compare two @expiration_paths arrays for “equality”.
-
#expires? ⇒ Boolean
Does this pkg expire by default?.
- #file_list ⇒ Object
-
#filename_exist?(name) ⇒ Boolean
included
from Validate
Check the existence of a filename in the JSS.
- #files ⇒ Object
-
#formatted_details ⇒ String
Generate a human-readable string of details about this installer.
-
#index(files_only = false) ⇒ Array<String>
The index of this package This is an array of paths (as Strings) from the pkg’s Jamf Pro index.
-
#indexed? ⇒ Boolean
Is this pkg ‘indexed’ in the jss, so that it can be removable?.
-
#initialize(args = {}) ⇒ Package
constructor
Existing d3 pkgs are looked up by providing :id, :name, :basename, :edition, or the combination of :basename, :version, and :revision (which comprise the edition).
-
#install(args = {}) ⇒ String
Install this pkg on this machine.
-
#installed? ⇒ Boolean
Is this pkg installed on this machine via d3? Note: this overrides JSS::Package#installed?.
-
#installed_files ⇒ Array<Pathname>
Get an array of files installed by this pkg Note that this does not show directories.
-
#live? ⇒ Boolean
included
from Basename
Is the status :live?.
-
#make_live(admin = @admin) ⇒ void
Make this package the live one for its basename.
-
#mark_missing_packages ⇒ void
Mark missing packages as so on the server.
-
#missing? ⇒ Boolean
included
from Basename
Is the status :missing?.
-
#mk_index(args = {}) ⇒ void
Create, or re-create, the BOM index records for this Package in the JSS Database.
-
#new_script(args = {}) ⇒ Integer
Add or replace a pre- or post- script for this package.
-
#pilot? ⇒ Boolean
included
from Basename
Is the status :pilot?.
-
#policy_ids ⇒ Array<Integer>
An Array of ids of all Jamf Pro policies using this package.
- #post_install ⇒ Object
-
#post_install=(new_val = @post_install_script_id) ⇒ void
Set the post_install_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
- #post_install_script= ⇒ Object
-
#post_install_script? ⇒ Boolean
Does this pkg have a post-install script?.
-
#post_install_script_name ⇒ String?
-
The name of the post install script for this pkg, or nil if none.
-
- #post_install_script_name= ⇒ Object
- #post_remove ⇒ Object
-
#post_remove=(new_val = @post_remove_script_id) ⇒ void
Set the post_remove_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
- #post_remove_script= ⇒ Object
-
#post_remove_script? ⇒ Boolean
Does this pkg have a post-remove script?.
-
#post_remove_script_name ⇒ String?
-
The name of the post remove script for this pkg, or nil if none.
-
- #post_remove_script_name= ⇒ Object
-
#pre_install ⇒ Object
aliases for getting the script names.
-
#pre_install=(new_val = @pre_install_script_id) ⇒ void
Set the pre_install_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
-
#pre_install_script= ⇒ Object
aliases for assigning scripts, since assignment methods can take ids, names, or paths clean these up someday!.
-
#pre_install_script? ⇒ Boolean
Does this pkg have a pre-install script?.
-
#pre_install_script_name ⇒ String?
-
The name of the pre-install script for this pkg, or nil if none.
-
- #pre_install_script_name= ⇒ Object
- #pre_remove ⇒ Object
-
#pre_remove=(new_val = @pre_remove_script_id) ⇒ void
Set the pre_remove_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
- #pre_remove_script= ⇒ Object
-
#pre_remove_script? ⇒ Boolean
Does this pkg have a pre-remove script?.
-
#pre_remove_script_name ⇒ String?
-
The name of the pre remove script for this pkg, or nil if none.
-
- #pre_remove_script_name= ⇒ Object
-
#prohibiting_processes=(new_val = @prohibiting_processes) ⇒ void
Set the prohibiting processes for this installer.
-
#queue_for_puppies(force = D3::forced?) ⇒ Object
This just queues this installer for installation at the next puppywalk For now, we’re intentionally NOT caching the installer for off-line installation.
- #release ⇒ Object
-
#remove_auto_groups(groupnames) ⇒ void
Remove one or more groups the to list of auto_groups.
-
#remove_excluded_groups(groupnames) ⇒ void
Remove one or more groups the to list of excluded_groups.
-
#remove_expiration_path(path) ⇒ Object
Remove a path from expiration_paths The paths are those recorded in d3RepoMan’s timestamp plists.
-
#revision=(new_val = @revision) ⇒ Object
Set the basename of this package.
-
#run_make_live_script ⇒ Process::Status
Run the make_live script, if any.
-
#run_post_install_script(verbose = false) ⇒ Integer?
Run the post-install script, if any.
-
#run_pre_install_script(verbose = false) ⇒ Integer?
Run the pre-install script, if any.
-
#save ⇒ Object
An alias for both save and update.
-
#saved? ⇒ Boolean
Is this pkg on the server?.
-
#script_ids ⇒ Hash{Symbol=>Integer}
The type and ids of all pre- and post- scripts for this pkg.
-
#script_names ⇒ Hash{Symbol=>String}
The type and names of all pre- and post- scripts for this pkg.
-
#skipped? ⇒ Boolean
Is this pkg in pilot? (saved, but never made live).
-
#update ⇒ Integer
Update this package in the JSS and in d3.
-
#update_apple_receipt_data(dist_pw, unmount = true) ⇒ void
Learn the apple package id’s installed by this pkg by querying the package on the current dist.
-
#upload_master_file(local_file_path, rw_pw, unmount = true) ⇒ void
Upload a locally-readable file to the master distribution point.
- #validate_auto_groups(groups) ⇒ Object included from Validate
-
#validate_category(cat) ⇒ String
included
from Validate
Check the validity of a category name Raise an exception if not valid.
-
#validate_cpu_type(type) ⇒ Symbol
included
from Validate
Check the validity of a CPU type Raise an exception if not valid.
-
#validate_edition(edition) ⇒ String
included
from Validate
Check if an edition exists and raise an exception if so Also check that it contains at least two hyphens.
- #validate_excluded_groups(groups) ⇒ Object included from Validate
-
#validate_expiration(exp) ⇒ Integer
included
from Validate
Confirm the validity of an expiration.
-
#validate_expiration_path(path) ⇒ Pathname
included
from Validate
Confirm the validity of an expiration path.
-
#validate_expiration_paths(paths) ⇒ Array<Pathname>
included
from Validate
Confirm the validity of one or more expiration paths.
-
#validate_filename(name) ⇒ String
included
from Validate
check that the given filename doesn’t already exist.
-
#validate_groups(groups, check_for_std = false) ⇒ Array
included
from Validate
Confirm the existence of a list of Computer Group names (String or Array) and return them as an Array.
-
#validate_non_overlapping_groups(auto, excl) ⇒ True
included
from Validate
Make sure auto and excluded groups don’t have any members in common, raise an exception if they do.
-
#validate_oses(os_list) ⇒ Array
included
from Validate
Check the validity of a list of OSes Raise an exception if not valid.
-
#validate_package_name(name) ⇒ Object
included
from Validate
check that the given package name doesn’t already exist.
-
#validate_post_install_script(script) ⇒ Object
included
from Validate
Check the validity of a post_install script.
-
#validate_post_remove_script(script) ⇒ Object
included
from Validate
Check the validity of a pre_remove script.
-
#validate_pre_install_script(script) ⇒ Object
included
from Validate
Check the validity of a pre_install script.
-
#validate_pre_remove_script(script) ⇒ Object
included
from Validate
Check the validity of a pre_remove script.
-
#validate_prohibiting_process(process_name) ⇒ String
included
from Validate
Check a single prohibiting process for validity.
-
#validate_revision(rev) ⇒ Integer
included
from Validate
Confirm the validity of a revision.
-
#validate_script(script) ⇒ Pathname, String
included
from Validate
Check the validity of a script, either Pathname, JSS id, or JSS name Raise an exception if not valid.
-
#validate_version(vers) ⇒ String
included
from Validate
Confirm the validity of a version.
-
#validate_yes_no(yn) ⇒ Boolean
included
from Validate
check the validity of a yes/no,true/false,1/0 input value.
-
#version=(new_val = @version) ⇒ Object
Set the version of this package.
Constructor Details
#initialize(args = {}) ⇒ Package
Existing d3 pkgs are looked up by providing :id, :name, :basename, :edition, or the combination of :basename, :version, and :revision (which comprise the edition)
If passed only a :basename, the currently-live package for that basename
is used, an exception is raised if no version of the basename is live.
When creating a new d3 package use :id => :new, as for JSS::Package. You must provide :name, :basename, :version, and :revision.
To add a pkg to d3 that’s already in the JSS, use import (q.v.)
For new (or imported) packages, you may also provide any of the other
data keys mentioned in P_FIELDS.keys and they will be applied to the
new Package. You may also set them after instantiation using their
respective setter methods. A value for :admin must be set before
calling {#create}.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/d3/package/constructor.rb', line 50 def initialize (args={}) # refresh our pkg data first D3::Package.package_data :refresh # if we didn't get an edition, did we get the parts? if args[:basename] && args[:version] && args[:revision] args[:edition] ||= "#{args[:basename]}-#{args[:version]}-#{args[:revision]}" end args[:category] ||= D3::CONFIG.jss_default_pkg_category ############ Adding a New d3/jss package if args[:id] == :new # make sure we have the needed args unless args[:basename] and args[:version] and args[:revision] raise JSS::MissingDataError, "New d3 packages need :basename, :version, & :revision." end # does the edition we're creating already exist? if D3::Package.all_editions.include? args[:edition] raise JSS::AlreadyExistsError, "Package edition #{args[:edition]} already exists in d3" end @adding = true ############ Importing an existing JSS pkg? elsif args[:import] # args[:import] should only ever come from D3::Package.import # in ruby 1.8 use caller[1][/`([^']*)'/, 1] to get the label 'import' # doesn't matter since JSS now requires ruby 1.9.2 raise JSS::InvalidDataError, "Use D3::Package.import to import existing JSS packages to d3." unless caller_locations(2,1)[0].label == "import" # data checking was done in the import class method @importing = true ############ Looking up an existing package by id, name, basename, or edition else if args[:id] status = D3::Package.statuses_by(:id)[args[:id]] if status @status = :missing if status == :missing else raise JSS::NoSuchItemError, "No package in d3 with id: #{args[:id]}" end elsif args[:name] status = D3::Package.statuses_by(:name)[args[:name]] if status @status = :missing if status == :missing args[:id] = JSS::Package.map_all_ids_to(:name).invert[args[:name]] else raise JSS::NoSuchItemError, "No package in d3 with name: #{args[:name]}" end elsif args[:edition] status = D3::Package.statuses_by(:edition)[args[:edition]] if status @status = :missing if status == :missing args[:id] = D3::Package.ids_to_editions.invert[ args[:edition]] else raise JSS::NoSuchItemError, "No package in d3 with edition: #{args[:edition]}" end elsif args[:basename] args[:id] = D3::Package.basenames_to_live_ids[args[:basename]] raise JSS::NoSuchItemError, "No live package for basename '#{args[:basename]}'" unless args[:id] end # if args :id @lookup_existing = true end # if args[:id] == :new # if the pkg is missing from the jss, there's nothing to do below here return if @status == :missing # now we have an :id (which might be :new) so let JSS::Package do its work # this will tie us to a new or existing jss pkg super args # does this pkg need to be added to d3? if @adding or @importing d3pkg_data = args @status = :unsaved @in_d3 = false else # package already exists in both JSS and d3... # This prevents some checks from happening, since the data came from the DB @initializing = true d3pkg_data = D3::Package.package_data(:refresh)[@id] @in_d3 = true end # if @adding or @importing @basename = d3pkg_data[:basename] @version = d3pkg_data[:version] @revision = d3pkg_data[:revision] # process the d3 data if d3pkg_data # Loop through the field definitions for the pkg table and process each one # into it's ruby attribute. P_FIELDS.each do |fld_key, fld_def| # skip if we already have a value, e.g. basename was set above. next if self.send(fld_key) # Note - the d3pkgdata has already been 'rubyized' via the D3::Database.table_records method # (which was used by D3::Package.package_data) fld_val = d3pkg_data[fld_key] # if we have a setter method for this key, call it to set the attribute. setter = "#{fld_key}=".to_sym self.send(setter, fld_val) if self.respond_to?(setter, true) # the 'true' makes respond_to? look at private methods also end # PFIELDS.each end # if d3pkg_data # some nil-values shouldn't be nil @auto_groups ||= [] @excluded_groups ||= [] # expiration_paths should always be an array @expiration_paths ||= [] # prohibiting_processes should always be an array @prohibiting_processes ||= [] # these don't come from the table def. @admin = args[:admin] # dmg or pkg? @package_type = @receipt.to_s.end_with?(".dmg") ? :dmg : :pkg # this needs to be an array @apple_receipt_data ||= [] # re-enable checks @initializing = false end |
Instance Attribute Details
#added_by ⇒ String?
Returns the login name of the admin who added this packge to d3.
59 60 61 |
# File 'lib/d3/package/attributes.rb', line 59 def added_by @added_by end |
#added_date ⇒ Time
Returns when was this package was added to d3.
56 57 58 |
# File 'lib/d3/package/attributes.rb', line 56 def added_date @added_date end |
#admin ⇒ String (readonly) Originally defined in module Basename
Returns who’s uploading, releasing, installing, or archiving this thing?.
#apple_receipt_data ⇒ Array<Hash>
Returns the apple receipt data for the items installed by this pkg. When .[m]pkgs are installed, their identifiers and metadata are recorded in the OS’s receipts database and are accessible via the pkgutil command. (e.g. pkgutil –pkg-info com.company.application). Storing it in the DB allows us to do uninstalls and other client tasks without needing to index the pkg in Jamf Pro. Each hash has these keys:
-
:apple_pkg_id => String
-
:version => String
-
:installed_kb => Integer.
53 54 55 |
# File 'lib/d3/package/attributes.rb', line 53 def apple_receipt_data @apple_receipt_data end |
#auto_groups ⇒ Array
Returns a list of JSS::ComputerGroup names whose members get this package installed automatically.
72 73 74 |
# File 'lib/d3/package/attributes.rb', line 72 def auto_groups @auto_groups end |
#basename ⇒ String (readonly) Originally defined in module Basename
Returns the basname of the thing installed.
#excluded_groups ⇒ Array
Returns a list of JSS::ComputerGroup names for whose members this package is not available without force.
76 77 78 |
# File 'lib/d3/package/attributes.rb', line 76 def excluded_groups @excluded_groups end |
#expiration ⇒ Integer (readonly) Originally defined in module Basename
Returns the days of disuse before an expirable edition expires. 0=never.
#expiration_paths ⇒ String (readonly) Originally defined in module Basename
Returns the path to the executable that needs come to the foreground to prevent expiration.
#id ⇒ Integer (readonly) Originally defined in module Basename
Returns the JSS id of this package.
#in_d3 ⇒ Boolean (readonly)
Returns does this pkg exist in the d3 pkg table?.
95 96 97 |
# File 'lib/d3/package/attributes.rb', line 95 def in_d3 @in_d3 end |
#package_type ⇒ Symbol (readonly) Originally defined in module Basename
Returns Is this package a .dmg or .pkg?.
#post_install_script_id ⇒ Integer? Also known as: post_install_id
Returns the JSS::Script id of the post-install script, if any.
86 87 88 |
# File 'lib/d3/package/attributes.rb', line 86 def post_install_script_id @post_install_script_id end |
#post_remove_script_id ⇒ Integer? Also known as: post_remove_id
Returns the JSS::Script id of the post-remove script, if any.
92 93 94 |
# File 'lib/d3/package/attributes.rb', line 92 def post_remove_script_id @post_remove_script_id end |
#pre_install_script_id ⇒ Integer? Also known as: pre_install_id
Returns the JSS::Script id of the pre-install script, if any.
83 84 85 |
# File 'lib/d3/package/attributes.rb', line 83 def pre_install_script_id @pre_install_script_id end |
#pre_remove_script_id ⇒ Integer? Also known as: pre_remove_id
Returns the JSS::Script id of the pre-remove script, if any.
89 90 91 |
# File 'lib/d3/package/attributes.rb', line 89 def pre_remove_script_id @pre_remove_script_id end |
#prohibiting_processes ⇒ Array<String> (readonly) Originally defined in module Basename
Returns an array of Strings for matching to the output lines of ‘/bin/ps -A -c -o comm’. If there’s a match, this pkg won’t be installed or uninstalled without a graceful quit.
#release_date ⇒ Time?
Returns when was this package made live in d3.
62 63 64 |
# File 'lib/d3/package/attributes.rb', line 62 def release_date @release_date end |
#released_by ⇒ String?
Returns the login name of the admin who made it live.
65 66 67 |
# File 'lib/d3/package/attributes.rb', line 65 def released_by @released_by end |
#remove_first ⇒ Boolean Also known as: remove_first?
Returns should any currently installed versions of this basename be uninstalled (if possible) before installing this package?.
80 81 82 |
# File 'lib/d3/package/attributes.rb', line 80 def remove_first @remove_first end |
#revision ⇒ Integer (readonly) Originally defined in module Basename
Returns the d3 release number of the thing installed.
#standard ⇒ Boolean (readonly) Also known as: standard?
Returns does this pkg get installed automatically on all non-exluded clients?.
68 69 70 |
# File 'lib/d3/package/attributes.rb', line 68 def standard @standard end |
#status ⇒ Symbol (readonly)
Returns the current status of the pkg in d3.
98 99 100 |
# File 'lib/d3/package/attributes.rb', line 98 def status @status end |
#status ⇒ Symbol Originally defined in module Basename
Returns whats the d3 status of this package? One of the values of D3::Basename::STATUSES.
#version ⇒ String (readonly) Originally defined in module Basename
Returns the version of the thing installed.
Class Method Details
.all_basenames(refresh = false) ⇒ Array<String>
An Array of basenames known to d3
184 185 186 |
# File 'lib/d3/package/class_methods.rb', line 184 def self.all_basenames(refresh = false) self.package_data(refresh).values.map{|p| p[:basename] }.uniq end |
.all_editions(refresh = false) ⇒ Array<String>
An Array of editions known to d3
194 195 196 |
# File 'lib/d3/package/class_methods.rb', line 194 def self.all_editions(refresh = false) self.package_data(refresh).values.map{|p| p[:edition] } end |
.all_filenames(refresh = false) ⇒ Hash{Integer: String}
A Hash of all packages filenames keyed by pkg id These are looked up via a DB query because otherwise we’d have to instantiate a Package object for every package which is way too slow.
207 208 209 210 211 212 213 214 215 |
# File 'lib/d3/package/class_methods.rb', line 207 def self.all_filenames(refresh = false) @@filenames = nil if refresh return @@filenames if @@filenames @@filenames = {} qr = JSS::DB_CNX.db.query "SELECT package_id, file_name FROM packages WHERE package_id IN (SELECT package_id FROM #{D3::Package::P_TABLE[:table_name]})" qr.each_hash{|p| @@filenames[p["package_id"].to_i] = p["file_name"]} qr.free @@filenames end |
.all_ids(refresh = false, api: JSS.api) ⇒ Array<Integer>
An Array of all packages ids known to d3
164 165 166 |
# File 'lib/d3/package/class_methods.rb', line 164 def self.all_ids(refresh = false, api: JSS.api) self.package_data(refresh).keys end |
.all_names(refresh = false, api: JSS.api) ⇒ Array<String>
An Array of all packages names known to d3
174 175 176 |
# File 'lib/d3/package/class_methods.rb', line 174 def self.all_names(refresh = false, api: JSS.api) self.package_data(refresh).values.map{|p| p[:name]} end |
.auto_install_ids_for_group(group, refresh = false) ⇒ Array<Integer>
An array of ids for all pkgs that are auto-installed for a given computer group. Returns an empty array if no such group.
488 489 490 491 |
# File 'lib/d3/package/class_methods.rb', line 488 def self.auto_install_ids_for_group (group, refresh = false) pkgs = D3::Package.package_data(refresh).values pkgs.select{|p| p[:auto_groups].include? group}.map{|p| p[:id]} end |
.basenames_to_live_ids(refresh = false) ⇒ Hash{String => Integer}
A Hash mapping all basenames to their currently live jss id
235 236 237 238 239 240 |
# File 'lib/d3/package/class_methods.rb', line 235 def self.basenames_to_live_ids(refresh = false) # Hashes don't have #map, so #merge back onto ourselves to have the # same effect. lp = self.live_data(refresh) lp.merge(lp){|id,p| p[:basename] }.invert end |
.check_computer_groups(groups) ⇒ Array<String>
Check for existence of one or more computer groups in the JSS, raise an exception if any group doesn’t exist.
584 585 586 587 588 589 590 |
# File 'lib/d3/package/class_methods.rb', line 584 def self.check_computer_groups(groups) parsed_groups = JSS.to_s_and_a(groups) parsed_groups[:arrayform].each do |g| raise JSS::NoSuchItemError, "No ComputerGroup named '#{g}' in the JSS" unless JSS::ComputerGroup.all_names.include? g end return parsed_groups[:arrayform] end |
.deprecated_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “deprecated” packages
This is the package_data Hash, limited to those packages whose status is :depreicated. These packages were once live, and still exist in the JSS and can be made live again, though that isn’t recommended.
297 298 299 |
# File 'lib/d3/package/class_methods.rb', line 297 def self.deprecated_data(refresh = false) self.package_data(refresh, :deprecated) end |
.exclude_ids_for_group(group, refresh = false) ⇒ Array<Integer>
An array of ids for all pkgs that are exclude for a given computer group. Returns an empty array if no such group.
503 504 505 506 |
# File 'lib/d3/package/class_methods.rb', line 503 def self.exclude_ids_for_group (group, refresh = false) pkgs = D3::Package.package_data(refresh).values pkgs.select{|p| p[:excluded_groups].include? group}.map{|p| p[:id]} end |
.find_package(search_term, type = :pkg) ⇒ D3::Package?
Get a single D3::Package by using a search term.
The term is searched in this order: edition, basename, id, display name, filename.
If basename, returns the currently live pkg
The first match is returned, nil if no match
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/d3/package/class_methods.rb', line 349 def self.find_package(search_term, type = :pkg ) return nil if search_term.nil? if self.all_editions.include? search_term id = D3::Package.ids_to_editions.invert[search_term] elsif self.all_basenames.include? search_term id = self.basenames_to_live_ids[search_term] elsif self.all_ids.include? search_term.to_i id = search_term.to_i elsif self.all_names.include? search_term id = D3::Package.map_all_ids_to(:name).invert[search_term] elsif self.all_filenames.values.include? search_term id = self.all_filenames.invert[search_term] else return nil end # if elsif..... return nil unless id and id.is_a? Fixnum return type == :pkg ? D3::Package.fetch(:id => id) : self.package_data[id] end |
.ids_for_basename(basename, refresh = false) ⇒ Array
An Array of ids for all pkgs with a given basename
154 155 156 |
# File 'lib/d3/package/class_methods.rb', line 154 def self.ids_for_basename(basename, refresh = false) self.package_data(refresh).values.select{|p| p[:basename] == basename}.map{|p| p[:id]} end |
.ids_to_editions(refresh = false) ⇒ Hash{Integer => String}
A Hash mapping package ids to editions in d3
A package’s ‘edition’ is the combination of its basename, version, and revision, joined with hyphens into a String, which must be unique in d3.
141 142 143 144 |
# File 'lib/d3/package/class_methods.rb', line 141 def self.ids_to_editions(refresh = false ) pd = self.package_data(refresh) pd.merge(pd){|id,p| p[:edition]} end |
.import(ident, args) ⇒ D3::Package
Import an existing JSS::Package into d3.
A d3 basename and version must be provided.
If no revision is provided, it is set to 1.
If the JSS package is an Apple installer pkg, the read-only password for the current distribution point must be provided so that the Apple package identifier(s) can be queried from the pkg on the server.
After the D3::Package is instantiated, these and other d3-specific values can be changed before creating it on the server.
IMPORTANT: Even though the JSS package already exists, you must call #create after instantiating this new D3::Package in order to save it into d3.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 |
# File 'lib/d3/package/class_methods.rb', line 547 def self.import (ident, args) id = if JSS::Package.all_ids.include? ident ident else JSS::Package.map_all_ids_to(:name).invert[ident] end raise JSS::NoSuchItemError, "No JSS Package with name or id matching '#{ident}'" unless id raise JSS::AlreadyExistsError, "That JSS package already exists in d3" if self.all_ids.include? id raise JSS::MissingDataError, "Importing packages requires :basename" unless args[:basename] raise JSS::MissingDataError, "Importing packages requires :version" if args[:version].to_s.strip.empty? args[:revision] ||= 1 jss_pkg = JSS::Package.fetch :id => id tmp_edition = "#{args[:basename]}-#{args[:version]}-#{args[:revision]}" if self.all_editions.include? tmp_edition raise JSS::InvalidDataError, "A d3 pkg for edition #{tmp_edition} already exists." end # unless args[:id] = id args[:import] = true args[:unmount] = true if args[:unmount].nil? imported_pkg = self.new(args) imported_pkg.update_apple_receipt_data args[:dist_pw], args[:unmount] imported_pkg end |
.live_basenames(refresh = false) ⇒ Array
An Array of basenames that have live packages.
268 269 270 |
# File 'lib/d3/package/class_methods.rb', line 268 def self.live_basenames(refresh = false) self.basenames_to_live_ids(refresh).keys end |
.live_data(refresh = false) ⇒ Hash{Integer=>Hash}
A Hash of Package Data for all live packages
This is the package_data Hash, limited to those packages whose status is :live, i.e. the package that gets installed for its basename.
225 226 227 |
# File 'lib/d3/package/class_methods.rb', line 225 def self.live_data(refresh = false) self.package_data(refresh, :live) end |
.live_ids(refresh = false) ⇒ Array<String>
An Array of all packages ids that are live
248 249 250 |
# File 'lib/d3/package/class_methods.rb', line 248 def self.live_ids(refresh = false) self.live_data(refresh).keys end |
.live_names(refresh = false) ⇒ Array<String>
An Array of all packages names that are live
258 259 260 |
# File 'lib/d3/package/class_methods.rb', line 258 def self.live_names(refresh = false) self.live_data(refresh).values.map{|p| p[:name]} end |
.missing_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “missing” packages
This is the package_data Hash, limited to those packages whose status us :missing - i.e. the package id no longer exists in the JSS.
325 326 327 |
# File 'lib/d3/package/class_methods.rb', line 325 def self.missing_data(refresh = false) self.package_data(refresh, :missing) end |
.most_recent_package_for(basename) ⇒ D3::Package?
Get the most recent package on the server for a given basename
383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/d3/package/class_methods.rb', line 383 def self.most_recent_package_for(basename) return nil unless D3::Package.all_basenames.include? basename # deal with potentially missing pkgs: # start with the highest id until we find an existing one self.ids_for_basename(basename).sort.reverse.each do |id| begin pkg = D3::Package.fetch :id => id return pkg rescue JSS::NoSuchItemError next end end return nil end |
.package_data(refresh = false, status = :all) ⇒ Hash<Hash>
Raw(ish) SQL data for all d3 packages as a Hash of Hashes.
The keys are the JSS ids of the packages
The values are records from the d3_packages table, as Hashes with keys matching the keys of D3::Database::PACKAGE_TABLE plus these fields from the JSS’s packages table:
:name, :require_reboot, :oses, :required_processor
This raw data, queried directly via SQL, lets us process lists of packages without instantiating each one as a D3::Package object, which is slow due to API calls for each package. We can use this data to instantiate those package objects when they are actually needed.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/d3/package/class_methods.rb', line 58 def self.package_data(refresh = false, status = :all) @@package_data = nil if refresh if @@package_data.nil? # get a few fields from the JSS package data # store them in a hash keyed by id #jss_q = "SELECT package_id, package_name, os_requirements, require_reboot, required_processor FROM #{JSS::Package::DB_TABLE} WHERE package_id IN (SELECT package_id from #{P_TABLE[:table_name]})" jss_q = "SELECT package_id, package_name, os_requirements, require_reboot, required_processor, allow_uninstall FROM #{JSS::Package::DB_TABLE}" r = JSS::DB_CNX.db.query jss_q jss_data = {} r.each_hash{|jpkg| jss_data[jpkg["package_id"].to_i] = jpkg } r.free # get the table records and add in the appropriate names @@package_data = {} D3::Database.table_records(D3::Package::P_TABLE).each do |p| d3_id = p[:id] @@package_data[d3_id] = p @@package_data[d3_id][:edition] = "#{p[:basename]}-#{p[:version]}-#{p[:revision]}" # D3::Database.table_records returns NULL as nil even if converting with STRING_TO_INT # which in general is a good thing, but expiration should always be an integer and NULL should be 0 @@package_data[d3_id][:expiration] = p[:expiration].to_i jinfo = jss_data[d3_id] # is this pkg still in the JSS? if jinfo @@package_data[d3_id][:name] = jinfo["package_name"] @@package_data[d3_id][:oses] = jinfo["os_requirements"] @@package_data[d3_id][:reboot] = (jinfo["require_reboot"] == "1") # boolean @@package_data[d3_id][:required_processor] = jinfo["required_processor"] @@package_data[d3_id][:removable] = (jinfo["allow_uninstall"] == "1") # or is it missing? else @@package_data[d3_id][:status] = :missing @@package_data[d3_id][:name] = "** missing from jss **" @@package_data[d3_id][:oses] = "" @@package_data[d3_id][:reboot] = false @@package_data[d3_id][:required_processor] = "None" @@package_data[d3_id][:removable] = false end # if jinfo end # D3::Database.table_records(D3::Package::P_TABLE).each do |p| end # if @@package_data.nil? if status == :all return @@package_data else raise JSS::InvalidDataError, "status must be one of :#{STATUSES.join(', :')}" unless STATUSES.include? status # reject because Hash#select returns an array of arrays return @@package_data.reject{|id,p| p[:status] != status } end #if status == :all end |
.packages_for_script(script, script_type = nil, refresh = false) ⇒ Array<Integer>
An Array of pkg ids for all pkgs that use a given script, optionally limiting to those pkgs that use the script for a given purpose.
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/d3/package/class_methods.rb', line 452 def self.packages_for_script (script, script_type = nil, refresh = false) if script_type raise JSS::InvalidDataError, "Script type must be one of :#{SCRIPT_TYPES.join(' :')}" unless SCRIPT_TYPES.include? script_type end # if screipt type pkgs = [] # confirm the ID of the script.. sid = JSS::Script.all_ids(refresh).include?(script) ? script : nil sid ||= JSS::Script.map_all_ids_to(:name).invert[script] # script id has to exist in JSS and some d3 pkg return pkgs unless sid and self.scripts(refresh)[sid] self.scripts(refresh)[sid].each do |pkg_id, uses| if script_type pkgs << pkg_id if uses.include? script_type else pkgs << pkg_id end end # each script id, pkgs pkgs end |
.pilot_data(refresh = false) ⇒ Hash<Hash>
A Hash of Package Data for all “pilot” packages
This is the package_data Hash, limited to those packages whose status is :pilot, i.e. they are not live and are newer than the live pkg for their basename.
282 283 284 |
# File 'lib/d3/package/class_methods.rb', line 282 def self.pilot_data(refresh = false) self.package_data(refresh, :pilot) end |
.receipt_data_from_bundle_pkg(pkg_path) ⇒ Object
givin a Pathname to a bundle pkg, return an array of hashes with data for all the pkg rcpts hat will be installed each has includes at least :apple_pkg_id, :version, and :installed_kb
Thanks to Greg Neagle for inspiration on this method from munkicommon.py
675 676 677 678 679 680 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 |
# File 'lib/d3/package/class_methods.rb', line 675 def self.receipt_data_from_bundle_pkg (pkg_path) pkg_path = Pathname.new(pkg_path) unless pkg_path.is_a? Pathname rcpts = [] # if this is a single pkg, not a metapkg, data comes from Info.plist if pkg_path.to_s.end_with? ".pkg" rcpt_data = {} info_plist = pkg_path + "Contents/Info.plist" if info_plist.exist? plist = D3.parse_plist info_plist rcpt_data[:apple_pkg_id] = plist["CFBundleIdentifier"] rcpt_data[:apple_pkg_id] ||= plist["Bundle Identifier"] rcpt_data[:apple_pkg_id] ||= pkg_path.basename.to_s rcpt_data[:installed_kb] = plist["IFPkgFlagInstalledSize"] rcpt_data[:version] = plist["CFBundleShortVersionString"] rcpt_data[:version] ||= plist["CFBundleVersion"] rcpt_data[:version] ||= plist["Bundle versions string, short"] rcpts << rcpt_data end # if plist exist? end # if rcpts is empty, it could be an mpkg, which installs more than one pkg if rcpts.empty? contents_dir = info_plist = pkg_path + "Contents" dist_file = contents_dir.children.select{|c| c.to_s.end_with? ".dist"}[0] return self.receipt_data_from_xml(dist_file, pkg_path) if dist_file # no dist file - any other embedded packages? pkg_path.find do |sub_item| next unless sub_item.to_s =~ PKG_RE rcpts += self.receipt_data_from_bundle_pkg(sub_item) end # pkg_path.find end # if pkg_path.to_s.end_with? ".pkg" rcpts end |
.receipt_data_from_flat_pkg(pkg_path) ⇒ Object
Givinn a Pathname to a flat package, return an array of hashes with data for all the pkg rcpts hat will be installed each has includes at least :apple_pkg_id, :version, and :installed_kb
Thanks to Greg Neagle for inspiration on this method from munkicommon.py
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
# File 'lib/d3/package/class_methods.rb', line 620 def self.receipt_data_from_flat_pkg (pkg_path) pkg_path = Pathname.new(pkg_path) rcpts = [] pkg_contents = `/usr/bin/xar -tf #{Shellwords.escape pkg_path.to_s}` raise "Could not look at contents of flat package #{pkg_path.basename}" unless $CHILD_STATUS.exitstatus == 0 start_dir = Pathname.pwd work_dir = Pathname.new Dir.mktmpdir Dir.chdir work_dir begin # loop thru the items in the flat pkg # extracting any PackageInfo and Distribution files xml_files = [] pkg_contents.each_line do |line| line.chomp! # if there's a top level Dist or PackageInfo, use it exclusively if line. == "PackageInfo" or line == "Distribution" xml_files = [line] break # otherwise find all sub PackageInfos elsif line.end_with? ".pkg/PackageInfo" xml_files << line end end # pkg_contents.each_line do line # Extract whatever files we found interesting xml_files.each do |xml_file| system "/usr/bin/xar", "-xf", pkg_path.to_s, xml_file raise raise "Error reading #{xml_file} from flat package #{pkg_path.basename}" unless $CHILD_STATUS.exitstatus == 0 extracted_file = work_dir + xml_file rcpts += self.receipt_data_from_xml(extracted_file, pkg_path) end # xml files.each ensure Dir.chdir start_dir work_dir.rmtree end # begin rcpts end |
.receipt_data_from_pkg(pkg_path) ⇒ Array<Hash>
Givin a Pathname to a package, return an array of hashes with data for all the pkg rcpts that will be installed each hash includes at least :apple_pkg_id, :version, and :installed_kb
Thanks to Greg Neagle for inspiration on this method from munkicommon.py
602 603 604 605 606 607 608 609 610 611 612 |
# File 'lib/d3/package/class_methods.rb', line 602 def self.receipt_data_from_pkg (pkg_path) pkg_path = Pathname.new(pkg_path) unless pkg_path.is_a? Pathname raise "The path given must end with .pkg or .mpkg" unless pkg_path.to_s =~ PKG_RE raise "Package '#{pkg_path}' doesn't exist" unless pkg_path.exist? if pkg_path.directory? self.receipt_data_from_bundle_pkg(pkg_path).uniq else self.receipt_data_from_flat_pkg(pkg_path).uniq end end |
.receipt_data_from_xml(xml_file_path, pkg_path = nil) ⇒ Object
Parse an xml file (like a .dist, Distribution, or PackageInfo file) and find all pkg-ref or pkg-info elements to extract their pkg ids and other data, or locate and recurse on any sub-pkgs. Return an array of hashes of pkg data. xml_file_path is a String or Pathname to the xml file. If the xml file is not embedded in a pkg (eg it was extracted from a flat pkg), provide the path to the pkg as the second arg.
Thanks to Greg Neagle for inspiration on this method from munkicommon.py
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 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 |
# File 'lib/d3/package/class_methods.rb', line 725 def self.receipt_data_from_xml(xml_file_path, pkg_path = nil) # both args must be Pathnames if they're strings xml_file_path = Pathname.new(xml_file_path) unless xml_file_path.is_a? Pathname pkg_path = Pathname.new(pkg_path) if pkg_path and !pkg_path.is_a?(Pathname) # this will be returned - an array of hashes of data about this pkg and sub-pkgs rcpts = [] # parse the xml doc = REXML::Document.new(File.new(xml_file_path)) #### # pkg-info elements doc.elements.each("//pkg-info") do |pkg_info_element| attribs = pkg_info_element.attributes # we only care about elements with both an identifier and a version next unless attribs["identifier"] && attribs["version"] data = { :apple_pkg_id => attribs["identifier"], :version => attribs["version"] } payload = pkg_info_element.elements.to_a('payload')[0] data[:installed_kb] = payload.attributes["installKBytes"].to_i if payload.attributes["installKBytes"] rcpts << data unless rcpts.include? data return rcpts unless rcpts.empty? end # doc.elements.each("*/pkg-ref") do |pkg| #### # pkg-ref elements all_ref_data = {} doc.elements.each("//pkg-ref") do |pkg_ref_element| attribs = pkg_ref_element.attributes next unless attribs["id"] && attribs["version"] # make a new hash for this pkg if needed this_ref_data = {:apple_pkg_id => attribs["id"]} # any inner-content of the element is a path to a sub-pkg if pkg_ref_element.text this_ref_data[:sub_pkg_ref] = pkg_ref_element.text # if its a file: url, its relative to the pkg_path if this_ref_data[:sub_pkg_ref] =~ /^file:.*\.pkg$/ this_ref_data[:sub_pkg_path] = (pkg_path || xml_file_path.dirname) + URI.decode(this_ref_data[:sub_pkg_ref][5..-1]) # but it might be a relative path from the cwd, starting with a #, which suould be ignored. elsif this_ref_data[:sub_pkg_ref] =~ /^#.*\.pkg$/ this_ref_data[:sub_pkg_path] = xml_file_path.dirname + URI.decode(this_ref_data[:sub_pkg_ref][1..-1]) end # if this_ref_data[:sub_pkg_ref] =~ /^file:.*\.pkg$/ end # pkg_ref_element.text this_ref_data[:version] = attribs["version"] this_ref_data[:installed_kb] = attribs["installKBytes"].to_i this_ref_data[:auth] = attribs["auth"] sub_pkg_data = [] # if we have a file path to an existing pkg, try to get data from it rather than from this xml if this_ref_data[:sub_pkg_path] and this_ref_data[:sub_pkg_path].exist? sub_pkg_data = receipt_data_from_pkg(this_ref_data[:sub_pkg_path]) end # did the sub pkg have data? # if not, use the data from this xml, as long as we have a version # if sub_pkg_data.empty? if this_ref_data[:version] this_ref_data.delete :sub_pkg_path rcpts << this_ref_data end else # but if it did, then use the data from the sub pkg. rcpts += sub_pkg_data end # this_ref_data[:sub_pkg_path] end # doc.elements.each("*/pkg-ref") do |pkg_ref_element| # clean up each rcpt hash, subpkg ref isn't needed any more rcpts.each{|r| r.delete :sub_pkg_ref } rcpts end |
.scripts(refresh = false) ⇒ Hash{Integer => Hash{Integer => Array<Symbol>}}
A Hash of Hashes of all scripts used by d3 packages. Each key is a script ID, Each value is a sub-Hash with one entry per d3 pkg that uses the script.
The sub-Hashes have pkg id’s as keys, and an Array of script usages as values. (since a pkg can use the same script for any or all of the 4 script types)
Example: {
123 => { 234 => [:pre_install] },
456 => { 234 => [:post_install],
345 => [:post_install, :pre_remove] }
}
In the example above,
- script id 123 is used by pkg id 234 as a pre-install script
- script id 456 is used by pkg id 234 as a post-install script
and used by pkg id 345 as both a post-install and pre-remove script.
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/d3/package/class_methods.rb', line 423 def self.scripts(refresh = false) scripts = {} self.package_data(refresh, :all).each do |pkg_id, pkg_data| SCRIPT_TYPES.each do |script_type| script_id_key = "#{script_type}_script_id".to_sym if pkg_data[script_id_key] scr_id = pkg_data[script_id_key] scripts[scr_id] ||= {} scripts[scr_id][pkg_id] ||= [] scripts[scr_id][pkg_id] << script_type end end # each script type end # do each pkg_id, pkg_data scripts end |
.skipped_data(refresh = false) ⇒ Array<Hash>
A Hash of Package Data for all “skipped” packages
This is the package_data Hash, limited to those packages whose status us :skipped - i.e. the were never made live before a newer pkg of the same basename was made live.
311 312 313 |
# File 'lib/d3/package/class_methods.rb', line 311 def self.skipped_data(refresh = false) self.package_data(refresh, :skipped) end |
.statuses_by(identifier, refresh = false) ⇒ Hash{String,Integer => Symbol] the statuses of the packages
A Hash of package identifiers (id’s, names, editions) as keys, to the status of the package (symbols)
125 126 127 128 129 130 |
# File 'lib/d3/package/class_methods.rb', line 125 def self.statuses_by (identifier, refresh = false) raise JSS::InvalidDataError, "identifier must be one of :id, :edition, or :name" unless [:id, :name, :edition].include? identifier stati = {} self.package_data(refresh).values.each{|pkg| stati[pkg[identifier]] = pkg[:status] } stati end |
Instance Method Details
#<=>(other) ⇒ Object Originally defined in module Basename
Use comparable to give sortability and equality.
#add_auto_groups(groupnames) ⇒ void
This method returns an undefined value.
Add one or more groups the to list of auto_groups. The arg is a comma-separated string or an array of group names.
207 208 209 210 211 212 213 214 215 |
# File 'lib/d3/package/setters.rb', line 207 def add_auto_groups (groupnames) # are they real groups? new_groups = validate_groups(groupnames) # if they're already there, just return return @auto_groups if (@auto_groups & new_groups) == new_groups validate_non_overlapping_groups new_groups, @excluded_groups @auto_groups += new_groups @need_to_update_d3 = true unless @initializing end |
#add_excluded_groups(groupnames) ⇒ void
This method returns an undefined value.
Add one or more groups the to list of excluded_groups. The arg is a comma-separated string or an array of group names.
256 257 258 259 260 261 262 263 264 |
# File 'lib/d3/package/setters.rb', line 256 def add_excluded_groups (groupnames) # are they real groups? new_groups = validate_groups(groupnames) # if they're already there, just return return @excluded_groups if (@excluded_groups & new_groups) == new_groups validate_non_overlapping_groups new_groups, @auto_groups @excluded_groups += new_groups @need_to_update_d3 = true unless @initializing end |
#add_expiration_path(path) ⇒ Object
Add a path to expiration_paths The paths are those recorded in d3RepoMan’s timestamp plists.
145 146 147 148 149 |
# File 'lib/d3/package/setters.rb', line 145 def add_expiration_path (path) path = validate_expiration_path(path) @expiration_paths << path @need_to_update_d3 = true unless @initializing end |
#admin=(new_val = @admin) ⇒ void
This method returns an undefined value.
Set the login name of the admin who’s doing something with this pkg
38 39 40 41 42 |
# File 'lib/d3/package/setters.rb', line 38 def admin= (new_val = @admin) return nil if new_val == @admin raise "admin can't be empty!" if new_val.to_s == '' @admin = new_val end |
#auto_clean(admin) ⇒ void
This method returns an undefined value.
Perform any auto_cleanup, if the config says we should
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/d3/package/server_actions.rb', line 321 def auto_clean (admin) ### safety return unless D3::CONFIG.admin_auto_clean puts "Starting auto-clean of old packages for '#{@basename}'" #### First the deprecated pkgs # the id's of the deprecated pkgs for this basename, in numerical order # the last ones are the newest. deprecated_ids = D3::Package.deprecated_data.values.select{|dp| dp[:basename] == @basename} deprecated_ids.map!{|dp| dp[:id] }.sort! # keeping any? number_deprecated_to_keep = D3::CONFIG.admin_auto_clean_keep_deprecated number_deprecated_to_keep ||= 0 puts "Keeping #{number_deprecated_to_keep} deprecated packages." # 'pop' pulls them off the end deprecated_ids_to_keep = [] number_deprecated_to_keep.times{ deprecated_ids_to_keep << deprecated_ids.pop } deprecated_ids_to_keep.compact! # delete them if we should deprecated_ids.each do |id| next if deprecated_ids_to_keep.include? id victim = D3::Package.fetch(:id => id) victim.delete( admin: admin, keep_scripts: false, keep_in_jss: false, rwpw: D3::Admin::Auth.rw_credentials(:dist)[:password] ) puts "Deleted deprecated package: #{victim.edition}, id:#{victim.id}, filename: #{victim.filename}." end #### then the skipped pkgs skipped_ids = D3::Package.skipped_data.values.select{|sp| sp[:basename] == @basename} skipped_ids.map!{|sp| sp[:id] }.sort! # keep the ones newer than the just-deprecated pkg? if D3::CONFIG.admin_auto_clean_keep_latest_pilots deprecated_ids = D3::Package.deprecated_data(:refresh).values.select{|dp| dp[:basename] == @basename} deprecated_ids.map!{|dp| dp[:id] } just_deprecated = deprecated_ids.max just_deprecated ||= 0 skipped_ids_to_keep = skipped_ids.select{|id| id > just_deprecated } puts "Keeping most recent pilot packages as skipped." # no, delete them all else skipped_ids_to_keep = [] puts "Not keeping any pilot or skipped packages." end # delete them if we should skipped_ids.each do |id| next if skipped_ids_to_keep.include? id victim = D3::Package.fetch(:id => id) victim.delete( admin: admin, keep_scripts: false, keep_in_jss: false, rwpw: D3::Admin::Auth.rw_credentials(:dist)[:password] ) puts "Deleted skipped package: #{victim.edition}, id:#{victim.id}, filename: #{victim.filename}." end puts "Finished auto-clean of old packages for '#{@basename}'" return true end |
#basename=(new_val = @basename) ⇒ Object
Set the basename of this package
new_val = string
48 49 50 51 52 53 |
# File 'lib/d3/package/setters.rb', line 48 def basename= (new_val = @basename) return nil if new_val == @basename validate_edition "#{new_val}-#{@version}-#{@revision}" @basename = new_val.to_s @need_to_update_d3 = true unless @initializing end |
#basename_exist?(name) ⇒ Boolean Originally defined in module Validate
Check the existence of a basename in d3.
#bom ⇒ Object
75 |
# File 'lib/d3/package/aliases.rb', line 75 alias bom index |
#check_cpu ⇒ Object
Check if this machine is OK wrt to the processor limitations Raise an exception if not
return [void]
87 88 89 90 |
# File 'lib/d3/package/validate.rb', line 87 def check_cpu my_cpu = `/usr/bin/uname -p`.chomp raise D3::InstallError, "This machine doesn't have the correct OS to install #{self.edition}." unless JSS.processor_ok? @required_processor, my_cpu end |
#check_for_deprecated ⇒ Object
Check that we’re not installing a deprecated pkg, and raise an exception if we are.
return [void]
48 49 50 |
# File 'lib/d3/package/validate.rb', line 48 def check_for_deprecated raise D3::InstallError, "#{edition} is deprecated. Use --force if needed." if deprecated? end |
#check_for_exclusions ⇒ Object
Check if this machine is in an excluded group. Raise an exception if so.
return [void]
65 66 67 68 69 |
# File 'lib/d3/package/validate.rb', line 65 def check_for_exclusions excl_grps = D3::Client.computer_groups & @excluded_groups raise D3::InstallError, "This machine is excluded for #{edition}. Use --force if needed." unless excl_grps.empty? true end |
#check_for_newer_version ⇒ Object
Check that there’s not a newer version of this thing alreay installed Raise an exception if so.
return [void]
38 39 40 41 42 |
# File 'lib/d3/package/validate.rb', line 38 def check_for_newer_version rcpt = D3::Client::Receipt.all[@basename] # if D3::Client::Receipt.basenames.include? @basename return unless rcpt raise D3::InstallError, "The installed #{rcpt.edition} (#{rcpt.status}) is the same or newer. Use --force if needed." if rcpt.id >= @id end |
#check_for_skipped ⇒ Object
Check that we’re not trying to install a skipped pkg, and raise an exception if we are.
return [void]
56 57 58 |
# File 'lib/d3/package/validate.rb', line 56 def check_for_skipped raise D3::InstallError, "#{edition} was skipped. Use --force if needed." if skipped? end |
#check_oses ⇒ Object
Check if this machine is OK wrt to the os limitations Raise an exception if not
return [void]
76 77 78 79 80 |
# File 'lib/d3/package/validate.rb', line 76 def check_oses my_os = `/usr/bin/sw_vers -productVersion`.chomp raise D3::InstallError, "This machine doesn't have the correct OS to install #{self.edition}." unless JSS.os_ok? @os_requirements, my_os true end |
#create ⇒ Integer
Create this package in the JSS if needed, and in d3
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/d3/package/server_actions.rb', line 34 def create # if it's already there, just return return @id if @in_d3 # gotta know who did this raise JSS::MissingDataError, "An admin name must be set before creating this new d3 package. Use #admin= " unless @admin # create the JSS package if needed super unless @in_jss # who and when are we adding this pkg? @added_date = Time.now @added_by = @admin # change status from unsaved to pilot @status = :pilot # loop through the field definitions, and # use them to get data for the insert statement field_names = [] sql_values = [] P_FIELDS.each_pair do |key,field_def| field_names << field_def[:field_name] # nils and empty strings become NULL sql_values << (self.send(key).to_s.empty? ? 'NULL' : "'#{to_sql(key)}'") end # do |key,field_def # use the two arrays to build the SQL statement stmt = JSS::DB_CNX.db.prepare <<-ENDINSERT INSERT INTO #{P_TABLE[:table_name]} ( #{field_names.join(",\n ")} ) VALUES ( #{sql_values.join(",\n ")} ) ENDINSERT # Execute it to create the record stmt_result = stmt.execute # while we're writing to the db, mark any missing packages as missing mark_missing_packages @in_d3 = true return @id end |
#created? ⇒ Boolean
Returns Is this pkg in on the server?.
52 53 54 |
# File 'lib/d3/package/questions.rb', line 52 def created? @in_jss and @in_d3 end |
#delete(keep_in_jss: false, keep_scripts: false, admin: @admin, rwpw: nil) ⇒ Array<String>
Delete this package from d3, possibly leaving it in the JSS
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/d3/package/server_actions.rb', line 408 def delete (keep_in_jss: false, keep_scripts: false, admin: @admin, rwpw: nil) unless keep_in_jss # raise an exception if any polcies are using this pkg. pols = policy_ids unless pols.empty? names = pols.map{|pid| JSS::Policy.map_all_ids_to(:name)[pid]}.join(', ') raise JSS::UnsupportedError, "Can't delete package from JSS, used by these policies: #{names} " end # unless pols.empty end # unles keep in jss # use @ admin if its defined and needed admin ||= @admin # delete scripts or not? the result is an array of what happened. script_actions = keep_scripts ? [] : delete_pkg_scripts # delete it from the pakcages table stmt = JSS::DB_CNX.db.prepare "DELETE FROM #{P_TABLE[:table_name]} WHERE #{P_FIELDS[:id][:field_name]} = '#{@id}'" stmt_result = stmt.execute @status = :deleted # delete it from the JSS unless asked not to unless keep_in_jss super delete_file: true, rw_pw: rwpw, unmount: false end # while we're writing to the db, mark any missing packages as missing mark_missing_packages # update our knowledge of the world D3::Package.package_data :refresh return script_actions end |
#deleted? ⇒ Boolean Originally defined in module Basename
Is the status :deleted?
#deprecated? ⇒ Boolean
Returns Is this pkg in pilot? (saved, but never made live).
64 65 66 |
# File 'lib/d3/package/questions.rb', line 64 def deprecated? @status == :deprecated end |
#edition ⇒ String Originally defined in module Basename
While several packages can have the same basename, the combination of basename, version, and revision (called the ‘edition’) must be unique among the d3 packages.
#edition_exist?(edition) ⇒ Boolean Originally defined in module Validate
Check the existence of an edition in d3.
#expiration=(new_val = @expiration) ⇒ void
This method returns an undefined value.
Set the expiration period for all installs of this pkg. Once installed, if this many days go by without the @expiration_paths being launched, as noted by d3repoman, the pkg will be silently uninstalled.
Use nil, false, or 0 (the default) to prevent expiration.
When expiration happens, a policy can be triggered to notify the user or take other actions. See Client#expiration_policy
Can be over-ridden on a per-install basis using the :expiration option with the #install method
111 112 113 114 115 116 |
# File 'lib/d3/package/setters.rb', line 111 def expiration= (new_val = @expiration) return @expiration if new_val == @expiration new_val ||= 0 @expiration = validate_expiration(new_val) @need_to_update_d3 = true unless @initializing end |
#expiration_paths=(new_val = @expiration_paths) ⇒ void
This method returns an undefined value.
Set the expiration paths for this pkg. These are abosulute paths to executables one of which must be brought to the foreground at least once every @expiration days to prevent silent un-installing of this package.
The paths are those recorded in d3RepoMan’s timestamp plists.
133 134 135 136 137 |
# File 'lib/d3/package/setters.rb', line 133 def expiration_paths= (new_val = @expiration_paths) return @expiration_paths if D3::Admin::OPTIONS[:expiration_paths][:compare].call(@expiration_paths, new_val) @expiration_paths = validate_expiration_paths (new_val) @need_to_update_d3 = true unless @initializing end |
#expiration_paths_match?(other_exp_paths) ⇒ Boolean Originally defined in module Basename
Does a given array of pathnames have the same elements as This is generally used to compare two @expiration_paths arrays for “equality”
#expires? ⇒ Boolean
Returns Does this pkg expire by default?.
83 84 85 |
# File 'lib/d3/package/questions.rb', line 83 def expires? @expiration.to_i > 0 && @expiration_paths end |
#file_list ⇒ Object
76 |
# File 'lib/d3/package/aliases.rb', line 76 alias file_list installed_files |
#filename_exist?(name) ⇒ Boolean Originally defined in module Validate
Check the existence of a filename in the JSS.
#files ⇒ Object
77 |
# File 'lib/d3/package/aliases.rb', line 77 alias files installed_files |
#formatted_details ⇒ String
Generate a human-readable string of details about this installer
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/d3/package/getters.rb', line 84 def formatted_details os_disp = JSS.to_s_and_a(@os_requirements)[:stringform] auto_disp = JSS.to_s_and_a(@auto_groups)[:stringform] excl_disp = JSS.to_s_and_a(@excluded_groups)[:stringform] msg = <<-END_DEETS Edition: #{edition} Status: #{@status} ---- Description ---- #{@notes.gsub("\r", "\n") if @notes} --------------------- Added by: #{@added_by or 'unknown'} Added date: #{@added_date ? @added_date.strftime('%Y-%m-%d') : 'unknown'} Jamf Pro Package: #{@name} (id: #{@id}) Filename: #{@filename} Category: #{@category or 'None'} Needs reboot (puppytime): #{@reboot_required or 'false'} Un-installable: #{removable? or 'false'} Pre-install script: #{pre_install_script_name or 'None'} Post-install script: #{post_install_script_name or 'None'} Pre-remove script: #{pre_remove_script_name or 'None'} Post-remove script: #{post_remove_script_name or 'None'} CPU limitation: #{@required_processor or 'None'} OS limitations: #{os_disp.empty? ? 'None' : os_disp} Uninstalls older versions: #{@remove_first or 'false'} Installation prohibited by process(es): #{D3::Admin::OPTIONS[:prohibiting_processes][:display_conversion].call @prohibiting_processes or 'None'} Auto installed for groups: #{auto_disp.empty? ? 'None' : auto_disp} Excluded for groups: #{excl_disp.empty? ? 'None' : excl_disp} Expiration period: #{@expiration.to_i} days Expiration path(s): #{D3::Admin::OPTIONS[:expiration_paths][:display_conversion].call @expiration_paths} Released by: #{@released_by or '-'} Release date: #{@release_date ? @release_date.strftime('%Y-%m-%d') : '-'} END_DEETS end |
#index(files_only = false) ⇒ Array<String>
The index of this package This is an array of paths (as Strings) from the pkg’s Jamf Pro index
126 127 128 129 130 131 132 |
# File 'lib/d3/package/getters.rb', line 126 def index (files_only = false) q = "SELECT file FROM #{PKG_CONTENTS_TABLE} WHERE package_id = #{@id}" q += " AND mode NOT LIKE 'd%'" if files_only @index = [] JSS::DB_CNX.db.query(q).each {|record| @index << record[0]} @index end |
#indexed? ⇒ Boolean
Returns Is this pkg ‘indexed’ in the jss, so that it can be removable?.
89 90 91 |
# File 'lib/d3/package/questions.rb', line 89 def indexed? not index.empty? end |
#install(args = {}) ⇒ String
Install this pkg on this machine. D3 pkgs are more involved than plain JSS pkgs.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/d3/package/client_actions.rb', line 89 def install(args = {}) # force can't get around these: raise D3::InstallError, "This package is missing from the JSS, cannot install." if @status == :missing check_oses check_cpu begin # for the ensure below # if this item is now being installed by puppies at logout, # the PuppyQ has our admin name, force setting, and expiration. # It if isn't in the puppyQ, then those values should # have come from the args if args[:puppywalk] and D3::PUPPY_Q.q[@basename] then args[:admin] = D3::PUPPY_Q.q[@basename].admin ? D3::PUPPY_Q.q[@basename].admin : "puppy-install" args[:force] = D3::PUPPY_Q.q[@basename].force args[:expiration] = D3::PUPPY_Q.q[@basename].expiration else # if this is a manual install, we should know # who's doing it args[:admin] ||= D3.admin end forced = args[:force] or D3::forced? # excluded pkgs check_for_exclusions unless forced @admin = args[:admin] # pilot? D3::Client.set_env :installing, edition D3::Client.set_env :pkg_status, @status # force? @using_force = forced ? " with force" : "" # pilot installs need an admin from the args or the queue raise JSS::MissingDataError, "Missing :admin for pilot install." if pilot? and @admin == D3::AUTO_INSTALL_ADMIN # If we aren't actually installing the puppy queue items # and there's already a member of this basename in the queue # raise an exception, if D3::PUPPY_Q.pups.include? @basename && (not args[:puppywalk]) && (not forced) raise D3::InstallError, "#{@basename} (#{D3::PUPPY_Q.q[@basename].edition}) is already queued for puppies. Use force if needed." end if removable? if args[:expiration] && @expiration != args[:expiration] @expiration_to_apply = args[:expiration] @custom_expiration = args[:expiration] else @expiration_to_apply = @expiration @custom_expiration = false end else # not removable, can't expire @expiration_to_apply = 0 end # if remmovable ### ### Queue for Puppies ### if reboot? && (not args[:puppywalk]) queue_for_puppies forced ### ### Regular Install... ### else unless forced raise D3::InstallError, "#{edition} cannot be installed now because one or more of the following processes is running: #{D3::Admin::OPTIONS[:prohibiting_processes][:display_conversion].call @prohibiting_processes}." if install_prohibited_by_process? end # unless forced remove_previous_installs_if_needed (args[:verbose]) D3.log "Installing: #{edition} (#{@status})#{@using_force}", :warn # pre-install script pre_install_status = run_pre_install_script(args[:verbose]) # exit 111 means write receipts, but don't acutally install if pre_install_status == 111 then D3.log "Pre-install script for #{edition} exited with status '111'; Not installing but writing receipt.", :info write_rcpt # if this was a puppy install, remove it from the queue D3::PUPPY_Q - @basename return pre_install_status elsif pre_install_status != 0 then D3.log "Pre_install script for #{edition} failed, exit status: #{pre_install_status}, not installing.", :error raise D3::PreInstallError, "Pre_install script for #{edition} failed, exit status: #{pre_install_status}, not installing." end # pre_install_status == 111 # if forced, make the os forget this has been installed before if forced and @apple_receipt_data.is_a? Array @apple_receipt_data.each do |r| D3.log "Forcing OS to forget installer receipt for: #{r[:apple_pkg_id]}", :info system "#{JSS::Composer::PKG_UTIL} --forget '#{r[:apple_pkg_id]}' &>/dev/null" end # each do r end # if force # get the read-only passwd for the dist point, if needed args[:ro_pw] = D3::Client.get_ro_pass :dist # Install It Already! D3.log "Running 'jamf install' of #{edition}", :info if install_result = super(args) # install was good... D3.log "Finished 'jamf install' of #{edition}", :debug # write our receipt write_rcpt # if this was a puppy install, remove it from the queue D3::PUPPY_Q - @basename # run a postflight if needed post_install_status = run_post_install_script(args[:verbose]) if post_install_status != 0 D3.log "Post_install script for #{edition} failed, exit status: #{post_install_status}", :error raise D3::PostInstallError, "Post_install script for #{edition} failed, exit status: #{post_install_status}" end D3.log "Done installing #{edition}#{@using_force}", :warn else # bad install raise D3::InstallError, "There was a problem installing #{edition}, 'jamf install' failed" end # if super args return install_result end # if reboot? ensure D3::Client.unset_env :installing D3::Client.unset_env :pkg_status end # begin...ensure end |
#installed? ⇒ Boolean
Returns Is this pkg installed on this machine via d3? Note: this overrides JSS::Package#installed?.
77 78 79 |
# File 'lib/d3/package/questions.rb', line 77 def installed? D3::Client::Receipt.basenames.include?(@basename) and D3::Client::Receipt.all[@basename].id == @id end |
#installed_files ⇒ Array<Pathname>
Get an array of files installed by this pkg Note that this does not show directories. use #index to see dirs as well
140 141 142 |
# File 'lib/d3/package/getters.rb', line 140 def installed_files index :files_only end |
#live? ⇒ Boolean Originally defined in module Basename
Is the status :live?
#make_live(admin = @admin) ⇒ void
This method returns an undefined value.
Make this package the live one for its basename
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/d3/package/server_actions.rb', line 148 def make_live(admin = @admin) return :live if @status == :live # gotta know who did this raise JSS::MissingDataError, "An admin name must be set before making this d3 package live. Use the admin= method." if admin.to_s.empty? @admin = admin # who and when are we making this pkg live? @release_date = Time.now @released_by = @admin id_field = P_FIELDS[:id][:field_name] status_field = P_FIELDS[:status][:field_name] basename_field = P_FIELDS[:basename][:field_name] rel_date_field = P_FIELDS[:release_date][:field_name] rel_by_field = P_FIELDS[:released_by][:field_name] # if any OLDER pkg is live for this basename, make it deprecated q = <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:deprecated)}' WHERE #{basename_field} = '#{to_sql :basename}' AND #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:live)}' AND #{id_field} < '#{to_sql(:id)}' ENDUPDATE stmt = JSS::DB_CNX.db.prepare q stmt_result = stmt.execute # now make any older pilot pkgs for this basename :skipped q = <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:skipped)}' WHERE #{basename_field} = '#{to_sql :basename}' AND #{id_field} < #{to_sql(:id)} AND #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:pilot)}' ENDUPDATE stmt = JSS::DB_CNX.db.prepare q stmt_result = stmt.execute # any NEWER pkgs for this basename, become pilot (perhaps again) # This is for when we re-enliven an old pkg q = <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:pilot)}' WHERE #{basename_field} = '#{to_sql :basename}' AND #{id_field} > '#{to_sql(:id)}' ENDUPDATE stmt = JSS::DB_CNX.db.prepare q stmt_result = stmt.execute # now make this pkg live @status = :live q = <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{status_field} = '#{to_sql :status}', #{rel_by_field} = '#{to_sql :released_by}', #{rel_date_field} = '#{to_sql :release_date}' WHERE #{id_field} = #{@id} ENDUPDATE stmt = JSS::DB_CNX.db.prepare q stmt_result = stmt.execute # update our knowledge of the world self.class.package_data :refresh # while we're writing to the db, mark any missing packages as missing mark_missing_packages puts "Done '#{edition}' is now live." # auto_clean if we should auto_clean(admin) if D3::CONFIG.admin_auto_clean # run any post-make-live script if needed run_make_live_script end |
#mark_missing_packages ⇒ void
This method returns an undefined value.
Mark missing packages as so on the server
This should run any time we write to the d3_packages table
500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/d3/package/server_actions.rb', line 500 def mark_missing_packages missing_ids = self.class.missing_data.keys unless missing_ids.empty? q = <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{ P_FIELDS[:status][:field_name]} = '#{P_FIELDS[:status][:to_sql].call(:missing)}' WHERE #{P_FIELDS[:id][:field_name]} IN (#{missing_ids.join(',')}) ENDUPDATE stmt = JSS::DB_CNX.db.prepare q stmt_result = stmt.execute end # unless empty end |
#missing? ⇒ Boolean Originally defined in module Basename
Is the status :missing?
#mk_index(args = {}) ⇒ void
This method returns an undefined value.
Create, or re-create, the BOM index records for this Package in the JSS Database.
This is the equivalent of clicking the “index” button in Jamf Admin.app, and is necessary for Jamf Pro to be able to uninstall items. It can only happen after the item has already been saved to the JSS and has an id in the database.
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
# File 'lib/d3/package/server_actions.rb', line 562 def mk_index(args = {}) raise JSS::NoSuchItemError, "Please create this package in the JSS before indexing it." unless @in_jss raise JSS::InvalidConnectionError, "Indexing a package requires a database connection. Use JSS::DB_CNX.connect" unless JSS::DB_CNX.connected? if args[:local_filepath] file_to_index = Pathname.new(args[:local_filepath]) elsif args[:ro_pw] mdp = JSS::DistributionPoint.master_distribution_point file_to_index = mdp.mount(args[:ro_pw], :ro) +"#{DIST_POINT_PKGS_FOLDER}/#{@filename}" if file_to_index.to_s.end_with? ".zip" tmpdir = Pathname.new "/tmp/jss-tmp-#{$$}" system "/usr/bin/unzip '#{thing_to_index}' -d '#{tmpdir}'" file_to_index = tmpdir + file_to_index.basename.to_s.sub(/.zip$/, '') end else raise JSS::InvalidDataError, "Need a :local_filepath or :ro_pw" end # get the index data # is it an (m)pkg? if file_to_index.to_s =~ /\.m?pkg$/ bom_lines = '' # if the thing is a pkg bundle, find and read all the bom files it contains if (file_to_index + "Contents").directory? (file_to_index + "Contents").find do |path| bom_lines += `echo; /usr/bin/lsbom -p fugTsMc '#{path}'` if path.to_s =~ /\.bom$/ end # do path else # else its a flat file - so do it using pkgutil bom_files = `/usr/sbin/pkgutil --bom '#{file_to_index}'` bom_files.split("\n").each do |file| bom_lines += `/usr/bin/lsbom -p fugTsMc '#{file}'` end end # .directory? elsif file_to_index.to_s =~ /\.dmg$/ # if its a .dmg, mount it, make a tmp bom file, and read that mnt_line = `/usr/bin/hdiutil attach -readonly -nobrowse -noautoopen -owners on '#{file_to_index}'`.lines.last mnt_point = Pathname.new mnt_line.split("\t").last.chomp raise FileServiceError, "There was a problem mounting the image #{file_to_index}" unless mnt_point.mountpoint? tmp_bom = "/tmp/#{@filename}.#{$$}.bom" system "/usr/bin/mkbom '#{mnt_point}' '#{tmp_bom}'" bom_lines = `/usr/bin/lsbom -p fugTsMc '#{tmp_bom}'` system "/usr/bin/hdiutil detach '#{mnt_point}'" system "rm -rf '#{tmp_bom}'" else raise JSS::InvalidDataError, "#{@filename} doesn't looks like a .pkg or .dmg. Try Jamf Admin to index it." end # if filename .pkg # If there are no bomlines (perhaps a payloadless pkg?) just return return true if bom_lines.empty? # split the bom lines index_records = bom_lines.split "\n" # reset our lists of files @index = [] @file_list = [] # the start of the SQL insert statement insert_stmt = "INSERT INTO package_contents (package_id,file,owner_name,group_name,modification_date,size,mode,checksum) VALUES" insert_vals = [] # loop through the bom data and make a new record for each line index_records.each do |line| next if line.empty? #break out the data for each item (path,uid,gid,modtime,size,mode,checksum) = line.split "\t" # if the path is just a dot (usually the first one) # make it a / if path == "." clean_path = "/" elsif path.start_with? "." clean_path = path.sub ".", "" else clean_path = path end # rebuild our local lists of files @index << { 'path' => clean_path, 'uid' => uid, 'gif' => gid, 'modtime' => modtime, 'size' => size, 'mode' => mode } @file_list << clean_path unless mode.start_with? "d" # JSS stores modtime as string w/o the weekday modtime.gsub!(/^(Sun|Mon||Tue|Wed|Thu|Fri|Sat) /, '') if defined? modtime insert_vals << "('#{@id}','#{Mysql.quote clean_path}','#{uid}','#{gid}','#{modtime}','#{size}','#{mode}','#{checksum}')" end # do line # first delete any existing index records for this pkg stmt = JSS::DB_CNX.db.prepare "DELETE FROM #{PKG_CONTENTS_TABLE} WHERE package_id = #{@id}" stmt_result = stmt.execute # now insert the new values stmt = JSS::DB_CNX.db.prepare(insert_stmt + " " + insert_vals.join(',')) stmt_result = stmt.execute # while we're writing to the db, mark any missing packages as missing mark_missing_packages return true end |
#new_script(args = {}) ⇒ Integer
Add or replace a pre- or post- script for this package.
This adds a new script to the JSS, and the sets this package to use it.
If the desired script already exists in the JSS, use an appropriate setter method: #pre_install_script_id=, #post_install_script_id=, #pre_remove_script_id=, #post_remove_script_id=, #pre_install_script_name=,#post_install_script_name=, #pre_remove_script_name=, #post_remove_script_name=
254 255 256 257 258 259 260 261 262 263 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 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/d3/package/server_actions.rb', line 254 def new_script (args = {}) raise JSS::InvalidDataError, ":script_type must be one of :#{SCRIPT_TYPES.join(', :')}" unless SCRIPT_TYPES.include? args[:script_type] args[:script_category] ||= D3::CONFIG.jss_default_script_category if args[:script_category] raise JSS::NoSuchItemError, "No such category '#{args[:script_category]}' in the JSS." unless JSS::Category.all_names.include? args[:script_category] end args[:script_name] ||= "#{@basename}-d3#{args[:script_type]}-#{Time.now.strftime('%Y%m%d%H%M%S')}" file_source = nil file_source = case args[:source] when Pathname args[:source] when String Pathname.new(args[:source]) if args[:source].start_with? "/" else raise JSS::InvalidDataError, ":source must be a full path (Pathname or String), or a String containing the script code." end # case if file_source raise JSS::MissingDataError, "The file #{file_source} is missing or unreadable." unless file_source.readable? code = file_source.read else code = args[:source] end # get the new script into the JSS script = JSS::Script.make :name => args[:script_name] script.contents = code script.category = args[:script_category] new_script_id = script.save # update our knowledge of all JSS scripts so the next steps don't fail. JSS::Script.all :refresh case args[:script_type] when :pre_install old_script_id = pre_install_script_id self.pre_install_script_id = new_script_id when :post_install old_script_id = post_install_script_id self.post_install_script_id = new_script_id when :pre_remove old_script_id = pre_remove_script_id self.pre_remove_script_id = new_script_id when :post_remove old_script_id = post_remove_script_id self.post_remove_script_id = new_script_id end # delete the old? if args[:delete_current] and old_script_id JSS::Script.fetch(:id => old_script_id).delete if JSS::Script.all_ids.include? old_script_id end new_script_id end |
#pilot? ⇒ Boolean Originally defined in module Basename
Is the status :pilot?
#policy_ids ⇒ Array<Integer>
An Array of ids of all Jamf Pro policies using this package
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/d3/package/getters.rb', line 148 def policy_ids qry = <<-ENDQ SELECT p.policy_id FROM policies p JOIN policy_packages pp ON p.policy_id = pp.policy_id WHERE pp.package_id = '#{@id}' ENDQ res = JSS::DB_CNX.db.query qry pols = [] res.each{|r| pols << r[0].to_i } res.free pols end |
#post_install ⇒ Object
38 |
# File 'lib/d3/package/aliases.rb', line 38 alias post_install post_install_script_name |
#post_install=(new_val = @post_install_script_id) ⇒ void
This method returns an undefined value.
Set the post_install_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
The script must exist in the JSS or the local file must exist
308 309 310 311 312 313 314 315 316 |
# File 'lib/d3/package/setters.rb', line 308 def post_install= (new_val = @post_install_script_id) name_or_path = validate_post_install_script(new_val) if name_or_path.is_a?(Pathname) @post_install_script_id = new_script script_type: :post_install, source: name_or_path else @post_install_script_id = JSS::Script.map_all_ids_to(:name).invert[name_or_path] end @need_to_update_d3 = true unless @initializing end |
#post_install_script= ⇒ Object
53 |
# File 'lib/d3/package/aliases.rb', line 53 alias post_install_script= post_install= |
#post_install_script? ⇒ Boolean
Returns Does this pkg have a post-install script?.
36 37 38 |
# File 'lib/d3/package/questions.rb', line 36 def post_install_script? not @post_install_script_id.nil? end |
#post_install_script_name ⇒ String?
Returns - The name of the post install script for this pkg, or nil if none.
39 40 41 42 |
# File 'lib/d3/package/getters.rb', line 39 def post_install_script_name return nil unless @post_install_script_id JSS::Script.map_all_ids_to(:name)[@post_install_script_id] end |
#post_install_script_name= ⇒ Object
63 |
# File 'lib/d3/package/aliases.rb', line 63 alias post_install_script_name= post_install= |
#post_remove ⇒ Object
40 |
# File 'lib/d3/package/aliases.rb', line 40 alias post_remove post_remove_script_name |
#post_remove=(new_val = @post_remove_script_id) ⇒ void
This method returns an undefined value.
Set the post_remove_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
The script must exist in the JSS or the local file must exist
346 347 348 349 350 351 352 353 354 |
# File 'lib/d3/package/setters.rb', line 346 def post_remove= (new_val = @post_remove_script_id) name_or_path = validate_post_remove_script(new_val) if name_or_path.is_a?(Pathname) @post_remove_script_id = new_script script_type: :post_remove, source: name_or_path else @post_remove_script_id = JSS::Script.map_all_ids_to(:name).invert[name_or_path] end @need_to_update_d3 = true unless @initializing end |
#post_remove_script= ⇒ Object
55 |
# File 'lib/d3/package/aliases.rb', line 55 alias post_remove_script= post_remove= |
#post_remove_script? ⇒ Boolean
Returns Does this pkg have a post-remove script?.
46 47 48 |
# File 'lib/d3/package/questions.rb', line 46 def post_remove_script? not @post_remove_script_id.nil? end |
#post_remove_script_name ⇒ String?
Returns - The name of the post remove script for this pkg, or nil if none.
53 54 55 56 |
# File 'lib/d3/package/getters.rb', line 53 def post_remove_script_name return nil unless @post_remove_script_id JSS::Script.map_all_ids_to(:name)[@post_remove_script_id] end |
#post_remove_script_name= ⇒ Object
65 |
# File 'lib/d3/package/aliases.rb', line 65 alias post_remove_script_name= post_remove= |
#pre_install ⇒ Object
aliases for getting the script names
37 |
# File 'lib/d3/package/aliases.rb', line 37 alias pre_install pre_install_script_name |
#pre_install=(new_val = @pre_install_script_id) ⇒ void
This method returns an undefined value.
Set the pre_install_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
The script must exist in the JSS or the local file must exist
289 290 291 292 293 294 295 296 297 |
# File 'lib/d3/package/setters.rb', line 289 def pre_install= (new_val = @pre_install_script_id) name_or_path = validate_pre_install_script(new_val) if name_or_path.is_a?(Pathname) @pre_install_script_id = new_script script_type: :pre_install, source: name_or_path else @pre_install_script_id = JSS::Script.map_all_ids_to(:name).invert[name_or_path] end @need_to_update_d3 = true unless @initializing end |
#pre_install_script= ⇒ Object
aliases for assigning scripts, since assignment methods can take ids, names, or paths clean these up someday!
52 |
# File 'lib/d3/package/aliases.rb', line 52 alias pre_install_script= pre_install= |
#pre_install_script? ⇒ Boolean
Returns Does this pkg have a pre-install script?.
31 32 33 |
# File 'lib/d3/package/questions.rb', line 31 def pre_install_script? not @pre_install_script_id.nil? end |
#pre_install_script_name ⇒ String?
Returns - The name of the pre-install script for this pkg, or nil if none.
32 33 34 35 |
# File 'lib/d3/package/getters.rb', line 32 def pre_install_script_name return nil unless @pre_install_script_id JSS::Script.map_all_ids_to(:name)[@pre_install_script_id] end |
#pre_install_script_name= ⇒ Object
62 |
# File 'lib/d3/package/aliases.rb', line 62 alias pre_install_script_name= pre_install= |
#pre_remove ⇒ Object
39 |
# File 'lib/d3/package/aliases.rb', line 39 alias pre_remove pre_remove_script_name |
#pre_remove=(new_val = @pre_remove_script_id) ⇒ void
This method returns an undefined value.
Set the pre_remove_script for this package, either by name or JSS id, or a Path (String or Pathname) to a local file.
The script must exist in the JSS or the local file must exist
327 328 329 330 331 332 333 334 335 |
# File 'lib/d3/package/setters.rb', line 327 def pre_remove= (new_val = @pre_remove_script_id) name_or_path = validate_pre_remove_script(new_val) if name_or_path.is_a?(Pathname) @pre_remove_script_id = new_script script_type: :pre_remove, source: name_or_path else @pre_remove_script_id = JSS::Script.map_all_ids_to(:name).invert[name_or_path] end @need_to_update_d3 = true unless @initializing end |
#pre_remove_script= ⇒ Object
54 |
# File 'lib/d3/package/aliases.rb', line 54 alias pre_remove_script= pre_remove= |
#pre_remove_script? ⇒ Boolean
Returns Does this pkg have a pre-remove script?.
41 42 43 |
# File 'lib/d3/package/questions.rb', line 41 def pre_remove_script? not @pre_remove_script_id.nil? end |
#pre_remove_script_name ⇒ String?
Returns - The name of the pre remove script for this pkg, or nil if none.
46 47 48 49 |
# File 'lib/d3/package/getters.rb', line 46 def pre_remove_script_name return nil unless @pre_remove_script_id JSS::Script.map_all_ids_to(:name)[@pre_remove_script_id] end |
#pre_remove_script_name= ⇒ Object
64 |
# File 'lib/d3/package/aliases.rb', line 64 alias pre_remove_script_name= pre_remove= |
#prohibiting_processes=(new_val = @prohibiting_processes) ⇒ void
This method returns an undefined value.
Set the prohibiting processes for this installer.
The value of this attribute is compared at install time to the lines output by the command ‘ps -A -c -o comm’ (case insensitive)
If any line matches, d3 will attempt to quit the process (or prompt the user to quit the Application) to proceed with installation.
175 176 177 178 179 180 |
# File 'lib/d3/package/setters.rb', line 175 def prohibiting_processes= (new_val = @prohibiting_processes) new_val = JSS.to_s_and_a(new_val)[:arrayform] return @prohibiting_processes if D3::Admin::OPTIONS[:prohibiting_processes][:compare].call(@prohibiting_processes, new_val) @prohibiting_processes = new_val.each {|process| validate_prohibiting_process process} @need_to_update_d3 = true unless @initializing end |
#queue_for_puppies(force = D3::forced?) ⇒ Object
This just queues this installer for installation at the next puppywalk For now, we’re intentionally NOT caching the installer for off-line installation. The puppy installer will only run when the machine can talk to the JSS
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/d3/package/client_actions.rb', line 231 def queue_for_puppies (force = D3::forced?) # create the puppy new_pup = D3::PuppyTime::PendingPuppy.new( :basename => @basename, :version => @version, :revision => @revision, :admin => @admin, :force => force, :custom_expiration => @custom_expiration, :status => @status ) # add it to the queue - this will return true or false added_2_q = D3::PUPPY_Q + new_pup # tell someone D3.log "Added #{edition} (#{@status}) to the puppy queue#{@using_force}", :warn if added_2_q return true end |
#release ⇒ Object
68 |
# File 'lib/d3/package/aliases.rb', line 68 alias release make_live |
#remove_auto_groups(groupnames) ⇒ void
This method returns an undefined value.
Remove one or more groups the to list of auto_groups. The arg is a comma-separated string or an array of group names.
225 226 227 228 229 |
# File 'lib/d3/package/setters.rb', line 225 def remove_auto_groups (groupnames) remove_groups = JSS.to_s_and_a(groupnames)[:arrayform] @auto_groups -= remove_groups @need_to_update_d3 = true unless @initializing end |
#remove_excluded_groups(groupnames) ⇒ void
This method returns an undefined value.
Remove one or more groups the to list of excluded_groups. The arg is a comma-separated string or an array of group names.
274 275 276 277 278 |
# File 'lib/d3/package/setters.rb', line 274 def remove_excluded_groups (groupnames) remove_groups = JSS.to_s_and_a(groupnames)[:arrayform] @excluded_groups -= remove_groups @need_to_update_d3 = true unless @initializing end |
#remove_expiration_path(path) ⇒ Object
Remove a path from expiration_paths The paths are those recorded in d3RepoMan’s timestamp plists.
157 158 159 160 |
# File 'lib/d3/package/setters.rb', line 157 def remove_expiration_path (path) @expiration_paths.delete Pathname.new(path) @need_to_update_d3 = true unless @initializing end |
#revision=(new_val = @revision) ⇒ Object
Set the basename of this package
new_val = string
71 72 73 74 75 76 77 |
# File 'lib/d3/package/setters.rb', line 71 def revision= (new_val = @revision) return nil if new_val == @revision new_val = validate_revision new_val validate_edition "#{basename}-#{@version}-#{new_val}" @revision = new_val @need_to_update_d3 = true unless @initializing end |
#run_make_live_script ⇒ Process::Status
Run the make_live script, if any. We do this by creating and runngin a tmp file, rather than using the jamf binary, because this wont’ be done as root, so the jamf binary can’t be run.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/d3/package/client_actions.rb', line 36 def run_make_live_script # Run the make_live script if any if script = D3::CONFIG.admin_make_live_script if JSS::Script.all_names.include? script code = JSS::Script.fetch(name: script).code elsif JSS::Script.all_ids.include? script code = JSS::Script.fetch(id: script).code else return nil end return nil unless code return nil unless code.start_with? "#!" tmp_file = Pathname.new Tempfile.new("mklive") tmp_file.jss_touch tmp_file.chmod 0700 tmp_file.jss_save code ENV['D3_MAKE_LIVE_EDITION'] = edition ENV['D3_MAKE_LIVE_ADMIN'] = @admin ENV['D3_MAKE_LIVE_DESC'] = description ENV['D3_MAKE_LIVE_AUTO_GROUPS'] = auto_groups.join(',') ENV['D3_MAKE_LIVE_EXCL_GROUPS'] = excluded_groups.join(',') system tmp_file.to_s tmp_file.delete ENV['D3_MAKE_LIVE_EDITION'] = nil ENV['D3_MAKE_LIVE_ADMIN'] = nil ENV['D3_MAKE_LIVE_DESC'] = nil ENV['D3_MAKE_LIVE_AUTO_GROUPS'] = nil ENV['D3_MAKE_LIVE_EXCL_GROUPS'] = nil end end |
#run_post_install_script(verbose = false) ⇒ Integer?
Run the post-install script, if any.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/d3/package/client_actions.rb', line 278 def run_post_install_script(verbose = false) return 0 unless post_install_script? begin D3::Client.set_env :post_install, edition D3.log "Running post_install script for #{edition}", :info (exit_status, output) = JSS::Script.fetch(:id => @post_install_script_id).run :verbose => verbose, :show_output => verbose D3.log "Finished post_install script for #{edition}", :debug rescue D3::ScriptError raise PostInstallError, $! ensure D3::Client.unset_env :post_install end # begin return exit_status end |
#run_pre_install_script(verbose = false) ⇒ Integer?
Run the pre-install script, if any.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/d3/package/client_actions.rb', line 257 def run_pre_install_script(verbose = false) return 0 unless pre_install_script? begin D3::Client.set_env :pre_install, edition D3.log "Running pre_install script for #{edition}", :info (exit_status, output) = JSS::Script.fetch(:id => @pre_install_script_id).run :verbose => verbose, :show_output => verbose D3.log "Finished pre_install script for #{edition}", :debug rescue D3::ScriptError raise PreInstallError, $! ensure D3::Client.unset_env :pre_install end # begin return exit_status end |
#save ⇒ Object
An alias for both save and update
134 135 136 137 138 139 140 |
# File 'lib/d3/package/server_actions.rb', line 134 def save if @in_jss update # this will create the d3 data if needed else create end end |
#saved? ⇒ Boolean
Returns Is this pkg on the server?.
58 59 60 |
# File 'lib/d3/package/questions.rb', line 58 def saved? not (@need_to_update or @need_to_update_d3) end |
#script_ids ⇒ Hash{Symbol=>Integer}
Returns The type and ids of all pre- and post- scripts for this pkg.
60 61 62 63 64 65 66 67 |
# File 'lib/d3/package/getters.rb', line 60 def script_ids { :pre_install => @pre_install_script_id, :post_install => @post_install_script_id, :pre_remove => @pre_remove_script_id, :post_remove => @post_remove_script_id } end |
#script_names ⇒ Hash{Symbol=>String}
Returns The type and names of all pre- and post- scripts for this pkg.
71 72 73 74 75 76 77 78 |
# File 'lib/d3/package/getters.rb', line 71 def script_names { :pre_install => @pre_install_script_name, :post_install => @post_install_script_name, :pre_remove => @pre_remove_script_name, :post_remove => @post_remove_script_name } end |
#skipped? ⇒ Boolean
Returns Is this pkg in pilot? (saved, but never made live).
70 71 72 |
# File 'lib/d3/package/questions.rb', line 70 def skipped? @status == :skipped end |
#update ⇒ Integer
Update this package in the JSS and in d3
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/d3/package/server_actions.rb', line 88 def update # we might be importing an existing JSS pkg to d3, which # means we need to create the d3 record, but the JSS record needs updating create if @importing and (not @in_d3) # update the JSS first, if needed super # and return unless we need to do something. return unless @need_to_update_d3 # Loop thru the field defs to build the SQL update statement new_vals = [] P_FIELDS.each_pair do |key,field_def| # start builing the SET clause values, e.g. "basename = 'foobar'" field_val = "#{field_def[:field_name]} = " # finish the SET clause value field_val << (self.send(key).to_s.empty? ? 'NULL' : "'#{to_sql(key)}'") # add it to the array new_vals << field_val end # do |key,field_def # use the new_vals array to create the update statement stmt = JSS::DB_CNX.db.prepare <<-ENDUPDATE UPDATE #{P_TABLE[:table_name]} SET #{new_vals.join(", ")} WHERE #{P_FIELDS[:id][:field_name]} = #{@id} ENDUPDATE # Execute it to update the record stmt_result = stmt.execute # while we're writing to the db, mark any missing packages as missing mark_missing_packages return @id end |
#update_apple_receipt_data(dist_pw, unmount = true) ⇒ void
This method returns an undefined value.
Learn the apple package id’s installed by this pkg by querying the package on the current dist. point. This is primarily used for importing or repairing packages already on the server.
When adding new packages, the #upload_master_file method will query the data before uploading the file.
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/d3/package/server_actions.rb', line 459 def update_apple_receipt_data(dist_pw, unmount = true) return nil if @filename.end_with? ".dmg" raise JSS::NoSuchItemError, "Please create this package on the server before updating the Apple receipt data" unless @in_jss mdp = JSS::DistributionPoint.my_distribution_point raise JSS::MissingDataError, "Missing :dist_pw for distrib. point '#{mdp.name}'" unless dist_pw # try the passwd both with ro and rw begin mnt_path = mdp.mount(dist_pw, :ro) rescue JSS::InvalidDataError mnt_path = mdp.mount(dist_pw, :rw) end pkg_path = mnt_path + JSS::Package::DIST_POINT_PKGS_FOLDER + @filename raise JSS::NoSuchItemError, "Package file #{@filename} doesn't exist on the current dist. point." unless pkg_path.exist? pkg_to_query = pkg_path # do we need to unzip a bundle pkg? if @filename.end_with? ".zip" work_dir = Pathname.new Dir.mktmpdir unless system "/usr/bin/unzip -qq -o -d #{Shellwords.escape work_dir.to_s} #{Shellwords.escape pkg_path.to_s}" raise RuntimeError, "Failed to unzip bundle pkg #{@filename}" end #system pkg_to_query = work_dir + @filename.sub(/\.zip$/, '') cleanup_work_dir = true end # if @filename.end_with? ".zip" @apple_receipt_data = D3::Package.receipt_data_from_pkg(pkg_to_query) @need_to_update_d3 = true unless @initializing work_dir.rmtree if cleanup_work_dir mdp.unmount if unmount end |
#upload_master_file(local_file_path, rw_pw, unmount = true) ⇒ void
This method returns an undefined value.
Upload a locally-readable file to the master distribution point. If the file is a directory (like a bundle .pk/.mpkg) it will be zipped before uploading and the @filename will be adjusted accordingly
If you’ll be uploading several files you can specify unmount as false, and do it manually when all are finished with JSS::DistributionPoint.master_distribution_point.unmount
This method is mostly performed by the parent class, see JSS::Package.upload_master_file. Before calling super, this method populates @apple_receipt_data with info from the local file.
533 534 535 536 537 538 539 540 541 |
# File 'lib/d3/package/server_actions.rb', line 533 def upload_master_file (local_file_path, rw_pw, unmount = true) raise JSS::NoSuchItemError, "Please create this package in d3 before uploading it." unless @in_d3 if local_file_path.to_s =~ PKG_RE @apple_receipt_data = D3::Package.receipt_data_from_pkg(local_file_path) @need_to_update_d3 = true end # if local_file_path.to_s =~ PKG_RE super update end |
#validate_auto_groups(groups) ⇒ Object Originally defined in module Validate
#validate_category(cat) ⇒ String Originally defined in module Validate
Check the validity of a category name Raise an exception if not valid. nil and empty strings are acceptable to unset the category.
#validate_cpu_type(type) ⇒ Symbol Originally defined in module Validate
Check the validity of a CPU type Raise an exception if not valid
#validate_edition(edition) ⇒ String Originally defined in module Validate
Check if an edition exists and raise an exception if so Also check that it contains at least two hyphens
#validate_excluded_groups(groups) ⇒ Object Originally defined in module Validate
#validate_expiration(exp) ⇒ Integer Originally defined in module Validate
Confirm the validity of an expiration. Raise an exception if invalid.
#validate_expiration_path(path) ⇒ Pathname Originally defined in module Validate
Confirm the validity of an expiration path. Any string that starts with a / is valid, d3 can’t confirm the paths existing on client machines.
#validate_expiration_paths(paths) ⇒ Array<Pathname> Originally defined in module Validate
Confirm the validity of one or more expiration paths. Any string that starts with a / is valid. The strings “n” or “none” returns an empty array.
#validate_filename(name) ⇒ String Originally defined in module Validate
check that the given filename doesn’t already exist
#validate_groups(groups, check_for_std = false) ⇒ Array Originally defined in module Validate
Confirm the existence of a list of Computer Group names (String or Array) and return them as an Array
If “n”, “”, “none” or nil are passed, an empty array is returned.
Raise an exception if any group is not valid.
#validate_non_overlapping_groups(auto, excl) ⇒ True Originally defined in module Validate
Make sure auto and excluded groups don’t have any members in common, raise an exception if they do.
#validate_oses(os_list) ⇒ Array Originally defined in module Validate
Check the validity of a list of OSes Raise an exception if not valid
#validate_package_name(name) ⇒ Object Originally defined in module Validate
check that the given package name doesn’t already exist
#validate_post_install_script(script) ⇒ Object Originally defined in module Validate
Check the validity of a post_install script
#validate_post_remove_script(script) ⇒ Object Originally defined in module Validate
Check the validity of a pre_remove script
#validate_pre_install_script(script) ⇒ Object Originally defined in module Validate
Check the validity of a pre_install script
#validate_pre_remove_script(script) ⇒ Object Originally defined in module Validate
Check the validity of a pre_remove script
#validate_prohibiting_process(process_name) ⇒ String Originally defined in module Validate
Check a single prohibiting process for validity
#validate_revision(rev) ⇒ Integer Originally defined in module Validate
Confirm the validity of a revision. Raise an exception if invalid.
#validate_script(script) ⇒ Pathname, String Originally defined in module Validate
Check the validity of a script, either Pathname, JSS id, or JSS name Raise an exception if not valid
#validate_version(vers) ⇒ String Originally defined in module Validate
Confirm the validity of a version. Raise an exception if invalid.
#validate_yes_no(yn) ⇒ Boolean Originally defined in module Validate
check the validity of a yes/no,true/false,1/0 input value
TrueClass, “true”, “y”,“yes”, and 1 are true
FalseClass, nil, “false”, “n”, “no”, and 0 are false
(Strings are case-insensitive) Anything else raises an exception.
#version=(new_val = @version) ⇒ Object
Set the version of this package
new_val = string
59 60 61 62 63 64 65 |
# File 'lib/d3/package/setters.rb', line 59 def version= (new_val = @version) return nil if new_val == @version new_val = validate_version new_val validate_edition "#{basename}-#{new_val}-#{@revision}" @version = new_val @need_to_update_d3 = true unless @initializing end |