File: base.sh

package info (click to toggle)
charliecloud 0.43-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,116 kB
  • sloc: python: 6,021; sh: 4,284; ansic: 3,863; makefile: 598
file content (232 lines) | stat: -rw-r--r-- 5,948 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# shellcheck shell=sh
set -e

ch_bin="$(cd "$(dirname "$0")" && pwd)"
# shellcheck disable=SC2034
ch_base=${ch_bin%/*}

ch_lib=${ch_bin}/../lib
. "${ch_lib}/version.sh"


# Log level. Incremented by “--verbose” and decremented by “--quiet”, as in the
# Python code.
log_level=0

# Logging functions. Note that we disable SC2059 because we want these functions
# to behave exactly like printf(1), e.g. we want
#
#   >>> VERBOSE "foo %s" "bar"
#   foo bar
#
# Implementing the suggestion in SC2059 would instead result in something like
#
#   >>> VERBOSE "foo %s" "bar"
#   foo %sbar

DEBUG () {
    if [ "$log_level" -ge 2 ]; then
        # shellcheck disable=SC2059
        printf "$@" 1>&2
        printf '\n' 1>&2
    fi
}

FATAL () {
    printf 'error: ' 1>&2
    # shellcheck disable=SC2059
    printf "$@" 1>&2
    printf '\n' 1>&2
    exit 1
}

INFO () {
    if [ "$log_level" -ge 0 ]; then
        # shellcheck disable=SC2059
        printf "$@" 1>&2
        printf '\n' 1>&2
    fi
}

VERBOSE () {
    if [ "$log_level" -ge 1 ]; then
        # shellcheck disable=SC2059
        printf "$@" 1>&2
        printf '\n' 1>&2
    fi
}

WARNING () {
    if [ "$log_level" -ge -1 ]; then
        printf 'warning: ' 1>&2
        # shellcheck disable=SC2059
        printf "$@" 1>&2
        printf '\n' 1>&2
    fi
}

# Return success if path $1 exists, without dereferencing links, failure
# otherwise. (“test -e” dereferences.)
exist_p () {
    stat "$1" > /dev/null 2>&1
}

# Try to parse $1 as a common argument. If accepted, either exit (for things
# like --help) or return success; otherwise, return failure (i.e., not a
# common argument).
parse_basic_arg () {
    case $1 in
        --_lib-path)  # undocumented
            echo "$ch_lib"
            exit 0
            ;;
        --help)
            usage 0   # exits
            ;;
        -q|--quiet)
            if [ $log_level -gt 0 ]; then
                FATAL "incompatible options: --quiet, --verbose"
            fi
            log_level=$((log_level-1))
            return 0
            ;;
        -v|--verbose)
            if [ $log_level -lt 0 ]; then
                FATAL "incompatible options: --quiet, --verbose"
            fi
            log_level=$((log_level+1))
            return 0
            ;;
        --version)
            version   # exits
            ;;
    esac
    return 1  # not a basic arg
}

# Redirect standard streams (or not) depending on “quiet” level. See table in
# FAQ.
quiet () {
    if [ $log_level -lt -2 ]; then
        "$@" 1>/dev/null 2>/dev/null
    elif [ $log_level -lt -1 ]; then
        "$@" 1>/dev/null
    else
        "$@"
    fi
}

# Convert container registry path to filesystem compatible path.
#
# NOTE: This is used both to name user-visible stuff like tarballs as well as
# dig around in the ch-image storage directory.
tag_to_path () {
    echo "$1" | tr '/:' '%+'
}

usage () {
    echo "${usage:?}" 1>&2
    exit "${1:-1}"
}

version () {
    echo 1>&2 "$ch_version"
    exit 0
}


# Set a variable and print its value, human readable description, and origin.
# Parameters:
#
#   $1: string:   variable name
#   $2: string:   command line argument value (1st priority)
#   $3: string:   environment variable value (2nd priority)
#   $4: string:   default value (3rd priority)
#   $5: int:      width of description (use -1 for natural width)
#   $6: string:   human readable description for stdout
#   $7: boolean:  if true, suppress chatter
#
# FIXME: Shouldn't export the variable, and no POSIX sh indirection available.
# There are safe eval solution out there, but I was too lazy to deal with it.
vset () {
    var_name=$1
    cli_value=$2
    env_value=$3
    def_value=$4
    desc_width=$5
    var_desc=$6
    quiet=$7
    if   [ "$cli_value" ]; then
        export "$var_name"="$cli_value"
        value=$cli_value
        method='command line'
    elif [ "$env_value" ]; then
        export "$var_name"="$env_value"
        value=$env_value
        method='environment'
    else
        export "$var_name"="$def_value"
        value=$def_value
        method='default'
    fi
    # FIXME: Kludge: Assume it's a boolean variable and the empty string means
    # false. Print "no" instead of the empty string.
    if [ -z "$value" ]; then
        value=no
    fi
    if [ -z "$quiet" ]; then
        var_desc="$var_desc:"
        printf "%-*s %s (%s)\n" "$desc_width" "$var_desc" "$value" "$method"
    fi
}

# Is Docker present, and if so, do we need sudo? If docker is a wrapper for
# podman, “docker info” hangs (#1656), so treat that as not found.
if    ( ! command -v docker > /dev/null 2>&1 ) \
   || ( docker --help 2>&1 | grep -Fqi podman ); then
    docker_ () {
        echo 'docker not found; unreachable code reached' 1>&1
        exit 1
    }
elif docker info > /dev/null 2>&1; then
    docker_ () {
        docker "$@"
    }
else
    docker_ () {
        sudo docker "$@"
    }
fi

# Wrapper for rootless podman (for consistency w/ docker).

# The only thing we're really concerned with here is the trailing underscore,
# since we use it to construct function calls.
podman_ () {
    podman "$@"
}

# Use parallel gzip if it's available.
if command -v pigz > /dev/null 2>&1; then
    gzip_ () {
        pigz "$@"
    }
else
    gzip_ () {
        gzip "$@"
    }
fi

# Use pv(1) to show a progress bar, if it’s available and the quiet level is
# less than one, otherwise cat(1). WARNING: You must pipe in the file because
# arguments are ignored if this is cat(1). (We also don’t want a progress bar if
# stdin is not a terminal, but pv takes care of that). Note that we put the if
# statement in the scope of the function because doing so ensures that it gets
# evaluated after “quiet” is assigned an appropriate value by “parse_basic_arg”.
pv_ () {
    if command -v pv > /dev/null 2>&1 && [ "$log_level" -gt -1 ]; then
        pv -pteb "$@"
    else
        cat
    fi
}