---
- name: Set kubeadm_discovery_address
  set_fact:
    kubeadm_discovery_address: >-
      {%- if "127.0.0.1" in kube_apiserver_endpoint or "localhost" in kube_apiserver_endpoint -%}
      {{ first_kube_master }}:{{ kube_apiserver_port }}
      {%- else -%}
      {{ kube_apiserver_endpoint | replace("https://", "") }}
      {%- endif %}
  tags:
    - facts

- name: Check if kubelet.conf exists
  stat:
    path: "{{ kube_config_dir }}/kubelet.conf"
  register: kubelet_conf

- name: Check if kubeadm CA cert is accessible
  stat:
    path: "{{ kube_cert_dir }}/ca.crt"
  register: kubeadm_ca_stat
  delegate_to: "{{ groups['kube-master'][0] }}"
  run_once: true

- name: Calculate kubeadm CA cert hash
  shell: openssl x509 -pubkey -in {{ kube_cert_dir }}/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
  register: kubeadm_ca_hash
  when:
    - kubeadm_ca_stat.stat is defined
    - kubeadm_ca_stat.stat.exists
  delegate_to: "{{ groups['kube-master'][0] }}"
  run_once: true

- name: Create kubeadm token for joining nodes with 24h expiration (default)
  command: "{{ bin_dir }}/kubeadm token create"
  register: temp_token
  delegate_to: "{{ groups['kube-master'][0] }}"
  when: kubeadm_token is not defined

- name: Set kubeadm_token to generated token
  set_fact:
    kubeadm_token: "{{ temp_token.stdout }}"
  when: kubeadm_token is not defined

- name: gets the kubeadm version
  command: "{{ bin_dir }}/kubeadm version -o short"
  register: kubeadm_output

- name: sets kubeadm api version to v1beta1
  set_fact:
    kubeadmConfig_api_version: v1beta1
  when: kubeadm_output.stdout is version('v1.13.0', '>=')

- name: Create kubeadm client config
  template:
    src: "kubeadm-client.conf.{{ kubeadmConfig_api_version }}.j2"
    dest: "{{ kube_config_dir }}/kubeadm-client.conf"
    backup: yes
  when: not is_kube_master

- name: Join to cluster if needed
  environment:
    PATH: "{{ bin_dir }}:{{ ansible_env.PATH }}:/sbin"  # Make sure we can workaround RH / CentOS conservative path management
  when: not is_kube_master and (not kubelet_conf.stat.exists)
  block:

    - name: Join to cluster
      command: >-
        timeout -k {{ kubeadm_join_timeout }} {{ kubeadm_join_timeout }}
        {{ bin_dir }}/kubeadm join
        --config {{ kube_config_dir }}/kubeadm-client.conf
        --ignore-preflight-errors=DirAvailable--etc-kubernetes-manifests
      register: kubeadm_join

  rescue:

    - name: Join to cluster with ignores
      command: >-
        timeout -k {{ kubeadm_join_timeout }} {{ kubeadm_join_timeout }}
        {{ bin_dir }}/kubeadm join
        --config {{ kube_config_dir }}/kubeadm-client.conf
        --ignore-preflight-errors=all
      register: kubeadm_join

  always:

    - name: Display kubeadm join stderr if any
      when: kubeadm_join is failed
      debug:
        msg: |
          Joined with warnings
          {{ kubeadm_join.stderr_lines }}

- name: Update server field in kubelet kubeconfig
  lineinfile:
    dest: "{{ kube_config_dir }}/kubelet.conf"
    regexp: 'server:'
    line: '    server: {{ kube_apiserver_endpoint }}'
    backup: yes
  when:
    - kubeadm_config_api_fqdn is not defined
    - not is_kube_master
    - kubeadm_discovery_address != kube_apiserver_endpoint | replace("https://", "")
  notify: restart kubelet

# FIXME(mattymo): Need to point to localhost, otherwise masters will all point
#                 incorrectly to first master, creating SPoF.
- name: Update server field in kube-proxy kubeconfig
  shell: >-
    {{ bin_dir }}/kubectl --kubeconfig {{ kube_config_dir }}/admin.conf get configmap kube-proxy -n kube-system -o yaml
    | sed 's#server:.*#server: https://127.0.0.1:{{ kube_apiserver_port }}#g'
    | {{ bin_dir }}/kubectl --kubeconfig {{ kube_config_dir }}/admin.conf replace -f -
  run_once: true
  when:
    - inventory_hostname == groups['kube-master']|first
    - kubeadm_config_api_fqdn is not defined
    - kubeadm_discovery_address != kube_apiserver_endpoint | replace("https://", "")
    - not kube_proxy_remove
  tags:
    - kube-proxy

- name: Restart all kube-proxy pods to ensure that they load the new configmap
  shell: "{{ bin_dir }}/kubectl --kubeconfig {{ kube_config_dir }}/admin.conf delete pod -n kube-system -l k8s-app=kube-proxy --force --grace-period=0"
  run_once: true
  when:
    - inventory_hostname == groups['kube-master']|first
    - kubeadm_config_api_fqdn is not defined
    - kubeadm_discovery_address != kube_apiserver_endpoint | replace("https://", "")
    - not kube_proxy_remove
  tags:
    - kube-proxy

# FIXME(mattymo): Reconcile kubelet kubeconfig filename for both deploy modes
- name: Symlink kubelet kubeconfig for calico/canal
  file:
    src: "{{ kube_config_dir }}/kubelet.conf"
    dest: "{{ kube_config_dir }}/node-kubeconfig.yaml"
    state: link
    force: yes
  when:
    - kube_network_plugin in ['calico','canal']
    - calico_version is version('v3.3.0', '<')

# FIXME(jjo): need to post-remove kube-proxy until https://github.com/kubernetes/kubeadm/issues/776
# is fixed
- name: Delete kube-proxy daemonset if kube_proxy_remove set, e.g. kube_network_plugin providing proxy services
  shell: "{{ bin_dir }}/kubectl --kubeconfig /etc/kubernetes/admin.conf delete daemonset -n kube-system kube-proxy"
  run_once: true
  when:
    - inventory_hostname == groups['kube-master']|first
    - kube_proxy_remove
    - kubeadm_discovery_address != kube_apiserver_endpoint | replace("https://", "")
  # When scaling/adding nodes in the existing k8s cluster, kube-proxy wouldn't be created, as `kubeadm init` wouldn't run.
  ignore_errors: true
  tags:
    - kube-proxy

- name: Extract etcd certs from control plane if using etcd kubeadm mode
  include_tasks: kubeadm_etcd_node.yml
  when:
    - etcd_kubeadm_enabled
    - kubeadm_control_plane
    - inventory_hostname not in groups['kube-master']
    - kube_network_plugin in ["calico", "flannel", "canal", "cilium"]
    - kube_network_plugin != "calico" or calico_datastore == "etcd"
