Skip to content
Snippets Groups Projects
Commit c063db38 authored by Marco Malavolti's avatar Marco Malavolti
Browse files

Add Debug environment var & Fixed CRON jobs

parent 260aee82
No related branches found
No related tags found
No related merge requests found
......@@ -5,13 +5,7 @@ ARG CHROME_VERSION=126.0.6478.126-1
ARG CHROMEDRIVER_VERSION=126.0.6478.126
ARG PYFF_VERSION=2.1.2
ARG UID=1000
ARG GID=1000
USER root:root
RUN groupadd -g ${GID} -o eccs \
&& useradd -u ${UID} -g ${GID} -rms /bin/bash eccs \
&& apt-get update \
RUN apt-get update \
&& apt-get install -y apt-utils net-tools vim cron wget gpg gpg-agent unzip ca-certificates \
build-essential python3-dev libxml2-dev libxml2-dev libxslt1-dev logrotate \
apache2 php supervisor uwsgi uwsgi-plugin-python3 libapache2-mod-proxy-uwsgi libxml2-utils \
......@@ -59,8 +53,6 @@ RUN mkdir -p /var/run/supervisord
COPY start.sh /start.sh
RUN chmod +x /start.sh
USER eccs:eccs
# Get ECCS
WORKDIR /home/eccs
......@@ -71,17 +63,13 @@ RUN wget "https://storage.googleapis.com/chrome-for-testing-public/${CHROMEDRIVE
&& mv chromedriver-linux64/chromedriver . \
&& rm -rf chromedriver-linux64
COPY --chown=eccs:eccs pyff-config/ pyff-config/
COPY pyff-config/ pyff-config/
COPY --chown=eccs:eccs api.py clean7daysOldFiles.sh cleanAndRunEccs.sh eccs.ini eccs.py eccs_properties.py eccs-wsgi.py get-sps-metadata.sh retryFailedChecks.py runEccs.py utils.py ./
COPY --chown=eccs:eccs input/ input/
COPY --chown=eccs:eccs web/ web/
RUN mkdir -m 0755 output html logs selenium-logs \
&& chown -R eccs:eccs output html logs selenium-logs
COPY api.py clean7daysOldFiles.sh cleanAndRunEccs.sh eccs.ini eccs.py eccs_properties.py eccs-wsgi.py get-sps-metadata.sh retryFailedChecks.py runEccs.py utils.py ./
COPY input/ input/
COPY web/ web/
RUN mkdir -m 0755 -p output html stats debug/selenium-logs
EXPOSE 80
EXPOSE 443
USER root:root
CMD ["/start.sh"]
......@@ -75,7 +75,7 @@ Use the ECCS Docker container
* Create the ``eccs`` directory with the directories required by bind mounts:
* ``mkdir -p eccs/output eccs/html eccs/logs eccs/selenium-logs``
* ``mkdir -p eccs/output eccs/html eccs/stats eccs/debug/selenium-logs``
* Instance ECCS:
......@@ -91,7 +91,7 @@ Use the ECCS Docker container
* Run ECCS script:
* ``docker exec -it -w /home/eccs eccs ./cleanAndRunEccs.sh > logs/eccs-cron.log 2>&1``
* ``docker exec -it -w /home/eccs eccs ./cleanAndRunEccs.sh > /proc/1/fd/1 2>&1``
* Open ECCS Web Interface on your browser:
......
......
......@@ -431,11 +431,10 @@ class FedStats(Resource):
results.append(resultDict)
return jsonify(results)
# /api/webdata
class WebData(Resource):
def get(self):
file_path = f"{e_p.ECCS_LOGSDIR}/eccs-uwsgi-req.log" # will this name be moved to properties definer file ?
file_path = f"{e_p.ECCS_STATS_INPUT_DIR}/eccs-uwsgi-req.log" # will this name be moved to properties definer file ?
criteria = {}
criteria['date_from'] = criteria['date_to'] = e_p.DAY
# TBM to config
......@@ -478,7 +477,7 @@ class WebData(Resource):
criteria['logfile_date'] = (datetime.strptime(cur_date, '%Y-%m-%d') + timedelta(days=1)).strftime('%Y-%m-%d')
criteria['cur_date'] = cur_date
tmpDate = datetime.strptime(criteria['logfile_date'], '%Y-%m-%d').strftime('%Y%m%d')
file_path = f"{e_p.ECCS_LOGSDIR}/eccs-uwsgi-req.log-{tmpDate}"
file_path = f"{e_p.ECCS_STATS_INPUT_DIR}/eccs-uwsgi-req.log-{tmpDate}"
if useParsedFile == True:
json_file_path = f"{e_p.ECCS_DIR}/parsed/eccs-uwsgi-req-json-{tmpDate}"
......@@ -557,5 +556,5 @@ if __name__ == '__main__':
# Useful only for API development Server
#app.config['JSON_AS_ASCII'] = True
#app.logger.removeHandler(default_handler)
#app.logger = get_logger("eccs_api.log", e_p.ECCS_LOGSDIR, "w", "INFO")
#app.logger = get_logger("eccs_api.log", e_p.ECCS_STATS_INPUT_DIR, "w", "INFO")
app.run(port='5002')
......@@ -2,12 +2,12 @@
BASEDIR=/home/eccs
# Remove ECCS2 result older than 7 days
# Remove ECCS results older than 7 days
find $BASEDIR/output/* -mtime +6 -type f -delete
# Remove ECCS2 logs older than 7 days
find $BASEDIR/logs/* -mtime +6 -type f -delete
# Remove ECCS stats older than 7 days
#find $BASEDIR/stats/* -mtime +6 -type f -delete
# Remove ECCS2 HTML code older than 7 days
# Remove ECCS HTML code older than 7 days
find $BASEDIR/html/* -mtime +6 -type f -delete
find $BASEDIR/html -type d -empty -delete
#!/bin/bash
# logs/stderr_$date.log is kept to see which IdP had been errors
# debug/stderr_$date.log is kept to see which IdP had been errors
BASEDIR=/home/eccs
# Remove old IdP and Fed List
rm -f $BASEDIR/input/*.json
# Run ECCS2
# Run ECCS
$BASEDIR/runEccs.py
# Run again ECCS2 for those IdPs who failed check
# Run again ECCS for those IdPs who failed check
$BASEDIR/retryFailedChecks.py
# Remove useless files
......
......
# Ignore everything in this directory
*
# Except this file and selenium-logs directory
!.gitignore
!/selenium-logs
File moved
services:
eccs:
image: gitlab.software.geant.org:5050/edugain/eccs:2.1.2
container_name: eccs
hostname: eccs
ports:
- 127.0.0.1:8080:80
environment:
ECCS_SELENIUM_DEBUG: false
volumes:
- type: bind
source: ./output
target: /home/eccs/output
- type: bind
source: ./html
target: /home/eccs/html
- type: bind
source: ./stats
target: /home/eccs/stats
- type: bind
source: ./debug
target: /home/eccs/debug
......@@ -11,8 +11,9 @@ services:
container_name: eccs
hostname: eccs
ports:
- 80:80
- 443:443
- 127.0.0.1:8080:80
environment:
ECCS_SELENIUM_DEBUG: false
volumes:
- type: bind
source: ./output
......@@ -21,8 +22,8 @@ services:
source: ./html
target: /home/eccs/html
- type: bind
source: ./logs
target: /home/eccs/logs
source: ./stats
target: /home/eccs/stats
- type: bind
source: ./selenium-logs
target: /home/eccs/selenium-logs
source: ./debug
target: /home/eccs/debug
......@@ -7,8 +7,8 @@ chdir = %(base)/%(project)
master = true
processes = 2
uid = eccs
gid = eccs
uid = root
gid = root
socket = 127.0.0.1:8000
chmod-socket = 660
......
......
0 0 * * * root (echo "[start LogRotate]" && /usr/sbin/logrotate -v /etc/logrotate.d/eccs && echo "[end LogRotate]") > /proc/1/fd/1 2>&1
0 3 * * * eccs /usr/bin/touch /home/eccs/eccs.ini
0 3 * * * root /usr/bin/touch /home/eccs/eccs.ini
0 3 * * * root (echo "[start PyFF]" && /usr/local/bin/pyff --loglevel=DEBUG /home/eccs/pyff-config/sps-metadata.xml && echo "[end PyFF]") > /proc/1/fd/1 2>&1
0 4 * * * eccs /bin/bash /home/eccs/cleanAndRunEccs.sh > /home/eccs/logs/eccs-cron.log 2>&1
0 10 * * * eccs /bin/bash /home/eccs/clean7daysOldFiles.sh > /proc/1/fd/1 2>&1
0 4 * * * root (echo "[start ECCS]" && /bin/bash /home/eccs/cleanAndRunEccs.sh && echo "[end ECCS]") > /proc/1/fd/1 2>&1
0 10 * * * root (echo "[start ECCS clean]" && /bin/bash /home/eccs/clean7daysOldFiles.sh && echo "[end ECCS clean]") > /proc/1/fd/1 2>&1
......@@ -47,6 +47,7 @@ CA_BUNDLE_PATH = "/etc/ssl/certs/ca-certificates.crt"
ECCS_DIR = f"/home/eccs"
PATHCHROMEDRIVER = f"{ECCS_DIR}/chromedriver"
ECCS_PYTHON = f"python3"
ECCS_REQUESTS_TIMEOUT = 15 #seconds
# Input
ECCS_INPUTDIR = f"{ECCS_DIR}/input"
......@@ -59,25 +60,27 @@ ECCS_LISTFEDSFILE = f"{ECCS_INPUTDIR}/list_fed.json"
ECCS_OUTPUTDIR = f"{ECCS_DIR}/output"
ECCS_RESULTSLOG = f"eccs_{DAY}.log"
ECCS_HTMLDIR = f"{ECCS_DIR}/html"
ECCS_FAILEDCMD = f"{ECCS_OUTPUTDIR}/failed-cmd.sh"
# SPS Metadata
# SPs Metadata
SPS_MD_PATH = f"{ECCS_INPUTDIR}/sps-metadata.xml"
# Debug
ECCS_DEBUG_DIR = f"{ECCS_DIR}/debug"
ECCS_DEBUG_STDOUT = f"{ECCS_DEBUG_DIR}/stdout_{DAY}.log"
ECCS_DEBUG_STDERR = f"{ECCS_DEBUG_DIR}/stderr_{DAY}.log"
ECCS_DEBUG_STDOUT_IDP = f"{ECCS_DEBUG_DIR}/stdout_idp_{DAY}.log"
ECCS_DEBUG_STDERR_IDP = f"{ECCS_DEBUG_DIR}/stderr_idp_{DAY}.log"
ECCS_FAILEDCMDIDP = f"{ECCS_DEBUG_DIR}/failed-cmd-idp.sh"
# Selenium
ECCS_SELENIUMDEBUG = False
ECCS_SELENIUMLOGDIR = f"{ECCS_DIR}/selenium-logs"
ECCS_SELENIUMPAGELOADTIMEOUT = 60 #seconds (remind to change timeout seconds also on web/eccs.js)
ECCS_SELENIUMSCRIPTTIMEOUT = 60 #seconds
ECCS_REQUESTSTIMEOUT = 15 #seconds
# Logs
ECCS_LOGSDIR = f"{ECCS_DIR}/logs"
ECCS_STDOUT = f"{ECCS_LOGSDIR}/stdout_{DAY}.log"
ECCS_STDERR = f"{ECCS_LOGSDIR}/stderr_{DAY}.log"
ECCS_FAILEDCMD = f"{ECCS_LOGSDIR}/failed-cmd.sh"
ECCS_STDOUTIDP = f"{ECCS_LOGSDIR}/stdout_idp_{DAY}.log"
ECCS_STDERRIDP = f"{ECCS_LOGSDIR}/stderr_idp_{DAY}.log"
ECCS_FAILEDCMDIDP = f"{ECCS_LOGSDIR}/failed-cmd-idp.sh"
ECCS_SELENIUM_DEBUG = os.environ['ECCS_SELENIUM_DEBUG']
ECCS_SELENIUM_DEBUG_LOG_DIR = f"{ECCS_DEBUG_DIR}/selenium-logs"
ECCS_SELENIUM_PAGELOADTIMEOUT = 60 #seconds (remind to change timeout seconds also on web/eccs.js)
ECCS_SELENIUM_SCRIPTTIMEOUT = 60 #seconds
# Statistics
ECCS_STATS_INPUT_DIR = f"{ECCS_DIR}/stats"
# Number of processes to run in parallel
ECCS_NUMPROCESSES = 30
......
......
......@@ -87,8 +87,8 @@ if __name__=="__main__":
list_eccs_idps = utils.get_list_from_url(e_p.ECCS_LISTIDPSURL, e_p.ECCS_LISTIDPSFILE)
if (args.idp_entityid):
stdout_file = open(e_p.ECCS_STDOUTIDP,"w+")
stderr_file = open(e_p.ECCS_STDERRIDP,"w+")
stdout_file = open(e_p.ECCS_DEBUG_STDOUT_IDP,"w+")
stderr_file = open(e_p.ECCS_DEBUG_STDERR_IDP,"w+")
cmd_file = open(e_p.ECCS_FAILEDCMDIDP,"w+")
idpJsonList = utils.get_idp_list(list_eccs_idps,idp_entityid=args.idp_entityid[0])
......@@ -111,8 +111,8 @@ if __name__=="__main__":
cmd_file.close()
else:
stdout_file = open(e_p.ECCS_STDOUT,"w+")
stderr_file = open(e_p.ECCS_STDERR,"w+")
stdout_file = open(e_p.ECCS_DEBUG_STDOUT,"w+")
stderr_file = open(e_p.ECCS_DEBUG_STDERR,"w+")
cmd_file = open(e_p.ECCS_FAILEDCMD,"w+")
# Prepare input file for ECCS
......
......
#!/bin/bash
function modify_hosts_file() {
local action="$1"
local domain_name="$2"
local ip_address="$3"
local hosts_path="/etc/hosts"
if [[ "$action" == "add" ]]; then
if sudo grep -q "$ip_address $domain_name" "$hosts_path"; then
return 0
fi
echo -ne "\n$ip_address\t$domain_name" | sudo tee -a "$hosts_path" >/dev/null
#echo "\nAdded $ip_address $domain_name to /etc/hosts file"
elif [[ "$action" == "remove" ]]; then
if ! sudo grep -q "$domain_name" "$hosts_path"; then
return 0
fi
sudo sed -i "/$domain_name/d" "$hosts_path"
#echo "\nRemoved line containing $domain_name from /etc/hosts file"
else
echo "Usage: modify_hosts_file [add|remove] <domain> [<IP address>]"
return 1
fi
}
echo "Stop and remove all"
docker compose down
echo "Stop any local Apache2 Web Server started"
sudo systemctl stop apache2.service
echo "Remove old container image to be able to create it from scratch"
docker rmi gitlab.software.geant.org:5050/edugain/eccs:dev
echo "Start docker container creation"
docker compose up -d
ECCS_IP=$(docker inspect eccs | grep "IPAddress" | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | tail -n 1)
DOMAIN_NAME="eccs.edugain.org"
modify_hosts_file "remove" "$DOMAIN_NAME"
modify_hosts_file "add" "$DOMAIN_NAME" "$ECCS_IP"
echo ""
echo "ECCS URL:"
echo ""
echo "http://localhost:8080/eccs"
echo ""
echo "Useful command to access the 'eccs' terminal:"
echo ""
......@@ -50,7 +20,7 @@ echo "docker exec -it -u root eccs bash"
echo ""
echo "Run ECCS manually for today:"
echo ""
echo "docker exec -it -u eccs -w /home/eccs eccs ./cleanAndRunEccs.sh > logs/eccs-cron.log 2>&1"
echo "docker exec -it -u eccs -w /home/eccs eccs ./cleanAndRunEccs.sh > /proc/1/fd/1 2>&1"
echo ""
echo "Delete all results of today"
echo ""
......
......
# Ignore everything in this directory
*
# Except this file
!.gitignore
......@@ -27,7 +27,6 @@ autorestart=true
[program:uwsgi]
command=uwsgi --ini /home/eccs/eccs.ini
user=eccs
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
......
......
......@@ -226,10 +226,10 @@ def get_driver_selenium(idp=None,sp=None,debugSelenium=False):
label_sp = get_label(sp['entityID'])
sha1_idp = sha1(idp['entityID'])
try:
driver = webdriver.Chrome(e_p.PATHCHROMEDRIVER, options=chrome_options, service_args=['--verbose', f'--log-path={e_p.ECCS_SELENIUMLOGDIR}/{sha1_idp}_{label_idp}_{label_sp}.log'])
driver = webdriver.Chrome(e_p.PATHCHROMEDRIVER, options=chrome_options, service_args=['--verbose', f'--log-path={e_p.ECCS_SELENIUM_DEBUG_LOG_DIR}/{sha1_idp}_{label_idp}_{label_sp}.log'])
except: # Wait 3 seconds before try again to get the webdriver for all kind of exception will occur
time.sleep(3)
driver = webdriver.Chrome(e_p.PATHCHROMEDRIVER, options=chrome_options, service_args=['--verbose', f'--log-path={e_p.ECCS_SELENIUMLOGDIR}/{sha1_idp}_{label_idp}_{label_sp}.log'])
driver = webdriver.Chrome(e_p.PATHCHROMEDRIVER, options=chrome_options, service_args=['--verbose', f'--log-path={e_p.ECCS_SELENIUM_DEBUG_LOG_DIR}/{sha1_idp}_{label_idp}_{label_sp}.log'])
else:
try:
driver = webdriver.Chrome(e_p.PATHCHROMEDRIVER, options=chrome_options)
......@@ -325,10 +325,10 @@ def check_idp_response_selenium(sp,idp,test):
'User-Agent': f'{e_p.ROBOTS_USER_AGENT}'
}
check_time = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + 'Z'
robots = requests.get(f"https://{fqdn_idp}/robots.txt", headers=hdrs, verify=e_p.CA_BUNDLE_PATH, timeout=e_p.ECCS_REQUESTSTIMEOUT)
robots = requests.get(f"https://{fqdn_idp}/robots.txt", headers=hdrs, verify=e_p.CA_BUNDLE_PATH, timeout=e_p.ECCS_REQUESTS_TIMEOUT)
if (robots == ""):
robots = requests.get(f"http://{fqdn_idp}/robots.txt", headers=hdrs, verify=False, timeout=e_p.ECCS_REQUESTSTIMEOUT)
robots = requests.get(f"http://{fqdn_idp}/robots.txt", headers=hdrs, verify=False, timeout=e_p.ECCS_REQUESTS_TIMEOUT)
# Catch SSL Exceptions and block the ECCS check
except requests.exceptions.SSLError as e:
......@@ -360,15 +360,15 @@ def check_idp_response_selenium(sp,idp,test):
try:
# WebDriver MUST be instanced here to avoid problems with SESSION
driver = get_driver_selenium(idp,sp,e_p.ECCS_SELENIUMDEBUG)
driver = get_driver_selenium(idp,sp,e_p.ECCS_SELENIUM_DEBUG)
# Exception of WebDriver raises
if (driver == None):
sys.stderr.write(f"get_driver_selenium() returned None for IDP {idp['entityID']}(SHA1: {sha1(idp['entityID'])}) with SP {get_label(sp['entityID'])}")
return None
driver.set_page_load_timeout(e_p.ECCS_SELENIUMPAGELOADTIMEOUT)
driver.set_script_timeout(e_p.ECCS_SELENIUMSCRIPTTIMEOUT)
driver.set_page_load_timeout(e_p.ECCS_SELENIUM_PAGELOADTIMEOUT)
driver.set_script_timeout(e_p.ECCS_SELENIUM_SCRIPTTIMEOUT)
check_time = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') + 'Z'
......@@ -417,7 +417,7 @@ def check_idp_response_selenium(sp,idp,test):
if (not pwd_found):
follow_all_nested_iframes(driver)
WebDriverWait(driver, e_p.ECCS_SELENIUMPAGELOADTIMEOUT).until(
WebDriverWait(driver, e_p.ECCS_SELENIUM_PAGELOADTIMEOUT).until(
EC.presence_of_element_located((By.XPATH,e_p.XPATH_CHECK_PATTERN))
)
......@@ -451,7 +451,7 @@ def check_idp_response_selenium(sp,idp,test):
return (idp['entityID'],sp['entityID'],check_time,"No-SP-Metadata-Error",webdriver_error)
else:
try:
response = requests.get(f"{driver.current_url}", timeout=e_p.ECCS_REQUESTSTIMEOUT)
response = requests.get(f"{driver.current_url}", timeout=e_p.ECCS_REQUESTS_TIMEOUT)
if (response.status_code == 401):
if (test):
......@@ -500,9 +500,9 @@ def check_idp_response_selenium(sp,idp,test):
return (idp['entityID'],sp['entityID'],check_time,"Unable-To-Check",webdriver_error)
else:
if (test):
header = f"\nTimeout: No valid login form loaded in {e_p.ECCS_SELENIUMPAGELOADTIMEOUT} seconds.\n[PAGE_SOURCE]\n{pgsrc}"
header = f"\nTimeout: No valid login form loaded in {e_p.ECCS_SELENIUM_PAGELOADTIMEOUT} seconds.\n[PAGE_SOURCE]\n{pgsrc}"
else:
header = f"<h1>Timeout</h1><h2>No valid login form found in {e_p.ECCS_SELENIUMPAGELOADTIMEOUT} seconds.</h2><h3>[PAGE_SOURCE]</h3>"
header = f"<h1>Timeout</h1><h2>No valid login form found in {e_p.ECCS_SELENIUM_PAGELOADTIMEOUT} seconds.</h2><h3>[PAGE_SOURCE]</h3>"
stored = store_page_source(idp,sp,test,pgsrc,header)
if (stored):
return (idp['entityID'],sp['entityID'],check_time,"Timeout",webdriver_error)
......@@ -510,9 +510,9 @@ def check_idp_response_selenium(sp,idp,test):
# Exceptions that are not "NoSuchElementExceptions"
except e:
if (test):
header = f"\nTimeout: No valid login form loaded in {e_p.ECCS_SELENIUMPAGELOADTIMEOUT} seconds.\n[PAGE_SOURCE]\n{pgsrc}"
header = f"\nTimeout: No valid login form loaded in {e_p.ECCS_SELENIUM_PAGELOADTIMEOUT} seconds.\n[PAGE_SOURCE]\n{pgsrc}"
else:
header = f"<h1>Timeout - No valid login form found in {e_p.ECCS_SELENIUMPAGELOADTIMEOUT} seconds.</h1><h2>[PAGE_SOURCE]</h2>"
header = f"<h1>Timeout - No valid login form found in {e_p.ECCS_SELENIUM_PAGELOADTIMEOUT} seconds.</h1><h2>[PAGE_SOURCE]</h2>"
pgsrc = ""
stored = store_page_source(idp,sp,test,pgsrc,header)
if (stored):
......
......
......@@ -332,7 +332,7 @@ $(document).ready(function() {
],
"rowCallback": function( row, data, index ) {
if (data.status == "ERROR") {
//$('td', row).css('background-color', '#EA4335'); // NEW ECCS2
//$('td', row).css('background-color', '#EA4335'); // NEW ECCS
$('td', row).css('background-color', '#EA3D3F'); // OLD ECCS
//$('td', row).css('background-color', '#FF0000');
//$('td', row).css('background-color', '#F22422');
......@@ -342,7 +342,7 @@ $(document).ready(function() {
}
if (data.status == "OK") {
//$('td', row).css('background-color', '#34A853');
//$('td', row).css('background-color', '#00CE00'); // NEW ECCS2
//$('td', row).css('background-color', '#00CE00'); // NEW ECCS
$('td', row).css('background-color', '#72F81B'); // OLD ECCS
}
if (data.status == "UNKNOWN") {
......
......
<?php
$directory = "../output";
$files = scandir($directory);
$firstFile = $files[ 3 ]; // [0] = '.' ; [1] = '..' ; [2] = '.gitignore'
$eccsResults = array();
if ($handle = opendir($directory)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != ".." && $entry != ".gitignore" && $entry != "failed-cmd.sh") {
array_push($eccsResults, $entry);
}
}
closedir($handle);
}
sort($eccsResults);
$str2strip = array("eccs_", ".log");
$firstFile = $eccsResults[array_key_first($eccsResults)];
$firstDate = str_replace($str2strip, "", $firstFile);
$files = scandir($directory, SCANDIR_SORT_DESCENDING);
$lastFile = $files[ 0 ];
$lastFile = $eccsResults[array_key_last($eccsResults)];
$lastDate = str_replace($str2strip, "", $lastFile);
$data = array();
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment