Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AIRFLOW-1755] Url prefix support for flower and webservice #2723

Closed
wants to merge 1 commit into from

Conversation

gsemet
Copy link

@gsemet gsemet commented Oct 24, 2017

Dear Airflow maintainers,

Please accept this PR. I understand that it will not be reviewed until I have checked off all the steps below!

JIRA

Description

  • Here are some details about my PR, including screenshots of any UI changes:

I would like to submit this change that allows Airflow to support accessing easily the web UI from an endpoint such as /airflow and Flower with /flower.

For Flower, it is already possible to use the flowerconfig.py to convey the url_prefix setting, but I prefered aligning it

I propose this change that add 2 new settings on airflow.cfg and the CLI arguments:

# Root URL to use for the web server
web_server_url_prefix = /airflow
...
# The root URL for Flower
flower_url_prefix = /flower

Tests

  • My PR adds the following unit tests OR does not need testing for this extremely good reason:

I am open to suggestion on how to add unit tests on this PR. Tests will be done on a preproduction cluster running Kubernetes.

Commits

  • My commits all reference JIRA issues in their subject lines, and I have squashed multiple commits if they address the same issue. In addition, my commits follow the guidelines from "How to write a good git commit message":
    1. Subject is separated from body by a blank line
    2. Subject is limited to 50 characters
    3. Subject does not end with a period
    4. Subject uses the imperative mood ("add", not "adding")
    5. Body wraps at 72 characters
    6. Body explains "what" and "why", not "how"

@gsemet gsemet changed the title Url prefix support for flower and webservice [AIRFLOW-1755] Url prefix support for flower and webservice Oct 24, 2017
@codecov-io
Copy link

codecov-io commented Oct 24, 2017

Codecov Report

Merging #2723 into master will increase coverage by <.01%.
The diff coverage is 54.16%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2723      +/-   ##
==========================================
+ Coverage   73.39%   73.39%   +<.01%     
==========================================
  Files         160      160              
  Lines       12199    12208       +9     
==========================================
+ Hits         8953     8960       +7     
- Misses       3246     3248       +2
Impacted Files Coverage Δ
airflow/models.py 87% <ø> (+0.04%) ⬆️
airflow/bin/cli.py 54.68% <0%> (-0.26%) ⬇️
airflow/configuration.py 85.41% <100%> (+0.43%) ⬆️
airflow/settings.py 81.9% <100%> (ø) ⬆️
airflow/www/app.py 96.51% <100%> (-0.05%) ⬇️
airflow/www/views.py 71.21% <14.28%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e46cde4...32a7e58. Read the comment docs.

@@ -31,14 +31,14 @@ <h3 style="float: left">
{% block head %}
{{ super() }}
<link rel="stylesheet" type="text/css" href="{{ url_for("static", filename="dataTables.bootstrap.css") }}">
<link href="/admin/static/vendor/select2/select2.css" rel="stylesheet">
<link href="{{ url_for('admin.index')}}static/vendor/select2/select2.css" rel="stylesheet">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this, (all many similar url_for calls in the templates) not be {{ url_for("static", filename="vendor/select2/select2.css") }}?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably, but can I open a ticket on JIRA for someone to take it later? I may not have time to do it on this pull request

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @ashb.. in the current form the code doesn't look good..

Copy link
Author

@gsemet gsemet Nov 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is actually harder than I though, I'll change the ones that are easy but some are tricky (ex: task_instances, what is the right url_for ? {{ url_for('admin.task_instances') }} does not work)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I try {{ url_for('admin.task_instances') }} Flask throws a helpful error:

werkzeug.routing.BuildError: Could not build url for endpoint 'task_instances'. Did you mean 'airflow.task_instances' instead?

And trying that we get the right URL: "/admin/airflow/object/task_instances"

@gsemet gsemet mentioned this pull request Oct 26, 2017
4 tasks
@@ -170,6 +170,9 @@ web_server_host = 0.0.0.0
# The port on which to run the web server
web_server_port = 8080

# Root URL to use for the web server
web_server_url_prefix: /
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep the defaults empty.. also it should be = insted of :?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually works with both : and =, but I put = to keep coherency

@gsemet gsemet force-pushed the url_prefix branch 3 times, most recently from 8f2c165 to ea3d0f4 Compare November 2, 2017 11:08
@@ -29,7 +29,7 @@
<a
class="btn"
style="border: none; background-color:{{ State.color(state)}}; color: {{ State.color_fg(state) }};"
href="/admin/taskinstance/?flt0_dag_id_equals={{ dag.dag_id }}&flt2_state_equals={{ state }}">
href="{{ url_for('admin.index')}}taskinstance/?flt0_dag_id_equals={{ dag.dag_id }}&flt2_state_equals={{ state }}">
Copy link
Author

@gsemet gsemet Nov 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much difficult than I though:

  • url_for('admin.taskinstance')}} does not work (template render exception)
  • url_for('airflow.task_instances')}} works but the URL pointed is wrong (wants to go to /<urlprefix>/admin/airflow/object/taskinstance) and ends up in a 404

@ashb
Copy link
Member

ashb commented Nov 2, 2017

Using a variation of this script (with a bit of tweaking and cajoling) I build this list of URLS that might help you

Full list of methods
Name Methods URL
admin.index HEAD,OPTIONS,GET http://localhost/admin/
admin.static HEAD,OPTIONS,GET http://localhost/admin/admin/[filename]
airflow.blocked HEAD,OPTIONS,GET http://localhost/admin/airflow/blocked
airflow.chart HEAD,OPTIONS,GET http://localhost/admin/airflow/chart
airflow.chart_data HEAD,OPTIONS,GET http://localhost/admin/airflow/chart_data
airflow.clear HEAD,OPTIONS,GET http://localhost/admin/airflow/clear
airflow.code HEAD,OPTIONS,GET http://localhost/admin/airflow/code
airflow.dag_details HEAD,OPTIONS,GET http://localhost/admin/airflow/dag_details
airflow.dag_stats HEAD,OPTIONS,GET http://localhost/admin/airflow/dag_stats
airflow.dagrun_clear HEAD,OPTIONS,GET http://localhost/admin/airflow/dagrun_clear
airflow.dagrun_success HEAD,OPTIONS,GET http://localhost/admin/airflow/dagrun_success
airflow.duration HEAD,OPTIONS,GET http://localhost/admin/airflow/duration
airflow.gantt HEAD,OPTIONS,GET http://localhost/admin/airflow/gantt
airflow.graph HEAD,OPTIONS,GET http://localhost/admin/airflow/graph
airflow.index HEAD,OPTIONS,GET http://localhost/admin/airflow/
airflow.landing_times HEAD,OPTIONS,GET http://localhost/admin/airflow/landing_times
airflow.log HEAD,OPTIONS,GET http://localhost/admin/airflow/log
airflow.login HEAD,POST,OPTIONS,GET http://localhost/admin/airflow/login
airflow.logout HEAD,OPTIONS,GET http://localhost/admin/airflow/logout
airflow.noaccess HEAD,OPTIONS,GET http://localhost/admin/airflow/noaccess
airflow.paused POST,OPTIONS http://localhost/admin/airflow/paused
airflow.pickle_info HEAD,OPTIONS,GET http://localhost/admin/airflow/pickle_info
airflow.refresh HEAD,OPTIONS,GET http://localhost/admin/airflow/refresh
airflow.refresh_all HEAD,OPTIONS,GET http://localhost/admin/airflow/refresh_all
airflow.rendered HEAD,OPTIONS,GET http://localhost/admin/airflow/rendered
airflow.run HEAD,OPTIONS,GET http://localhost/admin/airflow/run
airflow.success HEAD,OPTIONS,GET http://localhost/admin/airflow/success
airflow.task HEAD,OPTIONS,GET http://localhost/admin/airflow/task
airflow.task_instances HEAD,OPTIONS,GET http://localhost/admin/airflow/object/task_instances
airflow.task_stats HEAD,OPTIONS,GET http://localhost/admin/airflow/task_stats
airflow.tree HEAD,OPTIONS,GET http://localhost/admin/airflow/tree
airflow.tries HEAD,OPTIONS,GET http://localhost/admin/airflow/tries
airflow.trigger HEAD,OPTIONS,GET http://localhost/admin/airflow/trigger
airflow.variables HEAD,POST,OPTIONS,GET http://localhost/admin/airflow/variables/[form]
airflow.varimport HEAD,POST,OPTIONS,GET http://localhost/admin/airflow/varimport
airflow.xcom HEAD,OPTIONS,GET http://localhost/admin/airflow/xcom
api_experimental.create_pool POST,OPTIONS http://localhost/api/experimental/pools
api_experimental.delete_pool OPTIONS,DELETE http://localhost/api/experimental/pools/[name]
api_experimental.get_pool HEAD,OPTIONS,GET http://localhost/api/experimental/pools/[name]
api_experimental.get_pools HEAD,OPTIONS,GET http://localhost/api/experimental/pools
api_experimental.latest_dag_runs HEAD,OPTIONS,GET http://localhost/api/experimental/latest_runs
api_experimental.task_info HEAD,OPTIONS,GET http://localhost/api/experimental/dags/[dag_id]/tasks/[task_id]
api_experimental.task_instance_info HEAD,OPTIONS,GET http://localhost/api/experimental/dags/[dag_id]/dag_runs/[execution_date]/tasks/[task_id]
api_experimental.test HEAD,OPTIONS,GET http://localhost/api/experimental/test
api_experimental.trigger_dag POST,OPTIONS http://localhost/api/experimental/dags/[dag_id]/dag_runs
basejob.action_view POST,OPTIONS http://localhost/admin/basejob/action/
basejob.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/basejob/ajax/lookup/
basejob.ajax_update POST,OPTIONS http://localhost/admin/basejob/ajax/update/
basejob.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/basejob/new/
basejob.delete_view POST,OPTIONS http://localhost/admin/basejob/delete/
basejob.details_view HEAD,OPTIONS,GET http://localhost/admin/basejob/details/
basejob.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/basejob/edit/
basejob.export HEAD,OPTIONS,GET http://localhost/admin/basejob/export/[export_type]/
basejob.index_view HEAD,OPTIONS,GET http://localhost/admin/basejob/
chart.action_view POST,OPTIONS http://localhost/admin/chart/action/
chart.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/chart/ajax/lookup/
chart.ajax_update POST,OPTIONS http://localhost/admin/chart/ajax/update/
chart.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/chart/new/
chart.delete_view POST,OPTIONS http://localhost/admin/chart/delete/
chart.details_view HEAD,OPTIONS,GET http://localhost/admin/chart/details/
chart.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/chart/edit/
chart.export HEAD,OPTIONS,GET http://localhost/admin/chart/export/[export_type]/
chart.index_view HEAD,OPTIONS,GET http://localhost/admin/chart/
configurationview.conf HEAD,OPTIONS,GET http://localhost/admin/configurationview/
connection.action_view POST,OPTIONS http://localhost/admin/connection/action/
connection.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/connection/ajax/lookup/
connection.ajax_update POST,OPTIONS http://localhost/admin/connection/ajax/update/
connection.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/connection/new/
connection.delete_view POST,OPTIONS http://localhost/admin/connection/delete/
connection.details_view HEAD,OPTIONS,GET http://localhost/admin/connection/details/
connection.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/connection/edit/
connection.export HEAD,OPTIONS,GET http://localhost/admin/connection/export/[export_type]/
connection.index_view HEAD,OPTIONS,GET http://localhost/admin/connection/
dagmodel.action_view POST,OPTIONS http://localhost/admin/dagmodel/action/
dagmodel.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/dagmodel/ajax/lookup/
dagmodel.ajax_update POST,OPTIONS http://localhost/admin/dagmodel/ajax/update/
dagmodel.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/dagmodel/new/
dagmodel.delete_view POST,OPTIONS http://localhost/admin/dagmodel/delete/
dagmodel.details_view HEAD,OPTIONS,GET http://localhost/admin/dagmodel/details/
dagmodel.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/dagmodel/edit/
dagmodel.export HEAD,OPTIONS,GET http://localhost/admin/dagmodel/export/[export_type]/
dagmodel.index_view HEAD,OPTIONS,GET http://localhost/admin/dagmodel/
dagrun.action_view POST,OPTIONS http://localhost/admin/dagrun/action/
dagrun.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/dagrun/ajax/lookup/
dagrun.ajax_update POST,OPTIONS http://localhost/admin/dagrun/ajax/update/
dagrun.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/dagrun/new/
dagrun.delete_view POST,OPTIONS http://localhost/admin/dagrun/delete/
dagrun.details_view HEAD,OPTIONS,GET http://localhost/admin/dagrun/details/
dagrun.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/dagrun/edit/
dagrun.export HEAD,OPTIONS,GET http://localhost/admin/dagrun/export/[export_type]/
dagrun.index_view HEAD,OPTIONS,GET http://localhost/admin/dagrun/
knownevent.action_view POST,OPTIONS http://localhost/admin/knownevent/action/
knownevent.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/knownevent/ajax/lookup/
knownevent.ajax_update POST,OPTIONS http://localhost/admin/knownevent/ajax/update/
knownevent.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/knownevent/new/
knownevent.delete_view POST,OPTIONS http://localhost/admin/knownevent/delete/
knownevent.details_view HEAD,OPTIONS,GET http://localhost/admin/knownevent/details/
knownevent.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/knownevent/edit/
knownevent.export HEAD,OPTIONS,GET http://localhost/admin/knownevent/export/[export_type]/
knownevent.index_view HEAD,OPTIONS,GET http://localhost/admin/knownevent/
log.action_view POST,OPTIONS http://localhost/admin/log/action/
log.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/log/ajax/lookup/
log.ajax_update POST,OPTIONS http://localhost/admin/log/ajax/update/
log.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/log/new/
log.delete_view POST,OPTIONS http://localhost/admin/log/delete/
log.details_view HEAD,OPTIONS,GET http://localhost/admin/log/details/
log.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/log/edit/
log.export HEAD,OPTIONS,GET http://localhost/admin/log/export/[export_type]/
log.index_view HEAD,OPTIONS,GET http://localhost/admin/log/
pool.action_view POST,OPTIONS http://localhost/admin/pool/action/
pool.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/pool/ajax/lookup/
pool.ajax_update POST,OPTIONS http://localhost/admin/pool/ajax/update/
pool.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/pool/new/
pool.delete_view POST,OPTIONS http://localhost/admin/pool/delete/
pool.details_view HEAD,OPTIONS,GET http://localhost/admin/pool/details/
pool.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/pool/edit/
pool.export HEAD,OPTIONS,GET http://localhost/admin/pool/export/[export_type]/
pool.index_view HEAD,OPTIONS,GET http://localhost/admin/pool/
queryview.query HEAD,POST,OPTIONS,GET http://localhost/admin/queryview/
routes.health HEAD,OPTIONS,GET http://localhost/health
routes.index HEAD,OPTIONS,GET http://localhost/
slamiss.action_view POST,OPTIONS http://localhost/admin/slamiss/action/
slamiss.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/slamiss/ajax/lookup/
slamiss.ajax_update POST,OPTIONS http://localhost/admin/slamiss/ajax/update/
slamiss.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/slamiss/new/
slamiss.delete_view POST,OPTIONS http://localhost/admin/slamiss/delete/
slamiss.details_view HEAD,OPTIONS,GET http://localhost/admin/slamiss/details/
slamiss.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/slamiss/edit/
slamiss.export HEAD,OPTIONS,GET http://localhost/admin/slamiss/export/[export_type]/
slamiss.index_view HEAD,OPTIONS,GET http://localhost/admin/slamiss/
static HEAD,OPTIONS,GET http://localhost/static/[filename]
taskinstance.action_view POST,OPTIONS http://localhost/admin/taskinstance/action/
taskinstance.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/taskinstance/ajax/lookup/
taskinstance.ajax_update POST,OPTIONS http://localhost/admin/taskinstance/ajax/update/
taskinstance.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/taskinstance/new/
taskinstance.delete_view POST,OPTIONS http://localhost/admin/taskinstance/delete/
taskinstance.details_view HEAD,OPTIONS,GET http://localhost/admin/taskinstance/details/
taskinstance.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/taskinstance/edit/
taskinstance.export HEAD,OPTIONS,GET http://localhost/admin/taskinstance/export/[export_type]/
taskinstance.index_view HEAD,OPTIONS,GET http://localhost/admin/taskinstance/
user.action_view POST,OPTIONS http://localhost/admin/user/action/
user.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/user/ajax/lookup/
user.ajax_update POST,OPTIONS http://localhost/admin/user/ajax/update/
user.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/user/new/
user.delete_view POST,OPTIONS http://localhost/admin/user/delete/
user.details_view HEAD,OPTIONS,GET http://localhost/admin/user/details/
user.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/user/edit/
user.export HEAD,OPTIONS,GET http://localhost/admin/user/export/[export_type]/
user.index_view HEAD,OPTIONS,GET http://localhost/admin/user/
variable.action_view POST,OPTIONS http://localhost/admin/variable/action/
variable.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/variable/ajax/lookup/
variable.ajax_update POST,OPTIONS http://localhost/admin/variable/ajax/update/
variable.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/variable/new/
variable.delete_view POST,OPTIONS http://localhost/admin/variable/delete/
variable.details_view HEAD,OPTIONS,GET http://localhost/admin/variable/details/
variable.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/variable/edit/
variable.export HEAD,OPTIONS,GET http://localhost/admin/variable/export/[export_type]/
variable.index_view HEAD,OPTIONS,GET http://localhost/admin/variable/
versionview.version HEAD,OPTIONS,GET http://localhost/admin/versionview/
xcom.action_view POST,OPTIONS http://localhost/admin/xcom/action/
xcom.ajax_lookup HEAD,OPTIONS,GET http://localhost/admin/xcom/ajax/lookup/
xcom.ajax_update POST,OPTIONS http://localhost/admin/xcom/ajax/update/
xcom.create_view HEAD,POST,OPTIONS,GET http://localhost/admin/xcom/new/
xcom.delete_view POST,OPTIONS http://localhost/admin/xcom/delete/
xcom.details_view HEAD,OPTIONS,GET http://localhost/admin/xcom/details/
xcom.edit_view HEAD,POST,OPTIONS,GET http://localhost/admin/xcom/edit/
xcom.export HEAD,OPTIONS,GET http://localhost/admin/xcom/export/[export_type]/
xcom.index_view HEAD,OPTIONS,GET http://localhost/admin/xcom/

