"""
This example covers the following:
    - Create headless server
    - Create statefulset
    - Update statefulset
    - List contoller revisions which belong to specified statefulset
    - Roll out statefulset
Note:
    If your kubernetes version is lower than 1.22(exclude 1.22), the kubernetes-client version must be lower than 1.22(alse exclude 1.22).
    Because new feature 'AvailableReplicas' for StatefulSetStatus is supported in native kubernetes since version 1.22, mismatch version between kubernetes and kubernetes-client will raise execption ValueError
"""


from kubernetes import client, config


def create_service(core_v1_api):
    body = client.V1Service(
        api_version="v1",
        kind="Service",
        metadata=client.V1ObjectMeta(
            name="redis-test-svc"
        ),
        spec=client.V1ServiceSpec(
            selector={"app": "redis"},
            cluster_ip="None",
            type="ClusterIP",
            ports=[client.V1ServicePort(
                port=6379,
                target_port=6379
            )]
        )
    )
    # Create the service in specified namespace
    # (Can replace "default" with a namespace you may have created)
    core_v1_api.create_namespaced_service(namespace="default", body=body)


def create_stateful_set_object():
    container = client.V1Container(
        name="sts-redis",
        image="redis",
        image_pull_policy="IfNotPresent",
        ports=[client.V1ContainerPort(container_port=6379)],
    )
    # Template
    template = client.V1PodTemplateSpec(
        metadata=client.V1ObjectMeta(labels={"app": "redis"}),
        spec=client.V1PodSpec(containers=[container]))
    # Spec
    spec = client.V1StatefulSetSpec(
        replicas=1,
        service_name="redis-test-svc",
        selector=client.V1LabelSelector(
            match_labels={"app": "redis"}
        ),
        template=template)
    # StatefulSet
    statefulset = client.V1StatefulSet(
        api_version="apps/v1",
        kind="StatefulSet",
        metadata=client.V1ObjectMeta(name="statefulset-redis"),
        spec=spec)

    return statefulset


def create_stateful_set(apps_v1_api, stateful_set_object):
    # Create the Statefulset in default namespace
    # You can replace the namespace with you have created
    apps_v1_api.create_namespaced_stateful_set(
        namespace="default", body=stateful_set_object
    )


def update_stateful_set(apps_v1_api, statefulset):
    # Update container image
    statefulset.spec.template.spec.containers[0].image = "redis:6.2"
    statefulset_name = statefulset.metadata.name
    # Patch the statefulset
    apps_v1_api.patch_namespaced_stateful_set(
        name=statefulset_name, namespace="default", body=statefulset
    )


def list_controller_revision(apps_v1_api, namespace, stateful_set_name):
    # Get all controller revisions in specified namespace
    controller_revision_list = apps_v1_api.list_namespaced_controller_revision(
        namespace)
    # Get all controller revisions which belong to specified statefulset.
    controller_revision_belong_to_sts = []
    for controller_revision in controller_revision_list.items:
        owner_kind = controller_revision.metadata.owner_references[0].kind
        owner_name = controller_revision.metadata.owner_references[0].name
        if owner_kind == "StatefulSet" and owner_name == stateful_set_name:
            controller_revision_belong_to_sts.append(
                (controller_revision.metadata.name, controller_revision.revision))
    return sorted(controller_revision_belong_to_sts, key=lambda x: x[1])


def rollout_namespaced_stateful_set(
        apps_v1_api,
        name,
        namespace,
        controller_revision_name):

    # Get the specified controller revision object
    _controller_revision = apps_v1_api.read_namespaced_controller_revision(
        controller_revision_name, namespace)
    # Roll out statefulset to the specified revision
    apps_v1_api.patch_namespaced_stateful_set(
        name, namespace, body=_controller_revision.data)


def main():
    # Loading the local kubeconfig
    config.load_kube_config()
    apps_v1_api = client.AppsV1Api()
    core_v1_api = client.CoreV1Api()
    stateful_set_obj = create_stateful_set_object()

    create_service(core_v1_api)

    create_stateful_set(apps_v1_api, stateful_set_obj)

    update_stateful_set(apps_v1_api, stateful_set_obj)

    # Wait for finishing creation of controller revision
    import time
    time.sleep(15)
    # List the controller revison
    controller_revisions = list_controller_revision(
        apps_v1_api, "default", "statefulset-redis")
    rollout_namespaced_stateful_set(
        apps_v1_api,
        "statefulset-redis",
        "default",
        controller_revisions[0][0])


if __name__ == "__main__":
    main()
