---
- name: decide_master_name
  include_role:
    name: common
    tasks_from: decide_master_name

- name: JoinConfiguration - setup nodeRegistration to inventory_hostname - {{ inventory_hostname }} (instead of letting it use the default node) when inventory_hostname is fqdn
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'nodeRegistration': {'name': inventory_hostname } }, recursive=True) }}"
  when:
  - custom.networking.fqdn.always or custom.networking.fqdn.node
  - '"." in inventory_hostname'

- name: JoinConfiguration - setup nodeRegistration to ansible_fqdn - {{ ansible_fqdn }} (instead of letting it use the default node); (but we might hit ansible_fqdn bug) when inventory_hostname is not fqdn
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'nodeRegistration': {'name': ansible_fqdn } }, recursive=True) }}"
  when:
  - custom.networking.fqdn.always or custom.networking.fqdn.node
  - '"." not in inventory_hostname'

- name: "Wait 500 seconds for master at {{ master_name }}:{{ custom.networking.masterha_bindPort | default (6443) }} to become open (MasterHA)"
  wait_for:
    port: "{{ custom.networking.masterha_bindPort | default (6443) }}"
    host: "{{ master_name }}"
    delay: 1
    timeout: 500
  run_once: yes

### TOKEN:
- name: making sure there is a JoinConfiguration.discovery.bootstrapToken placeholder
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': {} } }, recursive=True) }}"

- name: set kubeadm_token to the value from InitConfiguration
  set_fact: kubeadm_token={{ InitConfiguration.bootstrapTokens[0].token }}
  when:
  - InitConfiguration.bootstrapTokens is defined
  - InitConfiguration.bootstrapTokens[0].token is defined
  - JoinConfiguration.discovery is not defined or JoinConfiguration.discovery.bootstrapToken is not defined or JoinConfiguration.discovery.bootstrapToken.token

- name: when defined, use JoinConfiguration.discovery.bootstrapToken.token on priority
  set_fact: kubeadm_token={{ JoinConfiguration.discovery.bootstrapToken.token }}
  when:
  - JoinConfiguration.discovery.bootstrapToken.token is defined

- name: when there is JoinConfiguration.discovery.bootstrapToken.token or InitConfiguration.bootstrapTokens[0].token and no hash, add skip hash
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': { 'unsafeSkipCAVerification': 'True' } } }, recursive=True) }}"
  when:
  - JoinConfiguration.discovery.bootstrapToken.token is defined or InitConfiguration.bootstrapTokens is defined
  - JoinConfiguration.discovery.bootstrapToken.token is defined or InitConfiguration.bootstrapTokens[0].token is defined 
  - JoinConfiguration.discovery.bootstrapToken.caCertHashes is not defined

### Generate a token&hash on master and use them
- block:
  - set_fact:
      env_kc: '{{ proxy_env |default({}) | combine ({"PATH" : "/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin"  }) | combine ({"KUBECONFIG" :"/etc/kubernetes/admin.conf"}) }}'

  - name: generate a join token on primary-master
    shell: kubeadm token create --description "kubeadm-playbook-node-joining-token" --ttl 15m --print-join-command  | awk '{print $5}{print $7}'
    environment: '{{env_kc}}'
    # environment -> is required due to a k8s bug which makes kubeadm need internet to generate a token. setting version is not allowed
    # Optionally using "--config /etc/kubernetes/kubeadm-master.conf" to get rid of the message that it tries to connect to internet for version
    register: kubeadm_token_whash_n
    delegate_to: "{{groups['primary-master'][0]}}"
    run_once: yes
    when: 
    - InitConfiguration is not defined or InitConfiguration.bootstrapTokens is not defined or InitConfiguration.bootstrapTokens[0].token is not defined
    changed_when: false

  - name: add API server JoinConfiguration.discovery.bootstrapToken.apiServerEndpoint
    set_fact:
      JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': { 'apiServerEndpoint': master_name + ':' + custom.networking.masterha_bindPort | default (6443) | string } } }, recursive=True) }}"

  - name: add discovery token hash to JoinConfiguration.discovery.bootstrapToken.caCertHashes
    set_fact:
      JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': { 'caCertHashes': [kubeadm_token_whash_n.stdout_lines[1]] } } }, recursive=True) }}"

  ### Add token must be the last in the block, otherwise condition (JoinConfiguration.discovery.bootstrapToken.token is not defined) resolves to false...
  - name: add token to the JoinConfiguration.discovery.bootstrapToken.token
    set_fact:
      JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': { 'token': kubeadm_token_whash_n.stdout_lines[0] } } }, recursive=True) }}"

  when:
  - JoinConfiguration.discovery is not defined or JoinConfiguration.discovery.bootstrapToken is not defined or JoinConfiguration.discovery.bootstrapToken.token is not defined
  - InitConfiguration.bootstrapTokens is not defined or InitConfiguration.bootstrapTokens[0].token is not defined
  tags:
  - token