@gsemet
Copy link
Author

gsemet commented Nov 2, 2017

ho, thanks that will help a lot ! I was looking for taskinstance.index_view actually :)

@gsemet gsemet force-pushed the url_prefix branch 2 times, most recently from 44f16b7 to 517232c Compare November 2, 2017 12:53
@gsemet
Copy link
Author

gsemet commented Nov 2, 2017

PR updated after your recommendations. I have tested it on my home dev environment, which is not the labs where i needed these url_prefix changes. So, manual tests looks ok. I will stress it next week again when I go back to work

@gsemet gsemet force-pushed the url_prefix branch 2 times, most recently from 28a83ac to 90e63f2 Compare November 6, 2017 12:25
@gsemet gsemet changed the title [AIRFLOW-1755] Url prefix support for flower and webservice WIP [AIRFLOW-1755] Url prefix support for flower and webservice Nov 8, 2017
@gsemet
Copy link
Author

gsemet commented Nov 8, 2017

-1 for the moment, I have encountered a bug when testing on our Kubernetes instance. Static files are not properly prefixed, while on my dev machine it works fine

app = Flask(__name__)
app.secret_key = configuration.get('webserver', 'SECRET_KEY')
app.config['LOGIN_DISABLED'] = not configuration.getboolean('webserver', 'AUTHENTICATE')
app = Flask(__name__, static_url_path=conf.get_url_prefix() + '/static')
Copy link
Author

