#!/usr/bin/env sh ### # ============LICENSE_START======================================================= # ONAP POLICY # ================================================================================ # Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved. # Modifications Copyright (C) 2024 Nordix Foundation. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============LICENSE_END========================================================= ## # ############################################################# # Features Directory Layout: # # POLICY_HOME/ # L─ features/ # L─ */ #     L─ [config]/ #     |   L─ + #     L─ [bin]/ #     |   L─ + #     L─ lib/ #     |  L─ [dependencies]/ #     |  | L─ + #     │  L─ feature/ #     │  L─ #     L─ [artifacts]/ #      L─ + #     L─ [install] #      L─ [enable] #      L─ [disable] #      L─ [other-directories-or-files] # # notes: [] = optional , * = 0 or more , + = 1 or more # directory without "feature-" prefix. # [config] feature configuration directory that contains all configuration # needed for this features # [config]/ preferable named with "feature-" prefix to # precisely match it against the exact features, source code, and # associated wiki page for configuration details. # [bin] feature bin directory that contains helper scripts for this feature # [bin]/ preferable named with "feature-" prefix. # lib jar libraries needed by this features # lib/[dependencies] 3rd party jar dependencies not provided by base installation # of pdp-d that are necessary for to operate # correctly. # lib/feature the single feature jar that implements the feature. # [artifacts] maven artifacts to be deployed in a maven repository. # [artifacts]/ maven artifact with identifiable maven coordinates embedded # in the artifact. # [install] custom installation directory where custom enable or disable scripts # and other free form data is included to be used for the enable and # and disable scripts. # [install]/[enable] enable script executed when the enable operation is invoked in # the feature. # [install]/[disable] disable script executed when the disable operation is invoked in # the feature. # [install]/[other-directories-or-files] other executables, or data that can be used # by the feature for any of its operations. The content is determined # by the feature designer. # # Operations: # install: installs a feature # uninstall: uninstalls a feature # enable : enables 1) dependencies, 2) configuration, 3) binaries 4) artifacts, # 5) feature, 6) customization. # disable: disables 1) dependencies, 2) configuration, 3) binaries, 4) feature, # 5) customization # status : status of a feature # # 'enable' operation details: # 0. Validates current state before the operation is committed # 1. sets the symbolic link to the actual feature jar in pdp-d classpath ($POLICY_HOME/lib) # 2. sets symbolic links to feature dependencies in pdp-d classpath ($POLICY_HOME/lib) # 3. sets symbolic links to feature configuration in pdp-d configuration directory ($POLICY_HOME/config) # 4. sets symbolic links to feature executables in pdp-d bin directory ($POLICY_HOME/bin) # 5. deploys any maven artifacts in the maven repositories in use (if any) # 6. cd to the feature 'install' directory an executes (if exists) the 'enable' script to allow for specific # customizations for this feature. # # 'disable' operation details: # 0. Validates current state before the operation is committed # 1. removes the symbolic link to the actual feature jar in pdp-d classpath ($POLICY_HOME/lib) # 2. removes symbolic links to feature dependencies in pdp-d classpath ($POLICY_HOME/lib) # 3. removes symbolic links to feature configuration in pdp-d configuration directory ($POLICY_HOME/config) # 4. removes symbolic links to feature executables in pdp-d bin directory ($POLICY_HOME/bin) # 5. cd to the feature 'install' directory an executes (if exists) the 'disable' script to allow for specific # customizations for this feature. # # Example: # # POLICY_HOME/ # L─ features/ # L── eelf/ # │   L── config/ # │   │   L── logback-eelf.xml # │   L─ lib/ # │   │ L─ dependencies/ # │   │ │ L─ ONAP-Logging-1.1.0-SNAPSHOT.jar # │   │ │ L─ eelf-core-1.0.0.jar # │   │ L─ feature/ # │   │ L─ feature-eelf-1.1.0-SNAPSHOT.jar # │   L─ install/ # │   L─ enable # │   L─ disable # L─ healthcheck/ # L── config/ # │   L─ feature-healthcheck.properties # L─ lib/ # L─ feature/ # L─ feature-healthcheck-1.1.0-SNAPSHOT.jar # ############################################################# if [ "${DEBUG}" = "y" ]; then echo "-- MAIN --" set -x fi # The directories at play LIB=${POLICY_HOME}/lib CONFIG=${POLICY_HOME}/config BIN=${POLICY_HOME}/bin FEATURES=${POLICY_HOME}/features if [ ! -d "${LIB}" ]; then echo "error: no ${LIB} directory" exit 1 fi if [ ! -d "${CONFIG}" ]; then echo "error: no ${CONFIG} directory" exit 2 fi # ensure that the directory exists mkdir -p "${FEATURES}" 2> /dev/null # relative per Feature Directory Paths FEATURE_DEPS="lib/dependencies" FEATURE_LIB="lib/feature" FEATURE_CONFIG="config" FEATURE_BIN="bin" FEATURE_INSTALL="install" FEATURE_ARTIFACTS="artifacts" featureJars=$(find "${FEATURES}" -name "feature-*.jar" -type f -exec basename {} \; 2> /dev/null) # default field lengths nameLength=20 versionLength=15 # update field lengths, if needed for jar in ${featureJars} ; do # get file name without 'jar' suffix tmp="${jar%\.jar}" # remove feature prefix tmp="${tmp#feature-}" # get feature name by removing the version portion name="${tmp%%-[0-9]*}" # extract version portion of name version="${tmp#${name}-}" # grow the size of the name/version field, if needed if [ ${#name} -gt $nameLength ] ; then nameLength=${#name} fi if [ ${#version} -gt $versionLength ] ; then versionLength=${#version} fi done # ########################################################## # usage: usage information # ########################################################## function usage { # print out usage information cat >&2 <<-'EOF' Usage: features status Get enabled/disabled status on all features features enable ... Enable the specified feature features disable ... Disable the specified feature features install [ | ] ... Install the specified feature features uninstall ... Uninstall the specified feature EOF } # ########################################################## # status: dump out status information # ########################################################## function status { if [ "${DEBUG}" = "y" ]; then echo "-- status --" set -x fi local tmp name version status local format="%-${nameLength}s %-${versionLength}s %s\n" printf "${format}" "name" "version" "status" printf "${format}" "----" "-------" "------" for jar in ${featureJars} ; do # get file name without 'jar' suffix tmp="${jar%\.jar}" # remove feature prefix tmp="${tmp#feature-}" # get feature name by removing the version portion name="${tmp%%-[0-9]*}" # extract version portion of name version="${tmp#${name}-}" # determine status status=disabled if [ -e "${LIB}/${jar}" ] ; then status=enabled fi printf "${format}" "${name}" "${version}" "${status}" done echo } # ########################################################## # enableDepAnalysis (featureName): # reports on potential dependency conflicts # featureName: name of the feature # ########################################################## function enableDepAnalysis () { if [ "${DEBUG}" = "y" ]; then echo "-- enableDepAnalysis $* --" set -x fi local featureName="$1" local featureDepJars featureDepJarPath depJarName multiVersionJars if [ -z "${featureName}" ]; then echo "warning: no feature name" fi featureDepJars=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null) for featureDepJarPath in ${featureDepJars}; do depJarName=$(basename "${featureDepJarPath}") # it could be a base jar if [ -f "${LIB}"/"${depJarName}" ]; then echo "warning: dependency ${depJarName} already in use" continue fi # it could be a link from another feature if [ -L "${LIB}"/"${depJarName}" ]; then continue fi # unadvisable if multiple versions exist but the base or # installed one wins. multiVersionJars=$(ls "${LIB}"/"${depJarName%%-[0-9]*.jar}"-*.jar 2> /dev/null) if [ -n "${multiVersionJars}" ]; then echo "warning: other version of library ${depJarName} present: ${multiVersionJars}" continue fi done } # ########################################################## # enableConfigAnalysis (featureName): # reports on potential dependency conflicts # featureName: name of the feature # ########################################################## function enableConfigAnalysis () { if [ "${DEBUG}" = "y" ]; then echo "-- enableConfigAnalysis $* --" set -x fi local featureName="$1" local featureConfigs configPath configFileName if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureConfigs=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ 2> /dev/null) for configPath in ${featureConfigs}; do configFileName=$(basename "${configPath}") if [ -e "${CONFIG}"/"${configFileName}" ]; then echo "error: a config file of the same name is already in the base installation: ${configFileName}" return 2 fi done } # ########################################################## # enableBinAnalysis (featureName): # reports on potential dependency conflicts # featureName: name of the feature # ########################################################## function enableBinAnalysis () { if [ "${DEBUG}" = "y" ]; then echo "-- enableBinAnalysis $* --" set -x fi local featureName="$1" local featureBins binPath binFileName if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureBins=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ 2> /dev/null) for binPath in ${featureBins}; do binFileName=$(basename "${binPath}") if [ -e "${CONFIG}"/"${binFileName}" ]; then echo "error: a bin file of the same name is already in the base installation: ${binFileName}" return 2 fi done } # ########################################################## # enableFeatureDeps(featureName): # enables feature dependencies # featureName: name of the feature # ########################################################## function enableFeatureDeps() { if [ "${DEBUG}" = "y" ]; then echo "-- enableFeatureDeps $* --" set -x fi local featureName="$1" local featureDeps featureDepPath depJarName if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null) for featureDepPath in ${featureDeps}; do depJarName=$(basename "${featureDepPath}") if [ ! -f "${LIB}"/"${depJarName}" ]; then ln -s -f "${featureDepPath}" "${LIB}/" fi done } # ########################################################## # enableFeatureConfig(featureName): # enables feature configuration # featureName: name of the feature # ########################################################## function enableFeatureConfig() { if [ "${DEBUG}" = "y" ]; then echo "-- enableFeatureConfig $* --" set -x fi local featureName="$1" local featureConfigs featureConfigPath if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null) for featureConfigPath in ${featureConfigs}; do ln -s -f "${featureConfigPath}" "${CONFIG}/" done } # ########################################################## # enableFeatureBin(featureName): enables feature binaries # featureName: name of the feature # ########################################################## function enableFeatureBin() { if [ "${DEBUG}" = "y" ]; then echo "-- enableFeatureBin $* --" set -x fi local featureName="$1" local featureBins featureBinPath if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureBins=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ -type f -maxdepth 1 2> /dev/null) for featureBinPath in ${featureBins}; do chmod u+x "${featureBinPath}" ln -s -f "${featureBinPath}" "${BIN}/" done } # ########################################################## # enableFeatureArtifacts(featureName): # deploys maven artifacts # featureName: name of the feature # ########################################################## function enableFeatureArtifacts() { if [ "${DEBUG}" = "y" ]; then echo "-- enableFeatureArtifacts $* --" set -x fi local featureName="$1" local artifacts if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi artifacts=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_ARTIFACTS}"/* 2> /dev/null) for artifactPath in ${artifacts}; do deploy-artifact -l -a "${artifactPath}" done } # ########################################################## # customize(featureName): # executes customized script for an operation. # # featureName - feature name # operation - operation, ie. # 'enable', 'disable', 'install', or 'uninstall' # ########################################################## function customOpScript() { if [ "${DEBUG}" = "y" ]; then echo "-- customOpScript $* --" set -x fi local featureName="$1" local operation="$2" if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi if [ -z "${operation}" ]; then echo "warning: ${featureName} : a custom operation script must be provided" return 1 fi local customScript="${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}"/"${operation}" if [ -f "${customScript}" ]; then cd "${FEATURES}"/"${featureName}"/"${FEATURE_INSTALL}" chmod u+x "${customScript}" ./"$(basename "${customScript}")" fi } # ########################################################## # enableFeature(featureName, featureJar): enables a feature # featureName: name of the feature # featureJar: path to feature jar implementation # ########################################################## function enableFeature() { if [ "$DEBUG" = "y" ]; then echo "-- enableFeature $* --" set -x fi local featureName="$1" local featureJar="$2" if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi if [ -z "${featureJar}" ]; then echo "warning: no feature jar" return 2 fi if ! enableDepAnalysis "${featureName}"; then return "$?" fi if ! enableConfigAnalysis "${featureName}"; then return "$?" fi if ! enableBinAnalysis "${featureName}"; then return "$?" fi # enable feature itself ln -s -f "${featureJar}" "${LIB}/" # enable dependent libraries if any enableFeatureDeps "${featureName}" # enable configuration enableFeatureConfig "${featureName}" # enable binaries enableFeatureBin "${featureName}" # enable feature artifacts enableFeatureArtifacts "${featureName}" # run custom enable if any customOpScript "${featureName}" "enable" } # ########################################################## # disableFeatureDeps(featureName): # disables feature dependencies # ########################################################## function disableFeatureDeps() { if [ "${DEBUG}" = "y" ]; then echo "-- disableFeatureDeps $* --" set -x fi local featureName="$1" local aDepsEnabledMap xDepsEnabledMap featureBaseDirs aFeatureDir aFeatureName local featureDeps aFeatureDep local depJarPath depJarName depJarRealPath if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureBaseDirs=$(ls -d "${FEATURES}"/*/ 2> /dev/null) for aFeatureDir in ${featureBaseDirs}; do aFeatureName=$(basename "${aFeatureDir}") if [ "${aFeatureName}" = "${featureName}" ]; then continue fi depJarPaths=$(ls "${aFeatureDir}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null) for depJarPath in ${depJarPaths}; do if [ "$?" = "0" ] ; then depJarName=$(basename "${depJarPath}") xDepsEnabledMap="${xDepsEnabledMap} ${depJarPath}" fi done done if [ "${DEBUG}" = "y" ]; then echo "${xDepsEnabledMap}" fi featureDeps=$(ls "${FEATURES}"/"${featureName}"/"${FEATURE_DEPS}"/*.jar 2> /dev/null) for aFeatureDep in ${featureDeps}; do depJarName=$(basename "${aFeatureDep}") # # check if defJarName is a sym-link # if [ -L "${LIB}"/"${depJarName}" ]; then depJarRealPath=$(readlink -f "${LIB}"/"${depJarName}") if [ "${depJarRealPath}" = "${aFeatureDep}" ]; then rm -f "${LIB}"/"${depJarName}" # case there were multiple features using this library # re-enable link from an enabled feature for aDepsEnabledMap in ${xDepsEnabledMap}; do if [ $(basename "${aDepsEnabledMap}") = ${depJarName} ]; then ln -s -f "${aDepsEnabledMap}" "${LIB}/" fi done fi fi done } # ########################################################## # disableFeatureConfig(featureName): # disables feature configuration # featureName: name of the feature # ########################################################## function disableFeatureConfig() { if [ "${DEBUG}" = "y" ]; then echo "-- disableFeatureConfig $* --" set -x fi local featureName="$1" local featureConfigs featureConfigPath if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureConfigs=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_CONFIG}"/ -type f -maxdepth 1 2> /dev/null) for featureConfigPath in ${featureConfigs}; do configFileName=$(basename "${featureConfigPath}") rm -f "${CONFIG}"/"${configFileName}" 2> /dev/null done } # ########################################################## # disableFeatureBin(featureName): # disables feature binaries # featureName: name of the feature # ########################################################## function disableFeatureBin() { if [ "${DEBUG}" = "y" ]; then echo "-- disableFeatureBin $* --" set -x fi local featureName="$1" local featureBins featureBinPath if [ -z "${featureName}" ]; then echo "warning: no feature name" return 1 fi featureBins=$(find "${FEATURES}"/"${featureName}"/"${FEATURE_BIN}"/ -type f -maxdepth 1 2> /dev/null) for featureBinPath in ${featureBins}; do binFileName=$(basename "${featureBinPath}") rm -f "${BIN}"/"${binFileName}" 2> /dev/null done } # ########################################################## # disableFeature(featureName): disables a feature # featureName: name of the feature # ########################################################## function disableFeature() { if [ "${DEBUG}" = "y" ]; then echo "-- disableFeature $* --" set -x fi local featureName="$1" if [ -z "${featureName}" ]; then echo "warning: no feature name" return fi # disable feature itself ( cd "${LIB}" rm -f feature-"${featureName}"-[0-9]*.jar 2> /dev/null ) # disable dependencies if any disableFeatureDeps "${featureName}" # disable configuration if any disableFeatureConfig "${featureName}" # disable binaries if any disableFeatureBin "${featureName}" # run custom disable if any customOpScript "${featureName}" "disable" } ############################################################ # configureFeature # # This was copied from 'policy-drools/docker-install.sh' # in the 'docker' repository, and modified where needed. ############################################################ function configureFeature() { if [ "$DEBUG" = "y" ]; then echo "-- configureFeature $* --" set -x fi local envConfig=$1 featureRoot=$2 local sedLine="sed -i" local sedFiles="" nonBinaryFiles sedFile name value while read line || [ -n "${line}" ]; do if [ -n "${line}" ] && [ "${line:0:1}" != \# ]; then name="${line%%=*}" value="${line#*=}" value=$(echo "${value}" | sed -e 's/[\/&]/\\&/g') if [ -z "${name}" ] || [ -z "${value}" ]; then echo "warning: ${line} missing name or value" fi sedLine+=" -e 's/\${{${name}}}/${value}/g' " fi done < "${envConfig}" nonBinaryFiles=$(find "${featureRoot}" -type f -exec grep -Iq . {} \; -print 2> /dev/null) for sedFile in ${nonBinaryFiles}; do if fgrep -l '${{' ${sedFile} > /dev/null 2>&1; then sedFiles+="${sedFile} " fi done if [ -n "${sedFiles}" ]; then sedLine+=${sedFiles} eval "${sedLine}" fi } ############################################################ # installFeatures ... # # This was copied from 'policy-drools/docker-install.sh' # in the 'docker' repository, and modified where needed. ############################################################ function installFeatures { if [ "$DEBUG" = "y" ]; then echo "-- installFeatures $* --" set -x fi local name featureConf feature conf if [ -d "${FEATURES}" ]; then SOURCE_DIR=$PWD for feature in "$@" ; do name="${feature}" feature=$(ls -v feature-"${name}"-[0-9]*.zip 2>/dev/null|tail -1) if [ ! -f "${feature}" ] ; then # include the file name in the error message, unless we don't # have one -- in this case, use the feature name echo "error: feature file ${feature:-for ${name}} not found" continue fi if [ -d "${FEATURES}/${name}" ] ; then echo "error: feature ${name} has already been installed" continue fi # extract contents of ZIP file in to feature directory mkdir -p "${FEATURES}/${name}" > /dev/null 2>&1 (cd "${FEATURES}/${name}"; jar xf "${SOURCE_DIR}"/"${feature}") # if there is a configuration file available, # use it to configure the feature featureConf="${dir:+$dir/}feature-${name}.conf" if [ -r "${featureConf}" ]; then configureFeature "${featureConf}" "${FEATURES}"/"${name}" cp "${featureConf}" "${POLICY_HOME}"/etc/profile.d echo "feature ${name} has been installed (configuration present)" else echo "feature ${name} has been installed (no configuration present)" fi customOpScript "${name}" "install" done # check the current directory and the 'config' directory for a # 'base.conf' file -- use the first one that is found for conf in base.conf ${POLICY_HOME}/config/base.conf ${POLICY_HOME}/etc/profile.d/base.conf; do if [ -f "${conf}" ] ; then echo "applying base configuration '${conf}' to features" configureFeature "${conf}" "${FEATURES}" break fi done else echo "error: aborting -- ${FEATURES} is not accessible" exit 1 fi } ############################################################ # uninstallFeature ... ############################################################ function uninstallFeature { if [ "$DEBUG" = "y" ]; then echo "-- uninstallFeature $* --" set -x fi local featureName="$1" if [ -z "${featureName}" ]; then echo "warning: no feature name" return fi disableFeature "${featureName}" customOpScript "${featureName}" "uninstall" if [ -n "${FEATURES}" ] && [ -n "${featureName}" ]; then rm -rf "${FEATURES:-???}/${featureName}" fi } ############################################################ # uninstallFeatures ... ############################################################ function uninstallFeatures { if [ "${DEBUG}" = "y" ]; then echo "-- uninstallFeatures --" set -x fi local name local allFeatures=$(cd ${FEATURES};ls) local aFeature local flag for name in "$@" ; do flag=false # the following check takes care of potentially troublesome names # like '.', '..', and names containing '/' for aFeature in ${allFeatures}; do if [ "${aFeature}" = "${name}" ] ; then uninstallFeature "${name}" echo "feature ${name} uninstalled" flag=true fi done if ! $flag; then echo "error: feature ${name} not found" fi done } case "$1" in status) { # dump out status information status };; enable) { if [ -f "${POLICY_HOME}"/PID ]; then echo "error: enable: not allowed when policy is running .." echo status exit 10 fi # enable the specified options shift match= for name in "$@" ; do # look for matches - 'file' has the full path name file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null) if [ "$?" != "0" ] ; then # no matching file echo "enable feature: ${name} failed" else # make sure there is only one feature jar countFeatureJars=$(echo "${file}" | wc -w) if [ ${countFeatureJars} -ne 1 ]; then echo "warning: skipping ${name}, ${countFeatureJars} feature libraries found" continue fi # found a match (handle multiple matches, just in case) match=true enableFeature "${name}" "${file}" fi done if [ "${match}" ] ; then echo status fi };; disable) { if [ -f "${POLICY_HOME}"/PID ]; then echo "error: disable: not allowed when policy is running .." echo status exit 11 fi # disable the specified options shift match= for name in "$@" ; do # look for matches -- 'file' has the last segment of the path name file=$(ls "${FEATURES}"/"${name}"/"${FEATURE_LIB}"/feature-"${name}"-[0-9]*.jar 2> /dev/null) if [ "$?" != "0" ] ; then echo "${name}: no such option" else # found a match (handle multiple matches, just in case) match=true disableFeature "${name}" fi done if [ "${match}" ] ; then echo status fi };; install) { shift installFeatures "$@" };; uninstall) { if [ -f "${POLICY_HOME}"/PID ]; then echo "error: uninstall: not allowed when policy is running .." echo status exit 12 fi shift uninstallFeatures "$@" };; *) { usage };; esac exit