---
# Ensures all the operator artifacts are created and waits for CSV to succeed.
#
# The following variables must exist:
#
#   op:
#     desired_csv
#     op_nsp
#     op_cat
#     op_pkg
#     op_chn
#     op_mod
#
# NOTE: Do NOT test by checking for presence of API resources - they do not always get cleaned up.

- debug:
    msg: Deploying operator {{ op.op_pkg }} in namespace {{ op.op_nsp }}...

# See if the CSV is available already (anywhere), meaning the operator is already installed.
- name: Check if the CSV exists already
  k8s_info:
    kubeconfig: tmp/kubeconfig-ocp4
    validate_certs: no
    api_version: operators.coreos.com/v1alpha1
    kind: clusterserviceversion
  register: all_csv

- name: Find the wanted CSV among all CSVs
  set_fact:
    found_csv: '{{ (all_csv | community.general.json_query("resources[?contains(metadata.name, `" + op.op_pkg + ".`)]")) }}'
  when:
    - all_csv.resources is defined
    - (all_csv.resources | length) > 0

- name: Get details about the CSV if found
  set_fact:
    csv_ns: "{{ found_csv[0] | community.general.json_query('metadata.namespace') }}"
    csv_name: "{{ found_csv[0] | community.general.json_query('metadata.name') }}"
  when:
    - found_csv is defined
    - (found_csv | length) > 0

- debug:
    msg: Operator {{ op.op_pkg }} is already installed in {{ csv_ns }} as {{ csv_name }} (desired_csv == {{ op.desired_csv }}).
  when:
    - csv_ns is defined
    - csv_name is defined

- name: Only proceed here if the CSV was not found anywhere yet
  block:

    # Check if package provides desired_csv in any of the channels
    - name: Get packagemanifest
      k8s_info:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: packages.operators.coreos.com/v1
        kind: packagemanifest
        namespace: openshift-marketplace
        name: "{{ op.op_pkg }}"
      register: pkg_mft

    - name: Search for any channels that provide desired_csv
      set_fact:
        found_chn: '{{ (pkg_mft | community.general.json_query("resources[0].status.channels[?currentCSV == `" + op.desired_csv + "`].name")) }}'
      when:
        - pkg_mft is defined
        - pkg_mft.resources | length > 0

    - name: Fail if no channel provides the desired_csv
      fail:
        msg: No {{ op.op_pkg }} channel provides {{ op.desired_csv }}
      when:
        - found_chn is defined
        - found_chn | length == 0

    - name: Fail if selected channel does not provide the desired_csv
      fail:
        msg: Operator {{ op.op_pkg }} selected channel {{ op.op_chn }} does not provide {{ op.desired_csv }} - {{ found_chn }}
      when:
        - found_chn is defined
        - found_chn | length > 0
        - op.op_chn not in found_chn

    # Create the namespace if necessary
    - name: Make sure the namespace is there
      k8s:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: v1
        kind: namespace
        name: "{{ op.op_nsp }}"

    # Check for operator group - if existing, make sure it's compatible; otherwise create it
    - name: See if there are any OperatorGroups
      k8s_info:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: operators.coreos.com/v1
        kind: operatorgroup
        namespace: "{{ op.op_nsp }}"
      register: found_opgrp

    - name: Die if more than one OG
      ansible.builtin.fail:
        msg: More than one OperatorGroup found in project.
      when: (found_opgrp.resources | length) > 1

    - name: Die if op_mod is not recognised
      ansible.builtin.fail:
        msg: Unrecognised op.op_mod ({{ op.op_mod }})
      when: op.op_mod not in ["all", "self"]

    - name: Die if op_mod does not match found OG mode
      ansible.builtin.fail:
        msg: Found OG mode does not match required ({{ op.op_mod }})
      when: |
        ((found_opgrp.resources | length) == 1 and op.op_mod == "all" and found_opgrp.resources[0].status.namespaces != [""]) or
        ((found_opgrp.resources | length) == 1 and op.op_mod == "self" and found_opgrp.resources[0].status.namespaces != [op.op_nsp])

    - name: Set fact for namespaces of OG if we require all
      set_fact:
        og_spec: []
      when:
        - op.op_mod == "all"
        - (found_opgrp.resources | length) == 0

    - name: Set fact for namespaces of OG if we require self
      set_fact:
        og_spec: ["{{op.op_nsp}}"]
      when:
        - op.op_mod == "self"
        - (found_opgrp.resources | length) == 0

    - name: Create the OperatorGroup if not there
      k8s:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: operators.coreos.com/v1
        kind: operatorgroup
        namespace: "{{ op.op_nsp }}"
        name: "{{ op.op_nsp }}"
        definition:
          spec:
            targetNamespaces: "{{ og_spec }}"
      when: (found_opgrp.resources | length) == 0

    # create a subscription if not there, and wait for the CSV
    - name: Also make sure there is a subscription
      k8s:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: operators.coreos.com/v1alpha1
        kind: subscription
        namespace: "{{ op.op_nsp }}"
        name: "{{ op.op_pkg }}"
        definition:
          spec:
            source: "{{ op.op_cat }}"
            sourceNamespace: openshift-marketplace
            name: "{{ op.op_pkg }}"
            channel: "{{ op.op_chn }}"
            installPlanApproval: Automatic

    # TODO: Finish this at some point.
    #- name: Wait for installPlan to show up
    #  k8s_info:
    #    kubeconfig: tmp/kubeconfig-ocp4
    #    validate_certs: no
    #    api_version: operators.coreos.com/v1alpha1
    #    kind: installplan
    #    namespace: "{{ op_nsp }}"
    #  register: installplan
    #  until:
    #    - installplan.resources is defined
    #    - (installplan.resources | length) > 0
    #    - installplan.resources[0].spec.approved
    #  retries: 12
    #  delay: 10

    - name: Wait for CSV to show up and complete
      k8s_info:
        kubeconfig: tmp/kubeconfig-ocp4
        validate_certs: no
        api_version: operators.coreos.com/v1alpha1
        kind: clusterserviceversion
        namespace: "{{ op.op_nsp }}"
        name: "{{ op.desired_csv }}"
      register: new_csv
      until:
        - new_csv.resources is defined
        - (new_csv.resources | length) > 0
        - new_csv.resources[0].status is defined
        - new_csv.resources[0].status.phase == "Succeeded"
      retries: 30
      delay: 10

    # TODO: Finish this at some point.
    #- name: Finally, wait for the pod
    #  k8s_info:
    #    kubeconfig: tmp/kubeconfig-ocp4
    #    validate_certs: no
    #    api_version: v1
    #    kind: pod
    #    namespace: rhsso
    #    label_selectors:
    #      - name = rhsso-operator
    #  register: sso_pod
    #  until:
    #    - sso_pod.resources is defined
    #    - (sso_pod.resources | length) > 0
    #    - sso_pod.resources[0].status is defined
    #    - sso_pod.resources[0].status.phase == "Running"
    #  retries: 30
    #  delay: 10

    - debug:
        msg: Operator {{ op.op_pkg }} deployed in namespace {{ op.op_nsp }} with CSV {{ op.desired_csv }}!

  when: found_csv is not defined or (found_csv | length) == 0
...