@gsemet gsemet Nov 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fixes static files url

@gsemet gsemet changed the title WIP [AIRFLOW-1755] Url prefix support for flower and webservice [AIRFLOW-1755] Url prefix support for flower and webservice Nov 8, 2017
@gsemet
Copy link
Author

gsemet commented Nov 8, 2017

I have found the root cause of the static file url issue. The fact to use url_for for static files didn't worked because of bad initialization of the Flask(...).

@gsemet
Copy link
Author

gsemet commented Nov 9, 2017

@ashb @msumit What do you think about my Pull Request?

@gsemet
Copy link
Author

gsemet commented Nov 20, 2017

Hello. Here is the result of some addiotional tests on Kube 1.8:

  • the use of url_for has a strange impact compared to the way it url_prefix handled in Webservice, that completely takes care of url rewriting; Access log shows something like;

    GET /<prefix>/admin/ HTTP/1.1" 200 68476 "http://airflow.myserver/<prefix>/admin/
    

    Which is quite good actually.

    On Flower, I need to request the load balancer to strip the pathprefix (With traefik: traefik.frontend.rule.type: PathPrefixStrip annotation), so it would access as:

    GET /index.html HTTP/1.1" 200 68476 "http://airflow.myserver/<prefix>/index.hmtl
    

Action: I would like to align both behavior

  • experimental api is not prefixed, for exemple /api/experimental/latest_runs

