from common_fixtures import *  # NOQA
from cattle import ApiError


def _create_stack(client):
    env = client.create_stack(name=random_str())
    env = client.wait_success(env)
    assert env.state == "active"
    return env


def test_create_duplicated_services(client, context):
    env = _create_stack(client)

    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}
    service_name = random_str()
    service1 = client.create_service(name=service_name,
                                     stackId=env.id,
                                     launchConfig=launch_config)
    client.wait_success(service1)

    with pytest.raises(ApiError) as e:
        client.create_service(name=service_name,
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'NotUnique'
    assert e.value.error.fieldName == 'name'

    with pytest.raises(ApiError) as e:
        client.create_service(name=service_name.upper(),
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'NotUnique'
    assert e.value.error.fieldName == 'name'

    with pytest.raises(ApiError) as e:
        client.create_externalService(name=service_name,
                                      stackId=env.id,
                                      launchConfig=launch_config,
                                      externalIpAddresses=["72.22.16.5"])
    assert e.value.error.status == 422
    assert e.value.error.code == 'NotUnique'
    assert e.value.error.fieldName == 'name'

    with pytest.raises(ApiError) as e:
        client.create_dnsService(name=service_name,
                                 stackId=env.id)
    assert e.value.error.status == 422
    assert e.value.error.code == 'NotUnique'
    assert e.value.error.fieldName == 'name'

    # remove the service and try to re-use its name
    client.wait_success(service1.remove())
    client.create_service(name=service_name,
                          stackId=env.id,
                          launchConfig=launch_config)


def test_external_service_w_hostname(client, context):
    env = _create_stack(client)
    # try to create external service with both hostname externalips
    with pytest.raises(ApiError) as e:
        ips = ["72.22.16.5", '192.168.0.10']
        client.create_externalService(name=random_str(),
                                      stackId=env.id,
                                      hostname="a.com",
                                      externalIpAddresses=ips)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidOption'


def test_circular_refs(client, context):
    env = _create_stack(client)

    # test direct circular ref
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid,
                     "dataVolumesFromLaunchConfigs": ['secondary']}

    secondary_lc = {"imageUuid": image_uuid, "name": "secondary",
                    "dataVolumesFromLaunchConfigs": ['primary']}

    with pytest.raises(ApiError) as e:
        client.create_service(name="primary",
                              stackId=env.id,
                              launchConfig=launch_config,
                              secondaryLaunchConfigs=[secondary_lc])
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidReference'

    # test indirect circular ref
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid,
                     "dataVolumesFromLaunchConfigs": ['secondary1']}

    s_lc1 = {"imageUuid": image_uuid, "name": "secondary1",
             "dataVolumesFromLaunchConfigs": ['secondary2']}

    s_lc2 = {"imageUuid": image_uuid, "name": "secondary2",
             "dataVolumesFromLaunchConfigs": ['primary']}

    with pytest.raises(ApiError) as e:
        client.create_service(name="primary",
                              stackId=env.id,
                              launchConfig=launch_config,
                              secondaryLaunchConfigs=[s_lc1, s_lc2])
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidReference'


def test_no_circular_ref(client, context):
    env = _create_stack(client)

    # test that there is no circular reference when secondary has both
    # net and volumes from and primary is using volumes_from
    image_uuid = context.image_uuid
    launch_config = {'imageUuid': image_uuid,
                     'dataVolumesFromLaunchConfigs': ['secondary1']}

    secondary1_lc = {'imageUuid': image_uuid, 'name': 'secondary1'}

    secondary2_lc = {'imageUuid': image_uuid, 'name': 'secondary2',
                     'dataVolumesFromLaunchConfigs': ['primary'],
                     'networkMode': 'container',
                     'networkLaunchConfig': 'primary'}

    svc = client.create_service(name="primary",
                                stackId=env.id,
                                launchConfig=launch_config,
                                secondaryLaunchConfigs=[secondary1_lc,
                                                        secondary2_lc])
    svc = client.wait_success(svc)
    assert svc.state == 'inactive'


