├── README.md └── dhyve /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED 2 | This project has been deprecated. I'm only maintaining [dlite](https://github.com/nlf/dlite). If you want something like dhyve, look at docker-machine or [corectl](https://github.com/TheNewNormal/corectl). 3 | 4 | A little helper script to make running boot2docker inside of xhyve simple 5 | 6 | Installation 7 | ------------ 8 | 9 | ```bash 10 | brew tap nlf/dhyve 11 | brew install --HEAD dhyve 12 | ``` 13 | 14 | Usage 15 | ----- 16 | 17 | ```bash 18 | dhyve init 19 | dhyve up 20 | eval "$(dhyve env)" 21 | ``` 22 | 23 | And now you can use docker like normal. 24 | 25 | Ports exposed from a docker container will be available on the vm, which will have an entry in `/etc/hosts` as `dhyve.vm` to make it easier to access. 26 | 27 | An NFS share is created on your host to expose the `/Users` directory to the vm network as well. It will *only* allow connections from the dhyve vm, so should be reasonably secure. 28 | 29 | If you need to access the virtual machine directly, an entry will be added to `/etc/hosts` with the name `dhyve.vm`. In addition to that, a configuration section will be added to `~/.ssh/config` so that you can use `ssh dhyve.vm`. 30 | 31 | See `dhyve help` for more usage information. 32 | 33 | 34 | CREDITS 35 | ------- 36 | 37 | This project draws code and inspiration from prior works. Thank you! 38 | 39 | - [ailispaw/boot2docker-xhyve](https://github.com/ailispaw/boot2docker-xhyve) 40 | - [boot2docker/boot2docker](https://github.com/boot2docker/boot2docker) 41 | -------------------------------------------------------------------------------- /dhyve: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | version="v2.0.0" 3 | 4 | print_help() { 5 | echo "Usage: dhyve [options]" 6 | echo "" 7 | echo " available commands:" 8 | echo " init: initialize a new vm" 9 | echo " destroy: destroy an existing vm" 10 | echo " up: start the dhyve vm" 11 | echo " down: stop the dhyve vm" 12 | echo " status: print the vm's status" 13 | echo " env: output environment variables to use docker with the dhyve vm" 14 | echo " to use, eval \"\$(dhyve env)\"" 15 | echo " upgrade: upgrade your vm to the newest image" 16 | echo "" 17 | echo " available options for init command" 18 | echo " -n: name of the vm to create [dhyve]" 19 | echo " -m: amount of memory to allocate to the vm [2G]" 20 | echo " -d: size of disk to create, in gigabytes [10]" 21 | echo " -c: number of cpus to assign [1]" 22 | return 1 23 | } 24 | 25 | get_ip() { 26 | local vm=$1 27 | local uuid=$(config get uuid) 28 | awk ' 29 | { 30 | if ($1 ~ /^name/) { 31 | name=substr($1, 6) 32 | } 33 | if ($1 ~ /^ip_address/) { 34 | ip_address=substr($1, 12) 35 | } 36 | if (name != "" && ip_address != "") { 37 | ip_addresses[name]=ip_address 38 | name=ip_address="" 39 | } 40 | } 41 | END { 42 | print ip_addresses["'$uuid'"] 43 | } 44 | ' /var/db/dhcpd_leases 45 | } 46 | 47 | config() { 48 | local cmd=$1; shift 49 | local key=$1; shift 50 | local file="$HOME/.dhyve/vm.cfg" 51 | [ ! -e "$file" ] && touch "$file" 52 | 53 | case "$cmd" in 54 | get) 55 | local value=$(/usr/bin/sed -n "s/^$key[ ]*=[ ]*\(.*\)/\1/p" "$file") 56 | [ -z "$value" ] && return 1 57 | echo "$value" 58 | ;; 59 | set) 60 | local value=$(/usr/bin/sed -n "s/^$key[ ]*=[ ]*\(.*\)/\1/p" "$file") 61 | if [ -z "$value" ]; then 62 | echo "$key=$@" >> "$file" 63 | else 64 | /usr/bin/sed -i '' "s/^$key[ ]*=[ ]*.*/$key=$@/g" "$file" 65 | fi 66 | ;; 67 | del) 68 | /usr/bin/sed -i '' "/^$key[ ]*=[ ]*.*/d" "$file" 69 | ;; 70 | esac 71 | } 72 | 73 | # this function copied from https://github.com/ailispaw/boot2docker-xhyve and modified lightly 74 | set_exports() { 75 | local vmnet="/Library/Preferences/SystemConfiguration/com.apple.vmnet" 76 | [ ! -e "${vmnet}.plist" ] && return 1 77 | 78 | local addr=$(defaults read ${vmnet} Shared_Net_Address) 79 | local mask=$(defaults read ${vmnet} Shared_Net_Mask) 80 | 81 | function ip2num() { 82 | local IFS=. 83 | local ip=($1) 84 | printf "%s\n" $(( (${ip[0]} << 24) | (${ip[1]} << 16) | (${ip[2]} << 8) | ${ip[3]} )) 85 | } 86 | 87 | function num2ip() { 88 | local n=$1 89 | printf "%d.%d.%d.%d\n" \ 90 | $(( $n >> 24 )) $(( ($n >> 16) & 0xFF )) $(( ($n >> 8) & 0xFF )) $(( $n & 0xFF )) 91 | } 92 | 93 | local num=$(( $(ip2num ${addr}) & $(ip2num ${mask}) )) 94 | local net=$(num2ip ${num}) 95 | 96 | local exports="/Users -network ${net} -mask ${mask} -alldirs -mapall=$(id -u $SUDO_USER):$(id -g $SUDO_USER)" 97 | 98 | touch /etc/exports 99 | grep -q "$exports" /etc/exports 100 | if [ $? -ne 0 ]; then 101 | echo "$exports" >> /etc/exports 102 | nfsd restart >/dev/null 2>&1 103 | fi 104 | } 105 | 106 | get_release() { 107 | local repo=$1 108 | local token=$(config get github_token) 109 | local args 110 | if [ $? -eq 0 ]; then 111 | args="-H \"Authorization: token $token\"" 112 | elif [ -n "$HOMEBREW_GITHUB_API_TOKEN" ]; then 113 | args="-H \"Authorization: token $HOMEBREW_GITHUB_API_TOKEN\"" 114 | fi 115 | 116 | local version=$(curl -s "$args" https://api.github.com/repos/$repo/releases/latest | awk -F'"' '/tag_name/ { print $4 }') 117 | if [ -z "$version" ]; then 118 | echo "[dhyve] failed to retrieve latest version information" 119 | exit 1 120 | fi 121 | 122 | echo $version 123 | } 124 | 125 | enforce_root() { 126 | local cond=$1; shift 127 | 128 | if ! eval "$cond"; then 129 | echo "[dhyve] this command requires root, restarting with sudo.." 130 | sudo "$0" "$@" 131 | exit $? 132 | fi 133 | } 134 | 135 | check_vm() { 136 | local expected=$1 # 0 = vm doesn't exist, 1 = vm is stopped, 2 = vm is running, -1 = don't care, just return the status 137 | local message=$2 138 | local status=0 139 | 140 | if [ -e "$HOME/.dhyve" -a -e "$HOME/.dhyve/vm.cfg" ]; then 141 | local pid; pid=$(config get pid) 142 | if [ $? != 0 ]; then 143 | status=1 144 | else 145 | ps $pid >/dev/null 2>&1 146 | if [ $? != 0 ]; then 147 | config del pid 148 | status=1 149 | else 150 | status=2 151 | fi 152 | fi 153 | fi 154 | 155 | [ "$expected" == "$status" ] && return $status 156 | 157 | if [ "$status" -eq 0 ]; then 158 | [ "$expected" -eq 0 ] && return $status 159 | echo "[dhyve] vm is missing, try running 'dhyve init' first" 160 | else 161 | [ "$expected" -eq -1 ] && return $status 162 | echo "[dhyve] $message" 163 | fi 164 | exit 1 165 | } 166 | 167 | download_dhyveos() { 168 | local version=$1 169 | 170 | curl -Lso "$HOME/.dhyve/bzImage" "https://github.com/nlf/dhyve-os/releases/download/$version/bzImage" 171 | curl -Lso "$HOME/.dhyve/rootfs.cpio.xz" "https://github.com/nlf/dhyve-os/releases/download/$version/rootfs.cpio.xz" 172 | config set version "$version" 173 | } 174 | 175 | upgrade_dhyveos() { 176 | check_vm 1 "is currently running, please stop it first" 177 | local version; version=$(get_release nlf/dhyve-os) 178 | if [ $? != 0 ]; then 179 | echo "$version" 180 | return 1 181 | fi 182 | 183 | if [ "$(config get version)" != "$version" ]; then 184 | echo -n "[dhyve] upgrading vm to version $version.." 185 | download_dhyveos "$version" 186 | echo "done" 187 | else 188 | echo "[dhyve] latest vm version ($version) already installed" 189 | fi 190 | } 191 | 192 | vm_init() { 193 | if [ -z "$(which xhyve)" ]; then 194 | if [ -z "$(which brew)" ]; then 195 | echo "[dhyve] xhyve is required, but homebrew is missing. please either install homebrew http://brew.sh or install xhyve manually" 196 | exit 1 197 | fi 198 | 199 | echo "[dhyve] installing xhyve with homebrew.." 200 | brew update 201 | brew install xhyve 202 | fi 203 | 204 | check_vm 0 "it looks like a vm already exists, use 'dhyve destroy' to remove it and try again" 205 | mkdir -p $HOME/.dhyve 206 | 207 | local name=dhyve 208 | local mem=2G 209 | local disk=10 210 | local cpu=1 211 | 212 | while getopts "m:d:c:n:" opt; do 213 | case $opt in 214 | m) 215 | mem=$(echo $OPTARG | tr '[:lower:]' '[:upper:]') 216 | ;; 217 | d) 218 | disk=$OPTARG 219 | ;; 220 | c) 221 | cpu=$OPTARG 222 | ;; 223 | n) 224 | name=$OPTARG 225 | ;; 226 | :) 227 | echo "[dhyve] -$OPTARG requires an argument." 228 | exit 1 229 | esac 230 | done 231 | 232 | echo -n "[dhyve] saving options.." 233 | local uuid="2996454B-06DE-43B0-9FC6-9E9F38D1DCAE" # $(uuidgen) 234 | config set uuid "$uuid" 235 | config set name "$name" 236 | local args="-A -m $mem -c $cpu -s 0:0,hostbridge -l com1,stdio -s 31,lpc -s 2:0,virtio-net -s 4,virtio-blk,$HOME/.dhyve/disk.img -U $uuid -f kexec,$HOME/.dhyve/bzImage,$HOME/.dhyve/rootfs.cpio.xz" 237 | local kargs="console=ttyS0 hostname=$name uuid=$uuid" 238 | config set args "$args" 239 | config set kargs "$kargs" 240 | echo "done" 241 | 242 | echo -n "[dhyve] generating a new ssh key.." 243 | ssh-keygen -t rsa -b 2048 -P "" -f "$HOME/.dhyve/key" >/dev/null 2>&1 244 | echo "done" 245 | 246 | echo -n "[dhyve] generating a disk image.." 247 | local tempdir=$(/usr/bin/mktemp -d -t dhyve) 248 | echo "dhyve, please format-me" > "$tempdir/dhyve, please format-me" 249 | mkdir -p "$tempdir/.ssh" 250 | cp "$HOME/.dhyve/key.pub" "$tempdir/.ssh/authorized_keys" 251 | tar cf "$HOME/.dhyve/disk.img" --directory="$tempdir" "dhyve, please format-me" ".ssh/authorized_keys" 252 | echo -n "." 253 | dd if=/dev/zero bs=1g count=$disk >> "$HOME/.dhyve/disk.img" 2>/dev/null 254 | echo "done" 255 | 256 | local version; version=$(get_release nlf/dhyve-os) 257 | if [ $? != 0 ]; then 258 | echo "$version" 259 | return 1 260 | fi 261 | 262 | echo -n "[dhyve] downloading vm version $version.." 263 | download_dhyveos "$version" 264 | echo "done" 265 | 266 | echo "[dhyve] finished creating vm, to start it run 'dhyve up'" 267 | } 268 | 269 | vm_destroy() { 270 | check_vm 1 "is running, you should stop it before attempting to destroy it" 271 | enforce_root '[ -w "$HOME/.dhyve" ]' destroy $@ 272 | 273 | echo -n "[dhyve] destroying vm.." 274 | rm -rf "$HOME/.dhyve" 275 | echo "done" 276 | } 277 | 278 | register_vm() { 279 | local name=$(config get name) 280 | local ip=$(get_ip $name) 281 | /usr/bin/sed -i '' "s/.* $name.vm$/$ip $name.vm/g" /etc/hosts 282 | grep -q "$ip $name.vm" /etc/hosts || echo "$ip $name.vm" >> /etc/hosts 283 | 284 | [ ! -e "$HOME/.ssh/config" ] && touch "$HOME/.ssh/config" 285 | grep -q "Host $name.vm" "$HOME/.ssh/config" 286 | if [ $? != 0 ]; then 287 | echo "Host $name.vm" >> "$HOME/.ssh/config" 288 | echo " User docker" >> "$HOME/.ssh/config" 289 | echo " IdentityFile $HOME/.dhyve/key" >> "$HOME/.ssh/config" 290 | echo " StrictHostKeyChecking no" >> "$HOME/.ssh/config" 291 | echo " UserKnownHostsFile /dev/null" >> "$HOME/.ssh/config" 292 | fi 293 | } 294 | 295 | vm_up() { 296 | check_vm 1 "is already running" 297 | enforce_root '[ "$USER" == "root" ]' up $@ 298 | 299 | local nfs_fail 300 | echo -n "[dhyve] starting.." 301 | set_exports 302 | [ $? -eq 1 ] && nfs_fail=1 303 | local args=$(config get args) 304 | local kargs=$(config get kargs) 305 | nohup xhyve $args,"$kargs" "$HOME/.dhyve/vm.log" 2>&1 & 306 | config set pid "$!" 307 | local name=$(config get name) 308 | until ping -c 1 -t 1 $(get_ip $name) >/dev/null 2>&1; do 309 | echo -n "." 310 | done 311 | register_vm 312 | echo "done" 313 | [ "$nfs_fail" == "1" ] && echo "[dhyve] your vm has started, however since this is your first start we were unable to configure nfs shares. please run 'dhyve down' and then 'dhyve up' to restart the vm and enable the nfs share" 314 | chown -R $SUDO_USER "$HOME/.dhyve" 315 | } 316 | 317 | vm_down() { 318 | check_vm 2 "is not running" 319 | enforce_root '[ "$USER" == "root" ]' down "$@" 320 | 321 | echo -n "[dhyve] stopping.." 322 | local pid=$(config get pid) 323 | kill $pid 324 | while ps $pid >/dev/null; do 325 | echo -n "." 326 | sleep 1 327 | done 328 | config del pid 329 | echo "done" 330 | } 331 | 332 | vm_status() { 333 | check_vm -1 334 | if [ $? == 2 ]; then 335 | status="running" 336 | else 337 | status="stopped" 338 | fi 339 | 340 | local name=$(config get name) 341 | echo "[dhyve] status: $status" 342 | echo " name: $name" 343 | if [ "$status" == "running" ]; then 344 | echo " pid: $(config get pid)" 345 | echo " ip: $(get_ip "$name") [$name.vm]" 346 | fi 347 | echo " disk: $(du -h "$HOME/.dhyve/disk.img")" 348 | echo " args: $(config get args)" 349 | echo " kargs: $(config get kargs)" 350 | echo " versions:" 351 | echo " dhyve: $version" 352 | echo " vm: $(config get version)" 353 | } 354 | 355 | vm_env() { 356 | local name=$(config get name) 357 | local ip=$(get_ip $name) 358 | if [ -z "$ip" ]; then 359 | echo "[dhyve] has not been started yet, please run 'dhyve up'" >&2 360 | return 1 361 | fi 362 | 363 | echo "unset DOCKER_TLS_VERIFY" 364 | echo "unset DOCKER_CERT_PATH" 365 | echo "export DOCKER_HOST=tcp://$ip:2375" 366 | } 367 | 368 | cmd=$1 369 | shift 370 | case "$cmd" in 371 | init) 372 | vm_init "$@" 373 | ;; 374 | destroy) 375 | vm_destroy 376 | ;; 377 | up|start) 378 | vm_up 379 | ;; 380 | down|stop) 381 | vm_down 382 | ;; 383 | status) 384 | vm_status 385 | ;; 386 | env) 387 | vm_env 388 | ;; 389 | upgrade) 390 | upgrade_dhyveos 391 | ;; 392 | *) 393 | print_help 394 | ;; 395 | esac 396 | --------------------------------------------------------------------------------