- name: Add the InitConfiguration.bootstrapTokens[0] to the JoinConfiguration
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'discovery': { 'bootstrapToken': { 'token': kubeadm_token } } }, recursive=True) }}"
  when:
  - JoinConfiguration.discovery is not defined or JoinConfiguration.discovery.bootstrapToken is not defined or JoinConfiguration.discovery.bootstrapToken.token is not defined
  tags:
  - token

  ### WHEN this node is control plane (secondary-master), we will add this info:
- name: control plane (secondary-master) -> add controlPlane.
  block:  

  ## PULL IMAGES
  - name: Pull images on master (from internet when images_repo is not defined)
    command: "kubeadm config images pull --kubernetes-version {{ ClusterConfiguration.kubernetesVersion }} --image-repository {{ images_repo |default ('k8s.gcr.io') }}"
    tags:
    - prepull_images
    register: command_result
    changed_when: '"Image is up to date" not in command_result.stdout or "Already exists" not in command_result.stdout'
    when: 
    - pre_pull_k8s_images is defined 
    - pre_pull_k8s_images 
    #- DOCKER_IMAGES is not defined ## or images_repo is not defined
    - ClusterConfiguration.kubernetesVersion is version_compare ( 'v1.11', '>=' )

  - name: "Make sure certs are ready for secondary master to join cluster: kubeadm init phase upload-certs --upload-certs"
    # Note: one cannot merge config from both config file anc cli. Only the config file will be used (when present)
    environment: '{{env_kc}}'
    shell: "/usr/bin/kubeadm init phase upload-certs --upload-certs -- 2>/dev/null | tail -1 "
    register: kubeadm_upload_certificate_key
    delegate_to: "{{groups['primary-master'][0]}}"
    run_once: yes
  
  ## TODO: try to remove this, and keep the autodetermined addr:
  - name: control plane (secondary-master) -> add controlPlane.localAPIEndpoint.advertiseAddress (ipv4; dns not allowed)
    set_fact:
      JoinConfiguration: "{{ JoinConfiguration | combine ( { 'controlPlane': { 'localAPIEndpoint': { 'advertiseAddress': ansible_default_ipv4.address } } }, recursive=True) }}"

  - name: control plane (secondary-master) -> add controlPlane.certificateKey
    set_fact:
      JoinConfiguration: "{{ JoinConfiguration | combine ( { 'controlPlane': { 'certificateKey': kubeadm_upload_certificate_key.stdout_lines[0] } }, recursive=True) }}"

  when:
  - inventory_hostname in groups['secondary-masters']

### Cloud Config
- name: JoinConfiguration - cloudProvider merging {{ ClusterConfiguration.cloudProvider }} to the JoinConfiguration.nodeRegistration.kubeletExtraArgs
  set_fact:
    JoinConfiguration: "{{ JoinConfiguration | combine ( { 'nodeRegistration': {'kubeletExtraArgs': {'cloud-provider': ClusterConfiguration.cloudProvider }  } }, recursive=True) }}"
  when:
  - ClusterConfiguration is defined
  - ClusterConfiguration.cloudProvider is defined
  - ( ClusterConfiguration.apiVersion >= "kubeadm.k8s.io/v1alpha3" ) or (ClusterConfiguration.apiVersion == "kubeadm.k8s.io/v1")

### Write configs to disk
- name: Writing JoinConfiguration KubeProxyConfiguration and KubeletConfiguration to /etc/kubernetes/kubeadm-node.conf
  copy:
    dest: /etc/kubernetes/kubeadm-node.conf
    force: yes
    content: |
      {{ JoinConfiguration | to_nice_yaml }}
      ---
      {{ KubeProxyConfiguration | to_nice_yaml }}
      ---
      {{ KubeletConfiguration | to_nice_yaml }}

### Join nodes to cluster
- name: Join cluster using kubeadm join --config /etc/kubernetes/kubeadm-node.conf
  #command: /usr/bin/kubeadm join {{ master_name }}:{{ custom.networking.masterha_bindPort | default (6443) }} --config /etc/kubernetes/kubeadm-node.conf
  command: /usr/bin/kubeadm join --config /etc/kubernetes/kubeadm-node.conf
  #{{ node_name_setting | default (" ") }} --token {{ kubeadm_token }} {{ kubeadm_join_args | default(" ") }}
  tags:
  - init
#  args:
#  creates: /etc/kubernetes/kubelet.conf

- name: wait for /etc/kubernetes/kubelet.conf to be created
  wait_for:
    path: /etc/kubernetes/kubelet.conf
    state: present
    timeout: 30
    delay: 3
  changed_when: false
  tags:
  - init

#This is required when proxy is used, and the inventory_hostname is defined with fqdn
#to ensure the connection to local server is not going via proxy (expecially when applying addons)
- name: replace master api server address to {{ master_name }} in the /etc/kubernetes/kubelet.conf
  replace:
    dest: /etc/kubernetes/kubelet.conf
    regexp: '(\s+)(server: https:\/\/)[A-Za-z0-9\-\.]+:'
    replace: '\1\2{{ master_name }}:'
    #backup: yes
  #when: proxy_env is defined and master is defined with fqdn in the inventory file (e.g. master.example.com)
  tags:
  - init
  notify:
  - Restart kubelet

### SANITY : At this stage it will not be "Ready", as there is no network deployed yet.
# - block:
#   # At this stage it will not be "Ready", as there is no network deployed yet.
#   - name: kubectl get nodes
#     shell: "kubectl get nodes --no-headers | grep {{ inventory_hostname }}"   # | grep -v -w 'Ready' || true "
#     register: command_result
#     until: command_result.stdout != ""
#     retries: 40
#     delay: 3
#     changed_when: false

#   - name: Get nodes
#     command: kubectl get nodes
#     register: command_result
#     changed_when: false

#   - name: Display nodes
#     debug:
#       var: command_result.stdout_lines
#     changed_when: false

#   - name: Check all nodes were registered
#     shell: "/usr/bin/test $(kubectl get nodes --no-headers | grep -ow Ready | wc -l) >= {{ groups['nodes'] | length + groups['masters'] | length }}"
#     register: command_result
#     retries: 10
#     delay: 3
#     until: command_result | success
#     changed_when: false

#   environment:
#     KUBECONFIG: /etc/kubernetes/kubelet.conf
#   tags:
#   - node
#   - node_sanity

###
- name: export KUBECONFIG in nodes' ~/.bashrc
  lineinfile:
    dest: ~/.bashrc
    line: "export KUBECONFIG=/etc/kubernetes/kubelet.conf"
    state: present
    create: yes
    regexp: '^export KUBECONFIG=.*'
  when: 
  - shell is undefined or shell == 'bash'
  - inventory_hostname in groups['nodes']

- name: export KUBECONFIG in secondary-masters' ~/.bashrc
  lineinfile: 
    dest: ~/.bashrc
    line: "export KUBECONFIG=/etc/kubernetes/admin.conf"
    state: present
    create: yes
    regexp: '^export KUBECONFIG=.*'
  when: 
  - shell is undefined or shell == 'bash'
  - inventory_hostname in groups['secondary-masters']
