├── .gitignore ├── Dockerfile ├── README.md ├── config ├── config.go └── database.go ├── controllers └── controllers.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── main.go ├── middleware └── cors.go ├── models ├── db.go └── db_test.go ├── run.sh └── wait-for-it.sh /.gitignore: -------------------------------------------------------------------------------- 1 | cert.pem 2 | key.pem 3 | .env 4 | .code-workspace 5 | .vscode 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | # This file includes artifacts of Go build that should not be checked in. 23 | # For files created by specific development environment (e.g. editor), 24 | # use alternative ways to exclude files from git. 25 | # For example, set up .git/info/exclude or use a global .gitignore. 26 | .DS_Store 27 | *.[56789ao] 28 | *.a[56789o] 29 | *.so 30 | *.pyc 31 | ._* 32 | .nfs.* 33 | [56789a].out 34 | *~ 35 | *.orig 36 | *.rej 37 | *.exe 38 | .*.swp 39 | core 40 | *.cgo*.go 41 | *.cgo*.c 42 | _cgo_* 43 | _obj 44 | _test 45 | _testmain.go 46 | 47 | /VERSION.cache 48 | /bin/ 49 | /build.out 50 | /doc/articles/wiki/*.bin 51 | /goinstall.log 52 | /last-change 53 | /misc/cgo/life/run.out 54 | /misc/cgo/stdio/run.out 55 | /misc/cgo/testso/main 56 | /pkg/ 57 | /src/*.*/ 58 | /src/cmd/cgo/zdefaultcc.go 59 | /src/cmd/dist/dist 60 | /src/cmd/go/internal/cfg/zdefaultcc.go 61 | /src/cmd/go/internal/cfg/zosarch.go 62 | /src/cmd/internal/objabi/zbootstrap.go 63 | /src/go/build/zcgo.go 64 | /src/go/doc/headscan 65 | /src/runtime/internal/sys/zversion.go 66 | /src/unicode/maketables 67 | /test.out 68 | /test/garbage/*.out 69 | /test/pass.out 70 | /test/run.out 71 | /test/times.out -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # We specify the base image we need for our 2 | # go application 3 | FROM golang:1.13.1 4 | 5 | RUN mkdir -p /go/src/app 6 | 7 | WORKDIR /go/src/app 8 | 9 | ADD . /go/src/app 10 | 11 | RUN go get -v 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go REST API Project for Dan Lam 2 | 3 | Project for Go API with a PostgreSQL database. 4 | 5 | ## Requirements 6 | At a minimum, Golang, Postgres, and Git need to be installed on your local machine. Optionally, you may run the app using Docker. 7 | * [Install Go](https://golang.org/doc/install) 8 | * [Install Postgres](https://www.postgresql.org/download/) 9 | * [Install Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 10 | * [Install Docker](https://docs.docker.com/v17.09/engine/installation/) 11 | 12 | ### Updating Project Module Names 13 | **Important! Replace ALL occurrences of the boilerplate name with you project name.** 14 | 15 | **This is necessary to ensure existing boilerplate modules are found and associated with your project!** 16 | 17 | The easiest way to update all instances of the project name in the boilerplate is to use an IDE, such as VS Code (recommended). 18 | 19 | Simply use the find and replace tool and replace `go-api-boilerplate` with your ``. 20 | 21 | ## Local Setup 22 | Ensure that you have started your local Postgres service and identified or created a database that you wish to use. 23 | 24 | You will need to create a `.env` file containing the following environment variables: 25 | ``` 26 | DB_CLIENT=postgres 27 | DB_USER= 28 | DB_PASSWORD= 29 | DB_NAME= 30 | DB_HOST= 31 | DB_PORT= 32 | ``` 33 | 34 | ### Using a Database Other Than Postgres 35 | If you wish to use a relational database other than postgres, you will need to update the `DB_CLIENT` environment variable and ensure that the connection string in the [config/database.go](./config/database.go) file is formatted correctly for the service. 36 | 37 | The necessary driver will need to be added to the config as well. 38 | 39 | Lastly, if you are using Docker, you will need to update the [docker-compose.yml](docker-compose.yml) db service with the appropriate image and supporting attributes. 40 | 41 | ## Running the App 42 | ### With Local Distribution 43 | This repository contains a shell script that is useful for running the app with your local Golang distribution. 44 | 45 | $ ./run.sh 46 | 47 | This script will install dependencies identified in your `go.mod` file and run your app 48 | with `main.go` as the default Go entry point. 49 | ### With Docker 50 | To run the application with Docker, first build the API image 51 | 52 | $ docker build -t katochojiro/ . 53 | 54 | Additionally, you must make the following changes to the project's [docker-compose.yml](./docker-compose.yml) file: 55 | * Add/change the appropriate environment variables if you are not using the default docker postgres credentials 56 | * Update the api service image with the name of your build image if it differs from your project name 57 | * `katochojiro/go-api-boilerplate:latest` --> `katochojiro/:latest` 58 | * See instructions for [cloning the repository](#cloning-the-repository) for details on globally replacing the boilerplate name with your project name. 59 | 60 | Now, you may bring up the project stack with the command 61 | 62 | $ docker-compose up 63 | 64 | And bring down the stack with the command 65 | 66 | $ docker-compose down 67 | 68 | ## Running Tests 69 | 70 | Per the golang documentation, tests in Golang can be run with the command `go test` which automates the execution of any function of the form: 71 | ``` 72 | func TestXxx(*testing.T) 73 | ``` 74 | 75 | Convention in this project is to add "_test" to the filename before the file extension to organize tests. See the `models/` folder for an example test file with the start of a mock database interface. 76 | 77 | Additional sample commands are as follows: 78 | ``` 79 | go test -run '' # Run all tests. 80 | go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar". 81 | go test -run Foo/A= # For top-level tests matching "Foo", run sub-tests matching "A=". 82 | go test -run /A=1 # For all top-level tests, run sub-tests matching "A=1". 83 | ``` 84 | 85 | ## Built With 86 | - [Golang](https://golang.org) - Fast, statically typed, compiled language that feels like a dynamically typed, interpreted language 87 | - [Docker](https://docker.com) - Container platform 88 | - [PostgreSQL](https://www.postgresql.org) - Leading open source relational database 89 | - [Git](https://git-scm.com/) - Open source version control system 90 | - [gorilla/mux](https://www.gorillatoolkit.org/pkg/mux) - HTTP request multiplexer 91 | - [godotenv](https://godoc.org/github.com/joho/godotenv) - Go port of the ruby dotenv library 92 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/joho/godotenv" 8 | ) 9 | 10 | //LoadEnvVars initialized dotenv and loads env vars from .env file 11 | func LoadEnvVars() { 12 | if err := godotenv.Load(); err != nil { 13 | log.Print("No .env file found") 14 | } 15 | } 16 | 17 | func getEnv(key string, defaultVal string) string { 18 | if value, exists := os.LookupEnv(key); exists { 19 | return value 20 | } 21 | 22 | return defaultVal 23 | } 24 | -------------------------------------------------------------------------------- /config/database.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/katochojiro/go-api-boilerplate/models" 9 | _ "github.com/lib/pq" 10 | ) 11 | 12 | // databaseConfig info for connecting to the database 13 | type databaseConfig struct { 14 | client string 15 | user string 16 | password string 17 | dbName string 18 | host string 19 | port string 20 | } 21 | 22 | // dbConfig creates a DB config struct 23 | func dbConfig() *databaseConfig { 24 | return &databaseConfig{ 25 | client: getEnv("DB_CLIENT", "postgres"), 26 | user: getEnv("DB_USER", "postgres"), 27 | password: getEnv("DB_PASSWORD", ""), 28 | dbName: getEnv("DB_NAME", "postgres"), 29 | host: getEnv("DB_HOST", "localhost"), 30 | port: getEnv("DB_PORT", "5432"), 31 | } 32 | } 33 | 34 | func (db *databaseConfig) getClient() string { 35 | return db.client 36 | } 37 | 38 | func (db *databaseConfig) getConnectionString() string { 39 | return fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%s sslmode=disable", db.user, db.password, db.dbName, db.host, db.port) 40 | } 41 | 42 | // ConnectToDatabase returns a db connection 43 | func ConnectToDatabase() *sql.DB { 44 | db, err := sql.Open(dbConfig().getClient(), dbConfig().getConnectionString()) 45 | 46 | if err != nil { 47 | log.Fatalln(fmt.Errorf("Unable to connect to database: '%v'", err)) 48 | } 49 | 50 | models.SetDatabase(db) 51 | 52 | return db 53 | } 54 | -------------------------------------------------------------------------------- /controllers/controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/mux" 7 | ) 8 | 9 | func Startup() *mux.Router { 10 | router := mux.NewRouter() 11 | router.HandleFunc("/", successResponse) 12 | return router 13 | } 14 | 15 | func successResponse(w http.ResponseWriter, _ *http.Request) { 16 | w.Header().Add("Content-Type", "text/html") 17 | w.WriteHeader(http.StatusOK) 18 | w.Write([]byte("OK")) 19 | } 20 | 21 | func notImplemented(w http.ResponseWriter, _ *http.Request) { 22 | w.Header().Add("Content-Type", "text/html") 23 | w.WriteHeader(http.StatusNotImplemented) 24 | w.Write([]byte("Method Not Implemented")) 25 | } 26 | 27 | func notFound(w http.ResponseWriter, _ *http.Request) { 28 | w.Header().Add("Content-Type", "text/html") 29 | w.WriteHeader(http.StatusNotFound) 30 | w.Write([]byte("Resources Not Found")) 31 | } 32 | 33 | func unauthorized(w http.ResponseWriter, _ *http.Request) { 34 | w.Header().Add("Content-Type", "text/html") 35 | w.WriteHeader(http.StatusUnauthorized) 36 | w.Write([]byte("Unauthorized")) 37 | } 38 | 39 | func serverError(w http.ResponseWriter, _ *http.Request) { 40 | w.Header().Add("Content-Type", "text/html") 41 | w.WriteHeader(http.StatusInternalServerError) 42 | w.Write([]byte("Internal Server Error")) 43 | } 44 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | db: 4 | image: postgres:11.2 5 | environment: 6 | DB_NAME: postgres 7 | DB_USER: postgres 8 | DB_PASSWORD: docker 9 | DB_HOST: 127.0.0.1 10 | DB_PORT: 5432 11 | ports: 12 | - 5432:5432 13 | volumes: [ ~/docker/volumes/postgres:/var/lib/postgresql/data ] 14 | 15 | api: 16 | image: backend_api:latest 17 | build: . 18 | container_name: backend_api 19 | hostname: backend 20 | command: 21 | [ 22 | "./wait-for-it.sh", 23 | "db:5432", 24 | "--", 25 | "go", 26 | "run", 27 | "main.go" 28 | ] 29 | volumes: 30 | - .:/go/src/app 31 | ports: 32 | - "8080:8080" 33 | environment: 34 | DB_CLIENT: postgres 35 | DB_NAME: postgres 36 | DB_USER: postgres 37 | DB_PASSWORD: docker 38 | DB_HOST: 127.0.0.1 39 | DB_PORT: 5432 40 | depends_on: 41 | - db 42 | links: 43 | - db 44 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/katochojiro/go-api-boilerplate 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gorilla/mux v1.7.3 7 | github.com/joho/godotenv v1.3.0 8 | github.com/lib/pq v1.2.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= 2 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 3 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 4 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 5 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 6 | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 7 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/katochojiro/go-api-boilerplate/config" 7 | "github.com/katochojiro/go-api-boilerplate/controllers" 8 | "github.com/katochojiro/go-api-boilerplate/middleware" 9 | ) 10 | 11 | func init() { 12 | config.LoadEnvVars() 13 | } 14 | 15 | func main() { 16 | r := controllers.Startup() 17 | db := config.ConnectToDatabase() 18 | defer db.Close() 19 | http.ListenAndServe( 20 | ":8080", 21 | &middleware.CORSMiddleware{ 22 | Next: r, 23 | }, 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "net/http" 4 | 5 | type CORSMiddleware struct { 6 | Next http.Handler 7 | } 8 | 9 | func (cm *CORSMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { 10 | if cm.Next == nil { 11 | cm.Next = http.DefaultServeMux 12 | } 13 | w.Header().Set("Access-Control-Allow-Origin", "*") 14 | 15 | cm.Next.ServeHTTP(w, r) 16 | } 17 | -------------------------------------------------------------------------------- /models/db.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "database/sql" 4 | 5 | var db DB 6 | 7 | type DB interface { 8 | Query(string, ...interface{}) (Rows, error) 9 | QueryRow(string, ...interface{}) Row 10 | Exec(string, ...interface{}) (Result, error) 11 | } 12 | 13 | type Row interface { 14 | Scan(...interface{}) error 15 | } 16 | 17 | type Rows interface { 18 | Scan(...interface{}) error 19 | Next() bool 20 | Close() error 21 | } 22 | 23 | type Result interface { 24 | LastInsertId() (int64, error) 25 | RowsAffected() (int64, error) 26 | } 27 | 28 | type sqlDB struct { 29 | db *sql.DB 30 | } 31 | 32 | func (s sqlDB) QueryRow(query string, args ...interface{}) Row { 33 | return s.db.QueryRow(query, args...) 34 | } 35 | 36 | func (s sqlDB) Exec(query string, args ...interface{}) (Result, error) { 37 | return s.db.Exec(query, args...) 38 | } 39 | 40 | func (s sqlDB) Query(query string, args ...interface{}) (Rows, error) { 41 | return s.db.Query(query, args...) 42 | } 43 | 44 | func SetDatabase(database *sql.DB) { 45 | db = &sqlDB{database} 46 | } 47 | -------------------------------------------------------------------------------- /models/db_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type mockDB struct { 4 | lastQuery string 5 | lastArgs []interface{} 6 | returnedRow Row 7 | returnedRows Rows 8 | } 9 | 10 | func (db *mockDB) QueryRow(query string, args ...interface{}) Row { 11 | db.lastQuery = query 12 | db.lastArgs = args 13 | return db.returnedRow 14 | } 15 | 16 | func (db *mockDB) Exec(query string, args ...interface{}) (Result, error) { 17 | return nil, nil 18 | } 19 | 20 | func (db *mockDB) Query(query string, args ...interface{}) (Rows, error) { 21 | db.lastQuery = query 22 | db.lastArgs = args 23 | return db.returnedRows, nil 24 | } 25 | 26 | type mockRow struct{} 27 | 28 | func (m mockRow) Scan(...interface{}) error { 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Installing and Compiling..." 4 | go install . 5 | echo "running Go app" 6 | go run . 7 | echo "App Stopped" 8 | -------------------------------------------------------------------------------- /wait-for-it.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Use this script to test if a given TCP host/port are available 3 | 4 | WAITFORIT_cmdname=${0##*/} 5 | 6 | echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } 7 | 8 | usage() 9 | { 10 | cat << USAGE >&2 11 | Usage: 12 | $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] 13 | -h HOST | --host=HOST Host or IP under test 14 | -p PORT | --port=PORT TCP port under test 15 | Alternatively, you specify the host and port as host:port 16 | -s | --strict Only execute subcommand if the test succeeds 17 | -q | --quiet Don't output any status messages 18 | -t TIMEOUT | --timeout=TIMEOUT 19 | Timeout in seconds, zero for no timeout 20 | -- COMMAND ARGS Execute command with args after the test finishes 21 | USAGE 22 | exit 1 23 | } 24 | 25 | wait_for() 26 | { 27 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 28 | echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 29 | else 30 | echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" 31 | fi 32 | WAITFORIT_start_ts=$(date +%s) 33 | while : 34 | do 35 | if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then 36 | nc -z $WAITFORIT_HOST $WAITFORIT_PORT 37 | WAITFORIT_result=$? 38 | else 39 | (echo > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 40 | WAITFORIT_result=$? 41 | fi 42 | if [[ $WAITFORIT_result -eq 0 ]]; then 43 | WAITFORIT_end_ts=$(date +%s) 44 | echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" 45 | break 46 | fi 47 | sleep 1 48 | done 49 | return $WAITFORIT_result 50 | } 51 | 52 | wait_for_wrapper() 53 | { 54 | # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 55 | if [[ $WAITFORIT_QUIET -eq 1 ]]; then 56 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 57 | else 58 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 59 | fi 60 | WAITFORIT_PID=$! 61 | trap "kill -INT -$WAITFORIT_PID" INT 62 | wait $WAITFORIT_PID 63 | WAITFORIT_RESULT=$? 64 | if [[ $WAITFORIT_RESULT -ne 0 ]]; then 65 | echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 66 | fi 67 | return $WAITFORIT_RESULT 68 | } 69 | 70 | # process arguments 71 | while [[ $# -gt 0 ]] 72 | do 73 | case "$1" in 74 | *:* ) 75 | WAITFORIT_hostport=(${1//:/ }) 76 | WAITFORIT_HOST=${WAITFORIT_hostport[0]} 77 | WAITFORIT_PORT=${WAITFORIT_hostport[1]} 78 | shift 1 79 | ;; 80 | --child) 81 | WAITFORIT_CHILD=1 82 | shift 1 83 | ;; 84 | -q | --quiet) 85 | WAITFORIT_QUIET=1 86 | shift 1 87 | ;; 88 | -s | --strict) 89 | WAITFORIT_STRICT=1 90 | shift 1 91 | ;; 92 | -h) 93 | WAITFORIT_HOST="$2" 94 | if [[ $WAITFORIT_HOST == "" ]]; then break; fi 95 | shift 2 96 | ;; 97 | --host=*) 98 | WAITFORIT_HOST="${1#*=}" 99 | shift 1 100 | ;; 101 | -p) 102 | WAITFORIT_PORT="$2" 103 | if [[ $WAITFORIT_PORT == "" ]]; then break; fi 104 | shift 2 105 | ;; 106 | --port=*) 107 | WAITFORIT_PORT="${1#*=}" 108 | shift 1 109 | ;; 110 | -t) 111 | WAITFORIT_TIMEOUT="$2" 112 | if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi 113 | shift 2 114 | ;; 115 | --timeout=*) 116 | WAITFORIT_TIMEOUT="${1#*=}" 117 | shift 1 118 | ;; 119 | --) 120 | shift 121 | WAITFORIT_CLI=("$@") 122 | break 123 | ;; 124 | --help) 125 | usage 126 | ;; 127 | *) 128 | echoerr "Unknown argument: $1" 129 | usage 130 | ;; 131 | esac 132 | done 133 | 134 | if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then 135 | echoerr "Error: you need to provide a host and port to test." 136 | usage 137 | fi 138 | 139 | WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} 140 | WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} 141 | WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} 142 | WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} 143 | 144 | # check to see if timeout is from busybox? 145 | WAITFORIT_TIMEOUT_PATH=$(type -p timeout) 146 | WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) 147 | if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then 148 | WAITFORIT_ISBUSY=1 149 | WAITFORIT_BUSYTIMEFLAG="-t" 150 | 151 | else 152 | WAITFORIT_ISBUSY=0 153 | WAITFORIT_BUSYTIMEFLAG="" 154 | fi 155 | 156 | if [[ $WAITFORIT_CHILD -gt 0 ]]; then 157 | wait_for 158 | WAITFORIT_RESULT=$? 159 | exit $WAITFORIT_RESULT 160 | else 161 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 162 | wait_for_wrapper 163 | WAITFORIT_RESULT=$? 164 | else 165 | wait_for 166 | WAITFORIT_RESULT=$? 167 | fi 168 | fi 169 | 170 | if [[ $WAITFORIT_CLI != "" ]]; then 171 | if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then 172 | echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" 173 | exit $WAITFORIT_RESULT 174 | fi 175 | exec "${WAITFORIT_CLI[@]}" 176 | else 177 | exit $WAITFORIT_RESULT 178 | fi 179 | --------------------------------------------------------------------------------