CoreOS Installation Script

From Coreos, 7 Years ago, written in Bash, viewed 1'113 times.
URL https://code.nat.moe/view/8edb7063 Embed
Download Paste or View Raw
  1. #!/bin/bash
  2. # Copyright (c) 2013 The CoreOS Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5.  
  6. set -e -o pipefail
  7.  
  8. # Everything we do should be user-access only!
  9. umask 077
  10.  
  11. if grep -q "^ID=coreos$" /etc/os-release; then
  12.     source /etc/os-release
  13.     [[ -f /usr/share/coreos/update.conf ]] && source /usr/share/coreos/update.conf
  14.     [[ -f /etc/coreos/update.conf ]] && source /etc/coreos/update.conf
  15. fi
  16.  
  17. # Fall back on the current beta if os-release isn't useful
  18. : ${VERSION_ID:=current}
  19. CHANNEL_ID=${GROUP:-beta}
  20.  
  21. OEM_ID=
  22. if [[ -e /etc/oem-release ]]; then
  23.     # Pull in OEM information too, but prefixing variables with OEM_
  24.     eval "$(sed -e 's/^/OEM_/' /etc/oem-release)"
  25. fi
  26.  
  27. USAGE="Usage: $0 [-V version] [-d /dev/device]
  28. Options:
  29.    -d DEVICE   Install CoreOS to the given device.
  30.    -V VERSION  Version to install (e.g. current) [default: ${VERSION_ID}]
  31.    -C CHANNEL  Release channel to use (e.g. beta) [default: ${CHANNEL_ID}]
  32.    -o OEM      OEM type to install (e.g. ami) [default: ${OEM_ID:-(none)}]
  33.    -c CLOUD    Insert a cloud-init config to be executed on boot.
  34.    -t TMPDIR   Temporary location with enough space to download images.
  35.    -v          Super verbose, for debugging.
  36.    -h          This ;-)
  37.  
  38. This tool installs CoreOS on a block device. If you PXE booted CoreOS on a
  39. machine then use this tool to make a permanent install.
  40. "
  41.  
  42. # Image signing key: buildbot@coreos.com
  43. GPG_LONG_ID="50E0885593D2DCB4"
  44. GPG_KEY="-----BEGIN PGP PUBLIC KEY BLOCK-----
  45. Version: GnuPG v2
  46.  
  47. mQINBFIqVhQBEADjC7oxg5N9Xqmqqrac70EHITgjEXZfGm7Q50fuQlqDoeNWY+sN
  48. szpw//dWz8lxvPAqUlTSeR+dl7nwdpG2yJSBY6pXnXFF9sdHoFAUI0uy1Pp6VU9b
  49. /9uMzZo+BBaIfojwHCa91JcX3FwLly5sPmNAjgiTeYoFmeb7vmV9ZMjoda1B8k4e
  50. 8E0oVPgdDqCguBEP80NuosAONTib3fZ8ERmRw4HIwc9xjFDzyPpvyc25liyPKr57
  51. UDoDbO/DwhrrKGZP11JZHUn4mIAO7pniZYj/IC47aXEEuZNn95zACGMYqfn8A9+K
  52. mHIHwr4ifS+k8UmQ2ly+HX+NfKJLTIUBcQY+7w6C5CHrVBImVHzHTYLvKWGH3pmB
  53. zn8cCTgwW7mJ8bzQezt1MozCB1CYKv/SelvxisIQqyxqYB9q41g9x3hkePDRlh1s
  54. 5ycvN0axEpSgxg10bLJdkhE+CfYkuANAyjQzAksFRa1ZlMQ5I+VVpXEECTVpLyLt
  55. QQH87vtZS5xFaHUQnArXtZFu1WC0gZvMkNkJofv3GowNfanZb8iNtNFE8r1+GjL7
  56. a9NhaD8She0z2xQ4eZm8+Mtpz9ap/F7RLa9YgnJth5bDwLlAe30lg+7WIZHilR09
  57. UBHapoYlLB3B6RF51wWVneIlnTpMIJeP9vOGFBUqZ+W1j3O3uoLij1FUuwARAQAB
  58. tDZDb3JlT1MgQnVpbGRib3QgKE9mZmljYWwgQnVpbGRzKSA8YnVpbGRib3RAY29y
  59. ZW9zLmNvbT6JAjkEEwECACMFAlIqVhQCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIe
  60. AQIXgAAKCRBQ4IhVk9LctFkGD/46/I3S392oQQs81pUOMbPulCitA7/ehYPuVlgy
  61. mv6+SEZOtafEJuI9uiTzlAVremZfalyL20RBtU10ANJfejp14rOpMadlRqz0DCvc
  62. Wuuhhn9FEQE59Yk3LQ7DBLLbeJwUvEAtEEXq8xVXWh4OWgDiP5/3oALkJ4Lb3sFx
  63. KwMy2JjkImr1XgMY7M2UVIomiSFD7v0H5Xjxaow/R6twttESyoO7TSI6eVyVgkWk
  64. GjOSVK5MZOZlux7hW+uSbyUGPoYrfF6TKM9+UvBqxWzz9GBG44AjcViuOn9eH/kF
  65. NoOAwzLcL0wjKs9lN1G4mhYALgzQx/2ZH5XO0IbfAx5Z0ZOgXk25gJajLTiqtOkM
  66. E6u691Dx4c87kST2g7Cp3JMCC+cqG37xilbV4u03PD0izNBt/FLaTeddNpPJyttz
  67. gYqeoSv2xCYC8AM9N73Yp1nT1G1rnCpe5Jct8Mwq7j8rQWIBArt3lt6mYFNjuNpg
  68. om+rZstK8Ut1c8vOhSwz7Qza+3YaaNjLwaxe52RZ5svt6sCfIVO2sKHf3iO3aLzZ
  69. 5KrCLZ/8tJtVxlhxRh0TqJVqFvOneP7TxkZs9DkU5uq5lHc9FWObPfbW5lhrU36K
  70. Pf5pn0XomaWqge+GCBCgF369ibWbUAyGPqYj5wr/jwmG6nedMiqcOwpeBljpDF1i
  71. d9zMN4kCHAQQAQIABgUCUipXUQAKCRDAr7X91+bcxwvZD/0T4mVRyAp8+EhCta6f
  72. Qnoiqc49oHhnKsoN7wDg45NRlQP84rH1knn4/nSpUzrB29bhY8OgAiXXMHVcS+Uk
  73. hUsF0sHNlnunbY0GEuIziqnrjEisb1cdIGyfsWUPc/4+inzu31J1n3iQyxdOOkrA
  74. ddd0iQxPtyEjwevAfptGUeAGvtFXP374XsEo2fbd+xHMdV1YkMImLGx0guOK8tgp
  75. +ht7cyHkfsyymrCV/WGaTdGMwtoJOxNZyaS6l0ccneW4UhORda2wwD0mOHHk2EHG
  76. dJuEN4SRSoXQ0zjXvFr/u3k7Qww11xU0V4c6ZPl0Rd/ziqbiDImlyODCx6KUlmJb
  77. k4l77XhHezWD0l3ZwodCV0xSgkOKLkudtgHPOBgHnJSL0vy7Ts6UzM/QLX5GR7uj
  78. do7P/v0FrhXB+bMKvB/fMVHsKQNqPepigfrJ4+dZki7qtpx0iXFOfazYUB4CeMHC
  79. 0gGIiBjQxKorzzcc5DVaVaGmmkYoBpxZeUsAD3YNFr6AVm3AGGZO4JahEOsul2FF
  80. V6B0BiSwhg1SnZzBjkCcTCPURFm82aYsFuwWwqwizObZZNDC/DcFuuAuuEaarhO9
  81. BGzShpdbM3Phb4tjKKEJ9Sps6FBC2Cf/1pmPyOWZToMXex5ZKB0XHGCI0DFlB4Tn
  82. in95D/b2+nYGUehmneuAmgde87kCDQRSKlZGARAAuMYYnu48l3AvE8ZpTN6uXSt2
  83. RrXnOr9oEah6hw1fn9KYKVJi0ZGJHzQOeAHHO/3BKYPFZNoUoNOU6VR/KAn7gon1
  84. wkUwk9Tn0AXVIQ7wMFJNLvcinoTkLBT5tqcAz5MvAoI9sivAM0Rm2BgeujdHjRS+
  85. UQKq/EZtpnodeQKE8+pwe3zdf6A9FZY2pnBs0PxKJ0NZ1rZeAW9w+2WdbyrkWxUv
  86. jYWMSzTUkWK6533PVi7RcdRmWrDMNVR/X1PfqqAIzQkQ8oGcXtRpYjFL30Z/LhKe
  87. c9Awfm57rkZk2EMduIB/Y5VYqnOsmKgUghXjOo6JOcanQZ4sHAyQrB2Yd6UgdAfz
  88. qa7AWNIAljSGy6/CfJAoVIgl1revG7GCsRD5Dr/+BLyauwZ/YtTH9mGDtg6hy/So
  89. zzDAM8+79Y8VMBUtj64GQBgg2+0MVZYNsZCN209X+EGpGUmAGEFQLGLHwFoNlwwL
  90. 1Uj+/5NTAhp2MQA/XRDTVx1nm8MZZXUOu6NTCUXtUmgTQuQEsKCosQzBuT/G+8Ia
  91. R5jBVZ38/NJgLw+YcRPNVo2S2XSh7liw+Sl1sdjEW1nWQHotDAzd2MFG++KVbxwb
  92. cXbDgJOB0+N0c362WQ7bzxpJZoaYGhNOVjVjNY8YkcOiDl0DqkCk45obz4hG2T08
  93. x0OoXN7Oby0FclbUkVsAEQEAAYkERAQYAQIADwUCUipWRgIbAgUJAeEzgAIpCRBQ
  94. 4IhVk9LctMFdIAQZAQIABgUCUipWRgAKCRClQeyydOfjYdY6D/4+PmhaiyasTHqh
  95. iui2DwDVdhwxdikQEl+KQQHtk7aqgbUAxgU1D4rbLxzXyhTbmql7D30nl+oZg0Be
  96. yl67Xo6X/wHsP44651aTbwxVT9nzhOp6OEW5z/qxJaX1B9EBsYtjGO87N854xC6a
  97. QEaGZPbNauRpcYEadkppSumBo5ujmRWc4S+H1VjQW4vGSCm9m4X7a7L7/063HJza
  98. SYaHybbu/udWW8ymzuUf/UARH4141bGnZOtIa9vIGtFl2oWJ/ViyJew9vwdMqiI6
  99. Y86ISQcGV/lL/iThNJBn+pots0CqdsoLvEZQGF3ZozWJVCKnnn/kC8NNyd7Wst9C
  100. +p7ZzN3BTz+74Te5Vde3prQPFG4ClSzwJZ/U15boIMBPtNd7pRYum2padTK9oHp1
  101. l5dI/cELluj5JXT58hs5RAn4xD5XRNb4ahtnc/wdqtle0Kr5O0qNGQ0+U6ALdy/f
  102. IVpSXihfsiy45+nPgGpfnRVmjQvIWQelI25+cvqxX1dr827ksUj4h6af/Bm9JvPG
  103. KKRhORXPe+OQM6y/ubJOpYPEq9fZxdClekjA9IXhojNA8C6QKy2Kan873XDE0H4K
  104. Y2OMTqQ1/n1A6g3qWCWph/sPdEMCsfnybDPcdPZp3psTQ8uX/vGLz0AAORapVCbp
  105. iFHbF3TduuvnKaBWXKjrr5tNY/njrU4zEADTzhgbtGW75HSGgN3wtsiieMdfbH/P
  106. f7wcC2FlbaQmevXjWI5tyx2m3ejG9gqnjRSyN5DWPq0m5AfKCY+4Glfjf01l7wR2
  107. 5oOvwL9lTtyrFE68t3pylUtIdzDz3EG0LalVYpEDyTIygzrriRsdXC+Na1KXdr5E
  108. GC0BZeG4QNS6XAsNS0/4SgT9ceA5DkgBCln58HRXabc25Tyfm2RiLQ70apWdEuoQ
  109. TBoiWoMDeDmGLlquA5J2rBZh2XNThmpKU7PJ+2g3NQQubDeUjGEa6hvDwZ3vni6V
  110. vVqsviCYJLcMHoHgJGtTTUoRO5Q6terCpRADMhQ014HYugZVBRdbbVGPo3YetrzU
  111. /BuhvvROvb5dhWVi7zBUw2hUgQ0g0OpJB2TaJizXA+jIQ/x2HiO4QSUihp4JZJrL
  112. 5G4P8dv7c7/BOqdj19VXV974RAnqDNSpuAsnmObVDO3Oy0eKj1J1eSIp5ZOA9Q3d
  113. bHinx13rh5nMVbn3FxIemTYEbUFUbqa0eB3GRFoDz4iBGR4NqwIboP317S27NLDY
  114. J8L6KmXTyNh8/Cm2l7wKlkwi3ItBGoAT+j3cOG988+3slgM9vXMaQRRQv9O1aTs1
  115. ZAai+Jq7AGjGh4ZkuG0cDZ2DuBy22XsUNboxQeHbQTsAPzQfvi+fQByUi6TzxiW0
  116. BeiJ6tEeDHDzdLkCDQRUDREaARAA+Wuzp1ANTtPGooSq4W4fVUz+mlEpDV4fzK6n
  117. HQ35qGVJgXEJVKxXy206jNHx3lro7BGcJtIXeRb+Wp1eGUghrG1+V/mKFxE4wulN
  118. tFXoTOJ//AOYkPq9FG12VGeLZDckAR4zMhDwdcwsJ208hZzBSslJOWAuZTPoWple
  119. +xie4B8jZiUcjf10XaWvBnlx4EPohhvtv5VEczZWNvGa/0VDe/FfI4qGknJM3+d0
  120. kvXK/7yaFpdGwnY3nE/V4xbwx2tggqQRXoFmYbjogGHpTcdXkWbGEz5F7mLNwzZ/
  121. voyTiZeukZP5I45CCLgiB+g2WTl8cm3gcxrnt/aZAJCAl/eclFeYQ/Xiq8sK1+U2
  122. nDEYLWRygoZACULmLPbUEVmQBOw/HAufE98sb36MHcFss634h2ijIp9/wvnX9GOE
  123. LgX4hgqkgM85QaMeaS3d2+jlMu8BdsMYxPkTumsEUShcFtAYgtrNrPSayHtV6I9I
  124. 41ISg8EIr9qEhH1xLGvSA+dfUvXqwa0cIBxhI3bXOa25vPHbT+SLtfQlvUvKySIb
  125. c6fobw2Wf1ZtM8lgFL3f/dHbT6fsvK6Jd/8iVMAZkAYFbJcivjS9/ugXbMznz5Wv
  126. g9O7hbQtXUvRjvh8+AzlASYidqSd6neW6o+i2xduUBlrbCfW6R0bPLX+7w9iqMaT
  127. 0wEQs3MAEQEAAYkERAQYAQIADwUCVA0RGgIbAgUJAeEzgAIpCRBQ4IhVk9LctMFd
  128. IAQZAQIABgUCVA0RGgAKCRClqWY15Wdu/JYcD/95hNCztDFlwzYi2p9vfaMbnWcR
  129. qzqavj21muB9vE/ybb9CQrcXd84y7oNq2zU7jOSAbT3aGloQDP9+N0YFkQoYGMRs
  130. CPiTdnF7/mJCgAnXei6SO+H6PIw9qgC4wDV0UhCiNh+CrsICFFbK+O+Jbgj+CEN8
  131. XtVhZz3UXbH/YWg/AV/XGWL1BT4bFilUdF6b2nJAtORYQFIUKwOtCAlI/ytBo34n
  132. M6lrMdMhHv4MoBHP91+Y9+t4D/80ytOgH6lq0+fznY8Tty+ODh4WNkfXwXq+0TfZ
  133. fJiZLvkoXGD+l/I+HE3gXn4MBwahQQZl8gzI9daEGqPF8KYX0xyyKGo+8yJG5/WG
  134. lfdGeKmz8rGP/Ugyo6tt8DTSSqJv6otAF/AWV1Wu/DCniehtfHYrp2EHZUlpvGRl
  135. 7Ea9D9tv9BKYm6S4+2yD5KkPu4qp3r6glVbePPCLeZ4NLQCEIpKakIERfxk66JqZ
  136. Tb5XI9HKKbnhKunOoGiL5SMXVsS67Sxt//Ta/3vSaLC3wnVwN5OeXNaa04Yx7jg/
  137. wtMJ9Jz0EYFtVv2NLizEeGCI8iPJOyMWOy+twCIk5zmvwsLu5MKmg1tLI2mtCTYz
  138. qo8uVIqETlojxIqAhRYtmeiYKf2fZs5um3+Sjv28v4nw3VfQgibTKc2uBjeqxxOe
  139. XGw0ysKnS2VO72SK879+EADd3HoF9U80odCgN5T6aljhaNaruqmG4CvBdRyzp3EQ
  140. 9RP7jPOEhcM00etw572orviK9AqCk+zwvfzEFbt/uC7zOpO0BJ8fnMAZ0Zn/fF8s
  141. 88zR4zq6BBq9WD4RCmazw2G6IyGXHvVAWi8UxoNjNoJJosLyLauFdPPUeoye5PxE
  142. g+fQew3behcCaebjZwUA+xZMj7dfwcNXlDa4VkCDHzTfU43znawBo9avB8hNwMeW
  143. CZYINmym+LSKyQnz3sirTpYcjorxtov1fyml8413tDJoOvkotSX9o3QQgbBPsyQ7
  144. nwLTscYc5eklGRH7iytXOPI+29EPpfRHX2DAnVyTeVSFPEr79tIsijy02ZBZTiKY
  145. lBlJy/Cj2C5cGhVeQ6v4jnj1Nt3sjHkZlVfmipSYVfcBoID1/4r2zHl4OFlLCjvk
  146. XUhbqhm9xWV8NdmItO3BBSlIEksFunykzz1HM6shvzw77sM5+TEtSsxoOxxys+9N
  147. ItCl8L6yf84A5333pLaUWh5HON1J+jGGbKnUzXKBsDxGSvgDcFlyVloBRQShUkv3
  148. FMem+FWqt7aA3/YFCPgyLp7818VhfM70bqIxLi0/BJHp6ltGN5EH+q7Ewz210VAB
  149. ju5IO7bjgCqTFeR3YYUN87l8ofdARx3shApXS6TkVcwaTv5eqzdFO9fZeRqHj4L9
  150. Pg==
  151. =LY4G
  152. -----END PGP PUBLIC KEY BLOCK-----
  153. "
  154.  
  155. DEVICE=""
  156. CLOUDINIT=""
  157.  
  158. while getopts "V:C:d:o:c:t:vh" OPTION
  159. do
  160.     case $OPTION in
  161.         V) VERSION_ID="$OPTARG" ;;
  162.         C) CHANNEL_ID="$OPTARG" ;;
  163.         d) DEVICE="$OPTARG" ;;
  164.         o) OEM_ID="$OPTARG" ;;
  165.         c) CLOUDINIT="$OPTARG" ;;
  166.         t) export TMPDIR="$OPTARG" ;;
  167.         v) set -x ;;
  168.         h) echo "$USAGE"; exit;;
  169.         *) exit 1;;
  170.     esac
  171. done
  172.  
  173. # Device is required, must not be a partition, must be writable
  174. if [[ -z "${DEVICE}" ]]; then
  175.     echo "$0: No target block device provided, -d is required." >&2
  176.     exit 1
  177. fi
  178.  
  179. if ! [[ $(lsblk -n -d -o TYPE "${DEVICE}") =~ ^(disk|loop|lvm)$ ]]; then
  180.     echo "$0: Target block device (${DEVICE}) is not a full disk." >&2
  181.     exit 1
  182. fi
  183.  
  184. if [[ ! -w "${DEVICE}" ]]; then
  185.     echo "$0: Target block device (${DEVICE}) is not writable (are you root?)" >&2
  186.     exit 1
  187. fi
  188.  
  189. if [[ -n "${CLOUDINIT}" && ! -f "${CLOUDINIT}" ]]; then
  190.     echo "$0: Cloud config file (${CLOUDINIT}) does not exist." >&2
  191.     exit 1
  192. fi
  193.  
  194. if [[ -n "${OEM_ID}" ]]; then
  195.     IMAGE_NAME="coreos_production_${OEM_ID}_image.bin.bz2"
  196. else
  197.     IMAGE_NAME="coreos_production_image.bin.bz2"
  198. fi
  199.  
  200. # for compatibility with old versions that didn't support channels
  201. if [[ "${VERSION_ID}" =~ ^(alpha|beta|stable)$ ]]; then
  202.     CHANNEL_ID="${VERSION_ID}"
  203.     VERSION_ID="current"
  204. fi
  205.  
  206. BASE_URL="http://${CHANNEL_ID}.release.core-os.net/amd64-usr/${VERSION_ID}"
  207. IMAGE_URL="${BASE_URL}/${IMAGE_NAME}"
  208. SIG_NAME="${IMAGE_NAME}.sig"
  209. SIG_URL="${BASE_URL}/${SIG_NAME}"
  210.  
  211. if ! wget --spider --quiet "${IMAGE_URL}"; then
  212.     echo "$0: Image URL unavailable: $IMAGE_URL" >&2
  213.     exit 1
  214. fi
  215.  
  216. if ! wget --spider --quiet "${SIG_URL}"; then
  217.     echo "$0: Image signature unavailable: $SIG_URL" >&2
  218.     exit 1
  219. fi
  220.  
  221. # Pre-flight checks pass, lets get this party started!
  222. WORKDIR=$(mktemp --tmpdir -d coreos-install.XXXXXXXXXX)
  223. trap "rm -rf '${WORKDIR}'" EXIT
  224.  
  225. # Setup GnuPG for verifying the image signature
  226. export GNUPGHOME="${WORKDIR}/gnupg"
  227. mkdir "${GNUPGHOME}"
  228. gpg --batch --quiet --import <<<"$GPG_KEY"
  229.  
  230. echo "Downloading the signature for ${IMAGE_URL}..."
  231. wget --no-verbose -O "${WORKDIR}/${SIG_NAME}" "${SIG_URL}"
  232.  
  233. echo "Downloading, writing and verifying ${IMAGE_NAME}..."
  234. declare -a EEND
  235. if ! wget --no-verbose -O - "${IMAGE_URL}" \
  236.     | tee >(bunzip2 --stdout >"${DEVICE}") \
  237.     | gpg --batch --trusted-key "${GPG_LONG_ID}" \
  238.         --verify "${WORKDIR}/${SIG_NAME}" -
  239. then
  240.     EEND=(${PIPESTATUS[@]})
  241.     [ ${EEND[0]} -ne 0 ] && echo "${EEND[0]}: Download of ${IMAGE_NAME} did not complete" >&2
  242.     [ ${EEND[1]} -ne 0 ] && echo "${EEND[1]}: Cannot expand ${IMAGE_NAME} to ${DEVICE}" >&2
  243.     [ ${EEND[2]} -ne 0 ] && echo "${EEND[2]}: GPG signature verification failed for ${IMAGE_NAME}" >&2
  244.     wipefs --all --backup "${DEVICE}"
  245.     exit 1
  246. fi
  247.  
  248. # inform the OS of partition table changes
  249. blockdev --rereadpt "${DEVICE}"
  250.  
  251. if [[ -n "${CLOUDINIT}" ]]; then
  252.     # The ROOT partition should be #9 but make no assumptions here!
  253.     # Also don't mount by label directly in case other devices conflict.
  254.     ROOT_DEV=$(blkid -t "LABEL=ROOT" -o device "${DEVICE}"*)
  255.  
  256.     if [[ -z "${ROOT_DEV}" ]]; then
  257.         echo "Unable to find new ROOT partition on ${DEVICE}" >&2
  258.         exit 1
  259.     fi
  260.  
  261.     echo "Installing cloud-config..."
  262.     mkdir -p "${WORKDIR}/rootfs"
  263.     mount -t btrfs -o subvol=root "${ROOT_DEV}" "${WORKDIR}/rootfs"
  264.     trap "umount '${WORKDIR}/rootfs' && rm -rf '${WORKDIR}'" EXIT
  265.  
  266.     mkdir -p "${WORKDIR}/rootfs/var/lib/coreos-install"
  267.     cp "${CLOUDINIT}" "${WORKDIR}/rootfs/var/lib/coreos-install/user_data"
  268.  
  269.     umount "${WORKDIR}/rootfs"
  270. fi
  271.  
  272. rm -rf "${WORKDIR}"
  273. trap - EXIT
  274.  
  275. echo "Success! CoreOS ${CHANNEL_ID} ${VERSION_ID}${OEM_ID:+ (${OEM_ID})} is installed on ${DEVICE}"

Reply to "CoreOS Installation Script"

Here you can reply to the paste above

captcha