I will work on it asap.

@gsemet
Copy link
Author

gsemet commented Nov 23, 2017

Hello. Fixed the experimental api url. i have also updated the documentation about reverse-proxying airflow

@jgao54
Copy link

jgao54 commented Dec 11, 2017

As for writing unit tests, you can test out the webserver configuration by (1) configure web_server_url_prefix (2) call the web/api endpoints with the custom url prefix (3) check 200 is returned.

@gsemet
Copy link
Author

gsemet commented Dec 11, 2017

I do not have any problem with any button. I however do all my test on Python 3, and this seems to be related to future package.

@jgao54
Copy link

jgao54 commented Dec 12, 2017

@stibbons it works in PY3 perfectly fine, the error only shows up in PY2. The future library converts str to newbytes, which made get_url_prefix() return a newbytes type, and werkzeug isn't equipped to handle this type.

A kind of patchy way to work around this is instead of:

def get_url_prefix():
    url_prefix = get('webserver', 'WEB_SERVER_URL_PREFIX')
    if not url_prefix or url_prefix == '/':
        url_prefix = ""
    return url_prefix

return the native type:

def get_url_prefix():
    url_prefix = get('webserver', 'WEB_SERVER_URL_PREFIX')
    if not url_prefix or url_prefix == '/':
        url_prefix = ""
    return url_prefix.__native__()

