sweileh opened a new issue #9498: Scheduled email worker is reporting an error
URL: https://github.com/apache/incubator-superset/issues/9498
 
 
   I have an issue with scheduled email of superset.
   
   After installing superset in containers using docker-compose, everything is 
working perfectly except for the scheduled email feature.
   After following exactly what is in the superset installation and 
configuration page (regarding the report emails), the worker that does the 
email task is reporting the below error.
   
   ### Expected results
   
   What is expected is for the worker to successfully send the email.
   
   ### Actual results
   ```
   Traceback (most recent call last):
     File "/usr/local/lib/python3.6/site-packages/celery/app/trace.py", line 
385, in trace_task
       R = retval = fun(*args, **kwargs)
     File "/app/superset/app.py", line 114, in __call__
       return task_base.__call__(self, *args, **kwargs)
     File "/usr/local/lib/python3.6/site-packages/celery/app/trace.py", line 
650, in __protected_call__
       return self.run(*args, **kwargs)
     File "/app/superset/tasks/schedules.py", line 372, in schedule_email_report
       deliver_dashboard(schedule)
     File "/app/superset/tasks/schedules.py", line 210, in deliver_dashboard
       driver = create_webdriver()
     File "/app/superset/tasks/schedules.py", line 155, in create_webdriver
       options.add_argument("--headless")
   UnboundLocalError: local variable 'options' referenced before assignment
   ```
   #### Screenshots
   
   ![Screenshot from 2020-04-09 
23-32-57](https://user-images.githubusercontent.com/34817857/78933583-988b6780-7aba-11ea-90a2-6604a6026d72.png)
   
   ![Screenshot from 2020-04-09 
23-26-24](https://user-images.githubusercontent.com/34817857/78933575-96290d80-7aba-11ea-9701-7c2f66e6df93.png)
   
   
![image](https://user-images.githubusercontent.com/34817857/78931744-7217fd00-7ab7-11ea-9a73-21e9e33f8b95.png)
   
   
   #### How to reproduce the bug
   
   Used the following Dockerfile (same as original but added the webdrivers to 
it and exposed 8088 port and 5555 port for flower).
   ```
   ######################################################################
   # PY stage that simply does a pip install on our requirements
   ######################################################################
   ARG PY_VER=3.6.9
   FROM python:${PY_VER} AS superset-py
   
   RUN mkdir /app \
           && apt-get update -y \
           && apt-get install -y --no-install-recommends \
               build-essential \
               default-libmysqlclient-dev \
               libpq-dev \
               libgtk-3-0 xvfb firefox-esr \
           && rm -rf /var/lib/apt/lists/*
   RUN wget 
https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz
   RUN tar -x geckodriver -zf geckodriver-v0.24.0-linux64.tar.gz -O > 
/usr/bin/geckodriver
   
   RUN chmod +x /usr/bin/geckodriver
   RUN rm geckodriver-v0.24.0-linux64.tar.gz
   RUN wget -q 
"https://chromedriver.storage.googleapis.com/79.0.3945.36/chromedriver_linux64.zip";
 -O /tmp/chromedriver.zip \
       && unzip /tmp/chromedriver.zip -d /usr/bin/ \
       && rm /tmp/chromedriver.zip
   
   # First, we just wanna install requirements, which will allow us to utilize 
the cache
   # in order to only build if and only if requirements change
   COPY ./requirements.txt /app/
   RUN cd /app \
           && pip install --no-cache -r requirements.txt
   
   ######################################################################
   # Node stage to deal with static asset construction
   ######################################################################
   FROM node:10-jessie AS superset-node
   
   ARG NPM_BUILD_CMD="build"
   ENV BUILD_CMD=${NPM_BUILD_CMD}
   
   # NPM ci first, as to NOT invalidate previous steps except for when 
package.json changes
   RUN mkdir -p /app/superset-frontend
   RUN mkdir -p /app/superset/assets
   COPY ./docker/frontend-mem-nag.sh /
   COPY ./superset-frontend/package* /app/superset-frontend/
   RUN /frontend-mem-nag.sh \
           && cd /app/superset-frontend \
           && npm ci
   
   # Next, copy in the rest and let webpack do its thing
   COPY ./superset-frontend /app/superset-frontend
   # This is BY FAR the most expensive step (thanks Terser!)
   RUN cd /app/superset-frontend \
           && npm run ${BUILD_CMD} \
           && rm -rf node_modules
   
   ######################################################################
   # Final lean image...
   ######################################################################
   ARG PY_VER=3.6.9
   FROM python:${PY_VER} AS lean
   
   ENV LANG=C.UTF-8 \
       LC_ALL=C.UTF-8 \
       FLASK_ENV=production \
       FLASK_APP="superset.app:create_app()" \
       PYTHONPATH="/app/pythonpath" \
       SUPERSET_HOME="/app/superset_home" \
       SUPERSET_PORT=8088
   
   RUN useradd --user-group --no-create-home --no-log-init --shell /bin/bash 
superset \
           && mkdir -p ${SUPERSET_HOME} ${PYTHONPATH} \
           && apt-get update -y \
           && apt-get install -y --no-install-recommends \
               build-essential \
               default-libmysqlclient-dev \
               libpq-dev \
               libgtk-3-0 xvfb firefox-esr \
           && rm -rf /var/lib/apt/lists/*
   RUN wget 
https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz
   RUN tar -x geckodriver -zf geckodriver-v0.24.0-linux64.tar.gz -O > 
/usr/bin/geckodriver
   
   RUN chmod +x /usr/bin/geckodriver
   RUN rm geckodriver-v0.24.0-linux64.tar.gz
   RUN wget -q 
"https://chromedriver.storage.googleapis.com/79.0.3945.36/chromedriver_linux64.zip";
 -O /tmp/chromedriver.zip \
       && unzip /tmp/chromedriver.zip -d /usr/bin/ \
   
   
   COPY --from=superset-py /usr/local/lib/python3.6/site-packages/ 
/usr/local/lib/python3.6/site-packages/
   # Copying site-packages doesn't move the CLIs, so let's copy them one by one
   COPY --from=superset-py /usr/local/bin/gunicorn /usr/local/bin/celery 
/usr/local/bin/flask /usr/bin/
   COPY --from=superset-node /app/superset/static/assets 
/app/superset/static/assets
   COPY --from=superset-node /app/superset-frontend /app/superset-frontend
   
   ## Lastly, let's install superset itself
   COPY superset /app/superset
   COPY setup.py MANIFEST.in README.md /app/
   RUN cd /app \
           && chown -R superset:superset * \
           && pip install -e .
   RUN Xvfb :10 -ac &
   RUN export DISPLAY=:10
   
   COPY ./docker/docker-entrypoint.sh /usr/bin/
   
   WORKDIR /app
   
   USER superset
   
   
   ```
   Used the following configuration file (same as original, but added the 
schedule email configurations):
   ```
   
   import logging
   import os
   
   from werkzeug.contrib.cache import FileSystemCache
   from celery.schedules import crontab
   
   logger = logging.getLogger()
   
   def get_env_variable(var_name, default=None):
       """Get the environment variable or raise exception."""
       try:
           return os.environ[var_name]
       except KeyError:
           if default is not None:
               return default
           else:
               error_msg = "The environment variable {} was missing, 
abort...".format(
                   var_name
               )
               raise EnvironmentError(error_msg)
   
   
   DATABASE_DIALECT = get_env_variable("DATABASE_DIALECT")
   DATABASE_USER = get_env_variable("DATABASE_USER")
   DATABASE_PASSWORD = get_env_variable("DATABASE_PASSWORD")
   DATABASE_HOST = get_env_variable("DATABASE_HOST")
   DATABASE_PORT = get_env_variable("DATABASE_PORT")
   DATABASE_DB = get_env_variable("DATABASE_DB")
   
   # The SQLAlchemy connection string.
   SQLALCHEMY_DATABASE_URI = "%s://%s:%s@%s:%s/%s" % (
       DATABASE_DIALECT,
       DATABASE_USER,
       DATABASE_PASSWORD,
       DATABASE_HOST,
       DATABASE_PORT,
       DATABASE_DB,
   )
   
   REDIS_HOST = get_env_variable("REDIS_HOST")
   REDIS_PORT = get_env_variable("REDIS_PORT")
   
   RESULTS_BACKEND = FileSystemCache('/app/superset_home/sqllab')
   
   class CeleryConfig(object):
       BROKER_URL = "redis://%s:%s/0" % (REDIS_HOST, REDIS_PORT)
       CELERY_IMPORTS = ("superset.sql_lab",)
       CELERY_RESULT_BACKEND = "redis://%s:%s/1" % (REDIS_HOST, REDIS_PORT)
       CELERY_TASK_PROTOCOL = 1
       CELERY_ANNOTATIONS = {
           'sql_lab.get_sql_results': {
               'rate_limit': '100/s',
           },
           'email_reports.send': {
               'rate_limit': '1/s',
               'time_limit': 120,
               'soft_time_limit': 150,
               'ignore_result': True,
           },
       }
       CELERYBEAT_SCHEDULE = {
           'email_reports.schedule_hourly': {
               'task': 'email_reports.schedule_hourly',
               'schedule': crontab(minute=1, hour='*'),
           },
       }
   
   
   CELERY_CONFIG = CeleryConfig
   
   # email configurations
   ENABLE_SCHEDULED_EMAIL_REPORTS = 
get_env_variable("ENABLE_SCHEDULED_EMAIL_REPORTS")
   EMAIL_NOTIFICATIONS = get_env_variable("EMAIL_NOTIFICATIONS")
   
   # smtp server configuration
   SMTP_HOST = get_env_variable("SMTP_HOST")
   SMTP_STARTTLS = get_env_variable("SMTP_STARTTLS")
   SMTP_SSL = get_env_variable("SMTP_SSL")
   SMTP_USER = get_env_variable("SMTP_USER")
   SMTP_PORT = get_env_variable("SMTP_PORT")
   SMTP_PASSWORD = get_env_variable("SMTP_PASSWORD")
   SMTP_MAIL_FROM = get_env_variable("SMTP_MAIL_FROM")
   
   # Email reports - minimum time resolution (in minutes) for the crontab
   EMAIL_REPORTS_CRON_RESOLUTION = 
get_env_variable("EMAIL_REPORTS_CRON_RESOLUTION")
   
   # Email report configuration
   # From address in emails
   EMAIL_REPORT_FROM_ADDRESS = get_env_variable("EMAIL_REPORT_FROM_ADDRESS")
   
   # Send bcc of all reports to this address. Set to None to disable.
   # This is useful for maintaining an audit trail of all email deliveries.
   EMAIL_REPORT_BCC_ADDRESS = get_env_variable("EMAIL_REPORT_BCC_ADDRESS")
   
   # User credentials to use for generating reports
   # This user should have permissions to browse all the dashboards and
   # slices.
   # TODO: In the future, login as the owner of the item to generate reports
   EMAIL_REPORTS_USER = get_env_variable("EMAIL_REPORTS_USER")
   EMAIL_REPORTS_SUBJECT_PREFIX = 
get_env_variable("EMAIL_REPORTS_SUBJECT_PREFIX")
   EMAIL_REPORTS_WEBDRIVER = get_env_variable("EMAIL_REPORTS_WEBDRIVER")
   
   # The base URL to query for accessing the user interface
   WEBDRIVER_BASEURL = get_env_variable("WEBDRIVER_BASEURL")
   
   #
   # Optionally import superset_config_docker.py (which will have been included 
on
   # the PYTHONPATH) in order to allow for local settings to be overridden
   #
   try:
       from superset_config_docker import *  # noqa
       import superset_config_docker
   
       logger.info(
           f"Loaded your Docker configuration at " 
f"[{superset_config_docker.__file__}]"
       )
   except ImportError:
       logger.info("Using default Docker config...")
   ```
   The .env file
   ```
   #
   # Licensed to the Apache Software Foundation (ASF) under one or more
   # contributor license agreements.  See the NOTICE file distributed with
   # this work for additional information regarding copyright ownership.
   # The ASF licenses this file to You 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.
   #
   COMPOSE_PROJECT_NAME=superset
   
   # database configurations (do not modify)
   DATABASE_DB=<SOME_DB>
   DATABASE_HOST=<SOME_HOST>
   DATABASE_PASSWORD=<SOME_PASSWORD>
   DATABASE_USER=<SOME_USER>
   
   # database engine specific environment variables
   # change the below if you prefers another database engine
   DATABASE_PORT=5432
   DATABASE_DIALECT=postgresql+psycopg2
   POSTGRES_DB=<SOME_DB>
   POSTGRES_USER=<SOME_USER>
   POSTGRES_PASSWORD=<SOME_PASSWORD>
   
   # Add the mapped in /app/pythonpath_docker which allows devs to override 
stuff
   PYTHONPATH=/app/pythonpath:/app/pythonpath_docker
   REDIS_HOST=redis
   REDIS_PORT=6379
   
   FLASK_ENV=production
   SUPERSET_ENV=production
   SUPERSET_LOAD_EXAMPLES=yes
   
   # Define the secert key
   SECRET_KEY=<SOME_KEY>
   
   # Email configurations
   ENABLE_SCHEDULED_EMAIL_REPORTS=True
   EMAIL_NOTIFICATIONS=True
   
   # smtp server configuration
   SMTP_HOST="smtp.gmail.com"
   SMTP_STARTTLS=False
   SMTP_SSL=True
   SMTP_USER="notifications"
   SMTP_PORT=587
   SMTP_PASSWORD="<SOME_PASSWORD>"
   SMTP_MAIL_FROM="<SOME_EMAIL>"
   
   # Email reports - minimum time resolution (in minutes) for the crontab
   EMAIL_REPORTS_CRON_RESOLUTION=15
   
   # Email report configuration
   # From address in emails
   EMAIL_REPORT_FROM_ADDRESS="<SOME_EMAIL>"
   
   # Send bcc of all reports to this address. Set to None to disable.
   # This is useful for maintaining an audit trail of all email deliveries.
   EMAIL_REPORT_BCC_ADDRESS="<SOME_EMAIL>"
   
   # User credentials to use for generating reports
   # This user should have permissions to browse all the dashboards and
   # slices.
   # TODO: In the future, login as the owner of the item to generate reports
   EMAIL_REPORTS_USER="<SOME_ADMIN_USER>"
   EMAIL_REPORTS_SUBJECT_PREFIX="[Superset Report] "
   EMAIL_REPORTS_WEBDRIVER="firefox"
   
   # Window size - this will impact the rendering of the data
   WEBDRIVER_WINDOW={"dashboard": (1600, 2000), "slice": (3000, 1200)}
   
   # The base URL to query for accessing the user interface
   WEBDRIVER_BASEURL="http://0.0.0.0:8088/";
   ```
   I tried both web drivers (firefox and chrome) and got the same error with 
both
   
   Finally, below is my docker-compose file (similar to original file but with 
extra worker, celery beat for email and flower added)
   ```
   x-superset-build: &superset-build
     args:
       NPM_BUILD_CMD: build-dev
     context: ./
     dockerfile: Dockerfile
     target: dev
   x-superset-depends-on: &superset-depends-on
     - redis
   x-superset-volumes: &superset-volumes
     # /app/pythonpath_docker will be appended to the PYTHONPATH in the final 
container
     - ./docker/docker-init.sh:/app/docker-init.sh
     - ./docker/pythonpath_dev:/app/pythonpath
     - ./superset:/app/superset
     - ./superset-frontend:/app/superset-frontend
     - superset_home:/app/superset_home
   
   version: "3.7"
   services:
     redis:
       image: redis:3.2
       restart: unless-stopped
       ports:
         - "127.0.0.1:6379:6379"
       volumes:
         - redis:/data
   
     superset:
       build: *superset-build
       env_file: docker/.env
       restart: unless-stopped
       ports:
         - 8088:8088
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-init:
       build: *superset-build
       command: ["/app/docker-init.sh"]
       env_file: docker/.env
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-node:
       image: node:10-jessie
       command: ["bash", "-c", "cd /app/superset-frontend && npm install 
--global webpack webpack-cli && npm install && npm run dev"]
       env_file: docker/.env
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-worker-1:
       build: *superset-build
       command: ["celery", "worker", "--pool=prefork", 
"--app=superset.tasks.celery_app:app", "-Ofair" , "-c 4"]
       env_file: docker/.env
       restart: unless-stopped
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-worker-2:
       build: *superset-build
       command: ["celery", "worker", "--pool=prefork", 
"--app=superset.tasks.celery_app:app", "-Ofair" , "-c 4"]
       env_file: docker/.env
       restart: unless-stopped
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-beat:
       build: *superset-build
       command: ["celery", "beat", "--app=superset.tasks.celery_app:app"]
       user: root
       env_file: docker/.env
       restart: unless-stopped
       depends_on: *superset-depends-on
       volumes: *superset-volumes
   
     superset-flower:
       build: *superset-build
       command: ["celery", "flower", "--app=superset.tasks.celery_app:app", 
"-Ofair"]
       env_file: docker/.env
       restart: unless-stopped
       depends_on: *superset-depends-on
       ports:
         - 5555:5555
       volumes: *superset-volumes
   
   
   volumes:
     superset_home:
       external: false
     redis:
       external: false
   ```
   
   ### Environment
   
   all the versions are what is exactly in the `master`.
   
   ### Checklist
   
   Make sure these boxes are checked before submitting your issue - thank you!
   
   - [x] I have checked the superset logs for python stacktraces and included 
it here as text if there are any.
   - [x] I have reproduced the issue with at least the latest released version 
of superset.
   - [x] I have checked the issue tracker for the same issue and I haven't 
found one similar.
   
   ### Additional context
   
   ps. I think it might be in the email report configurations, but I have been 
trying to solve it for almost 2 days. I though it might be the webdrivers but 
the code does not reach that point. The unbounded local error occur when the 
code tries to set the webdriver instance to headless.
   
   I also changed the docker file to install chrome as in miteshchavda reply 
[here](https://github.com/apache/incubator-superset/issues/9477#issuecomment-610679276)
 and I also changed the driver to chrome, but I still got the same issue.
   
   Thanks in advance
   

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to