1+ #!/usr/bin/env bash
2+ # Licensed to the Apache Software Foundation (ASF) under one
3+ # or more contributor license agreements. See the NOTICE file
4+ # distributed with this work for additional information
5+ # regarding copyright ownership. The ASF licenses this file
6+ # to you under the Apache License, Version 2.0 (the
7+ # "License"); you may not use this file except in compliance
8+ # with the License. You may obtain a copy of the License at
9+ #
10+ # http://www.apache.org/licenses/LICENSE-2.0
11+ #
12+ # Unless required by applicable law or agreed to in writing,
13+ # software distributed under the License is distributed on an
14+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+ # KIND, either express or implied. See the License for the
16+ # specific language governing permissions and limitations
17+ # under the License.
18+
19+ set -e
20+
21+ if [ $# -lt 8 ]; then
22+ echo "============================================================================================================"
23+ echo "CloudStack Kubernetes Service (CKS) - ISO Creation Script with Cilium"
24+ echo "============================================================================================================"
25+ echo ""
26+ echo "This script creates an ISO image containing Kubernetes binaries and dependencies for CKS with Cilium CNI."
27+ echo ""
28+ echo "Usage:"
29+ echo " ./create-kubernetes-binaries-iso-with-cilium.sh OUTPUT_PATH KUBERNETES_VERSION CNI_VERSION CRICTL_VERSION BUILD_NAME ARCH ETCD_VERSION CILIUM_VERSION"
30+ echo ""
31+ echo "Parameters:"
32+ echo " OUTPUT_PATH - Directory where the ISO file will be saved (e.g., ./ or /tmp/)"
33+ echo " KUBERNETES_VERSION - Kubernetes version without 'v' prefix (e.g., 1.35.0)"
34+ echo " CNI_VERSION - CNI Plugins version without 'v' prefix (e.g., 1.9.0)"
35+ echo " CRICTL_VERSION - CRI Tools version without 'v' prefix (e.g., 1.35.0)"
36+ echo " BUILD_NAME - Name for the output ISO file without extension (e.g., cks-v1.35.0)"
37+ echo " ARCH - Target architecture: amd64, x86_64, arm64, or aarch64"
38+ echo " ETCD_VERSION - etcd version without 'v' prefix (e.g., 3.6.7)"
39+ echo " CILIUM_VERSION - Cilium version without 'v' prefix (e.g., 1.19.0)"
40+ echo ""
41+ echo "Example:"
42+ echo " ./create-kubernetes-binaries-iso-with-cilium.sh ./ 1.35.0 1.9.0 1.35.0 cks-v1.35.0 amd64 3.6.7 1.19.0"
43+ echo ""
44+ echo "Output:"
45+ echo " The script will generate: cks-v1.35.0-x86_64.iso (or cks-v1.35.0-aarch64.iso for ARM)"
46+ echo "============================================================================================================"
47+ exit 1
48+ fi
49+
50+ ARCH="amd64"
51+ ARCH_SUFFIX="x86_64"
52+ if [ "${6}" = "x86_64" ] || [ "${6}" = "amd64" ]; then
53+ ARCH="amd64"
54+ ARCH_SUFFIX="x86_64"
55+ elif [ "${6}" = "aarch64" ] || [ "${6}" = "arm64" ]; then
56+ ARCH="arm64"
57+ ARCH_SUFFIX="aarch64"
58+ else
59+ echo "ERROR: ARCH must be 'x86_64' or 'aarch64'."
60+ exit 1
61+ fi
62+
63+ RELEASE="v${2}"
64+ VAL="1.18.0"
65+ output_dir="${1}"
66+ start_dir="$PWD"
67+ iso_dir="/tmp/iso"
68+ working_dir="${iso_dir}/"
69+ mkdir -p "${working_dir}"
70+ build_name="${5}-${ARCH_SUFFIX}.iso"
71+ [ -z "${build_name}" ] && build_name="setup-${RELEASE}-${ARCH_SUFFIX}.iso"
72+
73+ CNI_VERSION="v${3}"
74+ echo "Downloading CNI ${CNI_VERSION}..."
75+ cni_dir="${working_dir}/cni/"
76+ mkdir -p "${cni_dir}"
77+ cni_status_code=$(curl -sS -L --write-out "%{http_code}\n" "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz" -o "${cni_dir}/cni-plugins-${ARCH}.tgz")
78+ if [[ ${cni_status_code} -eq 404 ]]; then
79+ curl -sS -L --write-out "%{http_code}\n" "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-${ARCH}-${CNI_VERSION}.tgz" -o "${cni_dir}/cni-plugins-${ARCH}.tgz"
80+ fi
81+
82+ CRICTL_VERSION="v${4}"
83+ echo "Downloading CRI tools ${CRICTL_VERSION}..."
84+ crictl_dir="${working_dir}/cri-tools/"
85+ mkdir -p "${crictl_dir}"
86+ curl -sS -L "https://github.com/kubernetes-incubator/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz" -o "${crictl_dir}/crictl-linux-${ARCH}.tar.gz"
87+
88+ echo "Downloading Kubernetes tools ${RELEASE}..."
89+ k8s_dir="${working_dir}/k8s"
90+ mkdir -p "${k8s_dir}"
91+ cd "${k8s_dir}"
92+ curl -sS -L --remote-name-all https://dl.k8s.io/release/${RELEASE}/bin/linux/${ARCH}/{kubeadm,kubelet,kubectl}
93+ kubeadm_file_permissions=$(stat --format '%a' kubeadm)
94+ chmod +x kubeadm
95+
96+ echo "Downloading kubelet.service ${RELEASE}..."
97+ cd "${start_dir}"
98+ kubelet_service_file="${working_dir}/kubelet.service"
99+ touch "${kubelet_service_file}"
100+ if [[ $(echo "${2} $VAL" | awk '{print ($1 < $2)}') == 1 ]]; then
101+ curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/kubelet.service" | sed "s:/usr/bin:/opt/bin:g" > ${kubelet_service_file}
102+ else
103+ curl -sSL "https://raw.githubusercontent.com/shapeblue/cloudstack-nonoss/main/cks/kubelet.service" | sed "s:/usr/bin:/opt/bin:g" > ${kubelet_service_file}
104+ fi
105+
106+ echo "Downloading 10-kubeadm.conf ${RELEASE}..."
107+ kubeadm_conf_file="${working_dir}/10-kubeadm.conf"
108+ touch "${kubeadm_conf_file}"
109+ if [[ $(echo "${2} $VAL" | awk '{print ($1 < $2)}') == 1 ]]; then
110+ curl -sSL "https://raw.githubusercontent.com/kubernetes/kubernetes/${RELEASE}/build/debs/10-kubeadm.conf" | sed "s:/usr/bin:/opt/bin:g" > ${kubeadm_conf_file}
111+ else
112+ curl -sSL "https://raw.githubusercontent.com/shapeblue/cloudstack-nonoss/main/cks/10-kubeadm.conf" | sed "s:/usr/bin:/opt/bin:g" > ${kubeadm_conf_file}
113+ fi
114+
115+ AUTOSCALER_URL="https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/cloudstack/examples/cluster-autoscaler-standard.yaml"
116+ echo "Downloading kubernetes cluster autoscaler ${AUTOSCALER_URL}"
117+ autoscaler_conf_file="${working_dir}/autoscaler.yaml"
118+ curl -sSL ${AUTOSCALER_URL} -o ${autoscaler_conf_file}
119+
120+ PROVIDER_URL="https://raw.githubusercontent.com/apache/cloudstack-kubernetes-provider/main/deployment.yaml"
121+ echo "Downloading kubernetes cluster provider ${PROVIDER_URL}"
122+ provider_conf_file="${working_dir}/provider.yaml"
123+ curl -sSL ${PROVIDER_URL} -o ${provider_conf_file}
124+
125+ CILIUM_VERSION="${8}"
126+ echo "Generating Cilium ${CILIUM_VERSION} manifest..."
127+ network_conf_file="${working_dir}/network.yaml"
128+
129+ # Check if helm is installed
130+ if ! command -v helm > /dev/null 2>&1; then
131+ echo "ERROR: Helm is required to generate Cilium manifests. Please install Helm first."
132+ echo "Visit: https://helm.sh/docs/intro/install/"
133+ exit 1
134+ fi
135+
136+ helm repo add cilium https://helm.cilium.io/ > /dev/null 2>&1 || true
137+ echo "Updating Helm repositories..."
138+ helm repo update
139+ echo "Generating Cilium manifest with Helm..."
140+ if ! helm template cilium cilium/cilium --version "${CILIUM_VERSION}" \
141+ --kube-version "${RELEASE}" \
142+ --namespace kube-system \
143+ --set kubeProxyReplacement=true \
144+ --set socketLB.hostNamespaceOnly=true \
145+ --set cni.exclusive=true \
146+ --set cni.chainingMode=portmap \
147+ --set bgpControlPlane.enabled=true \
148+ --set encryption.enabled=true \
149+ --set encryption.type=wireguard \
150+ --set encryption.nodeEncryption=true \
151+ --set gatewayAPI.enabled=false \
152+ --set ingressController.enabled=false \
153+ --set l2announcements.enabled=true \
154+ --set l2podAnnouncements.enabled=true \
155+ --set l2podAnnouncements.interfacePattern=eth.* \
156+ --set ipam.mode=cluster-pool \
157+ --set ipam.operator.clusterPoolIPv4PodCIDRList={10.168.0.0/16} \
158+ --set ipam.operator.clusterPoolIPv4MaskSize=24 \
159+ --set hubble.relay.enabled=false \
160+ --set hubble.ui.enabled=false > ${network_conf_file}; then
161+ echo "ERROR: Failed to generate Cilium manifest with Helm"
162+ echo "Check if Cilium version ${CILIUM_VERSION} exists in the Helm repository"
163+ exit 1
164+ fi
165+ echo "Cilium manifest generated successfully"
166+
167+ DASHBOARD_VERSION="2.7.0"
168+ echo "Downloading Kubernetes Dashboard v${DASHBOARD_VERSION}..."
169+ dashboard_conf_file="${working_dir}/dashboard.yaml"
170+ curl -sSl "https://raw.githubusercontent.com/kubernetes/dashboard/v${DASHBOARD_VERSION}/aio/deploy/recommended.yaml" -o ${dashboard_conf_file}
171+
172+ csi_conf_file="${working_dir}/manifest.yaml"
173+ echo "Including CloudStack CSI Driver manifest"
174+ wget https://github.com/cloudstack/cloudstack-csi-driver/releases/download/v3.0.0/snapshot-crds.yaml -O ${working_dir}/snapshot-crds.yaml
175+ wget https://github.com/cloudstack/cloudstack-csi-driver/releases/download/v3.0.0/manifest.yaml -O ${csi_conf_file}
176+
177+ echo "Fetching k8s docker images..."
178+ if ! ctr -v > /dev/null 2>&1; then
179+ echo "Installing containerd..."
180+ if [ -f /etc/redhat-release ]; then
181+ if command -v dnf > /dev/null 2>&1; then
182+ PKG_MGR="dnf"
183+ else
184+ PKG_MGR="yum"
185+ fi
186+ sudo $PKG_MGR --assumeyes remove docker-common docker container-selinux docker-selinux docker-engine
187+ sudo $PKG_MGR --assumeyes install lvm2 device-mapper device-mapper-persistent-data device-mapper-event device-mapper-libs device-mapper-event-libs
188+ sudo $PKG_MGR --assumeyes install http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.107-3.el7.noarch.rpm
189+ sudo $PKG_MGR --assumeyes install containerd.io
190+ elif [ -f /etc/debian_version ] || command -v apt-get > /dev/null 2>&1; then
191+ sudo apt-get update && sudo apt-get install containerd.io --yes
192+ fi
193+ sudo systemctl enable --now containerd
194+ fi
195+ mkdir -p "${working_dir}/docker"
196+ output=$(${k8s_dir}/kubeadm config images list --kubernetes-version="${RELEASE}")
197+
198+ # Don't forget about the other image !
199+ autoscaler_image=$(grep "image:" ${autoscaler_conf_file} | cut -d ':' -f2- | tr -d ' ')
200+ output=$(printf "%s\n" "${output}" "${autoscaler_image}")
201+
202+ provider_image=$(grep "image:" ${provider_conf_file} | cut -d ':' -f2- | tr -d ' ')
203+ output=$(printf "%s\n" "${output}" "${provider_image}")
204+
205+ # Extract images from manifest.yaml and add to output
206+ csi_images=$(grep "image:" "${csi_conf_file}" | cut -d ':' -f2- | tr -d ' ' | tr -d "'")
207+ output=$(printf "%s\n%s" "${output}" "${csi_images}")
208+
209+ # Extract all images from yaml manifests (including Cilium network.yaml and Dashboard)
210+ echo "Extracting images from manifest files..."
211+ for i in ${network_conf_file} ${dashboard_conf_file}; do
212+ images=$(grep "image:" "$i" | cut -d ':' -f2- | tr -d ' ' | tr -d "'" | tr -d '"')
213+ output=$(printf "%s\n" "${output}" "${images}")
214+ done
215+
216+ while read -r line; do
217+ echo "Downloading image $line ---"
218+ if [[ $line == kubernetesui* ]] || [[ $line == apache* ]] || [[ $line == weaveworks* ]]; then
219+ line="docker.io/${line}"
220+ fi
221+ if [[ $line == kong* ]]; then
222+ line="docker.io/library/${line}"
223+ fi
224+ line=$(echo "$line" | tr -d '"' | tr -d "'")
225+
226+ # Pull image using containerd with k8s.io namespace
227+ if ! sudo ctr -n k8s.io images pull "$line"; then
228+ echo "ERROR: Failed to pull image $line"
229+ echo "Trying with default namespace..."
230+ if ! sudo ctr images pull "$line"; then
231+ echo "ERROR: Failed to pull image $line with both namespaces. Skipping..."
232+ continue
233+ fi
234+ # If successful with default namespace, use it for export
235+ image_name=$(echo "$line" | grep -oE "[^/]+$")
236+ sudo ctr images export "${working_dir}/docker/$image_name.tar" "$line"
237+ sudo ctr images rm "$line"
238+ else
239+ # If successful with k8s.io namespace
240+ image_name=$(echo "$line" | grep -oE "[^/]+$")
241+ sudo ctr -n k8s.io images export "${working_dir}/docker/$image_name.tar" "$line"
242+ sudo ctr -n k8s.io images rm "$line"
243+ fi
244+ done <<< "$output"
245+
246+ echo "Restore kubeadm permissions..."
247+ if [ -z "${kubeadm_file_permissions}" ]; then
248+ kubeadm_file_permissions=644
249+ fi
250+ chmod "${kubeadm_file_permissions}" "${working_dir}/k8s/kubeadm"
251+
252+ echo "Updating imagePullPolicy to IfNotPresent in yaml files..."
253+ sed -i "s/imagePullPolicy:.*/imagePullPolicy: IfNotPresent/g" ${working_dir}/*.yaml
254+
255+ etcd_dir="${working_dir}/etcd"
256+ mkdir -p "${etcd_dir}"
257+ ETCD_VERSION=v${7}
258+ echo "Downloading etcd ${ETCD_VERSION}..."
259+ curl -sS -L "https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz" -o ${etcd_dir}/etcd-linux-amd64.tar.gz
260+
261+ mkisofs -o "${output_dir}/${build_name}" -J -R -l "${iso_dir}"
262+
263+ rm -rf "${iso_dir}"
0 commit comments