Skip to content

Commit

Permalink
[AIRFLOW-3761] Decommission User & Chart models & Update doc accordin…
Browse files Browse the repository at this point in the history
…gly (apache#4577)

In master branch, we have already decommissioned the Flask-Admin UI.

In model definitions, User and Chart are only applicable for the
"old" UI based on Flask-Admin.
Hence we should decommission these two models as well.

Related doc are updated in this commit as well.
  • Loading branch information
XD-DENG authored and andyhuynh3 committed Jan 29, 2019
1 parent c17dccf commit 082dcef
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 108 deletions.
3 changes: 0 additions & 3 deletions airflow/config_templates/default_airflow.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,6 @@ access_logfile = -
error_logfile = -

# Expose the configuration file in the web server
# This is only applicable for the flask-admin based web UI (non FAB-based).
# In the FAB-based web UI with RBAC feature,
# access to configuration is controlled by role permissions.
expose_config = False

# Set to true to turn on authentication:
Expand Down
87 changes: 87 additions & 0 deletions airflow/migrations/versions/cf5dc11e79ad_drop_user_and_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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.

"""drop_user_and_chart
Revision ID: cf5dc11e79ad
Revises: 41f5f12752f8
Create Date: 2019-01-24 15:30:35.834740
"""
from alembic import op
from sqlalchemy.dialects import mysql
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'cf5dc11e79ad'
down_revision = '41f5f12752f8'
branch_labels = None
depends_on = None


def upgrade():
op.drop_table("chart")
op.drop_table("users")


def downgrade():
conn = op.get_bind()

op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=250), nullable=True),
sa.Column('email', sa.String(length=500), nullable=True),
sa.Column('password', sa.String(255)),
sa.Column('superuser', sa.Boolean(), default=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username')
)

op.create_table(
'chart',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('label', sa.String(length=200), nullable=True),
sa.Column('conn_id', sa.String(length=250), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('chart_type', sa.String(length=100), nullable=True),
sa.Column('sql_layout', sa.String(length=50), nullable=True),
sa.Column('sql', sa.Text(), nullable=True),
sa.Column('y_log_scale', sa.Boolean(), nullable=True),
sa.Column('show_datatable', sa.Boolean(), nullable=True),
sa.Column('show_sql', sa.Boolean(), nullable=True),
sa.Column('height', sa.Integer(), nullable=True),
sa.Column('default_params', sa.String(length=5000), nullable=True),
sa.Column('x_is_date', sa.Boolean(), nullable=True),
sa.Column('iteration_no', sa.Integer(), nullable=True),
sa.Column('last_modified', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)

if conn.dialect.name == 'mysql':
conn.execute("SET time_zone = '+00:00'")
op.alter_column(table_name='chart', column_name='last_modified', type_=mysql.TIMESTAMP(fsp=6))
else:
if conn.dialect.name in ('sqlite', 'mssql'):
return

if conn.dialect.name == 'postgresql':
conn.execute("set timezone=UTC")

op.alter_column(table_name='chart', column_name='last_modified', type_=sa.TIMESTAMP(timezone=True))
45 changes: 1 addition & 44 deletions airflow/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
func, or_, true as sqltrue, types
)
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import reconstructor, relationship, synonym
from sqlalchemy.orm import reconstructor, synonym

from croniter import (
croniter, CroniterBadCronError, CroniterBadDateError, CroniterNotAlphaError
Expand Down Expand Up @@ -655,24 +655,6 @@ def dagbag_report(self):
)


class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True)
username = Column(String(ID_LEN), unique=True)
email = Column(String(500))
superuser = Column(Boolean(), default=False)

def __repr__(self):
return self.username

def get_id(self):
return str(self.id)

def is_superuser(self):
return self.superuser


class TaskInstance(Base, LoggingMixin):
"""
Task instances store the state of a task instance. This table is the
Expand Down Expand Up @@ -4404,31 +4386,6 @@ def _test_cycle_helper(self, visit_map, task_id):
visit_map[task_id] = DagBag.CYCLE_DONE


class Chart(Base):
__tablename__ = "chart"

id = Column(Integer, primary_key=True)
label = Column(String(200))
conn_id = Column(String(ID_LEN), nullable=False)
user_id = Column(Integer(), ForeignKey('users.id'), nullable=True)
chart_type = Column(String(100), default="line")
sql_layout = Column(String(50), default="series")
sql = Column(Text, default="SELECT series, x, y FROM table")
y_log_scale = Column(Boolean)
show_datatable = Column(Boolean)
show_sql = Column(Boolean, default=True)
height = Column(Integer, default=600)
default_params = Column(String(5000), default="{}")
owner = relationship(
"User", cascade=False, cascade_backrefs=False, backref='charts')
x_is_date = Column(Boolean, default=True)
iteration_no = Column(Integer, default=0)
last_modified = Column(UtcDateTime, default=timezone.utcnow)

def __repr__(self):
return self.label


class Variable(Base, LoggingMixin):
__tablename__ = "variable"

Expand Down
20 changes: 0 additions & 20 deletions airflow/utils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ def merge_conn(conn, session=None):


def initdb():
session = settings.Session()

from airflow import models
from airflow.models.connection import Connection
upgradedb()
Expand Down Expand Up @@ -296,24 +294,6 @@ def initdb():
# Deactivate the unknown ones
models.DAG.deactivate_unknown_dags(dagbag.dags.keys())

Chart = models.Chart
chart_label = "Airflow task instance by type"
chart = session.query(Chart).filter(Chart.label == chart_label).first()
if not chart:
chart = Chart(
label=chart_label,
conn_id='airflow_db',
chart_type='bar',
x_is_date=False,
sql=(
"SELECT state, COUNT(1) as number "
"FROM task_instance "
"WHERE dag_id LIKE 'example%' "
"GROUP BY state"),
)
session.add(chart)
session.commit()

from flask_appbuilder.models.sqla import Base
Base.metadata.create_all(settings.engine)

Expand Down
36 changes: 1 addition & 35 deletions docs/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,44 +61,10 @@ Web Authentication
Password
''''''''

.. note::

This is for flask-admin based web UI only. If you are using FAB-based web UI with RBAC feature,
please use command line interface ``airflow users --create`` to create accounts, or do that in the FAB-based UI itself.

One of the simplest mechanisms for authentication is requiring users to specify a password before logging in.
Password authentication requires the used of the ``password`` subpackage in your requirements file. Password hashing
uses ``bcrypt`` before storing passwords.

.. code-block:: bash

[webserver]
authenticate = True
auth_backend = airflow.contrib.auth.backends.password_auth
When password auth is enabled, an initial user credential will need to be created before anyone can login. An initial
user was not created in the migrations for this authentication backend to prevent default Airflow installations from
attack. Creating a new user has to be done via a Python REPL on the same machine Airflow is installed.

.. code-block:: bash
Please use command line interface ``airflow users --create`` to create accounts, or do that in the UI.

# navigate to the airflow installation directory
$ cd ~/airflow
$ python
Python 2.7.9 (default, Feb 10 2015, 03:28:08)
Type "help", "copyright", "credits" or "license" for more information.
>>> import airflow
>>> from airflow import models, settings
>>> from airflow.contrib.auth.backends.password_auth import PasswordUser
>>> user = PasswordUser(models.User())
>>> user.username = 'new_user_name'
>>> user.email = 'new_user_email@example.com'
>>> user.password = 'set_the_password'
>>> session = settings.Session()
>>> session.add(user)
>>> session.commit()
>>> session.close()
>>> exit()

LDAP
''''
Expand Down
6 changes: 0 additions & 6 deletions tests/utils/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@ def test_database_schema_and_sqlalchemy_model_are_in_sync(self):

# known diffs to ignore
ignores = [
# users.password is not part of User model,
# otherwise it would show up in (old) UI
lambda t: (t[0] == 'remove_column' and
t[2] == 'users' and
t[3].name == 'password'),

# ignore tables created by celery
lambda t: (t[0] == 'remove_table' and
t[1].name == 'celery_taskmeta'),
Expand Down

0 comments on commit 082dcef

Please sign in to comment.