There may be a cleaner fix that i'm not aware of though.

@ashb
Copy link
Member

ashb commented Dec 12, 2017

Something from the six package is the usual way of dealing with py2/py3 issues.

@stibbons could you add a test case that exercises whatever code path was triggering the problem?

@gsemet
Copy link
Author

gsemet commented Dec 12, 2017

I am on it, I do not see any problem here with Python 2.7, I'll add a unit test to cover the fact get_url_prefix returns a str in python 2.7.

url_prefix.__native__() does not work, url_prefix is a unicode string in my tests. I'll update this PR asap

@gsemet
Copy link
Author

gsemet commented Dec 12, 2017

I have found the issue and fixed it. It works in when the string is empty and when it is not, in python 2.7.

So now should work in python 2.7, 3.5 and 3.6, at least.

I have some difficulties to add unit tests, I'll try to do something this afternoon.

@gsemet gsemet force-pushed the url_prefix branch 3 times, most recently from ce7e712 to eb8938b Compare December 14, 2017 20:09
@artwr
Copy link
Contributor

artwr commented Dec 19, 2017

I think the dask test issue has been fixed, so rebasing should help.

Allows Airflow to support accessing easily the
web UI from an endpoint such as `/airflow`
and Flower with `/flower`.

Adds 2 options in airflow.cfg:
- web_server_url_prefix for the webservice
- flower_url_prefix for Flower

