From e88aa7c96b1c1d16c7e52dd2090be8c5adb93e5c Mon Sep 17 00:00:00 2001
From: Victor Morales <chipahuac@hotmail.com>
Date: Fri, 21 Jan 2022 14:01:07 -0800
Subject: [PATCH] Add youki runtime support (#8411)

---
 docs/ansible.md                               |  1 +
 .../container-engine/cri-o/defaults/main.yml  |  7 +++
 roles/container-engine/cri-o/tasks/main.yaml  |  6 +++
 roles/container-engine/meta/main.yml          |  8 ++++
 .../container-engine/youki/defaults/main.yml  |  3 ++
 .../youki/molecule/default/converge.yml       | 11 +++++
 .../molecule/default/files/10-mynet.conf      | 17 +++++++
 .../molecule/default/files/container.json     | 10 ++++
 .../youki/molecule/default/files/sandbox.json | 10 ++++
 .../youki/molecule/default/molecule.yml       | 45 +++++++++++++++++
 .../youki/molecule/default/prepare.yml        | 48 +++++++++++++++++++
 .../molecule/default/tests/test_default.py    | 29 +++++++++++
 roles/container-engine/youki/tasks/main.yml   | 12 +++++
 roles/download/defaults/main.yml              | 24 ++++++++++
 .../container_runtimes/meta/main.yml          |  9 ++++
 .../container_runtimes/youki/tasks/main.yaml  | 19 ++++++++
 .../youki/templates/runtimeclass-youki.yml    |  6 +++
 roles/kubespray-defaults/defaults/main.yaml   |  4 ++
 18 files changed, 269 insertions(+)
 create mode 100644 roles/container-engine/youki/defaults/main.yml
 create mode 100644 roles/container-engine/youki/molecule/default/converge.yml
 create mode 100644 roles/container-engine/youki/molecule/default/files/10-mynet.conf
 create mode 100644 roles/container-engine/youki/molecule/default/files/container.json
 create mode 100644 roles/container-engine/youki/molecule/default/files/sandbox.json
 create mode 100644 roles/container-engine/youki/molecule/default/molecule.yml
 create mode 100644 roles/container-engine/youki/molecule/default/prepare.yml
 create mode 100644 roles/container-engine/youki/molecule/default/tests/test_default.py
 create mode 100644 roles/container-engine/youki/tasks/main.yml
 create mode 100644 roles/kubernetes-apps/container_runtimes/youki/tasks/main.yaml
 create mode 100644 roles/kubernetes-apps/container_runtimes/youki/templates/runtimeclass-youki.yml

diff --git a/docs/ansible.md b/docs/ansible.md
index 7aca19e01..1388186e3 100644
--- a/docs/ansible.md
+++ b/docs/ansible.md
@@ -202,6 +202,7 @@ The following tags are defined in playbooks:
 |             vsphere-csi-driver | Configuring csi driver: vsphere
 |                          weave | Network plugin Weave
 |                      win_nodes | Running windows specific tasks
+|                          youki | Configuring youki runtime
 
 Note: Use the ``bash scripts/gen_tags.sh`` command to generate a list of all
 tags found in the codebase. New tags will be listed with the empty "Used for"
diff --git a/roles/container-engine/cri-o/defaults/main.yml b/roles/container-engine/cri-o/defaults/main.yml
index 912428ff0..422bddfaf 100644
--- a/roles/container-engine/cri-o/defaults/main.yml
+++ b/roles/container-engine/cri-o/defaults/main.yml
@@ -73,6 +73,13 @@ crun_runtime:
   type: oci
   root: /run/crun
 
+# youki is an implementation of the OCI runtime-spec in Rust, similar to runc.
+youki_runtime:
+  name: youki
+  path: "{{ youki_bin_dir }}/youki"
+  type: oci
+  root: /run/youki
+
 # When this is true, CRI-O package repositories are added. Set this to false when using an
 # environment with preconfigured CRI-O package repositories.
 crio_add_repos: true
diff --git a/roles/container-engine/cri-o/tasks/main.yaml b/roles/container-engine/cri-o/tasks/main.yaml
index 46ef43548..3fb6ea0f6 100644
--- a/roles/container-engine/cri-o/tasks/main.yaml
+++ b/roles/container-engine/cri-o/tasks/main.yaml
@@ -54,6 +54,12 @@
   when:
     - crun_enabled
 
+- name: Build a list of crio runtimes with youki runtime
+  set_fact:
+    crio_runtimes: "{{ crio_runtimes + [youki_runtime] }}"
+  when:
+    - youki_enabled
+
 - name: Make sure needed folders exist in the system
   with_items:
     - /etc/crio
diff --git a/roles/container-engine/meta/main.yml b/roles/container-engine/meta/main.yml
index 8bd98bd02..7ef823629 100644
--- a/roles/container-engine/meta/main.yml
+++ b/roles/container-engine/meta/main.yml
@@ -23,6 +23,14 @@ dependencies:
       - container-engine
       - crun
 
+  - role: container-engine/youki
+    when:
+      - youki_enabled
+      - container_manager == 'crio'
+    tags:
+      - container-engine
+      - youki
+
   - role: container-engine/cri-o
     when:
       - container_manager == 'crio'
diff --git a/roles/container-engine/youki/defaults/main.yml b/roles/container-engine/youki/defaults/main.yml
new file mode 100644
index 000000000..2250f22ae
--- /dev/null
+++ b/roles/container-engine/youki/defaults/main.yml
@@ -0,0 +1,3 @@
+---
+
+youki_bin_dir: "{{ bin_dir }}"
diff --git a/roles/container-engine/youki/molecule/default/converge.yml b/roles/container-engine/youki/molecule/default/converge.yml
new file mode 100644
index 000000000..11ef8f6bf
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/converge.yml
@@ -0,0 +1,11 @@
+---
+- name: Converge
+  hosts: all
+  become: true
+  vars:
+    youki_enabled: true
+    container_manager: crio
+  roles:
+    - role: kubespray-defaults
+    - role: container-engine/cri-o
+    - role: container-engine/youki
diff --git a/roles/container-engine/youki/molecule/default/files/10-mynet.conf b/roles/container-engine/youki/molecule/default/files/10-mynet.conf
new file mode 100644
index 000000000..b9fa3ba73
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/files/10-mynet.conf
@@ -0,0 +1,17 @@
+{
+  "cniVersion": "0.4.0",
+  "name": "mynet",
+  "type": "bridge",
+  "bridge": "cni0",
+  "isGateway": true,
+  "ipMasq": true,
+  "ipam": {
+    "type": "host-local",
+    "subnet": "172.19.0.0/24",
+    "routes": [
+      {
+        "dst": "0.0.0.0/0"
+      }
+    ]
+  }
+}
diff --git a/roles/container-engine/youki/molecule/default/files/container.json b/roles/container-engine/youki/molecule/default/files/container.json
new file mode 100644
index 000000000..a5d509431
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/files/container.json
@@ -0,0 +1,10 @@
+{
+  "metadata": {
+    "name": "youki1"
+  },
+  "image": {
+    "image": "quay.io/kubespray/hello-world:latest"
+  },
+  "log_path": "youki1.0.log",
+  "linux": {}
+}
diff --git a/roles/container-engine/youki/molecule/default/files/sandbox.json b/roles/container-engine/youki/molecule/default/files/sandbox.json
new file mode 100644
index 000000000..b2a4ffe50
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/files/sandbox.json
@@ -0,0 +1,10 @@
+{
+  "metadata": {
+    "name": "youki1",
+    "namespace": "default",
+    "attempt": 1,
+    "uid": "hdishd83djaidwnduwk28bcsb"
+  },
+  "linux": {},
+  "log_directory": "/tmp"
+}
diff --git a/roles/container-engine/youki/molecule/default/molecule.yml b/roles/container-engine/youki/molecule/default/molecule.yml
new file mode 100644
index 000000000..14867fad5
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/molecule.yml
@@ -0,0 +1,45 @@
+---
+driver:
+  name: vagrant
+  provider:
+    name: libvirt
+    options:
+      driver: kvm
+lint: |
+  set -e
+  yamllint -c ../../../.yamllint .
+platforms:
+  - name: ubuntu20
+    box: generic/ubuntu2004
+    cpus: 1
+    memory: 1024
+    nested: true
+    groups:
+      - kube_control_plane
+  - name: almalinux8
+    box: almalinux/8
+    cpus: 1
+    memory: 1024
+    nested: true
+    groups:
+      - kube_control_plane
+provisioner:
+  name: ansible
+  env:
+    ANSIBLE_ROLES_PATH: ../../../../
+  config_options:
+    defaults:
+      callback_whitelist: profile_tasks
+      timeout: 120
+  lint:
+    name: ansible-lint
+    options:
+      c: ../../../.ansible-lint
+  inventory:
+    group_vars:
+      all:
+        become: true
+verifier:
+  name: testinfra
+  lint:
+    name: flake8
diff --git a/roles/container-engine/youki/molecule/default/prepare.yml b/roles/container-engine/youki/molecule/default/prepare.yml
new file mode 100644
index 000000000..e9486865f
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/prepare.yml
@@ -0,0 +1,48 @@
+---
+- name: Prepare generic
+  hosts: all
+  become: true
+  roles:
+    - role: kubespray-defaults
+    - role: bootstrap-os
+    - role: adduser
+      user: "{{ addusers.kube }}"
+  tasks:
+    - include_tasks: "../../../../download/tasks/download_file.yml"
+      vars:
+        download: "{{ download_defaults | combine(downloads.cni) }}"
+
+- name: Prepare container runtime
+  hosts: all
+  become: true
+  vars:
+    container_manager: crio
+    kube_network_plugin: cni
+  roles:
+    - role: kubespray-defaults
+    - role: network_plugin/cni
+    - role: container-engine/crictl
+  tasks:
+    - name: Copy test container files
+      copy:
+        src: "{{ item }}"
+        dest: "/tmp/{{ item }}"
+        owner: root
+        mode: 0644
+      with_items:
+        - container.json
+        - sandbox.json
+    - name: Create /etc/cni/net.d directory
+      file:
+        path: /etc/cni/net.d
+        state: directory
+        owner: root
+        mode: 0755
+    - name: Setup CNI
+      copy:
+        src: "{{ item }}"
+        dest: "/etc/cni/net.d/{{ item }}"
+        owner: root
+        mode: 0644
+      with_items:
+        - 10-mynet.conf
diff --git a/roles/container-engine/youki/molecule/default/tests/test_default.py b/roles/container-engine/youki/molecule/default/tests/test_default.py
new file mode 100644
index 000000000..54ed5c54c
--- /dev/null
+++ b/roles/container-engine/youki/molecule/default/tests/test_default.py
@@ -0,0 +1,29 @@
+import os
+
+import testinfra.utils.ansible_runner
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+
+def test_run(host):
+    youkiruntime = "/usr/local/bin/youki"
+    with host.sudo():
+        cmd = host.command(youkiruntime + " --version")
+    assert cmd.rc == 0
+    assert "youki" in cmd.stdout
+
+
+def test_run_pod(host):
+    runtime = "youki"
+
+    run_command = "/usr/local/bin/crictl run --with-pull --runtime {} /tmp/container.json /tmp/sandbox.json".format(runtime)
+    with host.sudo():
+        cmd = host.command(run_command)
+    assert cmd.rc == 0
+
+    with host.sudo():
+      log_f = host.file("/tmp/youki1.0.log")
+
+      assert log_f.exists
+      assert b"Hello from Docker" in log_f.content
diff --git a/roles/container-engine/youki/tasks/main.yml b/roles/container-engine/youki/tasks/main.yml
new file mode 100644
index 000000000..1095c3d2e
--- /dev/null
+++ b/roles/container-engine/youki/tasks/main.yml
@@ -0,0 +1,12 @@
+---
+- name: youki | Download youki
+  include_tasks: "../../../download/tasks/download_file.yml"
+  vars:
+    download: "{{ download_defaults | combine(downloads.youki) }}"
+
+- name: youki | Copy youki binary from download dir
+  copy:
+    src: "{{ local_release_dir }}/youki_v{{ youki_version | regex_replace('\\.', '_') }}_linux/youki-v{{ youki_version }}/youki"
+    dest: "{{ youki_bin_dir }}/youki"
+    mode: 0755
+    remote_src: true
diff --git a/roles/download/defaults/main.yml b/roles/download/defaults/main.yml
index 5087055e8..3f699c8fc 100644
--- a/roles/download/defaults/main.yml
+++ b/roles/download/defaults/main.yml
@@ -72,6 +72,7 @@ kubeadm_version: "{{ kube_version }}"
 crun_version: 1.4
 runc_version: v1.0.3
 kata_containers_version: 2.2.3
+youki_version: 0.0.1
 gvisor_version: 20210921
 containerd_version: 1.5.9
 
@@ -142,6 +143,7 @@ crictl_download_url: "https://github.com/kubernetes-sigs/cri-tools/releases/down
 helm_download_url: "https://get.helm.sh/helm-{{ helm_version }}-linux-{{ image_arch }}.tar.gz"
 runc_download_url: "https://github.com/opencontainers/runc/releases/download/{{ runc_version }}/runc.{{ image_arch }}"
 crun_download_url: "https://github.com/containers/crun/releases/download/{{ crun_version }}/crun-{{ crun_version }}-linux-{{ image_arch }}"
+youki_download_url: "https://github.com/containers/youki/releases/download/v{{ youki_version }}/youki_v{{ youki_version | regex_replace('\\.', '_') }}_linux.tar.gz"
 kata_containers_download_url: "https://github.com/kata-containers/kata-containers/releases/download/{{ kata_containers_version }}/kata-static-{{ kata_containers_version }}-{{ ansible_architecture }}.tar.xz"
 # gVisor only supports amd64 and uses x86_64 to in the download link
 gvisor_runsc_download_url: "https://storage.googleapis.com/gvisor/releases/release/{{ gvisor_version }}/{{ ansible_architecture }}/runsc"
@@ -430,6 +432,14 @@ crun_checksums:
     1.3: c0955cf6d3d832c0249bbaa71ed235abb35b8ca45fe07f2bd4501a00afb9bdc4
     1.4: 8e8081562503308f39f571acfe94afc663816ea0cb8f922145e2aaf0991415d7
 
+youki_checksums:
+  arm:
+    0.0.1: 0
+  amd64:
+    0.0.1: 8bd712fe95c8a81194bfbc54c70516350f95153d67044579af95788fbafd943b
+  arm64:
+    0.0.1: 0
+
 kata_containers_binary_checksums:
   arm:
     2.0.4: 0
@@ -512,6 +522,7 @@ crictl_binary_checksum: "{{ crictl_checksums[image_arch][crictl_version] }}"
 helm_archive_checksum: "{{ helm_archive_checksums[image_arch][helm_version] }}"
 runc_binary_checksum: "{{ runc_checksums[image_arch][runc_version] }}"
 crun_binary_checksum: "{{ crun_checksums[image_arch][crun_version] }}"
+youki_archive_checksum: "{{ youki_checksums[image_arch][youki_version] }}"
 kata_containers_binary_checksum: "{{ kata_containers_binary_checksums[image_arch][kata_containers_version] }}"
 gvisor_runsc_binary_checksum: "{{ gvisor_runsc_binary_checksums[image_arch][gvisor_version] }}"
 gvisor_containerd_shim_binary_checksum: "{{ gvisor_containerd_shim_binary_checksums[image_arch][gvisor_version] }}"
@@ -809,6 +820,19 @@ downloads:
     groups:
     - k8s_cluster
 
+  youki:
+    file: true
+    enabled: "{{ youki_enabled }}"
+    version: "{{ youki_version }}"
+    dest: "{{ local_release_dir }}/youki_v{{ youki_version | regex_replace('\\.', '_') }}_linux.tar.gz"
+    sha256: "{{ youki_archive_checksum }}"
+    url: "{{ youki_download_url }}"
+    unarchive: true
+    owner: "root"
+    mode: "0755"
+    groups:
+    - k8s_cluster
+
   runc:
     file: true
     enabled: "{{ container_manager == 'containerd' }}"
diff --git a/roles/kubernetes-apps/container_runtimes/meta/main.yml b/roles/kubernetes-apps/container_runtimes/meta/main.yml
index f63d38d61..8584117ef 100644
--- a/roles/kubernetes-apps/container_runtimes/meta/main.yml
+++ b/roles/kubernetes-apps/container_runtimes/meta/main.yml
@@ -20,3 +20,12 @@ dependencies:
       - apps
       - crun
       - container-runtimes
+
+  - role: kubernetes-apps/container_runtimes/youki
+    when:
+      - youki_enabled
+      - container_manager == 'crio'
+    tags:
+      - apps
+      - youki
+      - container-runtimes
diff --git a/roles/kubernetes-apps/container_runtimes/youki/tasks/main.yaml b/roles/kubernetes-apps/container_runtimes/youki/tasks/main.yaml
new file mode 100644
index 000000000..6da025f04
--- /dev/null
+++ b/roles/kubernetes-apps/container_runtimes/youki/tasks/main.yaml
@@ -0,0 +1,19 @@
+---
+
+- name: youki | Copy runtime class manifest
+  template:
+    src: runtimeclass-youki.yml
+    dest: "{{ kube_config_dir }}/runtimeclass-youki.yml"
+    mode: "0664"
+  when:
+    - inventory_hostname == groups['kube_control_plane'][0]
+
+- name: youki | Apply manifests
+  kube:
+    name: "runtimeclass-youki"
+    kubectl: "{{ bin_dir }}/kubectl"
+    resource: "runtimeclass"
+    filename: "{{ kube_config_dir }}/runtimeclass-youki.yml"
+    state: "latest"
+  when:
+    - inventory_hostname == groups['kube_control_plane'][0]
diff --git a/roles/kubernetes-apps/container_runtimes/youki/templates/runtimeclass-youki.yml b/roles/kubernetes-apps/container_runtimes/youki/templates/runtimeclass-youki.yml
new file mode 100644
index 000000000..b68bd0699
--- /dev/null
+++ b/roles/kubernetes-apps/container_runtimes/youki/templates/runtimeclass-youki.yml
@@ -0,0 +1,6 @@
+---
+kind: RuntimeClass
+apiVersion: node.k8s.io/v1
+metadata:
+  name: youki
+handler: youki
diff --git a/roles/kubespray-defaults/defaults/main.yaml b/roles/kubespray-defaults/defaults/main.yaml
index c9f6b8011..ada5bfa7d 100644
--- a/roles/kubespray-defaults/defaults/main.yaml
+++ b/roles/kubespray-defaults/defaults/main.yaml
@@ -275,6 +275,10 @@ gvisor_enabled: false
 # When enabled, it requires container_manager=crio
 crun_enabled: false
 
+# Enable youki as additional container runtime
+# When enabled, it requires container_manager=crio
+youki_enabled: false
+
 # Container on localhost (download images when download_localhost is true)
 container_manager_on_localhost: "{{ container_manager }}"
 
-- 
GitLab