diff --git a/.test/Dockerfile b/.test/Dockerfile index 855ed54..ce99070 100644 --- a/.test/Dockerfile +++ b/.test/Dockerfile @@ -1,7 +1,7 @@ FROM alpine:3.1 RUN apk update \ - && apk add openssh git \ + && apk add openssh git socat\ && addgroup -S git && adduser -D -S git -G git -s /bin/sh \ && mkdir -p /home/git/.ssh \ && chmod 0700 /home/git/.ssh \ diff --git a/.test/tests/86_recipe_simple_app b/.test/tests/86_recipe_simple_app index 867e95a..970f963 100755 --- a/.test/tests/86_recipe_simple_app +++ b/.test/tests/86_recipe_simple_app @@ -4,11 +4,11 @@ test -d app && rm -rf app mkdir app cd app cp ../podi . -./podi init_localhost -cp ../recipe/app/simple .pod/. -cp ../recipe/start/envfile .pod/. +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +./podi recipe run/baremetal +./podi init_runtime ./podi init git@localhost:/home/git/app5 master 2222 -./podi envset git@localhost app5 PORT=9000 +./podi envset git@localhost app5 PORT=9999 git add .pod app .env git commit -m "recipe app/simple" git push app5 master diff --git a/.test/tests/86_recipe_simple_app_webcli b/.test/tests/86_recipe_simple_app_webcli new file mode 100755 index 0000000..3ed198f --- /dev/null +++ b/.test/tests/86_recipe_simple_app_webcli @@ -0,0 +1,15 @@ +.test/test describe $0 + +test -d app && rm -rf app +mkdir app +cd app +cp ../podi . +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +set -x +./podi recipe run/baremetal_webcli +./podi init_runtime +./podi init git@localhost:/home/git/app5 master 2222 +./podi envset git@localhost app5 PORT=9999 +git add .pod app .env +git commit -m "recipe app/simple" +git push app5 master diff --git a/.test/tests/87_recipe_simple_app_container b/.test/tests/87_recipe_simple_app_container index a49a974..1e05444 100755 --- a/.test/tests/87_recipe_simple_app_container +++ b/.test/tests/87_recipe_simple_app_container @@ -1,17 +1,18 @@ .test/test describe $0 +HOST=git@server.app.isvery.ninja +GITPATH=/home/git/test +PORT=222 +ssh -p $PORT $HOST rm -rf /home/git/test test -d app && rm -rf app mkdir app cd app cp ../podi . -./podi init_localhost -echo -cp ../recipe/app/container .pod/. -cp ../recipe/start/envfile .pod/. -set -x -./podi init git@localhost:/home/git/app5 master 2222 -echo +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +./podi recipe run/container +./podi init_runtime +./podi init $HOST:$GITPATH master $PORT +./podi envset $HOST test PORT=9999 git add .pod app .env -git commit -m "recipe app/simple" &>/dev/null -git push app5 master -./podi envset git@localhost app5 PORT=9999 +git commit -m "recipe app/simple" +git push test master diff --git a/.test/tests/87_recipe_simple_app_container_webcli b/.test/tests/87_recipe_simple_app_container_webcli new file mode 100755 index 0000000..ca2e715 --- /dev/null +++ b/.test/tests/87_recipe_simple_app_container_webcli @@ -0,0 +1,18 @@ +.test/test describe $0 + +HOST=git@server.app.isvery.ninja +GITPATH=/home/git/test +PORT=222 +ssh -p $PORT $HOST rm -rf /home/git/test +test -d app && rm -rf app +mkdir app +cd app +cp ../podi . +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +./podi recipe run/container_webcli +./podi init_runtime +./podi init $HOST:$GITPATH master $PORT +./podi envset $HOST test PORT=9999 +git add .pod app .env +git commit -m "recipe app/simple" +git push test master diff --git a/.test/tests/88_recipe_simple_app_autosuspend b/.test/tests/88_recipe_simple_app_autosuspend new file mode 100755 index 0000000..da9fd9b --- /dev/null +++ b/.test/tests/88_recipe_simple_app_autosuspend @@ -0,0 +1,14 @@ +.test/test describe $0 + +test -d app && rm -rf app +mkdir app +cd app +cp ../podi . +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +./podi recipe run/baremetal_autosuspend +./podi init_runtime +./podi init git@localhost:/home/git/app5 master 2222 +./podi envset git@localhost app5 PORT=9999 +git add .pod app .env +git commit -m "recipe app/simple" +git push -f app5 master diff --git a/.test/tests/89_recipe_simple_app_container_autosuspend b/.test/tests/89_recipe_simple_app_container_autosuspend new file mode 100755 index 0000000..5f30083 --- /dev/null +++ b/.test/tests/89_recipe_simple_app_container_autosuspend @@ -0,0 +1,19 @@ +.test/test describe $0 + +HOST=git@server.app.isvery.ninja +GITPATH=/home/git/test +PORT=222 +set -x +ssh -p $PORT $HOST rm -rf /home/git/test +test -d app && rm -rf app +mkdir app +cd app +cp ../podi . +export RECIPE_REPOS=http://localhost:8081/recipe/index.txt +./podi recipe run/container_autosuspend +./podi init_runtime +./podi init $HOST:$GITPATH master $PORT +./podi envset $HOST test PORT=9998 +git add .pod app .env +git commit -m "recipe app/simple" +git push test master diff --git a/README.md b/README.md index 2686ce7..9abc341 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,6 @@ $ cd myapp $ wget "https://raw.githubusercontent.com/coderofsalvation/podi/master/podi" $ chmod 755 podi -$ ./podi recipe app/container # a template container recipe -$ ./podi recipe start/envfile # cli-cmd to set remote env-vars ``` > PROFIT! now init your (ssh)server to enable a heroku-ish workflow: @@ -19,11 +17,10 @@ $ ./podi recipe start/envfile # cli-cmd to set remote env-vars ## Features * fully hackable PaaS & Gitops-designer (embedded in your repo) -* per-branch and per-sshuser deployments +* multitenant: multi-branch and multi-sshuser deployments +* `podi ls`: gitops templates for containerizing, autosuspending services on baremetal etc * hookable (`on build callmyfunction arg1`) -* app templates -* autosuspending app templates -* podi weighs 5k, just needs ssh+git installed +* podi weighs ~7k, just needs ssh+git installed * works on raspberry pi zero but also on kubernetes ## Create recipes diff --git a/app/app b/app/app deleted file mode 100755 index 6136a92..0000000 --- a/app/app +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -d=-d -test -z $PODI_GITPUSH && { PODI_APP=$(basename $(pwd)); f="-f"; d=""; } - -IMAGE=docker.io/coderofsalvation/redbean:1.5 -CMD="/redbean.com -D . -p 80 -vv" -POD=$(which podman || which docker) - -test -f Dockerfile && IMAGE=$PODI_APP -set -x -$POD run $d --rm --volume $(pwd):/home/app -w /home/app -e PORT -p $PORT:80 --name $PODI_APP $IMAGE $CMD -set +x; -$POD logs $f $PODI_APP 2>&1 | awk "{print \"[app.log] \"\$0 }" diff --git a/podi b/podi index 8bed048..1dcaaf8 100755 --- a/podi +++ b/podi @@ -1,8 +1,8 @@ #!/bin/sh set -e required="awk ssh git hostname basename" -test -z $HOST && host=$USER@$(hostname) || host=$HOST -RECIPE_REPOS="https://raw.githubusercontent.com/coderofsalvation/podi/master/recipe/.index.txt" +test -z $HOST && host=$USER@$(hostname) || host=$HOST +test -z "$RECIPE_REPOS" && RECIPE_REPOS="https://raw.githubusercontent.com/coderofsalvation/podi/master/recipe/index.txt" #trap "trigger cleanup" 0 1 2 3 6 C_GREY="\\033[1;30m" C_BOLD="\\033[1;37;40m" @@ -12,12 +12,14 @@ C_PURPLE="\\033[38;5;207m" C_RED="\\033[0;31m" # some hipster wrappers to make things readable and sexy later on -extract() { awk '/^# '$1':/ { gsub("^# '$1': ","",$0); print $0 }' $0; } try() { set +e; "$@"; set -e; return 0; } -silent() { "$@" 1>/dev/null 2>/dev/null; } +silent() { "$@" 1>/dev/null 2>/dev/null; return $?; } installed() { which $1 2>/dev/null 1>/dev/null; } +verbose() { printf " $C_BOLD\$$C_NORMAL $*\n"; "$@"; } +prompt() { printf " [?] $C_CYAN$1$C_NORMAL"; printf "\n$2\n $3> "; read answer; } error() { printf " [$C_RED"e"$C_NORMAL] %s\n" "$*"; exit 1; } -print() { printf " $C_PURPLE│ $C_NORMAL %s\n" "$*"; } +print() { printf " $C_PURPLE│ $C_NORMAL %s\n" "$*"; return 0; } +soften() { cat | while read line; do printf " $C_PURPLE| $C_GREY%s$C_NORMAL\n" "$line"; done; } header() { h=$1; shift; printf " $C_PURPLE├─ $C_CYAN""%s""$C_PURPLE $C_NORMAL%s\n" "$h" "$*"; } evalfunc() { type $1 | awk 'NR>2 && /^[^{][^}]/ { print $0 }'; } foreach() { local err=0; local args="$1"; @@ -25,36 +27,30 @@ foreach() { local err=0; local args="$1"; for j in $args; do "$@" "$j" || err=1; done test $err = 1 && return 1 || return 0 } -trigger() { printf "$C_BOLD%-25s $C_NORMAL[$C_CYAN✓$C_NORMAL] ($C_GREY%s)$C_NORMAL\n" "$(test $host = $USER@$(hostname) && printf $1 || printf " | $1")" $host 1>&2 +run_yaml() { awk ' + BEGIN{ file=""; buffer="" } + /^[a-zA-Z0-9_]+: |-/ { gsub(":","",$1); file=$1 ; buffer = "" } + / .*/ { gsub(" ","",$0); buffer=buffer$0"\n" } + /^$/ { print "writing "file; print buffer > file } + ' $1 + } +trigger() { printf "$C_NORMAL [$C_CYAN✓$C_NORMAL] $C_BOLD%s$C_NORMAL\n" "$1" local cmd=$1; shift local actions="$(eval echo \$on_$cmd)" if test -n "$actions"; then silent try type $cmd && { $cmd "$@"; } for it in $actions; do trigger $it "$@"; done else - set +e silent type $cmd || return 0; silent type $cmd && $cmd "$@"; - set -e fi + return $? } on() { export on_$1="$2 $(eval echo \$on_$1)"; } -# pipeline: deploy(){ -# pipeline: trigger hello -# pipeline: trigger backup -# pipeline: trigger checkout -# pipeline: trigger build -# pipeline: trigger runtests -# pipeline: trigger start -# pipeline: } -# pipeline: -# pipeline: checkout(){ -# pipeline: git --work-tree=$(pwd) --git-dir=$(pwd)/.git checkout -f -# pipeline: } - init_post_receive(){ echo "#!/bin/sh + export PODI_GITPUSH=1 export PODI_REMOTE=$2 export PODI_USER=$3 export PODI_PORT=$4 @@ -100,10 +96,17 @@ init(){ # init git@server:/dir/to/deploy [branch] [port] [name] : initializes a } init_localhost(){ - test -d ~/.pod || { mkdir -p ~/.pod && extract config > ~/.pod/config; } - test -d .pod || { - mkdir -p .pod && extract pipeline > .pod/pipeline; - test -d .pod && for i in .pod/* ; do . $i; done + test -d .git || git init + test -f .pod/pipeline || { + test -d .pod || mkdir -p .pod + test -d .pod/run || { + prompt "how to run this app?" "$(recipe | grep '^run/' | cat -n)" + runtime=$(recipe | grep '^run/' | awk "NR == $answer { print \$0 }") + silent recipe $runtime && . .pod/$runtime + init_runtime + } + test -f .pod/pipeline || recipe pipeline + test -d .pod && for i in .pod/* ; do . $i; done test -f podi || cp $0 . } } @@ -126,7 +129,7 @@ init_server(){ echo "export appname='$appname' " >> $config echo "export branch='$branch' " >> $config . $config - ssh -p $port $user@$server HOST=$user@$server mkdir $gitpath + try silent ssh -p $port $user@$server HOST=$user@$server mkdir $gitpath scp -r -P $port $0 .pod $user@$server:$gitpath/. 1>/dev/null try ssh -p $port $user@$server "cd $gitpath; ./podi init_gitops $gitpath $server $user $port" set +e @@ -146,7 +149,7 @@ init_gitops(){ test -d $1/.git || { silent git init --bare "$1/.git" || error could not create $1/.git } - trigger init_post_receive $1 $2 $3 $4 > $1/.git/hooks/post-receive + init_post_receive $1 $2 $3 $4 > $1/.git/hooks/post-receive chmod +x $1/.git/hooks/post-receive } @@ -154,21 +157,21 @@ recipe(){ # recipe : installs a recipe from podi repo or url list(){ for repo in $RECIPE_REPOS; do print $(dirname $repo) - curl $repo + curl -s $repo done } install(){ for repo in $RECIPE_REPOS; do - curl -s $repo | grep -E "^$1$" && { + curl -s $repo | silent grep -E "^$1$" && { + test -d .git || git init test -d .pod || mkdir .pod - print "installing $(dirname $repo)/$1" + print "writing .pod/$1" dir=$(dirname $1) test -d .pod/$dir || mkdir -p .pod/$dir curl -s $(dirname $repo)/$1 > .pod/$dir/$(basename $1) } done } - init_localhost test -z $1 && list test -z $1 || install $1 } @@ -191,7 +194,7 @@ usage(){ gsub(".*{ # ","",$0) gsub(" :.*","",$0) printf(" %-55s %s\n",$0,info) - }' $0 .pod/* 2>/dev/null + }' $0 $(find .pod -type f | awk '{ printf "%s ",$0 }') 2>/dev/null echo init_remotes printf "deploy targets:\n" diff --git a/recipe/index.txt b/recipe/index.txt index 9a82a40..42ad4d5 100644 --- a/recipe/index.txt +++ b/recipe/index.txt @@ -8,4 +8,7 @@ init/gitops/jail start/envfile run/baremetal_autosuspend run/container +run/container_autosuspend +run/baremetal_webcli run/baremetal +run/container_webcli diff --git a/recipe/run/baremetal b/recipe/run/baremetal index 877f868..4059eb8 100644 --- a/recipe/run/baremetal +++ b/recipe/run/baremetal @@ -14,17 +14,23 @@ start(){ test -f .pid && { print "stopping $PODI_APP"; silent try kill -15 $(cat .pid); } nohup ./podi daemonize $PODI_APP:$PORT &> app.log & echo $! > .pid - print "started $PODI_APP [PID $(cat .pid)]" + print "started $PODI_APP [PID $(cat .pid)] on $PODI_REMOTE:$PORT" } -ensure_file_app(){ +init_runtime(){ test -f app || { - print "'app' startfile not found, please create + commit it. For example:" - echo " $ echo 'node app.js' > app && chmod 755 app" | soften - exit 0 + generate(){ + echo '#!/bin/sh' + echo 'echo PID=$(cat .pid) => $(date) PORT=$PORT' + echo 'while sleep 1; do printf .; done ' + } + prompt "generate + commit 'app' startfile?" "$(generate | soften)" "[y/n] " + test $answer = "y" || error aborting + generate > app + chmod 755 app + git add app && git commit -m "podi: adding app file" } test -f .pod/extract/rollback_simple || recipe extract/rollback_simple test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey } - -on init_localhost ensure_file_app diff --git a/recipe/run/baremetal_autosuspend b/recipe/run/baremetal_autosuspend index 21e5b53..9e4bb97 100644 --- a/recipe/run/baremetal_autosuspend +++ b/recipe/run/baremetal_autosuspend @@ -3,15 +3,16 @@ # example usage: ./podman rollback git@server 3fe2f615 daemonize(){ - while sleep 1s; do + while sleep 0.2s; do set +e - echo "starting TTL=$TTL BOOTTIME=$BOOTTIME" + echo "starting $PODI_APP TTL=$TTL BOOTTIME=$BOOTTIME" TTLARG=$TTL + nc 2>&1 | silent grep BusyBox || NCARG="-N" timeout 2>&1 | silent grep BusyBox && TTLARG="-t $TTL" timeout -s 15 ${TTLARG} ./app echo "$(tail -n200 app.log)" > app.log echo sleeping on port $PORT - echo "HTTP/1.1 302 OK\nrefresh:$BOOTTIME;url=/\n\n" | nc -lp $PORT | awk '{ printf "\r",$0 }' + echo "HTTP/1.1 302 OK\nrefresh:$BOOTTIME;url=/\n\n" | nc $NCARG -lp $PORT | awk '{ printf "\r",$0 }' done } @@ -19,20 +20,27 @@ start(){ test -f .pid && { print "stopping $PODI_APP"; silent try kill -15 $(cat .pid); } nohup ./podi daemonize $PODI_APP:$PORT &> app.log & echo $! > .pid - print "started $PODI_APP [PID $(cat .pid)]" + print "started $PODI_APP [PID $(cat .pid)] on $PODI_REMOTE:$PORT" print "autosleeping after $TTL seconds (.env)" print "waking up after network request on port $PORT" } -ensure_file_app(){ +init_runtime(){ test -f app || { - print "'app' startfile not found, please create + commit it. For example:" - echo " $ echo 'node app.js' > app && chmod 755 app" | soften - exit 0 + generate(){ + echo '#!/bin/sh' + echo 'echo PID=$(cat .pid) => $(date) PORT=$PORT' + echo 'while sleep 1; do printf .; done ' + } + prompt "generate + commit 'app' startfile?" "$(generate | soften)" "[y/n] " + test $answer = "y" || error aborting + generate > app + chmod 755 app + git add app && git commit -m "podi: adding app file" } test -f .pod/extract/rollback_simple || recipe extract/rollback_simple test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey + echo "export TTL=5 # suspend after 5 seconds of activity (update me!)" >> .env + echo "export BOOTTIME=1 # give app 1 second to wakeup (update me!)" >> .env } - -on init_localhost ensure_file_app -on init_localhost create_app diff --git a/recipe/run/baremetal_webcli b/recipe/run/baremetal_webcli new file mode 100644 index 0000000..7d63ac6 --- /dev/null +++ b/recipe/run/baremetal_webcli @@ -0,0 +1,37 @@ +# info: simple app template +# installation: run 'podi recipe app/simple' or put this file into .pod folder +# example usage: ./podman rollback git@server 3fe2f615 + +daemonize(){ + while sleep 1s; do + set +e + ./app + echo "$(tail -n200 app.log)" > app.log + done +} + +start(){ + test -f .pid && { print "stopping $PODI_APP"; silent try kill -15 $(cat .pid); } + { nohup ./podi daemonize $PODI_APP:$PORT 1> app.log 2> app.log; } & + echo $! > .pid + print "started $PODI_APP [PID $(cat .pid)] on $PODI_REMOTE:$PORT" + print "just try 'curl $PODI_REMOTE:$PORT' or use your browser" +} + +init_runtime(){ + test -f app || { + generate(){ + echo '#!/bin/sh' + echo 'export JOB="cat hello.txt ; ls -la"' + echo 'which socat &>/dev/null || { echo "please install socat as root"; exit 1; }' + echo 'socat -t2 TCP4-LISTEN:$PORT,fork,max-children=3,forever,reuseaddr SYSTEM:"$JOB",pty,echo=0;' + } + generate > app + chmod 755 app + echo "HTTP/1.1 200\n\n H E L L O " > hello.txt + git add app hello.txt && git commit -m "podi: adding app file" + } + test -f .pod/extract/rollback_simple || recipe extract/rollback_simple + test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey +} diff --git a/recipe/run/container b/recipe/run/container index d8e48a8..cdaee9a 100644 --- a/recipe/run/container +++ b/recipe/run/container @@ -1,5 +1,3 @@ -container=$(which podman || which docker || echo "") -test -z $PODI_APP && PODI_APP=$(basename $(pwd)) hint_systemd(){ service=/etc/systemd/system/$PODI_APP.service @@ -14,47 +12,50 @@ hint_systemd(){ build(){ header .pod/app/container + test -f Dockerfile || { print "'Dockerfile' not found..skipping build"; } test -f Dockerfile && { - silent which $container || print '[!] please install podman (or docker)' - silent which $container && verbose $container build -t $PODI_APP . + silent which $POD || print '[!] please install podman (or docker)' + silent which $POD && verbose $POD build -t $PODI_APP . } | soften + return 0 } start(){ header .pod/app/container - silent which $container && { - silent $container kill $PODI_APP - silent $container rm -f $PODI_APP + POD=$(which podman || which docker || echo "") + silent which $POD || error "please install podman (or docker)" + test -z $POD || { + silent try $POD kill $PODI_APP + silent try $POD rm -f $PODI_APP export PODI_APP=$PODI_APP eval "$(cat .env)" + cat app verbose ./app hint_systemd print "" print "your container is running at $PODI_REMOTE:$PORT" } + return 0 } -ensure_file_app(){ +init_runtime(){ test -f app || { generate(){ - echo '#!/bin/sh' > app - echo 'c=$(which podman || which docker)' - echo 'follow=-f' - echo 'bg=-d' - echo 'test -z $PODI_APP && { bg=""; PODI_APP=$(basename $(pwd)); follow=""; }' - echo 'set -x' - echo '$c run $bg --rm --volume $(pwd):/home/app -w /home/app -e PORT -p $PORT:80 --name $PODI_APP docker.io/coderofsalvation/redbean /redbean.com -D . -p 80 -vv' - echo 'set +x' - echo '$c logs $follow $PODI_APP' + echo '#!/bin/sh' + echo 'POD=$(which podman || which docker)' + echo 'test -z $PODI_APP && export PODI_APP=$(basename $(dir))' + echo + echo '$POD run -d --rm --volume $(pwd):/home/app -w /home/app -e PORT -p $PORT:80 --name $PODI_APP $PODI_APP' + echo '$POD logs $PODI_APP' } - prompt "'app' file not found:" - generate | soften - prompt "generate + commit it?" + prompt "generate + commit 'app' startfile?" "$(generate | soften)" "[y/n] " test $answer = "y" || error aborting + echo "FROM docker.io/coderofsalvation/redbean:1.5" > Dockerfile generate > app chmod 755 app - git add app && git commit -m "podi: adding app file" + git add app Dockerfile && git commit -m "podi: adding app file" } test -f .pod/extract/rollback_simple || recipe extract/rollback_simple test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey } diff --git a/recipe/run/container_autosuspend b/recipe/run/container_autosuspend new file mode 100644 index 0000000..8ca437b --- /dev/null +++ b/recipe/run/container_autosuspend @@ -0,0 +1,83 @@ + +hint_systemd(){ + service=/etc/systemd/system/$PODI_APP.service + silent which podman || return 0 + test -f $service && return 0 + silent podman inspect $PODI_APP || return 0 + podman generate systemd $PODI_APP > $PODI_APP.service + print "to survive server-reboots please run as root:" + echo "cp $(pwd)/$PODI_APP.service /etc/systemd/system/." | soften + echo "systemctl enable $PODI_APP.service" | soften +} + +build(){ + header .pod/app/container + test -f Dockerfile || { print "'Dockerfile' not found..skipping build"; } + test -f Dockerfile && { + silent which $container || print '[!] please install podman (or docker)' + silent which $container && verbose $container build -t $PODI_APP . + } | soften + return 0 +} + +daemonize(){ + while sleep 0.2s; do + set +e + POD=$(which podman || which docker) + nc 2>&1 | silent grep BusyBox || NCARG="-N" + echo "starting $PODI_APP TTL=$TTL BOOTTIME=$BOOTTIME" + test -z $($POD ps -a --filter=name=$PODI_APP| head -n-1) && ./app + $POD start $PODI_APP + sleep $TTL + $POD stop $PODI_APP + echo sleeping on port $PORT + echo "HTTP/1.1 302 OK\nrefresh:$BOOTTIME;url=/\n\n" | nc $NCARG -lp $PORT | awk '{ printf "\r",$0 }' + done +} + +start(){ + header .pod/app/container + POD=$(which podman || which docker || echo "") + silent which $POD || error "please install podman (or docker)" + test -f .pid && { print "stopping $PODI_APP"; silent try kill -15 $(cat .pid); } + test -z $POD || { + silent try $POD kill $PODI_APP + silent try $POD rm -f $PODI_APP + export PODI_APP=$PODI_APP + export PODI_GITPUSH=$PODI_GITPUSH + eval "$(cat .env)" + { nohup ./podi daemonize $PODI_APP:$PORT 1> app.log 2> app.log; } & + echo $! > .pid + hint_systemd + print "" + print "started container $PODI_APP [PID $(cat .pid)] on $PODI_REMOTE:$PORT" + print "autosleeping after $TTL seconds (.env)" + print "waking up after network request on port $PORT" + } + return 0 +} + +init_runtime(){ + test -f app || { + generate(){ + echo '#!/bin/sh' + echo 'test -z $PODI_APP && PODI_APP=$(basename $(pwd))' + echo 'test -f Dockerfile && IMAGE=$PODI_APP || IMAGE=docker.io/coderofsalvation/redbean:1.5' + echo 'CMD="/redbean.com -D . -p 80 -vv"' + echo 'POD=$(which podman || which docker)' + echo + echo '$POD run -d --rm --volume $(pwd):/home/app -w /home/app -e PORT -p $PORT:80 --name $PODI_APP $IMAGE $CMD' + echo '$POD logs $PODI_APP 2>&1 | awk "{print \"[app.log] \"\$0 }"' + } + prompt "generate + commit 'app' startfile?" "$(generate | soften)" "[y/n] " + test $answer = "y" || error aborting + generate > app + chmod 755 app + git add app && git commit -m "podi: adding app file" + } + test -f .pod/extract/rollback_simple || recipe extract/rollback_simple + test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey + echo "export TTL=5 # suspend after 5 seconds of activity (update me!)" >> .env + echo "export BOOTTIME=2 # give app 2 second to wakeup (update me!)" >> .env +} diff --git a/recipe/run/container_webcli b/recipe/run/container_webcli new file mode 100644 index 0000000..c60fe62 --- /dev/null +++ b/recipe/run/container_webcli @@ -0,0 +1,62 @@ +POD=$(which podman || which docker || echo "") + +hint_systemd(){ + service=/etc/systemd/system/$PODI_APP.service + silent which podman || return 0 + test -f $service && return 0 + silent podman inspect $PODI_APP || return 0 + silent podman generate systemd $PODI_APP 1> $PODI_APP.service 2>/dev/null + print "to survive server-reboots please run as root:" + echo "cp $(pwd)/$PODI_APP.service /etc/systemd/system/." | soften + echo "systemctl enable $PODI_APP.service" | soften +} + +build(){ + header .pod/app/container + test -f Dockerfile || { print "'Dockerfile' not found..skipping build"; } + test -f Dockerfile && { + silent which $POD || print '[!] please install podman (or docker)' + silent which $POD && verbose $POD build -t $PODI_APP . + } | soften + return 0 +} + +start(){ + header .pod/app/container + test -z $PODI_APP && PODI_APP=$(basename $(pwd)) + silent test -z $POD && error "please install podman (or docker)" + test -z $POD || { + export PODI_APP=$PODI_APP + silent try $POD kill $PODI_APP + silent try $POD rm -f $PODI_APP + eval "$(cat .env)" + POD=$(which podman || which docker) + verbose $POD run -d --volume $(pwd):/home/app -w /home/app -e PORT -p $PORT:$PORT --name $PODI_APP --entrypoint=sh $PODI_APP -c ./app + verbose $POD logs $PODI_APP 2>&1 | awk "{print \"[app.log] \"\$0 }" + hint_systemd + print "" + print "your container is running at $PODI_REMOTE:$PORT" + } + return 0 +} + +init_runtime(){ + test -f app || { + generate(){ + echo '#!/bin/sh' + echo 'export JOB="cat hello.txt ; ls -la"' + echo 'which socat &>/dev/null || { echo "please install socat as root"; exit 1; }' + echo 'socat -t2 TCP4-LISTEN:$PORT,fork,max-children=3,forever,reuseaddr SYSTEM:"$JOB",pty,echo=0;' + } + prompt "generate + commit 'app' startfile?" "$(generate | soften)" "[y/n] " + test $answer = "y" || error aborting + generate > app + chmod 755 app + echo "HTTP/1.1 200\n\n H E L L O " > hello.txt + echo "FROM docker.io/alpine/socat" > Dockerfile + git add app Dockerfile hello.txt && git commit -m "podi: adding app file" + } + test -f .pod/extract/rollback_simple || recipe extract/rollback_simple + test -f .pod/start/envfile || recipe start/envfile + test -f .pod/init/server/sshkey || recipe init/server/sshkey +} diff --git a/recipe/start/envfile b/recipe/start/envfile index c03e0a0..d767b5d 100644 --- a/recipe/start/envfile +++ b/recipe/start/envfile @@ -6,8 +6,8 @@ envfile(){ header .pod/envfile - test -f .env && { print "reading '.env'" ; . .env; } - test -f .env.live && { print "reading '.env.live'"; . .env.live; } + test -f .env && { print "reading '.env'" ; eval "$(cat .env)"; } + test -f .env.live && { print "reading '.env.live'"; eval "$(cat .env.live)"; } } envset(){ # envset [git@server] [app] [FOO=bar] : shows or sets [remote] environment variables @@ -27,11 +27,9 @@ envset(){ # envset [git@server] [app] [FOO=bar] : shows or sets [remote] environ } ensure_file_env(){ - test -f app || { + test -f .env || { generate(){ echo "export PORT=$(awk 'BEGIN{ srand(); print int(rand()*1000)+8000 }')"; } - prompt "'.env' file not found:" - generate | soften - prompt "generate + commit it?" + prompt "generate + commit '.env' startfile?" "$(generate | soften)" "[y/n] " test $answer = "y" || error aborting generate > .env git add .env && git commit -m "podi: adding .env file"