def test_vip_requested_ip(client, context):
    env = _create_stack(client)
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}
    # vip out of the range - still accepted
    vip = "169.255.65.30"
    svc = client.create_service(name=random_str(),
                                stackId=env.id,
                                launchConfig=launch_config,
                                vip=vip)
    assert svc.vip == vip


def test_add_svc_to_removed_stack(client, context):
    env = _create_stack(client)

    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}

    client.create_service(name=random_str(),
                          stackId=env.id,
                          launchConfig=launch_config)

    client.create_service(name=random_str(),
                          stackId=env.id,
                          launchConfig=launch_config)

    env.remove()

    with pytest.raises(ApiError) as e:
        client.create_service(name=random_str(),
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidState'
    assert e.value.error.fieldName == 'stackId'

    with pytest.raises(ApiError) as e:
        client.create_service(name=random_str(),
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidState'
    assert e.value.error.fieldName == 'stackId'


def test_validate_launch_config_name(client, context):
    env = _create_stack(client)
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}
    svc_name = random_str()
    service = client.create_service(name=svc_name,
                                    stackId=env.id,
                                    launchConfig=launch_config)

    client.wait_success(service)

    launch_config = {"imageUuid": image_uuid}

    secondary_lc = {"imageUuid": image_uuid,
                    "name": svc_name}

    with pytest.raises(ApiError) as e:
        client.create_service(name=random_str(),
                              stackId=env.id,
                              launchConfig=launch_config,
                              secondaryLaunchConfigs=[secondary_lc])
    assert e.value.error.status == 422
    assert e.value.error.code == 'NotUnique'


def test_validate_service_token(client, context, super_client):
    env = _create_stack(client)
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}
    svc_name = random_str()
    service = client.create_service(name=svc_name,
                                    stackId=env.id,
                                    launchConfig=launch_config)

    client.wait_success(service)

    service = super_client.reload(service)

    assert service.state == "inactive"
    assert service.data.fields.token is not None

    token = service.data.fields.token

    svc_name = random_str()

    client.update(service, scale=2)

    client.wait_success(service)

    service = super_client.reload(service)

    assert service.data.fields.token is not None
    assert service.data.fields.token == token


def test_ip_retain(client, context, super_client):
    env = _create_stack(client)

    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}

    svc = client.create_service(name=random_str(),
                                stackId=env.id,
                                launchConfig=launch_config,
                                scale=1,
                                retainIp=True)
    svc = client.wait_success(svc)
    assert svc.state == "inactive"

    # validate that startFirst can't be used on a service with retainIp = true
    strategy = {"launchConfig": launch_config,
                "intervalMillis": 100,
                "startFirst": True}
    with pytest.raises(ApiError) as e:
        svc.upgrade_action(inServiceStrategy=strategy)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidOption'


def test_null_scale(client, context):
    env = _create_stack(client)

    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}

    svc = client.create_service(name=random_str(),
                                stackId=env.id,
                                launchConfig=launch_config,
                                scale=None)
    svc = client.wait_success(svc)
    assert svc.state == "inactive"
    assert svc.scale is not None


