Skip to content

Commit

Permalink
Fixes #10388: Add a generic method that handles using options in pack…
Browse files Browse the repository at this point in the history
…age actions
  • Loading branch information
amousset committed Apr 25, 2017
1 parent a4804dd commit 2ab7304
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 241 deletions.
2 changes: 1 addition & 1 deletion tests/acceptance/30_generic_methods/package_present.cf
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ bundle agent test
bundle agent check {

vars:
"file_diff_test" string => "/usr/bin/diff \"${this.promise_filename}.expected\" \"${sys.workdir}/tmp/TEST.cfengine\"";
"file_diff_test" string => "/usr/bin/diff \"${this.promise_filename}.expected\" \"${sys.workdir}/modules/packages/test_db\"";

classes:
"ok" expression => returnszero("${file_diff_test}", "useshell"),
Expand Down
43 changes: 31 additions & 12 deletions tree/10_ncf_internals/modules/packages/apt_get
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import sys
import os
import subprocess
import re
from distutils.version import LooseVersion

PY3 = sys.version_info > (3,)

Expand All @@ -22,11 +23,26 @@ dpkg_query_cmd = os.environ.get('CFENGINE_TEST_DPKG_QUERY_CMD', "/usr/bin/dpkg-q
dpkg_output_format = "Name=${Package}\nVersion=${Version}\nArchitecture=${Architecture}\n"
dpkg_status_format = "Status=${Status}\n" + dpkg_output_format

apt_get_cmd = os.environ.get('CFENGINE_TEST_APT_GET_CMD', "/usr/bin/apt-get")

# Some options only work with specific versions of apt, so we must know the
# current version in order to do the right thing.
apt_version = subprocess.Popen([ apt_get_cmd , '-v'],
stdout=subprocess.PIPE, universal_newlines=True).communicate()[0]
apt_version = apt_version.splitlines()[0].split(' ')[1]

apt_get_options = ["-o", "Dpkg::Options::=--force-confold",
"-o", "Dpkg::Options::=--force-confdef",
"-y",
"--force-yes"]
apt_get_cmd = os.environ.get('CFENGINE_TEST_APT_GET_CMD', "/usr/bin/apt-get")
"-y"]

if LooseVersion(apt_version) < LooseVersion("1.1"):
apt_get_options.append("--force-yes")

else:
# The --force-yes option was deprecated in apt-get 1.1
apt_get_options.extend( [ "--allow-downgrades",
"--allow-remove-essential",
"--allow-change-held-packages"])

os.environ['DEBIAN_FRONTEND'] = "noninteractive"
os.environ['LC_ALL'] = "C"
Expand Down Expand Up @@ -166,15 +182,18 @@ def list_updates(online):
# Note multiple repositories in this one:
# Inst linux-libc-dev [2.6.32-48squeeze4] (2.6.32-48squeeze6 Debian:6.0.10/oldstable, Debian-Security:6.0/oldoldstable [amd64])
#
# Another example (note the addition of jessie:jessie without a comma):
# Inst rudder-agent [4.1.0~rc1-jessie0] (4.1.0-jessie0 release/4.1.0-2 jessie:jessie [amd64])
#
# name old version new version
# | | |
# /-------+-------\ /--+--\ /------+-------\
match = re.match("^Inst\s+(?P<name>[^\s:]+)(?::\S+)?\s+\[[^\]\s]+\]\s+\((?P<version>\S+)" +
match = re.match("^Inst\s+(?P<name>[^\s:]+)(?::\S+)?\s+\[[^]\s]+\]\s+\((?P<version>\S+)" +

# repository(ies) arch (might be optional)
# | |
# /----------+----------\ /---------+---------\
"\s+[^\s]+(?:\s+[^)\[\]\s]+)*(\s+\[(?P<arch>[^]\s]+)\])?\).*", line)
# repository(ies) arch (might be optional)
# | |
# /--+-\ /---------+---------\
"\s+[^[)]*(\s\[(?P<arch>[^]\s]+)\])?\).*", line)

if match is not None:
sys.stdout.write("Name=" + match.group("name") + "\n")
Expand Down Expand Up @@ -286,12 +305,12 @@ def repo_install():
if (not args):
return 0

# Convert list of tuples into two lists so that first element of each
# Convert list of tuples into two lists so that first element of each
# tuple belongs to list 'a1' and the second one to list 'a2'.
a1, a2 = map(list, zip(*args))

# For 'repo_insrtall' both 'a1' and 'a2' should be equal so we can operate
# on 'a1' elements only.
# on 'a1' elements only.
if a1:
return subprocess_call(cmd_line + a1, stdout=NULLFILE)
return 0
Expand All @@ -304,7 +323,7 @@ def remove():
if (not args):
return 0

# Convert list of tuples into two lists so that first element of each
# Convert list of tuples into two lists so that first element of each
# tuple belongs to list 'a1' and the second one to list 'a2'.
#
# In case of multi arch support elements of 'a1' list should not contain
Expand All @@ -314,7 +333,7 @@ def remove():

# As there seems to be no unified method to remove packages matching
# native OS architecture we are trying first to remove packages providing
# just a package name and if this call is failing we are trying
# just a package name and if this call is failing we are trying
# 'package_name:architecture' approach.
ret = subprocess_call(cmd_line + a1, stdout=NULLFILE)
if ret != 0 and a1 != a2:
Expand Down
233 changes: 5 additions & 228 deletions tree/30_generic_methods/package_state.cf
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
#
# The last parameter is the provider, which is documented in the next section.
#
# You can use [package_state_options](#package_state_options) to pass options to the underlying package manager
# (currently only with *apt* package manager).
#
# #### Package providers
#
# This method supports several package managers. You can specify the package manager
Expand Down Expand Up @@ -101,7 +104,7 @@
# package_absent("postgresql", "", "", "");
# ```
#
# See also : [package_present](#package_present), [package_absent](#package_absent)
# See also : [package_present](#package_present), [package_absent](#package_absent), [package_state_options](#package_state_options)
# @parameter name Name of the package, or path to a local package if state is present
# @parameter version Version of the package, can be "latest" for latest version or "any" for any version (defaults to "any")
# @parameter_constraint version "allow_empty_string" : true
Expand All @@ -119,232 +122,6 @@

bundle agent package_state(name, version, architecture, provider, state)
{

vars:
"canonified_name" string => canonify("${name}");
"old_class_prefix" string => "package_${state}_${canonified_name}";
"promisers" slist => { @{this.callers_promisers}, cf_null }, policy => "ifdefined";
"class_prefix" string => canonify(join("_", "promisers"));
"args" slist => { "${name}", "${version}", "${architecture}", "${provider}", "${state}" };

# Build string vars used for reporting

# State
state_present::
"state_description" string => "Presence";
!state_present::
"state_description" string => "Absence";

# Architecture
architecture_specified::
"architecture_description" string => " for ${architecture} architecture ";
!architecture_specified::
"architecture_description" string => " ";

# Version
version_latest::
"version_description" string => "in latest available version";
!version_specified::
"version_description" string => "in any version";
version_specified.!version_latest::
"version_description" string => "in version ${version}";

defaults:
"version" string => "any", if_match_regex => "";
"architecture" string => "default", if_match_regex => "";
"provider" string => "default", if_match_regex => "";
# Select the default packager for this platform if "default"
"provider" string => "${package_module_knowledge.platform_default}", if_match_regex => "default";
"state" string => "present", if_match_regex => "";

classes:
"version_latest" expression => strcmp("latest", "${version}");
# As "latest" is understood by package promises,
# the only special case is "any", which maps to
# no specified version in the promise
"version_specified" not => strcmp("any", "${version}");

"state_present" expression => strcmp("present", "${state}");

# If architecture is not specified, do not add it to the promise.
# The package module will pick the default one for the local platform
"architecture_specified" not => strcmp("default", "${architecture}");

# Select the right package manager
# The default one has already been selected if needed using mackage_module_knowledge
"use_apt_provider" expression => strcmp("apt", "${provider}");
"use_yum_provider" expression => strcmp("yum", "${provider}");
"use_zypper_provider" expression => strcmp("zypper", "${provider}");
"use_pkg_provider" expression => strcmp("pkg", "${provider}");
# Used in tests
"use_test_provider" expression => strcmp("test", "${provider}");

any::
"supported_provider" expression => "use_apt_provider|use_yum_provider|use_zypper_provider|use_pkg_provider|use_test_provider";


packages:

# Unfortunately, we have to duplicate the 4 cases for each supported package module

#### apt ####

use_apt_provider.architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => apt_get,
architecture => "${architecture}",
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_apt_provider.architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => apt_get,
architecture => "${architecture}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_apt_provider.!architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => apt_get,
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_apt_provider.!architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => apt_get,
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

#### yum ####

use_yum_provider.architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => yum,
architecture => "${architecture}",
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_yum_provider.architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => yum,
architecture => "${architecture}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_yum_provider.!architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => yum,
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_yum_provider.!architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => yum,
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

#### zypper ####

use_zypper_provider.architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => zypper,
architecture => "${architecture}",
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_zypper_provider.architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => zypper,
architecture => "${architecture}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_zypper_provider.!architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => zypper,
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_zypper_provider.!architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => zypper,
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

#### pkg ####

use_pkg_provider.architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => pkg,
architecture => "${architecture}",
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_pkg_provider.architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => pkg,
architecture => "${architecture}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_pkg_provider.!architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => pkg,
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_pkg_provider.!architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => pkg,
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

#### test ####

use_test_provider.architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => test,
architecture => "${architecture}",
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_test_provider.architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => test,
architecture => "${architecture}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_test_provider.!architecture_specified.version_specified::
"${name}"
policy => "${state}",
package_module => test,
version => "${version}",
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

use_test_provider.!architecture_specified.!version_specified::
"${name}"
policy => "${state}",
package_module => test,
classes => classes_generic_two("${old_class_prefix}", "${class_prefix}");

methods:

supported_provider::
"reports" usebundle => _log("${state_description} of package ${name}${architecture_description}${version_description}", "${old_class_prefix}", "${class_prefix}", @{args});

!supported_provider::
"force failure class" usebundle => _classes_failure("${class_prefix}");
"force failure class" usebundle => _classes_failure("${old_class_prefix}");
"report failure" usebundle => _log("Presence of package ${name} - package managers other than yum, apt and pkg are not currently supported", "${old_class_prefix}", "${class_prefix}", @{args});

"enforce state" usebundle => package_state_options("${name}", "${version}", "${architecture}", "${provider}", "${state}", "");
}

0 comments on commit 2ab7304

Please sign in to comment.