#!/usr/bin/python2
# -*- coding: utf-8 -*-

# This file is part of Cockpit.
#
# Copyright (C) 2013 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import os
import sys
import tempfile
import time

base_dir = os.path.dirname(os.path.realpath(__file__))
test_dir = os.path.dirname(base_dir)
sys.path.append(test_dir)

from verify import parent
parent # pyflakes

from verify import kubelib
from testlib import *
from testvm import VirtMachine

class OCBrowser(Browser):
    def login_and_go(self, path=None, user=None, host=None, authorized=None):
        if user is None:
            user = self.default_user
        href = path
        if not href:
            href = "/"
        if host:
            href = "/@" + host + href
        self.open(href)

        self.expect_load()
        self.wait_visible("#inputUsername")
        self.wait_visible("#inputPassword")

        self.set_val("#inputUsername", user)
        self.set_val("#inputPassword", "fubar")
        self.click('.btn-primary')
        self.expect_load()
        self.expect_load_frame("cockpit1:localhost/" + ("Registry" in self.label and "registry" or "kubernetes"))
        self.wait_present('#content')
        self.wait_visible('#content')
        if path:
            self.enter_page(path.split("#")[0], host=host)


class OCMachine(VirtMachine):
    run_registry = False
    def __init__(self, *args, **kwargs):
        self.started = False
        VirtMachine.__init__(self, *args, **kwargs)

    def wait_oc(self):
        i = 0
        while True:
            try:
                self.execute("oc status 2>&1")
                break;
            except:
                if i > 120:
                    raise
                i = i + 1
                time.sleep(1)

    def start_cockpit(self, host=None):
        if self.started:
            return

        path = os.path.join(base_dir, "files", "cockpit-openshift-template.json")
        self.upload([path], "/tmp")
        self.wait_oc()

        # TODO: For now we only support cluster admins
        # This sometimes fails if openshift is still modifying the clusterpolicybinding
        cmd = "oc adm policy add-cluster-role-to-user cluster-admin admin"
        try:
            self.execute(cmd)
        except:
            self.execute(cmd)

        args = [
            "-v", "COCKPIT_KUBE_INSECURE=true",
            "-v", "COCKPIT_KUBE_URL=http://{}".format(self.forward["30000"]),
            "-v", "OPENSHIFT_OAUTH_PROVIDER_URL=https://{}".format(self.forward["8443"])
        ]

        if self.run_registry:
            args += ["-v", "REGISTRY_ONLY=true"]

        # docker sets iptables FORWARD policy to DROP, which blocks outside requests from qemu user network
        self.execute("iptables -P FORWARD ACCEPT")

        self.execute("oc process -f /tmp/cockpit-openshift-template.json {0} > out.json".format(' '.join(args)))
        self.execute("oc create -f out.json")
        self.wait_for_cockpit_running(seconds=180, address="10.111.112.101", port=30000)
        self.started = True

class RegistryMachine(OCMachine):
    run_registry = True

class OCCase(kubelib.KubernetesCase):
    machine_class = OCMachine
    def setUp(self):
        self.machine = self.new_machine(image='openshift', forward={ "30000": 30000, "8443": 8443 })
        self.machine.start()
        self.machine.wait_boot()

        path = os.path.join(test_dir, "verify", "files", "mock-app-openshift.json")
        self.machine.upload([path], "/tmp")

        # HACK: Headless Chromium breaks with client SSL cert requests from OAuth; work around by using "full" chrome
        # with xvfb (https://bugs.chromium.org/p/chromium/issues/detail?id=757181)
        self.browser = OCBrowser(self.machine.forward["30000"], self.label(), headless=False)
        self.addCleanup(lambda: self.browser.kill())
        # our OAuth provider uses a self-signed SSL certificate
        self.browser.ignore_ssl_certificate_errors(True)

        self.tmpdir = tempfile.mkdtemp()
        self.openshift = self.machine
        self.machine.wait_oc()

        # Expect the default container user limitations during testing
        self.openshift.execute("oc patch scc restricted -p '{ \"runAsUser\": { \"type\": \"MustRunAsRange\" } }'")

        def intercept():
            if not self.checkSuccess():
                self.snapshot("FAIL")
                self.copy_js_log("FAIL")
                self.copy_journal("FAIL")
                self.copy_cores("FAIL")
        self.addCleanup(intercept)

