Nate Stephany
2020-03-10 e5c58270876bf324b5065f1236f9e6b2602a91a5
OCP4 NFS workload role (#1274)

* nfs-on-ocp role init

* update to nfs-role

* remove and updates to workload
9 files added
425 ■■■■■ changed files
ansible/roles/ocp4-workload-nfs-server/README.md 38 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/defaults/main.yml 14 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/files/Dockerfile 5 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/files/run_nfs.sh 91 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/tasks/main.yml 20 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/tasks/post_workload.yml 9 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/tasks/pre_workload.yml 8 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/tasks/remove_workload.yml 38 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/tasks/workload.yml 202 ●●●●● patch | view | raw | blame | history
ansible/roles/ocp4-workload-nfs-server/README.md
New file
@@ -0,0 +1,38 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
    - hosts: servers
      roles:
         - { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
ansible/roles/ocp4-workload-nfs-server/defaults/main.yml
New file
@@ -0,0 +1,14 @@
---
# defaults file for ocp4-workload-nfs-server
_nfs_app: nfs-server
_nfs_project: nfs-server
_nfs_pvc_type: standard
_nfs_pvc_size: 20Gi
_nfs_server_image: quay.io/nstephan/nfs-server:v0.0.1
_nfs_provided_storage_class: manual
_nfs_pv_provided_size: 5Gi
#TODO: Implement this so you can have a variable number of exports
#_nfs_provided_exports: 10
ansible/roles/ocp4-workload-nfs-server/files/Dockerfile
New file
@@ -0,0 +1,5 @@
FROM gcr.io/google_containers/volume-nfs:0.8
COPY run_nfs.sh /usr/local/bin/
RUN chmod 775 /usr/local/bin/run_nfs.sh
ansible/roles/ocp4-workload-nfs-server/files/run_nfs.sh
New file
@@ -0,0 +1,91 @@
#!/bin/bash
# Copyright 2015 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
NFSROOT=/exports
function start()
{
    unset gid
    # accept "-G gid" option
    while getopts "G:" opt; do
        case ${opt} in
            G) gid=${OPTARG};;
        esac
    done
    shift $(($OPTIND - 1))
    # prepare /etc/exports
    mkdir $NFSROOT
    chown root:root $NFSROOT
    if [ -v gid ] ; then
        chmod 777 $NFSROOT
        chgrp $gid $NFSROOT
    fi
    echo "$NFSROOT *(rw,fsid=0,insecure,no_root_squash,no_wdelay)" >> /etc/exports
    for i in "$@"; do
        if [ ! -d $NFSROOT/$i ]; then
            mkdir -p $NFSROOT/$i
        fi
        if [ -v gid ] ; then
            chmod 777 $NFSROOT/$i
            chgrp $gid $NFSROOT/$i
        fi
    done
    # start rpcbind if it is not started yet
    /usr/sbin/rpcinfo 127.0.0.1 > /dev/null; s=$?
    if [ $s -ne 0 ]; then
       echo "Starting rpcbind"
       /usr/sbin/rpcbind -w
    fi
    mount -t nfsd nfsd /proc/fs/nfsd
    # -V 3: enable NFSv3
    /usr/sbin/rpc.mountd -N 2 -V 3
    /usr/sbin/exportfs -r
    # -G 10 to reduce grace time to 10 seconds (the lowest allowed)
    /usr/sbin/rpc.nfsd -G 10 -N 2 -V 3
    /usr/sbin/rpc.statd --no-notify
    echo "NFS started"
}
function stop()
{
    echo "Stopping NFS"
    /usr/sbin/rpc.nfsd 0
    /usr/sbin/exportfs -au
    /usr/sbin/exportfs -f
    kill $( pidof rpc.mountd )
    umount /proc/fs/nfsd
    echo > /etc/exports
    exit 0
}
trap stop TERM
start "$@"
# Ugly hack to do nothing and wait for SIGTERM
while true; do
    sleep 5
done
ansible/roles/ocp4-workload-nfs-server/tasks/main.yml
New file
@@ -0,0 +1,20 @@
---
- name: Running Pre Workload Tasks
  import_tasks: ./pre_workload.yml
  become: false
  when: ACTION == "create" or ACTION == "provision"
- name: Running Workload Tasks
  import_tasks: ./workload.yml
  become: false
  when: ACTION == "create" or ACTION == "provision"
- name: Running Post Workload Tasks
  import_tasks: ./post_workload.yml
  become: false
  when: ACTION == "create" or ACTION == "provision"
- name: Running Workload removal Tasks
  import_tasks: ./remove_workload.yml
  become: false
  when: ACTION == "destroy" or ACTION == "remove"
ansible/roles/ocp4-workload-nfs-server/tasks/post_workload.yml
New file
@@ -0,0 +1,9 @@
---
# Implement your Post Workload deployment tasks here
# Leave this as the last task in the playbook.
- name: post_workload tasks complete
  debug:
    msg: "Post-Workload Tasks completed successfully."
  when: not silent|bool
ansible/roles/ocp4-workload-nfs-server/tasks/pre_workload.yml
New file
@@ -0,0 +1,8 @@
---
# Implement your Pre Workload deployment tasks here
# Leave this as the last task in the playbook.
- name: pre_workload tasks complete
  debug:
    msg: "Pre-Workload tasks completed successfully."
  when: not silent|bool
ansible/roles/ocp4-workload-nfs-server/tasks/remove_workload.yml
New file
@@ -0,0 +1,38 @@
---
# Implement your Workload removal tasks here
- name: Remove NFS Storage Project
  k8s:
    name: "{{ _nfs_project }}"
    api_version: v1
    kind: Namespace
    state: absent
- name: Clean up PVs
  k8s:
    api_version: v1
    kind: PersistentVolume
    name: "{{ item }}"
    state: absent
  loop:
    - "vol0"
    - "vol1"
    - "vol2"
    - "vol3"
    - "vol4"
    - "vol5"
    - "vol6"
    - "vol7"
- name: Remove storage class
  k8s:
    api_version: storage.k8s.io/v1
    kind: StorageClass
    name: "{{ _nfs_provided_storage_class }}"
    state: absent
# Leave this as the last task in the playbook.
- name: remove_workload tasks complete
  debug:
    msg: "Remove Workload tasks completed successfully."
  when: not silent|bool
ansible/roles/ocp4-workload-nfs-server/tasks/workload.yml
New file
@@ -0,0 +1,202 @@
---
- name: Create or update storage class
  k8s:
    state: present
    definition:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: "{{ _nfs_provided_storage_class }}"
      provisioner: kubernetes.io/no-provisioner
      reclaimPolicy: Retain
      volumeBindingMode: WaitForFirstConsumer
- name: Create project for NFS server
  k8s:
    state: present
    definition:
      apiVersion: v1
      kind: Namespace
      metadata:
        name: "{{ _nfs_project }}"
- name: Create PVC for NFS server underlying storage
  k8s:
    state: present
    definition:
      kind: PersistentVolumeClaim
      apiVersion: v1
      metadata:
        labels:
          app: "{{ _nfs_app }}"
        name: "{{ _nfs_app }}-pvc"
        namespace: "{{ _nfs_project }}"
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: "{{ _nfs_pvc_type }}"
        resources:
          requests:
            storage: "{{ _nfs_pvc_size }}"
- name: Create ImageStream for nfs server image
  k8s:
    state: present
    definition:
      apiVersion: v1
      kind: ImageStream
      metadata:
        labels:
          app: "{{ _nfs_app }}"
        name: "{{ _nfs_app }}"
        namespace: "{{ _nfs_project }}"
      spec: {}
- name: Import image to ImageStream
  shell: oc import-image --from={{ _nfs_server_image }} --scheduled {{ _nfs_app }} -n {{ _nfs_project }}
- name: Create service account
  k8s:
    state: present
    definition:
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: "{{ _nfs_app }}"
        namespace: "{{ _nfs_project }}"
- name: Add anyuid SCC to SA
  shell: oc adm policy add-scc-to-user anyuid system:serviceaccount:{{ _nfs_project }}:{{ _nfs_app }}
- name: Add privileged SCC to SA
  shell: oc adm policy add-scc-to-user privileged system:serviceaccount:{{ _nfs_project }}:{{ _nfs_app }}
- name: Create NFS server StatefulSet
  k8s:
    state: present
    definition:
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        labels:
          app: "{{ _nfs_app }}"
        name: "{{ _nfs_app }}"
        namespace: "{{ _nfs_project }}"
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: "{{ _nfs_app }}"
            statefulset: "{{ _nfs_app }}"
        template:
          metadata:
            labels:
              app: "{{ _nfs_app }}"
              statefulset: "{{ _nfs_app }}"
          spec:
            serviceAccountName: "{{ _nfs_app }}"
            containers:
            - name: "{{ _nfs_app }}"
              command:
              - /usr/local/bin/run_nfs.sh
              - -G
              - "0"
              - "vol0"
              - "vol1"
              - "vol2"
              - "vol3"
              - "vol4"
              - "vol5"
              - "vol6"
              - "vol7"
              image: "{{ _nfs_server_image }}"
              ports:
                - name: nfs
                  containerPort: 2049
                - name: mountd
                  containerPort: 20048
                - name: rpcbind
                  containerPort: 111
              securityContext:
                privileged: true
              volumeMounts:
                - mountPath: /exports
                  name: pvc-{{ _nfs_app }}
            volumes:
              - name: pvc-{{ _nfs_app }}
                persistentVolumeClaim:
                  claimName: "{{ _nfs_app }}-pvc"
#TODO: Add health check to statefulset
#TODO: Add check to make sure Pod comes up
- name: Create NFS server service
  k8s:
    state: present
    definition:
      apiVersion: v1
      kind: Service
      metadata:
        labels:
          app: "{{ _nfs_app }}"
        name: "{{ _nfs_app }}"
        namespace: "{{ _nfs_project }}"
      spec:
        ports:
          - name: nfs
            port: 2049
          - name: mountd
            port: 20048
          - name: rpcbind
            port: 111
        selector:
          app: nfs-server
  register: r_nfs_service
- name: Get NFS server service clusterIP
  set_fact:
    nfs_ip: "{{ r_nfs_service.result.spec.clusterIP }}"
- name: Dump nfs_ip
  debug:
    var: nfs_ip
- name: Create PVs pointing to NFS
  k8s:
    state: present
    definition:
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        labels:
          app: "{{ _nfs_app }}"
        name: "{{ item }}"
      spec:
        storageClassName: "{{ _nfs_provided_storage_class }}"
        capacity:
          storage: "{{ _nfs_pv_provided_size }}"
        accessModes:
          - ReadWriteOnce
          - ReadOnlyMany
          - ReadWriteMany
        persistentVolumeReclaimPolicy: Recycle
        nfs:
          server: "{{ nfs_ip }}"
          path: "/{{ item }}"
        mountOptions:
          - vers=4
  loop:
    - "vol0"
    - "vol1"
    - "vol2"
    - "vol3"
    - "vol4"
    - "vol5"
    - "vol6"
    - "vol7"
# Leave this as the last task in the playbook.
- name: workload tasks complete
  debug:
    msg: "Workload Tasks completed successfully."
  when: not silent|bool