Update and improve Linux kickstart script
* Multiple Endpoints * Default Global Zone is added automatically * Stable without need for sysconfig fixes #1585
This commit is contained in:
parent
2c3c09cb9c
commit
217e3c1dce
|
@ -1,77 +1,203 @@
|
|||
# Make sure icinga2 is installed and running
|
||||
#!/bin/bash
|
||||
|
||||
# This generates and signs your required certificates. Please do not
|
||||
# forget to install the Icinga 2 package and your desired monitoring
|
||||
# plugins first.
|
||||
|
||||
# Config from Director
|
||||
#ICINGA2_NODENAME='@ICINGA2_NODENAME@'
|
||||
#ICINGA2_CA_TICKET='@ICINGA2_CA_TICKET@'
|
||||
#ICINGA2_PARENT_ZONE='@ICINGA2_PARENT_ZONE@'
|
||||
#ICINGA2_PARENT_ENDPOINTS='@ICINGA2_PARENT_ENDPOINTS@'
|
||||
#ICINGA2_CA_NODE='@ICINGA2_CA_NODE@'
|
||||
#ICINGA2_GLOBAL_ZONES='@ICINGA2_GLOBAL_ZONES@'
|
||||
|
||||
# Internal defaults
|
||||
: "${ICINGA2_OSFAMILY:=}"
|
||||
: "${ICINGA2_HOSTNAME:="$(hostname -f)"}"
|
||||
: "${ICINGA2_NODENAME:="${ICINGA2_HOSTNAME}"}"
|
||||
: "${ICINGA2_CA_NODE:=}"
|
||||
: "${ICINGA2_CA_PORT:=5665}"
|
||||
: "${ICINGA2_CA_TICKET:=}"
|
||||
: "${ICINGA2_PARENT_ZONE:=master}"
|
||||
: "${ICINGA2_PARENT_ENDPOINTS:=()}"
|
||||
: "${ICINGA2_GLOBAL_ZONES:=director-global}"
|
||||
: "${ICINGA2_DRYRUN:=}"
|
||||
|
||||
# Helper functions
|
||||
fail() {
|
||||
echo "ERROR: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo "$1" >&2
|
||||
echo "WARNING: $1" >&2
|
||||
}
|
||||
|
||||
echo -n "check: icinga2 installed - "; if icinga2 --version &>/dev/null ; then echo "OK" ; else fail "FAIL, install icinga2 !"; exit 2; fi
|
||||
info() {
|
||||
echo "INFO: $1" >&2
|
||||
}
|
||||
|
||||
check_command() {
|
||||
command -v "$@" &>/dev/null
|
||||
}
|
||||
|
||||
install_config() {
|
||||
if [ -e "$1" ] && [ ! -e "${1}.orig" ]; then
|
||||
info "Creating a backup at ${1}.orig"
|
||||
cp "$1" "${1}.orig"
|
||||
fi
|
||||
echo "Writing config to ${1}"
|
||||
echo "$2" > "${1}"
|
||||
}
|
||||
|
||||
[ "$BASH_VERSION" ] || fail "This is a Bash script"
|
||||
|
||||
RHEL_SYSCONFIG="/etc/sysconfig/icinga2"
|
||||
DEB_SYSCONFIG="/usr/lib/icinga2/icinga2"
|
||||
errors=
|
||||
for key in NODENAME CA_NODE CA_PORT CA_TICKET PARENT_ZONE PARENT_ENDPOINTS; do
|
||||
var="ICINGA2_${key}"
|
||||
if [ -z "${!var}" ]; then
|
||||
warn "The variable $var needs to be configured!"
|
||||
errors+=1
|
||||
fi
|
||||
done
|
||||
[ -z "$errors" ] || exit 1
|
||||
|
||||
|
||||
if [ -f "$RHEL_SYSCONFIG" ]; then
|
||||
ICINGA2_SYSCONFIG_FILE="$RHEL_SYSCONFIG"
|
||||
elif [ -f "$DEB_SYSCONFIG" ]; then
|
||||
ICINGA2_SYSCONFIG_FILE="$DEB_SYSCONFIG"
|
||||
# Detect osfamily
|
||||
if [ -n "$ICINGA2_OSFAMILY" ]; then
|
||||
info "Assuming supplied osfamily $ICINGA2_OSFAMILY"
|
||||
elif check_command rpm && ! check_command dpkg; then
|
||||
info "This should be a RedHat system"
|
||||
if [ -e /etc/sysconfig/icinga2 ]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/sysconfig/icinga2
|
||||
fi
|
||||
ICINGA2_OSFAMILY=redhat
|
||||
elif check_command dpkg; then
|
||||
info "This should be a Debian system"
|
||||
if [ -e /etc/default/icinga2 ]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/default/icinga2
|
||||
fi
|
||||
ICINGA2_OSFAMILY=debian
|
||||
else
|
||||
echo "ERROR: couldn't find your Icinga2 sysconfig file"
|
||||
fail "Could not determine your os type!"
|
||||
fi
|
||||
|
||||
. "$ICINGA2_SYSCONFIG_FILE"
|
||||
[ "$ICINGA2_USER" ] || fail "\$ICINGA2_USER has not been defined"
|
||||
ICINGA2_CONF_DIR=$(dirname "$ICINGA2_CONFIG_FILE")
|
||||
ICINGA2_SYSCONF_DIR=$(dirname "$ICINGA2_CONF_DIR")
|
||||
ICINGA2_INSTALL_PREFIX=$(dirname $(dirname "$DAEMON"))
|
||||
ICINGA2_CA_DIR="${ICINGA2_STATE_DIR}/lib/icinga2/ca"
|
||||
ICINGA2_SSL_DIR="${ICINGA2_CONF_DIR}/pki"
|
||||
ICINGA2_CA_PORT="5665"
|
||||
# internal defaults
|
||||
: "${ICINGA2_CONFIG_FILE:=/etc/icinga2/icinga2.conf}"
|
||||
: "${ICINGA2_CONFIGDIR:="$(dirname "$ICINGA2_CONFIG_FILE")"}"
|
||||
: "${ICINGA2_DATADIR:=/var/lib/icinga2}"
|
||||
: "${ICINGA2_SSLDIR_OLD:="${ICINGA2_CONFIGDIR}"/pki}"
|
||||
: "${ICINGA2_SSLDIR_NEW:="${ICINGA2_DATADIR}"/certs}"
|
||||
: "${ICINGA2_SSLDIR:=}"
|
||||
: "${ICINGA2_BIN:=icinga2}"
|
||||
|
||||
. "${ICINGA2_INSTALL_PREFIX}/lib/icinga2/prepare-dirs" "${ICINGA2_SYSCONFIG_FILE}"
|
||||
case "$ICINGA2_OSFAMILY" in
|
||||
debian)
|
||||
: "${ICINGA2_USER:=nagios}"
|
||||
: "${ICINGA2_GROUP:=nagios}"
|
||||
;;
|
||||
redhat)
|
||||
: "${ICINGA2_USER:=icinga}"
|
||||
: "${ICINGA2_GROUP:=icinga}"
|
||||
;;
|
||||
*)
|
||||
fail "Unknown osfamily '$ICINGA2_OSFAMILY'!"
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! [ -d $ICINGA2_SSL_DIR ]; then mkdir $ICINGA2_SSL_DIR; fi
|
||||
chown $ICINGA2_USER $ICINGA2_SSL_DIR
|
||||
icinga_version() {
|
||||
"$ICINGA2_BIN" --version 2>/dev/null | grep -oP 'version: [rv]\K\d+\.\d+\.\d+[^\)]*'
|
||||
}
|
||||
|
||||
if [ -f "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.crt" ]; then
|
||||
icinga_major() {
|
||||
icinga_version | grep -oP '^\d+\.\d+'
|
||||
}
|
||||
|
||||
# only can do float like versions (1.x)
|
||||
version_compare() {
|
||||
# args: v1 op v2
|
||||
return "$(awk "BEGIN {print !($1 $2 $3)}")"
|
||||
}
|
||||
|
||||
# Make sure icinga2 is installed and running
|
||||
echo -n "check: icinga2 installed - "
|
||||
if version=$(icinga_version); then
|
||||
echo "OK: $version"
|
||||
else
|
||||
fail "You need to install icinga2!"
|
||||
fi
|
||||
|
||||
if [ -z "${ICINGA2_SSLDIR}" ]; then
|
||||
if version_compare "$(icinga_major)" ">=" 2.8; then
|
||||
info "Using new SSL directory: ${ICINGA2_SSLDIR_NEW}"
|
||||
ICINGA2_SSLDIR="${ICINGA2_SSLDIR_NEW}"
|
||||
|
||||
if [ -f "${ICINGA2_SSLDIR_OLD}/${ICINGA2_NODENAME}.crt" ]; then
|
||||
warn "ERROR: a certificate for '${ICINGA2_NODENAME}' already exists"
|
||||
warn "Please move ${ICINGA2_SSLDIR_OLD}/${ICINGA2_NODENAME}.??? manually to"
|
||||
warn "${ICINGA2_SSLDIR_NEW}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
info "Using old SSL directory: ${ICINGA2_SSLDIR_OLD}"
|
||||
ICINGA2_SSLDIR="${ICINGA2_SSLDIR_OLD}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -d "$ICINGA2_SSLDIR" ]; then
|
||||
mkdir "$ICINGA2_SSLDIR"
|
||||
chown "$ICINGA2_USER.$ICINGA2_GROUP" "$ICINGA2_SSLDIR"
|
||||
fi
|
||||
|
||||
if [ -f "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.crt" ]; then
|
||||
warn "ERROR: a certificate for '${ICINGA2_NODENAME}' already exists"
|
||||
warn "Please remove ${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.??? in case you want a"
|
||||
warn "Please remove ${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.??? in case you want a"
|
||||
warn "new certificate to be generated and signed by ${ICINGA2_CA_NODE}"
|
||||
exit 1
|
||||
echo
|
||||
read -r -p "Do you want to continue updating config files? [y/N] " answer
|
||||
if [ "${answer,,}" != y ] && [ "${answer,,}" != yes ]; then
|
||||
fail "Aborting."
|
||||
fi
|
||||
elif [ -z "${ICINGA2_DRYRUN}" ]; then
|
||||
if ! "$ICINGA2_BIN" pki new-cert --cn "${ICINGA2_NODENAME}" \
|
||||
--cert "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.crt" \
|
||||
--csr "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.csr" \
|
||||
--key "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.key"
|
||||
then fail "Could not create self signed certificate!"
|
||||
fi
|
||||
|
||||
if ! "$ICINGA2_BIN" pki save-cert \
|
||||
--host "${ICINGA2_CA_NODE}" \
|
||||
--port "${ICINGA2_CA_PORT}" \
|
||||
--key "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.key" \
|
||||
--trustedcert "${ICINGA2_SSLDIR}/trusted-master.crt"
|
||||
then fail "Could not retrieve trusted certificate from host ${ICINGA2_CA_NODE}"
|
||||
fi
|
||||
|
||||
if ! "$ICINGA2_BIN" pki request \
|
||||
--host "${ICINGA2_CA_NODE}" \
|
||||
--port "${ICINGA2_CA_PORT}" \
|
||||
--ticket "${ICINGA2_CA_TICKET}" \
|
||||
--key "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.key" \
|
||||
--cert "${ICINGA2_SSLDIR}/${ICINGA2_NODENAME}.crt" \
|
||||
--trustedcert "${ICINGA2_SSLDIR}/trusted-master.crt" \
|
||||
--ca "${ICINGA2_SSLDIR}/ca.crt"
|
||||
then "Could not retrieve final certificate from host ${ICINGA2_CA_NODE}"
|
||||
fi
|
||||
else
|
||||
info "Would create certificates under ${ICINGA2_SSLDIR}, but in dry-run!"
|
||||
fi
|
||||
|
||||
"$DAEMON" pki new-cert --cn "${ICINGA2_NODENAME}" \
|
||||
--cert "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.crt" \
|
||||
--csr "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.csr" \
|
||||
--key "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.key"
|
||||
|
||||
"$DAEMON" pki save-cert \
|
||||
--host "${ICINGA2_CA_NODE}" \
|
||||
--port "${ICINGA2_CA_PORT}" \
|
||||
--key "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.key" \
|
||||
--trustedcert "${ICINGA2_SSL_DIR}/trusted-master.crt"
|
||||
|
||||
"$DAEMON" pki request \
|
||||
--host "${ICINGA2_CA_NODE}" \
|
||||
--port "${ICINGA2_CA_PORT}" \
|
||||
--ticket "${ICINGA2_CA_TICKET}" \
|
||||
--key "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.key" \
|
||||
--cert "${ICINGA2_SSL_DIR}/${ICINGA2_NODENAME}.crt" \
|
||||
--trustedcert "${ICINGA2_SSL_DIR}/trusted-master.crt" \
|
||||
--ca "${ICINGA2_SSL_DIR}/ca.crt"
|
||||
|
||||
|
||||
# Write Config Files
|
||||
CONF_ICINGA2=`cat << EOF
|
||||
# Prepare Config Files
|
||||
content_config=$(cat << EOF
|
||||
/** Icinga 2 Config - proposed by Icinga Director */
|
||||
|
||||
include "constants.conf"
|
||||
|
||||
$([ "${ICINGA2_HOSTNAME}" != "${ICINGA2_NODENAME}" ] || echo '// ')const NodeName = "${ICINGA2_NODENAME}"
|
||||
|
||||
include "zones.conf"
|
||||
include "features-enabled/*.conf"
|
||||
|
||||
|
@ -82,44 +208,94 @@ include <manubulon>
|
|||
include <windows-plugins>
|
||||
include <nscp>
|
||||
EOF
|
||||
`
|
||||
ZONES_ICINGA2=`cat << EOF
|
||||
)
|
||||
|
||||
endpoint_list=''
|
||||
for item in "${ICINGA2_PARENT_ENDPOINTS[@]}"; do
|
||||
endpoint=$(echo "$item" | cut -d, -f1)
|
||||
endpoint_list+="\"${endpoint}\", "
|
||||
done
|
||||
|
||||
content_zones=$(cat << EOF
|
||||
/** Icinga 2 Config - proposed by Icinga Director */
|
||||
|
||||
// TODO: improve establish connection handling
|
||||
object Endpoint "${ICINGA2_NODENAME}" {}
|
||||
object Endpoint "${ICINGA2_CA_NODE}" {}
|
||||
object Zone "${ICINGA2_PARENT_ZONE}" {
|
||||
endpoints = [ "$ICINGA2_PARENT_ENDPOINTS" ]
|
||||
// TODO: all endpoints in master zone
|
||||
}
|
||||
|
||||
object Zone "director-global" { global = true }
|
||||
|
||||
object Zone "${ICINGA2_NODENAME}" {
|
||||
parent = "${ICINGA2_PARENT_ZONE}"
|
||||
endpoints = [ "$ICINGA2_NODENAME" ]
|
||||
endpoints = [ "${ICINGA2_NODENAME}" ]
|
||||
}
|
||||
|
||||
object Zone "${ICINGA2_PARENT_ZONE}" {
|
||||
endpoints = [ ${endpoint_list%, } ]
|
||||
}
|
||||
EOF
|
||||
`
|
||||
)
|
||||
|
||||
API_ICINGA2=`cat << EOF
|
||||
/** Icinga 2 Config - proposed by Icinga Director */
|
||||
for item in "${ICINGA2_PARENT_ENDPOINTS[@]}"; do
|
||||
endpoint=$(echo "$item" | cut -d, -f1)
|
||||
host=$(echo "$item" | cut -s -d, -f2)
|
||||
|
||||
object ApiListener "api" {
|
||||
cert_path = SysconfDir + "/icinga2/pki/${ICINGA2_NODENAME}.crt"
|
||||
key_path = SysconfDir + "/icinga2/pki/${ICINGA2_NODENAME}.key"
|
||||
ca_path = SysconfDir + "/icinga2/pki/ca.crt"
|
||||
content_zones+=$(cat << EOF
|
||||
|
||||
object Endpoint "${endpoint}" {
|
||||
$([ -n "$host" ] && echo " host = \"${host}\"" || echo " //host = \"${endpoint}\"")
|
||||
}
|
||||
EOF
|
||||
)
|
||||
done
|
||||
|
||||
for zone in "${ICINGA2_GLOBAL_ZONES[@]}"; do
|
||||
content_zones+=$(cat << EOF
|
||||
|
||||
object Zone "${zone}" {
|
||||
global = true
|
||||
}
|
||||
EOF
|
||||
)
|
||||
done
|
||||
|
||||
content_api="/** Icinga 2 Config - proposed by Icinga Director */
|
||||
|
||||
object ApiListener \"api\" {"
|
||||
|
||||
if [ "${ICINGA2_SSLDIR}" = "${ICINGA2_SSLDIR_OLD}" ]; then
|
||||
content_api+="
|
||||
cert_path = SysconfDir + \"/icinga2/pki/${ICINGA2_NODENAME}.crt\"
|
||||
key_path = SysconfDir + \"/icinga2/pki/${ICINGA2_NODENAME}.key\"
|
||||
ca_path = SysconfDir + \"/icinga2/pki/ca.crt\"
|
||||
"
|
||||
fi
|
||||
content_api+="
|
||||
accept_commands = true
|
||||
accept_config = true
|
||||
}
|
||||
EOF
|
||||
`
|
||||
"
|
||||
|
||||
/usr/bin/printf "%b" "$CONF_ICINGA2" > $ICINGA2_CONF_DIR/icinga2.conf
|
||||
/usr/bin/printf "%b" "$ZONES_ICINGA2" > $ICINGA2_CONF_DIR/zones.conf
|
||||
/usr/bin/printf "%b" "$API_ICINGA2" > $ICINGA2_CONF_DIR/features-available/api.conf
|
||||
if [ -z "${ICINGA2_DRYRUN}" ]; then
|
||||
install_config "$ICINGA2_CONFIGDIR"/icinga2.conf "$content_config"
|
||||
install_config "$ICINGA2_CONFIGDIR"/zones.conf "$content_zones"
|
||||
install_config "$ICINGA2_CONFIGDIR"/features-available/api.conf "$content_api"
|
||||
|
||||
icinga2 feature enable api
|
||||
"$ICINGA2_BIN" feature enable api
|
||||
|
||||
echo "Please restart icinga2!"
|
||||
"$ICINGA2_BIN" daemon -C
|
||||
|
||||
echo "Please restart icinga2:"
|
||||
echo " service icinga2 restart"
|
||||
else
|
||||
output_code() {
|
||||
sed 's/^/ /m' <<<"$1"
|
||||
}
|
||||
echo "### $ICINGA2_CONFIGDIR"/icinga2.conf
|
||||
echo
|
||||
output_code "$content_config"
|
||||
echo
|
||||
echo "### $ICINGA2_CONFIGDIR"/zones.conf
|
||||
echo
|
||||
output_code "$content_zones"
|
||||
echo
|
||||
echo "### $ICINGA2_CONFIGDIR"/features-available/api.conf
|
||||
echo
|
||||
output_code "$content_api"
|
||||
fi
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This generates and signs your required certificates. Please do not
|
||||
# forget to install the Icinga 2 package and your desired monitoring
|
||||
# plugins first:
|
||||
|
|
@ -128,10 +128,7 @@ class AgentWizard
|
|||
|
||||
protected function loadPowershellModule()
|
||||
{
|
||||
return file_get_contents(
|
||||
dirname(dirname(dirname(__DIR__)))
|
||||
. '/contrib/windows-agent-installer/Icinga2Agent.psm1'
|
||||
);
|
||||
return $this->getContribFile('windows-agent-installer/Icinga2Agent.psm1');
|
||||
}
|
||||
|
||||
public function renderWindowsInstaller()
|
||||
|
@ -244,46 +241,48 @@ class AgentWizard
|
|||
|
||||
public function renderLinuxInstaller()
|
||||
{
|
||||
return $this->loadBashModuleHead()
|
||||
. $this->renderBashParameters(
|
||||
array(
|
||||
'ICINGA2_NODENAME' => $this->getCertName(),
|
||||
'ICINGA2_CA_TICKET' => $this->getTicket(),
|
||||
'ICINGA2_PARENT_ZONE' => $this->getParentZone()->getObjectName(),
|
||||
'ICINGA2_PARENT_ENDPOINTS' => array_keys($this->getParentEndpoints()),
|
||||
'ICINGA2_CA_NODE' => $this->getCaServer(),
|
||||
)
|
||||
)
|
||||
. "\n"
|
||||
. $this->loadBashModule()
|
||||
. "\n\n";
|
||||
$script = $this->loadBashModule();
|
||||
|
||||
$endpoints = [];
|
||||
foreach ($this->getParentEndpoints() as $endpoint) {
|
||||
$endpoints[$endpoint->getObjectName()] = $endpoint->get('host');
|
||||
}
|
||||
|
||||
return $this->replaceBashTemplate($script, [
|
||||
'ICINGA2_NODENAME' => $this->getCertName(),
|
||||
'ICINGA2_CA_TICKET' => $this->getTicket(),
|
||||
'ICINGA2_PARENT_ZONE' => $this->getParentZone()->getObjectName(),
|
||||
'ICINGA2_PARENT_ENDPOINTS' => $endpoints,
|
||||
'ICINGA2_CA_NODE' => $this->getCaServer(),
|
||||
'ICINGA2_GLOBAL_ZONES' => [$this->db()->getDefaultGlobalZoneName()],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function loadBashModule()
|
||||
{
|
||||
return file_get_contents(
|
||||
dirname(dirname(dirname(__DIR__)))
|
||||
. '/contrib/linux-agent-installer/Icinga2Agent.bash'
|
||||
);
|
||||
return $this->getContribFile('linux-agent-installer/Icinga2Agent.bash');
|
||||
}
|
||||
|
||||
protected function loadBashModuleHead()
|
||||
protected function replaceBashTemplate($script, $parameters)
|
||||
{
|
||||
return file_get_contents(
|
||||
dirname(dirname(dirname(__DIR__)))
|
||||
. '/contrib/linux-agent-installer/Icinga2AgentHead.bash'
|
||||
);
|
||||
}
|
||||
|
||||
protected function renderBashParameters($parameters)
|
||||
{
|
||||
$parts = array();
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
$parts[] = $this->renderBashParameter($key, $value);
|
||||
$quotedKey = preg_quote($key, '~');
|
||||
if (is_array($value)) {
|
||||
$list = [];
|
||||
foreach ($value as $k => $v) {
|
||||
if (!is_numeric($k)) {
|
||||
$v = "$k,$v";
|
||||
}
|
||||
$list[] = escapeshellarg($v);
|
||||
}
|
||||
$value = '(' . join(' ', $list) . ')';
|
||||
} else {
|
||||
$value = escapeshellarg($value);
|
||||
}
|
||||
$script = preg_replace("~^#?$quotedKey='@$quotedKey@'$~m", "${key}=${value}", $script);
|
||||
}
|
||||
|
||||
return implode("\n", $parts);
|
||||
return $script;
|
||||
}
|
||||
|
||||
protected function renderBashParameter($key, $value)
|
||||
|
@ -307,4 +306,14 @@ class AgentWizard
|
|||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
protected function getContribDir()
|
||||
{
|
||||
return dirname(dirname(dirname(__DIR__))) . '/contrib';
|
||||
}
|
||||
|
||||
protected function getContribFile($path)
|
||||
{
|
||||
return file_get_contents($this->getContribDir() . '/' . $path);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue