# -*- coding: utf-8 -*-
#
# Copyright 2015 Outlier Bio, LLC
#
# 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.
#

"""
Tests for the Kubernetes Job wrapper.

Requires:

- pykube: ``pip install pykube-ng``
- A local minikube custer up and running: http://kubernetes.io/docs/getting-started-guides/minikube/

**WARNING**: For Python versions < 3.5 the kubeconfig file must point to a Kubernetes API
hostname, and NOT to an IP address.

Written and maintained by Marco Capuccini (@mcapuccini).
"""

import logging
import unittest

import mock
import pytest

import luigi
from luigi.contrib.kubernetes import KubernetesJobTask

logger = logging.getLogger("luigi-interface")

try:
    from pykube.config import KubeConfig
    from pykube.http import HTTPClient
    from pykube.objects import Job
except ImportError:
    raise unittest.SkipTest("pykube is not installed. This test requires pykube.")


class SuccessJob(KubernetesJobTask):
    name = "success"
    spec_schema = {"containers": [{"name": "hello", "image": "alpine:3.4", "command": ["echo", "Hello World!"]}]}


class FailJob(KubernetesJobTask):
    name = "fail"
    max_retrials = 3
    backoff_limit = 3
    spec_schema = {"containers": [{"name": "fail", "image": "alpine:3.4", "command": ["You", "Shall", "Not", "Pass"]}]}

    @property
    def labels(self):
        return {"dummy_label": "dummy_value"}


@pytest.mark.contrib
class TestK8STask(unittest.TestCase):
    def test_success_job(self):
        success = luigi.run(["SuccessJob", "--local-scheduler"])
        self.assertTrue(success)

    def test_fail_job(self):
        fail = FailJob()
        self.assertRaises(RuntimeError, fail.run)
        # Check for retrials
        kube_api = HTTPClient(KubeConfig.from_file("~/.kube/config"))  # assumes minikube
        jobs = Job.objects(kube_api).filter(selector="luigi_task_id=" + fail.job_uuid)
        self.assertEqual(len(jobs.response["items"]), 1)
        job = Job(kube_api, jobs.response["items"][0])
        self.assertTrue("failed" in job.obj["status"])
        self.assertTrue(job.obj["status"]["failed"] > fail.max_retrials)
        self.assertTrue(job.obj["spec"]["template"]["metadata"]["labels"] == fail.labels())

    @mock.patch.object(KubernetesJobTask, "_KubernetesJobTask__get_job_status")
    @mock.patch.object(KubernetesJobTask, "signal_complete")
    def test_output(self, mock_signal, mock_job_status):
        # mock that the job succeeded
        mock_job_status.return_value = "succeeded"
        # create a kubernetes job
        kubernetes_job = KubernetesJobTask()
        # set logger and uu_name due to logging in __track_job()
        kubernetes_job._KubernetesJobTask__logger = logger
        kubernetes_job.uu_name = "test"
        # track the job (bc included in run method)
        kubernetes_job._KubernetesJobTask__track_job()
        # Make sure successful job signals
        self.assertTrue(mock_signal.called)
