forked from CumulusNetworks/DUE
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibdue
2349 lines (2004 loc) · 91.1 KB
/
libdue
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash syntax
# SCRIPT_PURPOSE: A function library for Dedicated User Environment (DUE)
# Copyright 2021-2023 NVIDIA Corporation. All rights reserved.
# Copyright 2019,2020 Cumulus Networks, Inc. All rights reserved.
#
# SPDX-License-Identifier: MIT
######################################################################
# Init variables
######################################################################
# Enable extended pattern matching
shopt -s extglob
# Documentation and changelog builds get the official version from here.
DUE_VERSION="4.1.0"
# Debian packages required for DUE to run
REQUIRED_PACKAGES=" docker.io rsync bsdutils git"
# Packages to support processor emulation (ARM builds, etc)
RECOMMENDED_PACKAGES=" binfmt-support qemu qemu-user-static "
# Get enough information to add the current user account to a container
USER_NAME=$(whoami)
USER_ID=$( id -u )
USER_GROUP_ID=$( id -g )
# Get the name of the group if it has to be created in the container
USER_GROUP_NAME=$( getent group "$USER_GROUP_ID" | cut -d: -f1 )
# default the login shell
LOGIN_SHELL="/bin/bash --login"
# If debugging a container, this will be set
OVERRIDE_ENTRYPOINT=""
# Default to restricting results to containers that have been set up.
FILTER_SHOW_ONLY_DUE="TRUE"
# default tag to latest
DOCKER_IMAGE_TAG="latest"
# Options passed to Docker build, using --dockerarg
DOCKER_SPECIFIC_BUILD_ARGS=""
# List of example images
KNOWN_IMAGES="
debian:11
ubuntu:20.04
arm32v5/debian:jessie
arm32v7/debian:10
arm64v8/debian:11
ppc64le/debian:10
redhat/ubi9
opensuse/leap
bci/bci-base-15.3
fedora-33/fedora-33
"
# Max number of containers per user.
# Sometimes people need a reminder to clean up.
# The config file will override this, provided a read works.
DUE_USER_CONTAINER_LIMIT=10
# expected directory for template patch files
SPECIFIC_TEMPLATES_DIR="templates"
# Template directories may have sub directories that add additional
# files to the resulting image. These are specified usin sub-type
TEMPLATE_SUB_TYPE="sub-type"
# If the common-template directory is local to the directory where
# DUE is being run it will be used rather than the version that is
# installed in the system.
COMMON_TEMPLATE_DIR="${SPECIFIC_TEMPLATES_DIR}/common-templates"
# Default installed location for template files
SYSTEM_TEMPLATE_INSTALL_DIR="/usr/share/due"
if [ "$TOP_LEVEL_DIR" = "" ];then
TEMPLATE_INSTALL_PATH="$SYSTEM_TEMPLATE_INSTALL_DIR"
else
# Look to cwd for these files
TEMPLATE_INSTALL_PATH="$TOP_LEVEL_DIR"
fi
COMMON_TEMPLATE_PATH=${TEMPLATE_INSTALL_PATH}/$COMMON_TEMPLATE_DIR
SPECIFIC_TEMPLATE_PATH=${TEMPLATE_INSTALL_PATH}/${SPECIFIC_TEMPLATES_DIR}
# User can override the template path, so at build time, USE_TEMPLATE_PATH
# is the variable that gets referenced.
USE_TEMPLATE_PATH="$COMMON_TEMPLATE_PATH"
# if --use-template sets this, it will ID the container
DUE_IMAGE_TYPE_LABEL="default-type"
# Put build steps here
BUILD_MERGE_DIR="$(pwd)/due-build-merge"
# Pull this in to set variables like ID
source /etc/os-release
case "$ID" in
# Red Hat variants
'fedora' | 'rhel' )
#
# Red Hat uses a different package manager, so abstract that out.
# Ex: sudo $PACKAGE_UPDATE ; sudo $PACKAGE_INSTALL rsync
#
OS_TYPE="RedHat"
PACKAGE_UPDATE=" dnf check-update"
PACKAGE_INSTALL=" dnf install "
#
# Defaults for Red Hat systems have nosuid set when Docker runs,
# preventing the mount of the user's home directory.
# There may be a better work around, but for now,
# default to running --privileged
#
DOCKER_SPECIFIC_ARGS=" --privileged "
;;
'opensuse-leap' | 'sles' )
# SUSE uses RPM, but its package manager is zypper
OS_TYPE='Suse'
PACKAGE_UPDATE=" zypper update"
PACKAGE_INSTALL=" zypper install "
DOCKER_SPECIFIC_ARGS=" --privileged "
;;
* )
# Default to Debian variants
OS_TYPE="Debian"
PACKAGE_UPDATE=" apt-get update "
PACKAGE_INSTALL=" apt-get install "
;;
esac
######################################################################
# Common utility functions
######################################################################
# Takes: Path to directory
# list a directory with an indent to make it easier to pick out of
# a stream of text going by.
function fxnListDirContents()
{
fxnPP "Contents of: $1"
ls -l "$1" | sed -e 's/^/ /g'
}
# Create a container by choosing an example from the README.md examples.
# Takes: term to use with grep to filter entries.
function fxnCreateFromMenu()
{
local filterTerm="$1"
local ENTRY
local entryLine
local createCommands
createCommands=$(fxnListContainersForCreation 'menu' $filterTerm)
if [ "$createCommands" = '' ];then
# If no entries were returned (probably due to a filter) there's no point in continuing.
echo " --create --menu failed."
echo "No matching examples were found in any README.md file under the templates directory."
if [ "$filterTerm" != '' ];then
echo "Try a --filter other than [ $filterTerm ]"
else
echo "The templates directory may be damaged. Try a clean install"
fi
echo ""
exit 1
fi
# Print the list of options
echo "$createCommands"
# Blatant cut and paste from the run code.
echo -n "- Enter number or q to quit > "
read -r ENTRY
case $ENTRY in
q|Q|x|X )
echo "Exiting"
exit 0
;;
h|help|-h|--help )
echo ""
echo "Got [ $ENTRY ], so...executing '$0 create --help' and exiting."
echo ""
fxnHelpCreate
exit 0
;;
# At this point, only numbers are valid values.
# If it is not, then exit gracefully before trying
# to parse whatever this was later on.
''|*[!0-9]* )
echo ""
echo "Invalid entry! Must be one of: 'q', 'help', or a number. Exiting."
echo ""
exit 1
;;
esac
entryLine=$( echo "$createCommands" | sed -n "${ENTRY}p" )
if [ "$entryLine" = "" ];then
fxnERR "Invalid entry [ $ENTRY ]. Exiting."
exit 1
fi
# Replace the number, label and invocation of DUE with the
# current invocation of DUE
runCMD="$0 $(echo "$entryLine" | sed -e 's/^.*due //')"
# Let bash parse the command line, as the --description quotes don't persist.
bash -c "$runCMD"
# As this was a menu create, assume the intermediate build stages are not needed
# for debug, since this is the invocation of a known, working configuration.
echo ""
echo "Cleaning up [ $BUILD_MERGE_DIR ]"
echo ""
# Skip error checking as the remove works or it does not, and since this
# is the end of execution, let the error from rm inform the user as to
# where any problems might be.
rm -rf "$BUILD_MERGE_DIR"
}
# List what can be built.
# if $1 outputFormat = 'standard' - just print everything
# if $1 outputFormat = 'menu' - just examples with no context
# if $2 filterTerm is set, then grep for examples with it
function fxnListContainersForCreation()
{
local foundImages
local foundExamples
local formattedExamples
local outputFormat="$1"
local filterTerm="$2"
if [ "$outputFormat" != "JustDirs" ];then
# To simplify parsing, this counts on the example line in the README.md files being formatted
# in exactly the same way for all, with everything up to the 'with:' getting deleted,
# and the 12th field being --platform, as that gets prepended to the example string
# via awk to specify the type of container being created.
foundExamples=$( find "${SPECIFIC_TEMPLATE_PATH}" \
-name README.md \
-exec grep "^Create" {} \; \
| grep -- --platform \
| sed -e 's/^.*with://g' \
| awk '{printf "%-20s %s\n", $12, $0}' | sort )
if [ "$DUE_CONFIG_SOURCING_SUMMARY" = '' ];then
# If DUE is being run from a system install rather than from local source,
# fix the path so the output is cut and paste.
foundExamples=$( echo "$foundExamples" | sed -e 's# ./due # due #g')
fi
if [ "$filterTerm" != "" ];then
if [ "$outputFormat" = 'standard' ];then
echo ""
echo "Filtering output to contain [ $filterTerm ]"
echo ""
fi
foundExamples=$( echo "$foundExamples" | grep "$filterTerm" )
fi
if [ "$outputFormat" = 'standard' ];then
echo " Example build environments"
echo " Image type | Creation command"
echo "-----------------+-----------------------------------------------------------"
echo " |"
fi
# If column is available, use it to get rid of excessive spaces between columns.
if [ "$(which column)" != "" ];then
# Awk replaces spaces in single quotes (setting FS and OFS to " with semicolons (;).
# Column formats the output (surprise!) in to columns, where each column starts with a space
# - t - Table formatting.
# - Double quoted text is its own column, because it has no spaces, just ;s
# Use tr to replace the ; with spaces again
formattedExamples=$( echo "$foundExamples" \
| awk '{for (i=2; i < NF; i+=2) gsub (/ /, ";", $i)} 1' FS=\' OFS=\' \
| column -t \
| tr ';' ' ' )
else
formattedExamples="$foundExamples"
fi
# Return a list of numbered entries?
if [ "$outputFormat" = 'menu' ];then
theSelection=1
# Use a menu built from help examples
# Set IFS so the command lines don't split on spaces
IFS=$'\n'
for item in ${formattedExamples[@]} ;do
#echo "$item" | sed -e "s/^/$theSelection /"
printf "%-3d %s\n" "$theSelection" "$item"
theSelection=$(( theSelection+1 ))
done
unset IFS
# Existing images below are not on the menu.
return
else
echo "$formattedExamples"
fi
fi
# filter any template directories
# Directories should always have this lib file, so
# it shouldn't confuse with anything else.
foundImages=$( find "$BUILD_MERGE_DIR" \
-maxdepth 2 \
-name install-config-common.lib 2>/dev/null \
| sed -e 's#\/install-config-common.lib##g' )
if [ "$foundImages" = "" ];then
foundImages="No locally built images found in $BUILD_MERGE_DIR"
else
echo ""
echo "Configured directories to build from."
echo "Use $0 --create --build-dir <path>"
echo "--------------------------------------------------------------------------------"
echo "$foundImages"
fi
}
# Print progress
function fxnPP()
{
printf "==== %-66s ====\n" "$1"
}
# A universal error checking function. Invoke as:
# fxnEC <command line> || exit 1
# Example: fxnEC cp ./foo /home/bar || exit 1
function fxnEC ()
{
# actually run the command
"$@"
# save the status so it doesn't get overwritten
status=$?
# Print calling chain (BASH_SOURCE) and lines called from (BASH_LINENO) for better debug
if [ $status -ne 0 ];then
echo "ERROR [ $status ] in ${BASH_SOURCE[1]##*/}, line #${BASH_LINENO[0]}, calls [ ${BASH_LINENO[*]} ] command: \"$*\"" 1>&2
fi
return $status
}
#
# Standardized error messaging
# Takes: error message to print as first argument
function fxnERR
{
echo ""
# Print script name, and line original macro was on.
printf "ERROR at ${BASH_SOURCE[1]##*/} line ${BASH_LINENO[1]} : %s\n" "$1"
echo ""
}
# print a header to make information stand out
function fxnHeader
{
echo ""
echo " __________________________________________________________________________ "
echo "|"
echo "| $1"
echo "|__________________________________________________________________________ "
echo ""
}
# Print a standard warning message
function fxnWarn()
{
echo ""
echo "Warning: $1"
echo ""
}
######################################################################
# Docker container manipulation functions
######################################################################
#
# Delete an existing Docker image
# Takes: value of $DELETE_TERM
# Does:
# This creates a separate script the user has to run to do the delete,
# to make sure they haven't mistyped anything
# Example: delete docker containers named 'none'
# fxnDodDelete "none"
#
# List of targets to delete, for user review
DELETE_SCRIPT="delete_these_docker_images.sh"
function fxnDeleteImage()
{
if [ "$DELETE_TERM" = "" ];then
echo "Existing images"
${DOCKER_APP} images | sed -e 's/^[ \t]*//g'
echo ""
echo "Pass a string to wildcard delete."
fi
echo "#/bin/bash" > "$DELETE_SCRIPT"
echo "# Created by $0 $INVOKED_WITH on $(date)" >> "$DELETE_SCRIPT"
chmod a+x "$DELETE_SCRIPT"
${DOCKER_APP} images | sed -e 's/^[ \t]*//g' \
| grep "$DELETE_TERM" \
| awk '{printf "# Delete: %s Tag: %s Image ID %s, Created %s %s %s, Size %s \n REPLACE_DOCKER_APP rmi --force %s \n", $1, $2, $3, $4, $5, $6, $7, $3}' \
>> "$DELETE_SCRIPT"
# Set the application
sed -i "s/REPLACE_DOCKER_APP/$DOCKER_APP/g" "$DELETE_SCRIPT"
echo " ________________________________________________________________________"
echo "| "
echo "| Run [ ./$DELETE_SCRIPT ] "
echo "| to delete the following images that matched \"$DELETE_TERM\" "
echo "|________________________________________________________________________"
echo ""
grep "Delete" "$DELETE_SCRIPT" | sed -e 's/# Delete:/ /g'
# echo all the text in one pass
{
echo ""
# show what's left when the script is run
echo "$DOCKER_APP images | sed -e 's/^[ \t]*//g' "
echo ""
# clean up after ourselves
echo "rm ./$DELETE_SCRIPT"
echo ""
} >> "$DELETE_SCRIPT"
if [ "$DO_DELETE_NOW" = "TRUE" ];then
./"$DELETE_SCRIPT"
fi
} #fxnDeleteImage
#
# PreProcess functions create copies of the specified file in the directory that
# the container will be made from, then replace the REPLACE_* terms with valid
# values, specific to the container.
# Takes: Path to where the file will be written out
# Optional argument to indicate non native architecture container: use qemu
# Does: Generates a Dockerfile.create file in the container creation directory.
function fxnPreProcessDockerFile()
{
local destDir="$1"
local useQEMU="$2"
local domainName
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/Dockerfile.create ];then
fxnPP "Not creating $destDir/Dockerfile.create as it exists"
else
domainName=$( dnsdomainname )
if [ "$domainName" = "" ];then
# Sometimes this comes back empty.
domainName="no-domain-name"
fi
fxnPP "Creating $destDir/Dockerfile.create from [ $USE_TEMPLATE_PATH ]"
cp "${USE_TEMPLATE_PATH}"/Dockerfile.template "$destDir"/Dockerfile.create
# now edit the copy
if [ "$FROM_IMAGE_TYPE" != "" ];then
sed -i "s#REPLACE_IMAGE_FROM\$#$FROM_IMAGE_TYPE#g" "$destDir"/Dockerfile.create
sed -i "s#REPLACE_DUE_IMAGE_TYPE_LABEL\$#$DUE_IMAGE_TYPE_LABEL#g" "$destDir"/Dockerfile.create
# if the maintainer/organization is still set to the default, change it to the current user.
sed -i "s/[email protected]/$(whoami)@${domainName}/" "$destDir"/Dockerfile.create
# Embed description of the image
sed -i "s#REPLACE_IMAGE_DESCRIPTION#\"$IMAGE_DESCRIPTION\"#g" "$destDir"/Dockerfile.create
# Log the version of DUE that created this container.
sed -i "s/REPLACE_DUE_CREATION_VERSION/${DUE_VERSION}/" "$destDir"/Dockerfile.create
# If qemu is to be installed, uncomment the copy of it
if [ "$useQEMU" != "" ];then
fxnPP "Configuring Dockerfile to copy in qemu as the first step."
sed -i "s/#REPLACE_DUE_INSTALL_QEMU//#g" "$destDir"/Dockerfile.create
fi
fi
fi
}
# Takes: Path to where the file will be written out
#
# This file holds things a container user might want in their bashrc.
# For example, the current due-bashrc.template supports setting the
# PS1 prompt and a function that will print the active branch of
# a git directory.
#
# Due edits the container's /etc/bashrc.bashrc to source this file on
# user login.
# If the user wants any other .bashrc configuration, they'll have to
# source it manually.
function fxnPreProcessDueBashrc()
{
local destDir="$1"
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/etc/due-bashrc ];then
fxnPP "Not creating $destDir/etc/due-bashrc as it exists"
else
fxnPP "Creating $destDir/etc/due-bashrc from [ $USE_TEMPLATE_PATH ]"
fxnEC cp "${USE_TEMPLATE_PATH}"/filesystem/etc/due-bashrc.template "$destDir"/due-bashrc || exit 1
# Default the sourcing of the /etc/due-bashrc file
USE_DUE_BASHRC="TRUE"
if [ "$USE_DUE_BASHRC" = "TRUE" ];then
# if enabling the bashrc to run
sed -i "s/REPLACE_ENABLE_DUE_BASHRC/TRUE/g" "$destDir"/due-bashrc
fi
# If NEW_PROMPT is set, put it in here, otherwise leave empty
sed -i "s/DUE_REPLACE_PROMPT/$NEW_PROMPT/g" "$destDir"/due-bashrc
fi
}
# Have the container identify itself on login
# Takes: Path to where the file will be written out
function fxnPreProcessDockerLoginMessage()
{
local destDir="$1"
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/etc/DockerLoginMessage ];then
fxnPP "Not creating $destDir/etc/DockerLoginMessage as it exists"
else
fxnPP "Creating $destDir/etc/DockerLoginMessage from [ $USE_TEMPLATE_PATH ]"
fxnEC cp "${USE_TEMPLATE_PATH}"/filesystem/etc/DockerLoginMessage.template "$destDir"/DockerLoginMessage || exit 1
# now edit the copy (/s may be in the string. Delineate sed with #s)
if [ "$IMAGE_DESCRIPTION" != "" ];then
sed -i "s#REPLACE_IMAGE_DESCRIPTION#$IMAGE_DESCRIPTION#g" "$destDir"/DockerLoginMessage
fi
fi
}
# Set up the script to be run BEFORE configuration
# Takes: Path to where the file will be written out
function fxnPreProcessPreInstallConfig()
{
local destDir="$1"
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/pre-install-config.sh ];then
fxnPP "Not creating $destDir/pre-install-config.sh as it exists"
else
fxnPP "Creating $destDir/pre-install-config.sh from [ $USE_TEMPLATE_PATH ]"
cp "${USE_TEMPLATE_PATH}"/pre-install-config.sh.template "$destDir"/pre-install-config.sh
# now edit the copy
if [ "$IMAGE_DESCRIPTION" != "" ];then
sed -i "s/REPLACE_IMAGE_NAME/$NEW_IMAGE_NAME/g" "$destDir"/pre-install-config.sh
fi
fi
}
# Set up the script to be run AFTER configuration
# Takes: Path to where the file will be written out
function fxnPreProcessPostInstallConfig()
{
local destDir="$1"
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/post-install-config.sh ];then
fxnPP "Not creating $destDir/post-install-config.sh as it exists"
else
# the script just invokes a function defined in install-config-common.lib, so there's nothing
# to replace at the moment, but if there was, it would be here
fxnPP "Creating $destDir/post-install-config.sh from [ $USE_TEMPLATE_PATH ]"
fxnEC cp "${USE_TEMPLATE_PATH}"/post-install-config.sh.template "$destDir"/post-install-config.sh || exit 1
# Make it executable as the template versions should not run and do not have their executable bits set.
# Also, Lintian will flag this.
chmod a+x "$destDir"/post-install-config.sh
#
# Any variable replacement via sed would happen here
#
fi
}
# Set up any of the common script utilities
# Takes: Path to where the file will be written out
function fxnPreProcessInstallConfigLib()
{
local destDir="$1"
# If there is an existing copy, do not overwrite.
# user may have custom modifications.
if [ -e "$destDir"/install-config-common.lib ];then
fxnPP "Not creating $destDir/install-config-common.lib as it exists"
else
fxnPP "Creating $destDir/install-config-common-lib from [ $USE_TEMPLATE_PATH ]"
fxnEC cp "${USE_TEMPLATE_PATH}"/install-config-common-lib.template "$destDir"/install-config-common.lib || exit 1
if [ "$IMAGE_DESCRIPTION" != "" ];then
sed -i "s/REPLACE_IMAGE_NAME/$NEW_IMAGE_NAME/g" "$destDir"/install-config-common.lib
fi
fi
}
#
# Create a new Docker Image
# Takes: FROM_IMAGE_TYPE - source image to build on
# NEW_IMAGE_NAME - Local name for image (my-stretch-build, for example)
# PLATFORM - specify architecture that may be different from the host. (optional)
# image patch dir - path to directory holding files to apply to the image before template
function fxnMakeNewDockerImage()
{
local imageFrom="$1"
local imageName="$2"
local platformArg
local imagePatchDir
# if the architecture is different from the host
local platformArch
# If image should be pulled from remote registry
local pullImage
local imageID
local imageTag
# Store the build command to be run, so it can be printed out to the user
local dockerBuildCommand=""
if [ "$2" = "" ];then
# 3rd argument is optional
fxnERR "Failed to pass enough arguments to fxnMakeNewDockerImage(). Exiting."
exit 1
fi
if [ "$imageFrom" = "" ];then
echo "ERROR: Must specify source image to use."
# List all target directories as a hint.
# common-template isn't a target directory
#fxnListContainersForCreation
echo "$KNOWN_IMAGES"
exit 1
fi
if [ "$3" != "" ];then
# pass the platform specifying architecture specification along.
# Ex: linux/aarch64
platformArg=" --platform $3"
platformArch=${3##*/}
fi
if [ "$4" != "" ];then
# Applying patches from this directory to the base image
imagePatchDir="$4"
fi
# DOCKER_IMAGE_TAG will have been initialized to 'latest' by default,
# or will have been overridden by the user.
# Leaving this around in case there's a need to reference the base
# container it was created from.
# DOCKER_IMAGE_TAG="$( echo "$imageFrom" | tr ':' '-' | tr '/' '-' )"
# Name of image and corresponding tag
CONFIGURED_NAME_AND_TAG=due-${imageName}:${DOCKER_IMAGE_TAG}
if [ "$(command -v "${DOCKER_APP}")" = "" ];then
if [ -e /.dockerenv ];then
fxnERR "Docker or podman was not found, but you are already running in a container."
else
fxnERR "Docker or podman was not found! You should:"
echo "1 - Install with: sudo $PACKAGE_UPDATE ; sudo $PACKAGE_INSTALL $REQUIRED_PACKAGES "
echo "2 - Add yourself to the docker group: sudo /usr/sbin/usermod -a -G docker $(whoami)"
echo "3 - Consider the recommended packages: sudo $PACKAGE_INSTALL $RECOMMENDED_PACKAGES "
echo "4 - Activate docker group membership: log yourself out and log in again."
echo "Exiting."
fi
exit 1
fi
#
# Create a directory that will have a all the files that go into
# the container, if it hasn't already been created.
#
if [ ! -e "${BUILD_MERGE_DIR}"/"${imageName}" ];then
if [ "$MERGE_IN_TEMPLATE_PATH" != "" ];then
# User has specified a template directory to pull in
# This sets USE_TEMPLATE_PATH
fxnGenerateTemplate "$MERGE_IN_TEMPLATE_PATH"
fi
#
# Copy everything that isn't a template file from the template directory
# to here. This allows the transport of user included files without
# having to specifically handle them.
# Template files, by their nature, will have to be dealt with on an
# individual basis.
#
# --archive - recursively preserve everything
# --verbose - show copy
#
fxnHeader "Creating directory to be used for container build at: [ $imageName ]"
fxnPP "Copying non-template files from [ $USE_TEMPLATE_PATH ] to [ $imageName ]"
if [ ! -e /usr/bin/rsync ];then
fxnERR "rsync is not installed. Try: sudo $PACKAGE_UPDATE; sudo $PACKAGE_INSTALL rsync"
exit 1
fi
fxnEC rsync \
--archive \
--verbose \
--exclude=*.template \
"${USE_TEMPLATE_PATH}"/ "${BUILD_MERGE_DIR}"/"${imageName}" || exit 1
# Files added to the container from the container-patches directory
#
if [ "$imagePatchDir" != "" ];then
fxnPP "Copying files from [ $imagePatchDir ] to ${BUILD_MERGE_DIR}/${imageName}"
fxnEC rsync --archive --verbose "$imagePatchDir" "${BUILD_MERGE_DIR}/${imageName}"
fi
fxnListDirContents "${BUILD_MERGE_DIR}/${imageName}"
#
# Check for cross architecture containers
# These can be run in emulation if they have QEMU as part of the container
# Currently only supporting armel, but more can be added.
# Map host architecture names to Docker names. Expect to add here as testable
# cases show up.
HOST_ARCH=$( uname -m )
case $HOST_ARCH in
"x86_64" )
HOST_ARCH="amd64"
;;
"aarch64" )
HOST_ARCH="arm64"
;;
"i686" )
HOST_ARCH="i386"
;;
* )
fxnERR "Cannot map [ $HOST_ARCH ] to a Docker container type. Exiting."
exit 1
;;
esac
# expect this to be amd64, arm, etc
# Super convenient that qemu arm seems to handle all the variants (armhf, arm64, armel...)
# So, has the image already been pulled?
imageArchitecture=$( ${DOCKER_APP} inspect -f '{{.Architecture}}' "${imageFrom}" 2>/dev/null )
if [ "$imageArchitecture" = "" ];then
# Do not have an image with that name
fxnPP "No image [ $imageFrom ] found. Will pull."
pullImage="TRUE"
else
fxnPP "Found existing image [ $imageFrom ]. Will not pull. Use --platform linux/amd64|aarch64 to override."
fi
if [ "$platformArg" != "" ];then
# If the user specified a platform for the image, and it is not here.
if [ "$imageArchitecture" != "$platformArch" ];then
fxnPP "Specified $platformArg and there was no match for [ $imageFrom ]. Will pull."
pullImage="TRUE"
fi
fi
if [ "$pullImage" = "TRUE" ];then
# Well, get a copy.
fxnPP "[ $imageFrom ] not found. Pulling an image to check its architecture before proceeding."
fxnEC "${DOCKER_APP}" pull $platformArg "$imageFrom" || exit 1
# Use image id rather than name here. Red Hat images use the same name for arm64 and x86 images, so
# if both are present, one will hide the other. Tag the image with the architecture to keep track.
# the host's.
# inspect handles finding the latest image in case there are multiple, and will take an imageFrom
# string that may have more than one '/' in it, in case there are multiple.
# However, it returns the ID with 'sha256:' stuck to the front.
imageID=$( "${DOCKER_APP}" inspect --format "{{.ID}}" "${imageFrom}" | sed -e 's/sha256://' )
if [ "$imageID" = "" ];then
fxnERR "Failed to properly id [ $imageFrom ]. Exiting."
exit 1
fi
# May want to remove platformArg logic and just key off of Docker vs Podman
if [ "$platformArg" != "" ];then
if [ "${DOCKER_APP}" = 'docker' ];then
# Print only the fields to the left and right of the last '/' to strip registry references.
imageFrom=$( echo $imageFrom | awk -F '/' '{print$(NF-1)"/"$NF}' )
fi
fi
imageArchitecture=$( ${DOCKER_APP} inspect -f '{{.Architecture}}' "$imageID" )
if [ "$platformArg" != "" ];then
# Print only the fields to the left and right of the last '/' to strip registry references.
# imageFrom=$( echo $imageFrom | awk -F '/' '{print$(NF-1)"/"$NF}' )
# Sanitize the tag and source by changing any : to -, as having
# multiple will break the auto-tagging below.
imageFrom=${imageFrom//:/-}
# Isolate the architecture
imageTag=${platformArg##*/}
# Tag to use
imageTag=${imageTag//:/-}
if [ "$imageArchitecture" = "" ];then
# If the architecture can't be determined at this point, it is a bad sign,
# but maybe not fatal. Continue and see what happens.
fxnWarn "Failed to determine architecture of [ $imageFrom ]. Guessing same as host."
imageArchitecture="$HOST_ARCH"
fi
# Tag it with the architecture
imageTag="$imageArchitecture"
# Tag the non-host architecture image, as its name may not be different on a per-architecture basis.
fxnEC "${DOCKER_APP}" tag $imageID ${imageFrom}:${imageTag} || exit 1
fxnPP "Tagged new image with ${imageFrom}:${platformArg##*/}"
fi
fi
#
# Take advantage of a naming convention to figure out which qemu-<arch>-static to copy.
#
if [ "$HOST_ARCH" != "$imageArchitecture" ];then
#
# ...although the naming convention doesn't always match.
# start stacking exceptions here for clarity.
case $imageArchitecture in
arm64 )
imageArchitecture="aarch64"
;;
esac
fxnPP "Image architecture [ $imageArchitecture ] does not match host's [ $HOST_ARCH ]. Copying qemu-${imageArchitecture}-static in for emulation."
# Have qemu-*-static in there so it can run
fxnPP "Adding qemu-${imageArchitecture}-static to $imageName/filesystem/usr/bin/qemu-${imageArchitecture}-static."
# qemu-user
STATIC_QEMU_SOURCE="/usr/bin/qemu-${imageArchitecture}-static"
if [ ! -e $STATIC_QEMU_SOURCE ];then
fxnWarn "Host system must have qemu-${imageArchitecture}-static to run an ${imageArchitecture} container. Try: sudo $PACKAGE_INSTALL qemu-user-static."
echo "Trying using the template's filesystem/usr/bin/"
STATIC_QEMU_SOURCE="${USE_TEMPLATE_PATH}/filesystem/usr/local/bin/qemu-${imageArchitecture}-static"
if [ ! -e "$STATIC_QEMU_SOURCE" ];then
fxnERR "Failed to find [ $STATIC_QEMU_SOURCE ] to run an ${imageArchitecture} container. Try copying it in under there."
# Clean this up so that a re-run does not report success despite not having the emulation support.
echo "Deleting partially built image at: ${BUILD_MERGE_DIR}/${imageName}"
rm -rf "${BUILD_MERGE_DIR}/${imageName}"
exit 1
fi
fi
if [ ! -e /proc/sys/fs/binfmt_misc/qemu-${imageArchitecture} ];then
fxnERR "Failed to find qemu-${imageArchitecture} under /proc/sys/fs/binfmt_misc. Packages may have to be installed or a restart is required."
# exit 1
fi
# Copy the host system's QEMU for image architecture to a location where it will be
# installed in the container.
if [ ! -e "${BUILD_MERGE_DIR}"/"$imageName"/filesystem/usr/bin ];then
mkdir -p "${BUILD_MERGE_DIR}"/"$imageName"/filesystem/usr/bin
fi
# if no static qemu already there, copy the system's in.
if [ ! -e "${BUILD_MERGE_DIR}/$imageName/filesystem/usr/bin/qemu-${imageArchitecture}-static" ];then
fxnEC cp -a "$STATIC_QEMU_SOURCE" "${BUILD_MERGE_DIR}"/"$imageName"/filesystem/usr/bin/qemu-${imageArchitecture}-static || exit 1
fi
# Set this to pass to Dockerfile configuration so that qemu-static
# will be present in the image _before_ Docker executes any commands in the image.
QEMU_STATIC="qemu-${imageArchitecture}-static"
fi
#
# Run preprocessing on all templates and put them in the
# appropriate directory locations to configure the image
fxnHeader "Replacing REPLACE strings in template files with supplied values."
# Supply directory destination for output of processed template
fxnPreProcessDockerLoginMessage "${BUILD_MERGE_DIR}"/"$imageName"/filesystem/etc
# Changes to the bashrc
fxnPreProcessDueBashrc "${BUILD_MERGE_DIR}"/"$imageName"/filesystem/etc
# Common functions used by pre/post install
fxnPreProcessInstallConfigLib "${BUILD_MERGE_DIR}"/"$imageName"
# First script to run
fxnPreProcessPreInstallConfig "${BUILD_MERGE_DIR}"/"$imageName"
# Last script to run
fxnPreProcessPostInstallConfig "${BUILD_MERGE_DIR}"/"$imageName"
# Set parameters in the generated Docker file
# If qemu needs to be installed, QEMU_STATIC will not be empty
fxnPreProcessDockerFile "${BUILD_MERGE_DIR}"/"$imageName" "$QEMU_STATIC"
#
# Put the current version of DUE in the incoming Dockerfile.config for
# any future compatibility determinations
#
fxnPP "Embedding current version of DUE into the new image."
{
echo ""
echo "#Version of DUE this was created with, for future compatibility"
echo "LABEL DUECreationVersion=$DUE_VERSION"
echo ""
} >> "${BUILD_MERGE_DIR}"/"${imageName}"/Dockerfile.config
#
# Merge any information from Dockerfile.config into the Dockerfile.create
# Put this right after DUE_INSERT_CONFIG
sed -i "/# DUE_INSERT_CONFIG/ r ${BUILD_MERGE_DIR}/${imageName}/Dockerfile.config" "${BUILD_MERGE_DIR}"/"${imageName}"/Dockerfile.create
fxnHeader "Created configuration directory [ $imageName ]"
echo ""
fxnPP "For additional configuration, modify the files in $imageName"
echo ""
DO_CREATE_IMAGE_NOW="TRUE"
if [ "$DO_CREATE_IMAGE_NOW" = "TRUE" ];then
# Create it all in one shot
fxnHeader "Creating the new image with: $0 --create --build-dir ${BUILD_MERGE_DIR}/$imageName"
$0 --create \
--build-dir "${BUILD_MERGE_DIR}/$imageName" \
--tag "$DOCKER_IMAGE_TAG" \
--dockerarg "$DOCKER_SPECIFIC_BUILD_ARGS"
else
fxnHeader " To create the new image, run: $0 --create --build-dir ${BUILD_MERGE_DIR}/$imageName"
fi
else
# Directory was already there. Not overwriting it in case the user had
# modifications.
echo ""
fxnPP "Confirmed directory $imageName exists."
echo ""
fxnPP "If there were changes in the /templates directory, delete this particular build with: "
echo " rm -r ${BUILD_MERGE_DIR}/$imageName ; rm -r ${BUILD_MERGE_DIR}/${templateName}"
echo ""
echo " Or delete all of ${BUILD_MERGE_DIR} with:"
echo " $0 --create --clean "
echo ""
fxnPP "To create the new image, run: $0 --create --build-dir ${BUILD_MERGE_DIR}/$imageName"
echo ""
fi
#
# Use the directory of files that was generated above to create the
# new Docker image.
#
if [ "$DO_CREATE_NEW_IMAGE" = "TRUE" ];then
# if this doesn't exist at this point, things have gone very wrong.
fxnEC cd "${BUILD_MERGE_DIR}/$imageName" || exit 1
# Sanity check the code doing most of the work
# Podman/Docker should return success
DockerAppPath=$( which "$DOCKER_APP" )
if [ "$DockerAppPath" = "" ];then
fxnERR "Failed to find path for [ $DOCKER_APP ]. Is it installed?"
exit 1
fi
if [ "$DOCKER_APP" = 'docker' ];then
# Check if the Docker daemon is running.
if [ ! -e /var/run/docker.sock ];then
fxnERR "Is Docker running?"
exit 1
fi
fi