Allow mounting on any kind of HTTP endpoint,
not only FQDN.

Example in`airflow.cfg`:

  # Root URL to use for the web server
  web_server_url_prefix = /airflow
  ...
  # The root URL for Flower
  flower_url_prefix = /flower

Updated documentation as well.

Signed-off-by: Gaetan Semet <gaetan@xeberon.net>
@gsemet
Copy link
Author

gsemet commented Jan 6, 2018

I've rebased the pull request, still works fine.

@artwr
Copy link
Contributor

artwr commented Jan 12, 2018

This change looks good to me. There is a weird add in the docs about Microsoft Azure which needs to be removed.

Can you check that this change still works if someone messes up with trailing/slashes? If so I would recommend using posixpath.join rather than string concatenation.

Once those two issues are addressed, I am +1 on this.

Copy link
Contributor

@artwr artwr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to test that the string concatenation does not lead to issues with trailing/leading slashes.

@@ -1006,6 +1007,7 @@ def mark_success_url(self):
iso = self.execution_date.isoformat()
BASE_URL = configuration.get('webserver', 'BASE_URL')
return BASE_URL + (
configuration.get_url_prefix() +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be something using posixpath.join in case of trailing slashes ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it requires to aways write a backslash before, so this get url returns either nothing (no prefix) or a path that does not ends with a backslash.
Double slash on a file path is not really a problem, it is more on some web server that might be an issue. But I do not know if I can put a urljoin/ file join everywhere, especially in JavaScript file

@@ -84,7 +84,7 @@ def timing(cls, stat, dt):
_/_/ |_/_/ /_/ /_/ /_/ \____/____/|__/
"""

BASE_LOG_URL = '/admin/airflow/log'
BASE_LOG_URL = conf.get_url_prefix() + '/admin/airflow/log'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Consider using posixpath.join

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually use os.path.join, is it better to use posixpath.join ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we do not guarantee compatibility with windows per se, os.path.join is os dependent. posixpath.join would be consistent.

@bolkedebruin
Copy link
Contributor

bolkedebruin commented Jan 13, 2018

As with the other PR:

Why is app.config['APPLICATION_ROOT'] = "/abc/def" not used? This removed the need for all reconfig of base urls.

Secondly, I am not a fan of adding another config option for this, as I don't think it is required. Why not use the base_url and use a split?

Furthermore please add some tests that cover this.

@bolkedebruin
Copy link
Contributor

I'll have a alternative PR soon, that will use MiddleWare as per documentation of wsgi.

@gsemet
Copy link
Author

gsemet commented Jan 18, 2018

Hello. Will this WSGI configuration works on kubernetes and other system that does not use Apache/Nginx? In my instance, airflow is behind a Traefik reverse proxy, both running on our internal kubernetes cluster.

@gsemet gsemet closed this Jan 18, 2018
bolkedebruin added a commit to bolkedebruin/airflow that referenced this pull request Jan 18, 2018
This enables Airflow and Celery Flower to live
below root. Draws on the work of Geatan Semet
(@stibbons).

This closes apache#2723 and closes apache#2818
asfgit pushed a commit that referenced this pull request Jan 19, 2018
This enables Airflow and Celery Flower to live
below root. Draws on the work of Geatan Semet
(@stibbons).

This closes #2723 and closes #2818

Closes #2952 from bolkedebruin/AIRFLOW-1755
Acehaidrey pushed a commit to Acehaidrey/incubator-airflow that referenced this pull request Jan 19, 2018
This enables Airflow and Celery Flower to live
below root. Draws on the work of Geatan Semet
(@stibbons).

This closes apache#2723 and closes apache#2818

Closes apache#2952 from bolkedebruin/AIRFLOW-1755
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants