You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@liminal.apache.org by jb...@apache.org on 2020/07/20 06:24:42 UTC

[incubator-liminal] 02/43: First code commit

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-liminal.git

commit 2887b1993ae5d17cc0e172a6f9f828ec4f362e04
Author: aviemzur <av...@gmail.com>
AuthorDate: Sun Mar 8 15:46:22 2020 +0200

    First code commit
---
 .gitignore                                         |   8 +
 LICENSE                                            | 250 +++++++++++++++++++
 rainbow/__init__.py                                |  17 ++
 rainbow/cli/__init__.py                            |  17 ++
 rainbow/core/__init__.py                           |  17 ++
 rainbow/docker/__init__.py                         |  17 ++
 rainbow/http/__init__.py                           |  17 ++
 rainbow/monitoring/__init__.py                     |  17 ++
 rainbow/runners/__init__.py                        |  17 ++
 rainbow/runners/airflow/__init__.py                |  17 ++
 rainbow/runners/airflow/compiler/__init__.py       |  17 ++
 .../runners/airflow/compiler/rainbow_compiler.py   |  26 ++
 rainbow/runners/airflow/dag/__init__.py            |  17 ++
 rainbow/runners/airflow/operators/__init__.py      |  17 ++
 .../runners/airflow/operators/cloudformation.py    | 270 +++++++++++++++++++++
 .../airflow/operators/job_status_operator.py       | 180 ++++++++++++++
 .../airflow/operators/kubernetes_pod_operator.py   | 140 +++++++++++
 rainbow/sql/__init__.py                            |  17 ++
 tests/__init__.py                                  |  17 ++
 tests/runners/__init__.py                          |  17 ++
 tests/runners/airflow/__init__.py                  |  17 ++
 tests/runners/airflow/compiler/__init__.py         |  17 ++
 tests/runners/airflow/compiler/rainbow.yml         | 115 +++++++++
 .../airflow/compiler/test_rainbow_compiler.py      |  33 +++
 tests/runners/airflow/operators/__init__.py        |  17 ++
 25 files changed, 1311 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e14e323
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.idea
+bin
+include
+lib
+venv
+.Python
+*.pyc
+pip-selfcheck.json
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8f1552e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,250 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed 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.
+
+============================================================================
+   APACHE AIRFLOW SUBCOMPONENTS:
+
+   The Apache Airflow project contains subcomponents with separate copyright
+   notices and license terms. Your use of the source code for the these
+   subcomponents is subject to the terms and conditions of the following
+   licenses.
+
+
+========================================================================
+Third party Apache 2.0 licenses
+========================================================================
+
+The following components are provided under the Apache 2.0 License.
+See project link for details. The text of each license is also included
+at licenses/LICENSE-[project].txt.
+
+    (ALv2 License) hue v4.3.0 (https://github.com/cloudera/hue/)
+    (ALv2 License) jqclock v2.3.0 (https://github.com/JohnRDOrazio/jQuery-Clock-Plugin)
+    (ALv2 License) bootstrap3-typeahead v4.0.2 (https://github.com/bassjobsen/Bootstrap-3-Typeahead)
+    (ALv2 License) airflow.contrib.auth.backends.github_enterprise_auth
+
+========================================================================
+MIT licenses
+========================================================================
+
+The following components are provided under the MIT License. See project link for details.
+The text of each license is also included at licenses/LICENSE-[project].txt.
+
+    (MIT License) jquery v3.4.1 (https://jquery.org/license/)
+    (MIT License) dagre-d3 v0.6.4 (https://github.com/cpettitt/dagre-d3)
+    (MIT License) bootstrap v3.2 (https://github.com/twbs/bootstrap/)
+    (MIT License) d3-tip v0.9.1 (https://github.com/Caged/d3-tip)
+    (MIT License) dataTables v1.10.20 (https://datatables.net)
+    (MIT License) Bootstrap Toggle v2.2.2 (http://www.bootstraptoggle.com)
+    (MIT License) normalize.css v3.0.2 (http://necolas.github.io/normalize.css/)
+    (MIT License) ElasticMock v1.3.2 (https://github.com/vrcmarcos/elasticmock)
+    (MIT License) MomentJS v2.24.0 (http://momentjs.com/)
+    (MIT License) python-slugify v2.0.1 (https://github.com/un33k/python-slugify)
+    (MIT License) python-nvd3 v0.15.0 (https://github.com/areski/python-nvd3)
+
+========================================================================
+BSD 3-Clause licenses
+========================================================================
+The following components are provided under the BSD 3-Clause license. See project links for details.
+The text of each license is also included at licenses/LICENSE-[project].txt.
+
+    (BSD 3 License) d3 v5.15.0 (https://d3js.org)
diff --git a/rainbow/__init__.py b/rainbow/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/cli/__init__.py b/rainbow/cli/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/cli/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/core/__init__.py b/rainbow/core/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/core/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/docker/__init__.py b/rainbow/docker/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/docker/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/http/__init__.py b/rainbow/http/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/http/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/monitoring/__init__.py b/rainbow/monitoring/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/monitoring/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/__init__.py b/rainbow/runners/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/runners/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/airflow/__init__.py b/rainbow/runners/airflow/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/runners/airflow/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/airflow/compiler/__init__.py b/rainbow/runners/airflow/compiler/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/runners/airflow/compiler/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/airflow/compiler/rainbow_compiler.py b/rainbow/runners/airflow/compiler/rainbow_compiler.py
new file mode 100644
index 0000000..818fdc5
--- /dev/null
+++ b/rainbow/runners/airflow/compiler/rainbow_compiler.py
@@ -0,0 +1,26 @@
+#
+# 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.
+"""
+Compiler for rainbows.
+"""
+import yaml
+
+
+def parse_yaml(path):
+    with open(path, 'r') as stream:
+        return yaml.safe_load(stream)
diff --git a/rainbow/runners/airflow/dag/__init__.py b/rainbow/runners/airflow/dag/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/runners/airflow/dag/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/airflow/operators/__init__.py b/rainbow/runners/airflow/operators/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/runners/airflow/operators/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/rainbow/runners/airflow/operators/cloudformation.py b/rainbow/runners/airflow/operators/cloudformation.py
new file mode 100644
index 0000000..0a70e5a
--- /dev/null
+++ b/rainbow/runners/airflow/operators/cloudformation.py
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+#
+# 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.
+"""
+This module contains CloudFormation create/delete stack operators.
+Can be removed when Airflow 2.0.0 is released.
+"""
+from typing import List
+
+from airflow.contrib.hooks.aws_hook import AwsHook
+from airflow.models import BaseOperator
+from airflow.sensors.base_sensor_operator import BaseSensorOperator
+from airflow.utils.decorators import apply_defaults
+from botocore.exceptions import ClientError
+
+
+# noinspection PyAbstractClass
+class CloudFormationHook(AwsHook):
+    """
+    Interact with AWS CloudFormation.
+    """
+
+    def __init__(self, region_name=None, *args, **kwargs):
+        self.region_name = region_name
+        self.conn = None
+        super().__init__(*args, **kwargs)
+
+    def get_conn(self):
+        self.conn = self.get_client_type('cloudformation', self.region_name)
+        return self.conn
+
+
+class BaseCloudFormationOperator(BaseOperator):
+    """
+    Base operator for CloudFormation operations.
+
+    :param params: parameters to be passed to CloudFormation.
+    :type dict
+    :param aws_conn_id: aws connection to uses
+    :type aws_conn_id: str
+    """
+    template_fields: List[str] = []
+    template_ext = ()
+    ui_color = '#1d472b'
+    ui_fgcolor = '#FFF'
+
+    @apply_defaults
+    def __init__(
+            self,
+            params,
+            aws_conn_id='aws_default',
+            *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.params = params
+        self.aws_conn_id = aws_conn_id
+
+    def execute(self, context):
+        self.log.info('Parameters: %s', self.params)
+
+        self.cloudformation_op(CloudFormationHook(aws_conn_id=self.aws_conn_id).get_conn())
+
+    def cloudformation_op(self, cloudformation):
+        """
+        This is the main method to run CloudFormation operation.
+        """
+        raise NotImplementedError()
+
+
+class CloudFormationCreateStackOperator(BaseCloudFormationOperator):
+    """
+    An operator that creates a CloudFormation stack.
+
+    :param params: parameters to be passed to CloudFormation. For possible arguments see:
+            https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.create_stack
+    :type dict
+    :param aws_conn_id: aws connection to uses
+    :type aws_conn_id: str
+    """
+    template_fields: List[str] = []
+    template_ext = ()
+    ui_color = '#6b9659'
+
+    @apply_defaults
+    def __init__(
+            self,
+            params,
+            aws_conn_id='aws_default',
+            *args, **kwargs):
+        super().__init__(params=params, aws_conn_id=aws_conn_id, *args, **kwargs)
+
+    def cloudformation_op(self, cloudformation):
+        cloudformation.create_stack(**self.params)
+
+
+class CloudFormationDeleteStackOperator(BaseCloudFormationOperator):
+    """
+    An operator that deletes a CloudFormation stack.
+
+    :param params: parameters to be passed to CloudFormation. For possible arguments see:
+            https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.delete_stack
+    :type dict
+    :param aws_conn_id: aws connection to uses
+    :type aws_conn_id: str
+    """
+    template_fields: List[str] = []
+    template_ext = ()
+    ui_color = '#1d472b'
+    ui_fgcolor = '#FFF'
+
+    @apply_defaults
+    def __init__(
+            self,
+            params,
+            aws_conn_id='aws_default',
+            *args, **kwargs):
+        super().__init__(params=params, aws_conn_id=aws_conn_id, *args, **kwargs)
+
+    def cloudformation_op(self, cloudformation):
+        cloudformation.delete_stack(**self.params)
+
+
+class BaseCloudFormationSensor(BaseSensorOperator):
+    """
+    Waits for a stack operation to complete on AWS CloudFormation.
+
+    :param stack_name: The name of the stack to wait for (templated)
+    :type stack_name: str
+    :param aws_conn_id: ID of the Airflow connection where credentials and extra configuration are
+        stored
+    :type aws_conn_id: str
+    :param poke_interval: Time in seconds that the job should wait between each try
+    :type poke_interval: int
+    """
+
+    @apply_defaults
+    def __init__(self,
+                 stack_name,
+                 complete_status,
+                 in_progress_status,
+                 aws_conn_id='aws_default',
+                 poke_interval=30,
+                 *args,
+                 **kwargs):
+        super().__init__(poke_interval=poke_interval, *args, **kwargs)
+        self.aws_conn_id = aws_conn_id
+        self.stack_name = stack_name
+        self.complete_status = complete_status
+        self.in_progress_status = in_progress_status
+        self.hook = None
+
+    def poke(self, context):
+        """
+        Checks for existence of the stack in AWS CloudFormation.
+        """
+        cloudformation = self.get_hook().get_conn()
+
+        self.log.info('Poking for stack %s', self.stack_name)
+
+        try:
+            stacks = cloudformation.describe_stacks(StackName=self.stack_name)['Stacks']
+            stack_status = stacks[0]['StackStatus']
+            if stack_status == self.complete_status:
+                return True
+            elif stack_status == self.in_progress_status:
+                return False
+            else:
+                raise ValueError(f'Stack {self.stack_name} in bad state: {stack_status}')
+        except ClientError as e:
+            if 'does not exist' in str(e):
+                if not self.allow_non_existing_stack_status():
+                    raise ValueError(f'Stack {self.stack_name} does not exist')
+                else:
+                    return True
+            else:
+                raise e
+
+    def get_hook(self):
+        """
+        Gets the AwsGlueCatalogHook
+        """
+        if not self.hook:
+            self.hook = CloudFormationHook(aws_conn_id=self.aws_conn_id)
+
+        return self.hook
+
+    def allow_non_existing_stack_status(self):
+        """
+        Boolean value whether or not sensor should allow non existing stack responses.
+        """
+        return False
+
+
+class CloudFormationCreateStackSensor(BaseCloudFormationSensor):
+    """
+    Waits for a stack to be created successfully on AWS CloudFormation.
+
+    :param stack_name: The name of the stack to wait for (templated)
+    :type stack_name: str
+    :param aws_conn_id: ID of the Airflow connection where credentials and extra configuration are
+        stored
+    :type aws_conn_id: str
+    :param poke_interval: Time in seconds that the job should wait between each try
+    :type poke_interval: int
+    """
+
+    template_fields = ['stack_name']
+    ui_color = '#C5CAE9'
+
+    @apply_defaults
+    def __init__(self,
+                 stack_name,
+                 aws_conn_id='aws_default',
+                 poke_interval=30,
+                 *args,
+                 **kwargs):
+        super().__init__(stack_name=stack_name,
+                         complete_status='CREATE_COMPLETE',
+                         in_progress_status='CREATE_IN_PROGRESS',
+                         aws_conn_id=aws_conn_id,
+                         poke_interval=poke_interval,
+                         *args,
+                         **kwargs)
+
+
+class CloudFormationDeleteStackSensor(BaseCloudFormationSensor):
+    """
+    Waits for a stack to be deleted successfully on AWS CloudFormation.
+
+    :param stack_name: The name of the stack to wait for (templated)
+    :type stack_name: str
+    :param aws_conn_id: ID of the Airflow connection where credentials and extra configuration are
+        stored
+    :type aws_conn_id: str
+    :param poke_interval: Time in seconds that the job should wait between each try
+    :type poke_interval: int
+    """
+
+    template_fields = ['stack_name']
+    ui_color = '#C5CAE9'
+
+    @apply_defaults
+    def __init__(self,
+                 stack_name,
+                 aws_conn_id='aws_default',
+                 poke_interval=30,
+                 *args,
+                 **kwargs):
+        super().__init__(stack_name=stack_name,
+                         complete_status='DELETE_COMPLETE',
+                         in_progress_status='DELETE_IN_PROGRESS',
+                         aws_conn_id=aws_conn_id,
+                         poke_interval=poke_interval, *args, **kwargs)
+
+    def allow_non_existing_stack_status(self):
+        return True
diff --git a/rainbow/runners/airflow/operators/job_status_operator.py b/rainbow/runners/airflow/operators/job_status_operator.py
new file mode 100644
index 0000000..dc318e5
--- /dev/null
+++ b/rainbow/runners/airflow/operators/job_status_operator.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+#
+# 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.
+from datetime import datetime
+
+import pytz
+from airflow.contrib.hooks.aws_hook import AwsHook
+from airflow.exceptions import AirflowException
+from airflow.models import BaseOperator
+from airflow.utils.decorators import apply_defaults
+
+
+class JobStatusOperator(BaseOperator):
+    """
+    Base operator for job status operators.
+    """
+    template_ext = ()
+
+    @apply_defaults
+    def __init__(
+            self,
+            backends,
+            *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.backends = backends
+        self.cloudwatch = CloudWatchHook()
+
+    def execute(self, context):
+        for backend in self.backends:
+            if backend in self.report_functions:
+                for metric in self.metrics(context):
+                    self.report_functions[backend](self, metric)
+            else:
+                raise AirflowException('No such metrics backend: {}'.format(backend))
+
+    def metrics(self, context):
+        raise NotImplementedError
+
+    def send_metric_to_cloudwatch(self, metric):
+        self.cloudwatch.put_metric_data(metric)
+
+    report_functions = {
+        'cloudwatch': send_metric_to_cloudwatch
+    }
+
+
+class JobStartOperator(JobStatusOperator):
+    ui_color = '#c5e5e8'
+
+    def __init__(
+            self,
+            namespace,
+            application_name,
+            backends,
+            *args, **kwargs):
+        super().__init__(backends=backends, *args, **kwargs)
+        self.namespace = namespace
+        self.application_name = application_name
+
+    def metrics(self, context):
+        return [Metric(self.namespace, 'JobStarted', 1,
+                       [Tag('ApplicationName', self.application_name)])]
+
+
+class JobEndOperator(JobStatusOperator):
+    ui_color = '#6d8fad'
+
+    def __init__(
+            self,
+            namespace,
+            application_name,
+            backends,
+            *args, **kwargs):
+        super().__init__(backends=backends, *args, **kwargs)
+        self.namespace = namespace
+        self.application_name = application_name
+
+    def metrics(self, context):
+        duration = round((pytz.utc.localize(datetime.utcnow()) - context[
+            'ti'].get_dagrun().start_date).total_seconds())
+
+        self.log.info('Elapsed time: %s' % duration)
+
+        task_instances = context['dag_run'].get_task_instances()
+        task_states = [task_instance.state for task_instance in task_instances[:-1]]
+
+        job_result = 0
+        if all(state == 'success' for state in task_states):
+            job_result = 1
+
+        return [
+            Metric(self.namespace, 'JobResult', job_result,
+                   [Tag('ApplicationName', self.application_name)]),
+            Metric(self.namespace, 'JobDuration', duration,
+                   [Tag('ApplicationName', self.application_name)])
+        ]
+
+
+# noinspection PyAbstractClass
+class CloudWatchHook(AwsHook):
+    """
+    Interact with AWS CloudWatch.
+    """
+
+    def __init__(self, region_name=None, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.region_name = region_name
+        self.conn = self.get_client_type('cloudwatch', self.region_name)
+
+    def get_conn(self):
+        return self.conn
+
+    def put_metric_data(self, metric):
+        value = metric.value
+
+        cloudwatch = self.get_conn()
+
+        dimensions = [{'Name': tag.name, 'Value': tag.value} for tag in metric.tags]
+
+        cloudwatch.put_metric_data(
+            Namespace=metric.namespace,
+            MetricData=[
+                {
+                    'MetricName': metric.name,
+                    'Dimensions': dimensions,
+                    'Timestamp': datetime.utcnow(),
+                    'Value': value,
+                    'Unit': 'None'
+                }
+            ]
+        )
+
+
+class Metric:
+    """
+    Metric.
+    :param namespace: namespace.
+    :type name: str
+    :param name: name.
+    :type name: str
+    :param value: value.
+    :type value: float
+    :param tags: list of tags.
+    :type tags: List[str]
+    """
+
+    def __init__(
+            self,
+            namespace,
+            name,
+            value,
+            tags):
+        self.namespace = namespace
+        self.name = name
+        self.value = value
+        self.tags = tags
+
+
+class Tag:
+    def __init__(
+            self,
+            name,
+            value):
+        self.name = name
+        self.value = value
diff --git a/rainbow/runners/airflow/operators/kubernetes_pod_operator.py b/rainbow/runners/airflow/operators/kubernetes_pod_operator.py
new file mode 100644
index 0000000..a7b0bdd
--- /dev/null
+++ b/rainbow/runners/airflow/operators/kubernetes_pod_operator.py
@@ -0,0 +1,140 @@
+from airflow.contrib.operators.kubernetes_pod_operator import KubernetesPodOperator
+import json
+import traceback
+from airflow.models import DAG, TaskInstance
+from airflow.utils import timezone
+from random import randint
+
+
+def split_list(seq, num):
+    avg = len(seq) / float(num)
+    out = []
+    last = 0.0
+
+    while last < len(seq):
+        out.append(seq[int(last):int(last + avg)])
+        last += avg
+
+    return out
+
+
+class ConfigureParallelExecutionOperator(KubernetesPodOperator):
+
+    def __init__(self,
+                 config_type=None,
+                 config_path=None,
+                 executors=1,
+                 *args,
+                 **kwargs):
+        namespace = kwargs['namespace']
+        image = kwargs['image']
+        name = kwargs['name']
+
+        del kwargs['namespace']
+        del kwargs['image']
+        del kwargs['name']
+
+        super().__init__(
+            namespace=namespace,
+            image=image,
+            name=name,
+            *args,
+            **kwargs)
+        self.config_type = config_type
+        self.config_path = config_path
+        self.executors = executors
+
+    def execute(self, context):
+        config_dict = {}
+
+        self.log.info(f'config type: {self.config_type}')
+
+        if self.config_type:
+            if self.config_type == 'file':
+                config_dict = {}  # future feature: return config from file
+            elif self.config_type == 'sql':
+                config_dict = {}  # future feature: return from sql config
+            elif self.config_type == 'task':
+                ti = context['task_instance']
+                self.log.info(self.config_path)
+                config_dict = ti.xcom_pull(task_ids=self.config_path)
+            elif self.config_type == 'static':
+                config_dict = json.loads(self.config_path)
+            else:
+                raise ValueError(f'Unknown config type: {self.config_type}')
+
+        run_id = context['dag_run'].run_id
+
+        return_conf = {'config_type': self.config_type,
+                       'splits': {'0': {'run_id': run_id, 'configs': []}}}
+
+        if config_dict:
+            self.log.info(f'configs dict: {config_dict}')
+
+            configs = config_dict['configs']
+
+            self.log.info(f'configs: {configs}')
+
+            config_splits = split_list(configs, self.executors)
+
+            for i in range(self.executors):
+                return_conf['splits'][str(i)] = {'run_id': run_id, 'configs': config_splits[i]}
+
+        return return_conf
+
+    def run_pod(self, context):
+        return super().execute(context)
+
+
+class ConfigurableKubernetesPodOperator(KubernetesPodOperator):
+
+    def __init__(self,
+                 config_task_id,
+                 task_split,
+                 *args,
+                 **kwargs):
+        namespace = kwargs['namespace']
+        image = kwargs['image']
+        name = kwargs['name']
+
+        del kwargs['namespace']
+        del kwargs['image']
+        del kwargs['name']
+
+        super().__init__(
+            namespace=namespace,
+            image=image,
+            name=name,
+            *args,
+            **kwargs)
+
+        self.config_task_id = config_task_id
+        self.task_split = task_split
+
+    def execute(self, context):
+        if self.config_task_id:
+            ti = context['task_instance']
+
+            config = ti.xcom_pull(task_ids=self.config_task_id)
+
+            if config:
+                split = {}
+
+                if 'configs' in config:
+                    split = configs
+                else:
+                    split = config['splits'][str(self.task_split)]
+
+                self.log.info(split)
+
+                if split and split['configs']:
+                    self.env_vars.update({'DATA_PIPELINE_CONFIG': json.dumps(split)})
+                    return super().execute(context)
+                else:
+                    self.log.info(
+                        f'Empty split config for split {self.task_split}. split config: {split}. config: {config}')
+            else:
+                raise ValueError('Config not found in task: ' + self.config_task_id)
+        else:
+            self.env_vars.update({'DATA_PIPELINE_CONFIG': '{}'})
+            return super().execute(context)
diff --git a/rainbow/sql/__init__.py b/rainbow/sql/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/rainbow/sql/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/tests/runners/__init__.py b/tests/runners/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/tests/runners/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/tests/runners/airflow/__init__.py b/tests/runners/airflow/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/tests/runners/airflow/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/tests/runners/airflow/compiler/__init__.py b/tests/runners/airflow/compiler/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/tests/runners/airflow/compiler/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.
diff --git a/tests/runners/airflow/compiler/rainbow.yml b/tests/runners/airflow/compiler/rainbow.yml
new file mode 100644
index 0000000..45333a8
--- /dev/null
+++ b/tests/runners/airflow/compiler/rainbow.yml
@@ -0,0 +1,115 @@
+
+---
+name: MyPipeline
+owner: Bosco Albert Baracus
+pipeline:
+  timeout-minutes: 45
+  schedule: 0 * 1 * *
+  metrics-namespace: TestNamespace
+  tasks:
+    - name: mytask1
+      type: sql
+      description: mytask1 is cool
+      query: "select * from mytable"
+      overrides:
+        - prod:
+          partition-columns: dt
+          output-table: test.test_impression_prod
+          output-path: s3://mybucket/myproject-test/impression
+          emr-cluster-name: spark-playground-prod
+        - stg:
+          query: "select * from mytable"
+          partition-columns: dt
+          output-table: test.test_impression_stg
+          output-path: s3://mybucket/haya-test/impression
+          emr-cluster-name: spark-playground-staging
+      tasks:
+        - name: my_static_config_task
+          type: python
+          description: my 1st ds task
+          artifact-id: mytask1artifactid
+          source: mytask1folder
+          env-vars:
+            env1: "a"
+            env2: "b"
+          config-type: static
+          config-path: "{\"configs\": [ { \"campaign_id\": 10 }, { \"campaign_id\": 20 } ]}"
+          cmd: python -u my_app.py
+        - task:
+          name: my_no_config_task
+          type: python
+          description: my 2nd ds task
+          artifact-id: mytask1artifactid
+          env-vars:
+            env1: "a"
+            env2: "b"
+          request-cpu: 100m
+          request-memory: 65M
+          cmd: python -u my_app.py foo bar
+        - task:
+          name: my_create_custom_config_task
+          type: python
+          description: my 2nd ds task
+          artifact-id: myconftask
+          source: myconftask
+          output-config-path: /my_conf.json
+          env-vars:
+            env1: "a"
+            env2: "b"
+          cmd: python -u my_app.py foo bar
+        - task:
+          name: my_custom_config_task
+          type: python
+          description: my 2nd ds task
+          artifact-id: mytask1artifactid
+          config-type: task
+          config-path: my_create_custom_config_task
+          env-vars:
+            env1: "a"
+            env2: "b"
+          cmd: python -u my_app.py foo bar
+        - task:
+          name: my_parallelized_static_config_task
+          type: python
+          description: my 3rd ds task
+          artifact-id: mytask1artifactid
+          executors: 5
+          env-vars:
+            env1: "x"
+            env2: "y"
+            myconf: $CONFIG_FILE
+          config-type: static
+          config-path: "{\"configs\": [ { \"campaign_id\": 10 }, { \"campaign_id\": 20 }, { \"campaign_id\": 30 }, { \"campaign_id\": 40 }, { \"campaign_id\": 50 }, { \"campaign_id\": 60 }, { \"campaign_id\": 70 }, { \"campaign_id\": 80 } ]}"
+          cmd: python -u my_app.py $CONFIG_FILE
+        - task:
+          name: my_parallelized_custom_config_task
+          type: python
+          description: my 4th ds task
+          artifact-id: mytask1artifactid
+          executors: 5
+          config-type: task
+          config-path: my_create_custom_config_task
+          cmd: python -u my_app.py
+        - task:
+          name: my_parallelized_no_config_task
+          type: python
+          description: my 4th ds task
+          artifact-id: mytask1artifactid
+          executors: 5
+          cmd: python -u my_app.py
+services:
+  - service:
+    name: myserver1
+    type: python-server
+    description: my python server
+    artifact-id: myserver1artifactid
+    source: myserver1logicfolder
+    endpoints:
+      - endpoint:
+        path: /myendpoint1
+        module: mymodule1
+        function: myfun1
+      - endpoint:
+        path: /myendpoint2
+        module: mymodule2
+        function: myfun2
diff --git a/tests/runners/airflow/compiler/test_rainbow_compiler.py b/tests/runners/airflow/compiler/test_rainbow_compiler.py
new file mode 100644
index 0000000..6e73d8f
--- /dev/null
+++ b/tests/runners/airflow/compiler/test_rainbow_compiler.py
@@ -0,0 +1,33 @@
+#
+# 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.
+
+import unittest
+
+from rainbow.runners.airflow.compiler import rainbow_compiler
+
+
+class TestRainbowCompiler(unittest.TestCase):
+
+    def test_parse(self):
+        expected = {'name': 'MyPipeline', 'owner': 'Bosco Albert Baracus', 'pipeline': {'timeout-minutes': 45, 'schedule': '0 * 1 * *', 'metrics-namespace': 'TestNamespace', 'tasks': [{'name': 'mytask1', 'type': 'sql', 'description': 'mytask1 is cool', 'query': 'select * from mytable', 'overrides': [{'prod': None, 'partition-columns': 'dt', 'output-table': 'test.test_impression_prod', 'output-path': 's3://mybucket/myproject-test/impression', 'emr-cluster-name': 'spark-playground-prod'},  [...]
+        actual = rainbow_compiler.parse_yaml('tests/runners/airflow/compiler/rainbow.yml')
+        self.assertEqual(expected, actual)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/runners/airflow/operators/__init__.py b/tests/runners/airflow/operators/__init__.py
new file mode 100644
index 0000000..217e5db
--- /dev/null
+++ b/tests/runners/airflow/operators/__init__.py
@@ -0,0 +1,17 @@
+#
+# 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.