def test_validate_svc_name(client, context):
    env = _create_stack(client)
    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}
    # svc_name starting with hyphen
    svc_name = "-" + random_str()
    with pytest.raises(ApiError) as e:
        client.create_service(name=svc_name,
                              stackId=env.id,
                              launchConfig=launch_config)

    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidCharacters'

    # svc_name ending in hyphen
    svc_name = random_str() + "-"
    with pytest.raises(ApiError) as e:
        client.create_service(name=svc_name,
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidCharacters'

    # svc_name with --
    svc_name = random_str() + "--end"
    with pytest.raises(ApiError) as e:
        client.create_service(name=svc_name,
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidCharacters'

    # svc_name with more than 63 chars
    svc_name = random_str() + "myLinkTOOLONGtoolongtoolongtoolongmy" \
                              "LinkTOOLONGtoolongtoolongtoolong"
    with pytest.raises(ApiError) as e:
        client.create_service(name=svc_name,
                              stackId=env.id,
                              launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'MaxLengthExceeded'

    # svc_name with single char
    svc = client.create_service(name='a',
                                stackId=env.id,
                                launchConfig=launch_config)
    svc = client.wait_success(svc)
    assert svc.state == "inactive"


def test_setlinks_on_removed(client, context):
    env = _create_stack(client)

    image_uuid = context.image_uuid
    launch_config = {"imageUuid": image_uuid}

    svc = client.create_service(name=random_str(),
                                stackId=env.id,
                                launchConfig=launch_config)
    svc = client.wait_success(svc)

    target = client.create_service(name=random_str(),
                                   stackId=env.id,
                                   launchConfig=launch_config)
    target = client.wait_success(target)
    client.wait_success(target.remove())

    link = {"serviceId": target.id, "name": "link1"}
    with pytest.raises(ApiError) as e:
        svc. \
            setservicelinks(serviceLinks=[link])
    assert e.value.error.status == 422
    assert e.value.error.code == 'InvalidReference'


def test_invalid_ports(client, context):
    env = _create_stack(client)

    launch_config = {"imageUuid": context.image_uuid,
                     "ports": [":45677"]}

    with pytest.raises(ApiError) as e:
        client. \
            create_service(name=random_str(),
                           stackId=env.id,
                           launchConfig=launch_config)
    assert e.value.error.status == 422
    assert e.value.error.code == 'PortWrongFormat'


def test_service_kind_external(client, context):
    stack = _create_stack(client)
    launch_config = {"imageUuid": context.image_uuid}
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       launchConfig=launch_config,
                       externalIpAddresses=['10.1.1.1'])
    assert svc.kind == 'externalService'
    assert svc.externalIpAddresses == ['10.1.1.1']


def test_service_kind_storage(client, context):
    stack = _create_stack(client)
    launch_config = {"imageUuid": context.image_uuid}
    driver_name = 'test' + random_str()
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       launchConfig=launch_config,
                       storageDriver={
                           'name': driver_name,
                           'scope': 'local',
                       })
    assert svc.kind == 'storageDriverService'
    assert svc.storageDriver is not None


def test_service_kind_network(client, context):
    stack = _create_stack(client)
    launch_config = {"imageUuid": context.image_uuid}
    driver_name = 'test' + random_str()
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       launchConfig=launch_config,
                       networkDriver={
                           'name': driver_name,
                       })
    assert svc.kind == 'networkDriverService'
    assert svc.networkDriver is not None


def test_service_kind_alias(client, context):
    stack = _create_stack(client)
    launch_config = {"imageUuid": "rancher/dns-service"}
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       launchConfig=launch_config)
    assert svc.kind == 'dnsService'


def test_service_kind_selector(client, context):
    stack = _create_stack(client)
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       selectorContainer="foo=bar")
    assert svc.kind == 'selectorService'
    assert svc.selectorContainer is not None
    svc = client.wait_success(svc)
    assert svc.state == 'inactive'

    svc = client.wait_success(svc.activate())
    assert svc.state == 'active'
    assert len(svc.instances()) == 0


def test_service_kind_lb(client, context):
    stack = _create_stack(client)
    hostname = "foo"
    path = "bar"
    port = 32
    port_rule = {"hostname": hostname,
                 "path": path, "sourcePort": port,
                 "selector": "foo=bar"}
    port_rules = [port_rule]
    launch_config = {"imageUuid": context.image_uuid}

    lb_config = {"portRules": port_rules}
    svc = client. \
        create_service(name=random_str(),
                       stackId=stack.id,
                       launchConfig=launch_config,
                       lbConfig=lb_config)
    assert svc.kind == 'loadBalancerService'
    assert svc.lbConfig is not None