class TestOpenshift(OCCase, kubelib.OpenshiftCommonTests):
    def testLogin(self):
        b = self.browser
        self.machine.start_cockpit()

        b.open("/")
        b.expect_load()

        b.wait_visible("#inputUsername")
        b.wait_visible("#inputPassword")
        b.wait_in_text("title", "OpenShift Origin")

        b.set_val("#inputUsername", "kuser")
        b.set_val("#inputPassword", "fubar")
        b.click('.btn-primary')

        b.expect_load()
        b.expect_load_frame("cockpit1:localhost/kubernetes")
        b.wait_present("#content")
        b.wait_text('#content-user-name', 'kuser')
        b.wait_in_text("title", "Openshift Cockpit")
        b.wait_in_text("#index-brand", "Openshift")
        b.enter_page("/kubernetes")
        b.wait_present("#node-list")

        b.logout()
        b.wait_in_text("#login-fatal-message", "Logout Successful")
        b.wait_not_visible('#login-button')

        b.open("/#error_description=AError")
        b.wait_not_visible('#login-button')
        b.wait_in_text("#login-fatal-message", "AError")
        b.click("#login-again")
        b.expect_load()

        b.set_val("#inputUsername", "kuser")
        b.set_val("#inputPassword", "foobar")
        b.click('.btn-primary')

        b.expect_load()
        b.expect_load_frame("cockpit1:localhost/kubernetes")
        b.wait_present("#content")

class TestRegistry(OCCase, kubelib.RegistryTests):
    machine_class = RegistryMachine
    registry_root = "/registry"

    def setup_user(self, user, password):
        self.browser.default_user = user
        self.browser.password = password

    def testLogin(self):
        b = self.browser
        self.machine.start_cockpit()

        b.open("/")
        b.expect_load()

        b.wait_visible("#inputUsername")
        b.wait_visible("#inputPassword")
        b.wait_in_text("title", "OpenShift Origin")

        b.set_val("#inputUsername", "kuser")
        b.set_val("#inputPassword", "fubar")
        b.click('.btn-primary')

        b.expect_load()
        b.expect_load_frame("cockpit1:localhost/registry")
        b.wait_present("#content")
        b.wait_text('#content-user-name', 'kuser')
        b.wait_in_text("title", "Image Registry")
        b.wait_in_text("#index-brand", "Atomic Registry")
        b.enter_page("/registry")
        b.wait_present(".dashboard-commands")

        b.switch_to_top()
        b.wait_present(".area-ct-body")
        b.wait_present(".area-ct-body.single-nav")

        b.logout()
        b.wait_in_text("#login-fatal-message", "Logout Successful")
        b.wait_not_visible('#login-button')

        b.open("/#error_description=AError")
        b.wait_not_visible('#login-button')
        b.wait_in_text("#login-fatal-message", "AError")
        b.click("#login-again")
        b.expect_load()

        b.set_val("#inputUsername", "kuser")
        b.set_val("#inputPassword", "foobar")
        b.click('.btn-primary')

        b.expect_load()
        b.expect_load_frame("cockpit1:localhost/registry")
        b.wait_present("#content")
        b.enter_page("/registry")
        b.wait_present(".dashboard-commands")

        tokens = self.machine.execute('oc get oauthaccesstokens --output=template --template="{{ range .items }}|{{.userName}}|{{ end }}"')
        self.assertIn("|kuser|", tokens.strip())

        b.logout()
        tokens = self.machine.execute('oc get oauthaccesstokens --output=template --template="{{ range .items }}|{{.userName}}|{{ end }}"')
        self.assertNotIn("|kuser|", tokens.strip())

if __name__ == '__main__':
    test_main()
