-
Notifications
You must be signed in to change notification settings - Fork 502
/
anago
executable file
·1893 lines (1679 loc) · 68.2 KB
/
anago
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
#!/usr/bin/env bash
#
# Copyright 2016 The Kubernetes Authors All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Set PROGram name
PROG=${0##*/}
########################################################################
#+
#+ NAME
#+ $PROG - Kubernetes Release Tool
#+
#+ SYNOPSIS
#+ $PROG <branch> [--yes] [--nomock] [--rc] [--official]
#+ [--clean] [--stage] [--github-release-draft]
#+ [--buildversion=<jenkins build version>]
#+ [--gcrio_path=<full gcr.io path>]
#+ [--basedir=<alt base work dir>]
#+ [--security_layer=/path/to/pointer/to/script]
#+ [--exclude-suites="<suite> ..."]
#+ [--build-at-head]
#+ [--prebuild] [--buildonly]
#+ [--mailto=<email1>,<email2>] [--gcb]
#+ [--tmpdir=</alt/tmp>]
#+ $PROG [--helpshort|--usage|-?]
#+ $PROG [--help|-man]
#+
#+ DESCRIPTION
#+ $PROG produces Kubernetes releases.
#+
#+ Driven by the named source <branch> and an optional [--rc] or
#+ [--official] flag, $PROG determines what needs to be released
#+ and calculates the version.
#+
#+ [--buildversion=] will override the automatic check for a build version
#+ Mostly used for testing as a way to re-use a known good build version.
#+
#+ What is going to be done is presented to the user and asks for
#+ confirmation before continuing.
#+
#+ All prerequisites are checked before the process begins.
#+
#+ $PROG runs in mock/dryrun mode by default. To actually execute a
#+ release, pass in the --nomock flag.
#+
#+ ++++ NEW (for 1.8+) ++++
#+ --stage writes all usual and extra artifacts to
#+ gs://$RELEASE_BUCKET/stage for later use by 'release' runs.
#+ * The 'staged' version identifier is the source JENKINS_BUILD_VERSION
#+ rather than the computed version(s) that stem from it to identify
#+ the final release artifacts. You may stage multiple source builds
#+ for potential release later
#+ * When you run a standard 'release', $PROG will look for staged bits:
#+ - LOCALLY: The complete source tree, including all commits & artifacts.
#+ - GCS: Artifacts only in
#+ gs://$RELEASE_BUCKET/stage/$JENKINS_BUILD_VERSION
#+ * This method works with both mock and --nomock INTERCHANGEABLY
#+ on the local disk. If relying on the GCS staging, be sure to use
#+ --nomock for both --stage and the real release run.
#+ * --stage also skips many of the usual release steps including many
#+ auth checks and pushes as it's primary function is to build and
#+ stage release bits and artifacts for later use.
#+
#+ Simply specify the <branch> you want to release from and follow the
#+ prompts. <branch> is one of master, release-1.2, etc. and the release
#+ is based on the <branch> value and the [--official] flag:
#+
#+ WARNING: --build-at-head bypasses any test analysis and builds directly
#+ at the HEAD Of the named branch. Mostly for testing.
#+
#+ Branch RC Official Type
#+ ------ -- -------- ----
#+ master alpha
#+ master alpha
#+ master X N/A
#+ master X N/A
#+ release-* beta
#+ release-* X release candidate
#+ release-* X official
#+
#+ NOTE: <branch> can exist already or not. If the branch doesn't exist,
#+ it will be branched from master as part of the run.
#+
#+ VALUES USED AND DISPLAYED DURING RUNS:
#+ * The RELEASE_VERSION dictionary is indexed by each of the types of
#+ releases that will be processed for a session (alpha,beta,rc,official)
#+ * RELEASE_VERSION_PRIME is the primary release version
#+ * GCRIO_PATH and RELEASE_BUCKET are the publish locations
#+ * Default GCRIO_PATH for non-mock runs utilizes a multi-region alias,
#+ k8s.gcr.io, and a single write alias, staging-k8s.gcr.io.
#+ * Switching between k8s.gcr.io and staging-k8s.gcr.io for the purposes
#+ of naming and pushing is handled by the tooling. If --gcrio_path
#+ overrides, only that name is used by the tooling in all cases.
#+
#+ A simple $USER check controls who can run in --nomock mode as ACLs
#+ restrict who can actually push bits anyway.
#+
#+ OPTIONS
#+ [--stage] - Write all artifacts to
#+ gs://$RELEASE_BUCKET/stage for later use
#+ [--yes] - Assume 'yes' to all queries
#+ [--nomock] - Complete an actual release with upstream
#+ pushes
#+ [--clean] - Force clean of previous session
#+ [--rc] - Release candidates on release branches only
#+ [--official] - Official releases on release branches only
#+ [--buildversion=ver] - Override Jenkins check and set a specific
#+ build version
#+ [--gcrio_path=] - Specify the full GCR path to use.
#+ defaults:
#+ - gcr.io/kubernetes-release-test (mock)
#+ - k8s.gcr.io for --nomock
#+ [--basedir=dir] - Specify an alternate base directory
#+ (default: $HOME/anago)
#+ [--security_layer=] - A file containing a path to a script to
#+ source/include:
#+ FLAGS_security_layer=/path/to/script
#+ Default: $HOME/.kubernetes-releaserc
#+ [--exclude-suites=] - Space separated list of CI suites regex to
#+ exclude from go/nogo criteria
#+ [--build-at-head] - Force a build at HEAD of named branch
#+ [--mailto=] - Comma-separated list of addresses to send
#+ announcement email to. Overrides default list.
#+ [--github-release-draft] - Force a github release draft for mock runs.
#+ The draft placeholder if left behind can
#+ cause issues with the real release.
#+ Users are prompted to remove draft releases,
#+ but the action to create them in the first
#+ place should be explicit.
#+ [--gcb] - Running from GCB
#+ [--tmpdir=] - Set an alternate temp dir
#+ [--prebuild] - Used during GCB to halt before building
#+ to allow switch to build container
#+ [--buildonly] - Used during GCB to halt after building
#+ to allow switch to release container
#+ [--help | -man] - display man page for this script
#+ [--usage | -?] - display in-line usage
#+
#+ EXAMPLES
#+ $PROG --yes master - Do a mock alpha release from master
#+ and don't stop/prompt
#+ $PROG release-1.1 - Do a mock beta release from release-1.1
#+ $PROG --rc release-1.1
#+ - Do a mock release candidate from release-1.1
#+ $PROG --official release-1.1
#+ - Do a mock official release from release-1.1
#+ $PROG --nomock --official release-1.1
#+ - Do an official release from release-1.1
#+ $PROG release-1.8 --stage --build-at-head
#+ - Stage a HEAD build locally and on GCS
#+ $PROG release-1.8 --nomock --build-at-head
#+ - Release the staged build
#+ (assuming HEAD hasn't changed)
#+
#+ FILES
#+ build/release.sh
#+
#+ SEE ALSO
#+ common.sh - common function definitions
#+ gitlib.sh - git/jenkins function definitions
#+ releaselib.sh - release/push-specific functions
#+
#+ BUGS/TODO
#+
########################################################################
# If NO ARGUMENTS should return usage, uncomment the following line:
usage=${1:-yes}
# Deal with OSX limitations out the gate for anyone that tries this there
BASE_ROOT=$(dirname $(readlink -e "$BASH_SOURCE" 2>&1)) \
|| BASE_ROOT="$BASH_SOURCE"
source $BASE_ROOT/lib/common.sh
source $TOOL_LIB_PATH/gitlib.sh
source $TOOL_LIB_PATH/releaselib.sh
# Clear or validate run state
if ((FLAGS_clean)) && [[ -f $PROGSTATE ]]; then
logrun mv $PROGSTATE $PROGSTATE.last
fi
common::validate_command_line "$ORIG_CMDLINE" || common::exit 1 "Exiting..."
# Validate command-line
common::argc_validate 1 || common::exit 1 "Exiting..."
# Set positional args
RELEASE_BRANCH=${POSITIONAL_ARGV[0]}
# Check branch format
[[ $RELEASE_BRANCH =~ $BRANCH_REGEX ]] \
|| common::exit 1 "Invalid branch name!"
# Check arg conflicts
if ((FLAGS_rc)) || ((FLAGS_official)); then
if [[ "$RELEASE_BRANCH" == "master" ]]; then
common::exit 1 "Can't do release candidate or official releases on master!"
fi
fi
if ((FLAGS_rc)) && ((FLAGS_official)); then
common::exit 1 "Can't do release candidate and official release!"
fi
###############################################################################
# FUNCTIONS
###############################################################################
###############################################################################
# common::cleanexit prog-specific override function
# Do stuff here to clean up after this specific script
# @param exit code
#
common::cleanexit () {
[[ -t 1 ]] && tput cnorm
logrun rm -rf $LOCAL_CACHE
# Reset gcloud auth context
# if GCP_USER is set then we are in a state to reset this
if ! ((FLAGS_gcb)) && [[ -n "$GCP_USER" ]]; then
logrun $GCLOUD config set account $GCP_USER
fi
if [[ -d $WORKDIR ]]; then
if ((FLAGS_gcb)); then
# Just copy the logs and files up. A completed release copies everything
archive_release --files-only
else
copy_logs_to_workdir
fi
fi
common::timestamp end
exit ${1:-0}
}
###############################################################################
# Copy logs to WORKDIR
copy_logs_to_workdir () {
local f
local logfiles=$(ls $LOGFILE{,.[0-9]} 2>/dev/null || true)
for f in $logfiles; do
common::strip_control_characters $f
common::sanitize_log $f
done
logecho -n "Copying $LOGFILE{,.[0-9]} to $WORKDIR: "
logrun -s cp -f $logfiles $WORKDIR
}
###############################################################################
# Ensures all registries that will be used during both mock and --nomock
# runs allow write access so we don't fall over later
# @param registries - A space separated list of registries
#
ensure_registry_acls () {
local registries=($1)
local emptyfile="$TMPDIR/empty-file.$$"
local gs_path
local r
local retcode=0
local artifact_namespace
# Make an empty file to test uploading
logrun touch $emptyfile
# Short of creating a hardcoded map of project-id to registry, translating
# _ to - seems to be a simple rule to keep this, well, simple.
for r in ${registries[*]//_/-}; do
# In this context, "google-containers" is still used
if [[ "$r" == "$GCRIO_PATH_PROD" ]]; then
artifact_namespace="google-containers"
else
artifact_namespace="${r/gcr.io\//}"
fi
gs_path="gs://artifacts.$artifact_namespace.appspot.com/containers"
logecho -n "Checking write access to registry $r: "
if logrun $GSUTIL -q cp $emptyfile $gs_path && \
logrun $GSUTIL -q rm $gs_path/${emptyfile##*/}; then
logecho $OK
else
logecho $FAILED
((retcode++))
fi
# Always reset back to $USER
((FLAGS_gcb)) || logrun $GCLOUD config set account $GCP_USER
done
logrun rm -f $emptyfile
return $retcode
}
###############################################################################
# Checks GCP users to ensure they are logged in and accessible
# return 1 on failure
ensure_gcp_users () {
local user
local -a users_to_check
users_to_check+=($GCP_USER)
for user in ${users_to_check[*]}; do
logecho -n "Checking cloud account/auth $user: "
if (logrun $GCLOUD config set account $user && \
logrun $GCLOUD docker -- version >/dev/null 2>&1); then
logecho -r "$OK"
else
logecho -r "$FAILED"
logecho
logecho "$user is not in the credentialed accounts list!"
logecho "Sign in with:"
logecho
logecho "$ $GCLOUD auth login $user"
return 1
fi
done
}
###############################################################################
# Checks release-specific prereqs
# Sets global GCLOUD_PROJECT
# @param package - A space separated list of packages to verify exist
# return 1 on failure
#
PROGSTEP[check_prerequisites]="CHECK PREREQUISITES"
check_prerequisites () {
local rb
# We check for docker here as a word which passes for docker-ce and
# docker-engine as docker packages are in transiation of deprecating
# docker-engine
common::check_packages jq pandoc docker ${PREREQUISITE_PACKAGES[*]} \
|| return 1
common::check_pip_packages yq || return 1
security_layer::auth_check 2 || return 1
logecho -n "Checking Docker version: "
docker_version=$(docker version --format '{{.Client.Version}}' | cut -d"-" -f1)
if [[ ${docker_version} != 18.06.0 && ${docker_version} < 18.06.0 ]]; then
logecho "Minimum docker version 18.06.0 is required for " \
"creating and pushing manifest images[found: ${docker_version}]"
return 1
fi
logecho -r "$OK"
# Ensure that the docker command line supports the manifest images
export DOCKER_CLI_EXPERIMENTAL=enabled
# TODO: Remove this section once docker manifest command promoted
# from Experimental
logecho -n "Verifying Docker CLI Experimental status: "
cli_experimental=$(docker version --format '{{.Client.Experimental}}' | cut -d"-" -f1)
if [[ "${cli_experimental}" == "false" ]]; then
logecho "Docker Client Experimental flag is false, should be enabled to " \
"push the manifest images"
logecho "More info: https://docs.docker.com/edge/engine/reference/commandline/manifest_create/"
return 1
fi
logecho -r "$OK"
if ! ((FLAGS_gcb)); then
ensure_gcp_users || return 1
fi
# Verify write access to all container registries that might be used
# to ensure both mock and --nomock runs will work.
ensure_registry_acls "${ALL_CONTAINER_REGISTRIES[*]}" || return 1
logecho -n "Checking cloud project state: "
GCLOUD_PROJECT=$($GCLOUD config get-value project 2>/dev/null)
if [[ -z "$GCLOUD_PROJECT" ]]; then
logecho -r "$FAILED"
logecho "No account authorized through gcloud. Please fix with:"
logecho
logecho "$ gcloud config set project <project id>"
return 1
fi
logecho -r "$OK"
# Verify write access to $WRITE_RELEASE_BUCKETS
for rb in ${WRITE_RELEASE_BUCKETS[*]}; do
release::gcs::check_release_bucket $rb || return 1
done
}
###############################################################################
# Updates openapi-spec version files
# Uses the RELEASE_VERSION global dict
# @param label - label index to RELEASE_VERSION
rev_openapi_versions () {
local label=$1
local -a swagger_files=("api/openapi-spec/swagger.json"
"federation/apis/openapi-spec/swagger.json")
local f
#########################################################################
# NOTE: To avoid a significant time sink from running update-all.sh as
# well as avoiding running the master-only doc stub creator
# update-generated-docs.sh, we simply and quickly modify the files that
# are dependent on a $version_file change
# If/when the great update-all.sh revolution occurs and this runs in
# under a minute, we can probably run that instead.
#########################################################################
for f in ${swagger_files[*]}; do
# Handle the recent move of federation out of k/k
if [[ ! -f $f ]]; then
logecho "Skipping $f..."
continue
fi
# Strip any suffix off incoming RELEASE_VERSION[$label]
sed -i -r 's,"version": "'${VER_REGEX[release]}'","version": "'${RELEASE_VERSION[$label]//-*}'",g' $f
logrun git add $f
done
# Only commit if the files have changed.
if [[ -n "$(git status -s)" ]]; then
logecho -n "Committing openapi-spec versioned files: "
logrun -s git commit -am \
"Kubernetes version ${RELEASE_VERSION[$label]} openapi-spec file updates"
fi
}
###############################################################################
# Update $CHANGELOG_FILE on master
PROGSTEP[generate_release_notes]="GENERATE RELEASE NOTES"
generate_release_notes () {
local release_tars=$TREE_ROOT/_output-$RELEASE_VERSION_PRIME/release-tars
local action="Update"
logecho -n "Generating release notes: "
logrun -s relnotes $RELEASE_VERSION_PRIME --release-tars=$release_tars \
--branch=${PARENT_BRANCH:-$RELEASE_BRANCH} --htmlize-md \
--markdown-file=$RELEASE_NOTES_MD \
--html-file=$RELEASE_NOTES_HTML \
--release-bucket=$RELEASE_BUCKET || return 1
logecho -n "Checkout master branch to make changes: "
logrun -s git checkout master || return 1
# The fetch and rebase before editing $CHANGELOG_FILE
# avoids merge conflicts if another release cut
# completed while we were building this one.
logecho -n "Fetch origin/master to get latest $CHANGELOG_FILE: "
logrun -s git fetch origin master || return 1
logecho -n "Rebase on origin/master before editing $CHANGELOG_FILE: "
logrun -s git rebase origin/master || return 1
if [[ ! -f $CHANGELOG_FILE ]]; then
cat<<EOF > $CHANGELOG_FILE
<!-- BEGIN MUNGE: GENERATED_TOC -->
<!-- END MUNGE: GENERATED_TOC -->
<!-- NEW RELEASE NOTES ENTRY -->
EOF
action="Add"
logecho -n "$CHANGELOG_FILE not found. Creating: "
logrun -s git add $CHANGELOG_FILE
fi
logecho -n "Insert $RELEASE_VERSION_PRIME notes into $CHANGELOG_FILE: "
# Pipe to logrun() vs using directly, because quoting.
sed -i -e 's/<!-- NEW RELEASE NOTES ENTRY -->/&\n/' \
-e "/<!-- NEW RELEASE NOTES ENTRY -->/r $RELEASE_NOTES_MD" \
$CHANGELOG_FILE | logrun -s
logecho -n "Update $CHANGELOG_FILE TOC: "
logrun -s common::mdtoc $CHANGELOG_FILE || return 1
logecho -n "Committing $CHANGELOG_FILE: "
logrun -s git commit -am \
"$action $CHANGELOG_FILE for $RELEASE_VERSION_PRIME." \
|| return 1
# Sync $CHANGELOG_FILE to release-* branch and clear all others
if [[ "$RELEASE_BRANCH" =~ release- ]]; then
logecho -n "Checkout $RELEASE_BRANCH branch to make changes: "
logrun -s git checkout $RELEASE_BRANCH || return 1
logecho -n "Remove any previous CHANGELOG-*.md files: "
logrun -s git rm -f CHANGELOG-*.md || return 1
logecho -n "Copy master $CHANGELOG_FILE to $RELEASE_BRANCH branch: "
logrun -s git checkout master -- $CHANGELOG_FILE || return 1
logecho -n "Committing $CHANGELOG_FILE: "
logrun -s git commit -am \
"Add/Update $CHANGELOG_FILE for $RELEASE_VERSION_PRIME." \
|| return 1
fi
}
##############################################################################
# Checkout a git object (branch/tag/commit hash)
# @param label - The label to process
checkout_object () {
local label="$1"
local version="${RELEASE_VERSION[$label]}"
local commit
local tree_object="$RELEASE_BRANCH"
local branch_arg
local branch_point
# When building and packaging on GCB, we split up the build and packaging
# process so the prepared tree contains all of the tags and file changes
# for the 2 release versions being built.
# In that case, if the tag already exists, simply check it out for build
# purposes.
if ((FLAGS_gcb)) && ! ((FLAGS_prebuild)) && \
git rev-parse "$version" >/dev/null 2>&1; then
logecho -n "Checking out $version: "
logrun -s git checkout $version || return 1
return 0
fi
# Only check and extract VER_REGEX[build] if JENKINS_BUILD_VERSION is set.
# It will not be set when branching from a tag (ex. release-1.7.5)
# We do want to capture the sha1 however any time it is available
# for use below in setting branch_point and tree_object. Those cases are
# covered by the fact that branch_point defaults to BRANCH_POINT (set globally
# earlier in the pipeline) and in the case where we're just doing a straight
# alpha release from master.
if [[ -n $JENKINS_BUILD_VERSION ]]; then
if ! [[ $JENKINS_BUILD_VERSION =~ ${VER_REGEX[build]} ]]; then
logecho "Unable to set checkout point for release!" \
"Invalid JENKINS_BUILD_VERSION=$JENKINS_BUILD_VERSION"
return 1
fi
# Get the sha1 from VER_REGEX[build]
commit="${BASH_REMATCH[2]}"
fi
# NOTE: For new branches, files are still also updated on master (CHANGELOG).
# Therefore, while the new branch may be based on a commit earlier than
# HEAD, all master activity must occur AT HEAD.
if [[ -n "$PARENT_BRANCH" ]]; then
if [[ $RELEASE_VERSION_PRIME == $version ]]; then
if [[ $tree_object =~ release- ]] && \
! git rev-parse $tree_object >/dev/null 2>&1; then
# Only create/reset (-B) and set a branch_point if the *release-*
# branch doesn't already exist locally
branch_arg="-b"
# Use BRANCH_POINT if set, otherwise, the hash from
# JENKINS_BUILD_VERSION
branch_point=${BRANCH_POINT:-$commit}
fi
else
# if this is not the PRIMary version on the named RELEASE_BRANCH, use the
# parent
tree_object=$PARENT_BRANCH
fi
else
[[ $label == "alpha" ]] && tree_object="$commit"
fi
# The above code determines what and how we should check out and where we
# should branch off. This information is captured in the variables
# `$branch_arg`, `$tree_object`, and `$branch_point`.
#
# Previously, when we didn't use the `$checkout_args` array to collect all
# the arguments to `git checkout ...`, the above mentioned variables were
# passed unquoted to the `logrun` function. This had some interesting
# consequences:
# - empty variables, because they were not quoted, did not make it into the
# function, they basically vanished
# - (potential) whitespaces or any characters interpreted by the shell would
# have broken this function
#
# Now we make sure to
# - quote all our arguments
# - exlicitely ignore empty variables as arguments, by not adding them to
# the `$checkout_args` array
#
# This means, `$checkout_args` will look like and result into the following
# `git checkout ...` calls:
# - ( 'release-1.12' ) => git checkout 'release-1.12'
# - ( '-b' 'release-1.12' '<shaToBanchOffFrom>' ) => git checkout '-b' 'release-1.12' '<shaToBranchOffFrom>'
local checkout_args=()
[ -n "${branch_arg}" ] && checkout_args+=( "${branch_arg}" )
[ -n "${tree_object}" ] && checkout_args+=( "${tree_object}" )
[ -n "${branch_point}" ] && checkout_args+=( "${branch_point}" )
logecho -n "Checking out $tree_object (${checkout_args[*]}): "
logrun -s git checkout "${checkout_args[@]}" || return 1
}
##############################################################################
# Commit a tag with a comment to the current HEAD of a branch
# @param label - The label to process
# @param branch - The branch to process
git_tag () {
local label=$1
local branch=$2
local commit_string
local label_common="$label"
# Ensure a common name for label in case we're using the special beta indexes
[[ "$label" =~ ^beta ]] && label_common="beta"
# Tagging
commit_string="Kubernetes $label_common release ${RELEASE_VERSION[$label]}"
logecho -n "Tagging $commit_string on $branch: "
logrun -s git tag -a -m "$commit_string" "${RELEASE_VERSION[$label]}"
}
##############################################################################
# Tag/Build a local kube_cross image based on the version in scope
PROGSTEP[local_kube_cross]="TAG/BUILD LOCAL KUBE CROSS"
local_kube_cross () {
local version
local image
local local_image
local docker_image
docker_image="${GCRIO_PATH_PROD}/kube-cross"
version="$(cat "${TREE_ROOT}/build/build-image/cross/VERSION")"
image="${docker_image}:${version}"
local_image="${docker_image}:local"
logecho -n "Checking if ${image} exists locally: "
if logrun -s docker pull "$image"; then
logecho -n "Tagging docker image ${image} as ${local_image}: "
logrun -s docker tag "$image" "$local_image"
else
logecho -n "Building docker image ${image}: "
logrun -s docker build --cache-from "$docker_image" -t "$image" \
-t "$local_image" "${TREE_ROOT}/build/build-image/cross/"
fi
}
##############################################################################
# Prepare sources for building for a given label
# @param label - The label to process
PROGSTEP[prepare_tree]="PREPARE AND TAG TREE"
prepare_tree () {
local label=$1
local label_common="$label"
local branch
# Check for tag first
if git rev-parse "${RELEASE_VERSION[$label]}" >/dev/null 2>&1; then
logecho "The ${RELEASE_VERSION[$label]} tag already exists!"
logecho "Possible reasons for this:"
logecho "* --buildversion is old."
logecho "* $WORKDIR is unclean"
return 1
fi
checkout_object $label || return 1
# Now set the branch we're on
branch=$(gitlib::current_branch)
# If this is a new branch, rev openapi-spec version files
# Because this modifies files on master and there's a good chance the
# master has moved ahead by now, the tag has to occur before this file
# change or the later rebase in gitlib::push_master() will rewrite the
# commit associated with the tag and orphan it.
if [[ -n "$PARENT_BRANCH" && $label == alpha ]]; then
git_tag $label $branch || return 1
rev_openapi_versions $label || return 1
return 0
fi
# rev openapi-spec version files on branch for beta tags
case $label in
beta*) rev_openapi_versions $label || return 1 ;;
esac
# COMMENTING OUT FOR NOW AS THE WAY FORWARD IS NOT CLEAR
# generate docs on new branches (from master) only
# If the entirety of this session is based on a branch from master
# (PARENT_BRANCH), and this iteration of prepare_tree() is operating on
# the NON-master branch itself, versionize the docs
#if [[ "$PARENT_BRANCH" == "master" && "$branch" != "master" ]]; then
# logecho -n "Generating docs for ${RELEASE_VERSION[$label]}: "
# logrun -s $TREE_ROOT/hack/generate-docs.sh || return 1
# logecho -n "Committing: "
# logrun -s git commit -am \
# "Generating docs for ${RELEASE_VERSION[$label]} on $branch."
#fi
# Clear all CHANGELOG-N.NN.md files on the branch.
# This replaces the above commented out change to the branch.
# Some unique change is required on the branch to allow it to be git
# describe'd as something other than the current tag on master
# If/when the generate-docs.sh issue is sorted out, this will become
# optional, though probably still useful anyway.
if [[ "$PARENT_BRANCH" == "master" && "$branch" != "master" ]]; then
logecho -n "Remove any previous CHANGELOG-*.md files: "
logrun -s git rm -f CHANGELOG-*.md || return 1
logecho -n "Copy master $CHANGELOG_FILE to $branch branch: "
logrun -s git checkout master -- $CHANGELOG_FILE || return 1
logecho -n "Committing deleted CHANGELOG-*.md files: "
logrun -s git commit -am \
"Delete extraneous CHANGELOG-*.md files on branch." \
|| return 1
fi
git_tag $label $branch
}
##############################################################################
# Make cross only
# This very low level split of the build required to run exactly 'make cross'
# only using the kube-cross container image
# @param label - The label to process
PROGSTEP[make_cross]="MAKE CROSS"
make_cross () {
local label=$1
local version=${RELEASE_VERSION[$label]}
local branch
checkout_object $label
branch=$(gitlib::current_branch)
# Convert a detached head state to something more readable
[[ "$branch" == HEAD ]] && branch="master (detached head)"
# For official releases we need to build BOTH the official and the beta and
# push those releases.
logecho -n "make cross only $version on $branch: "
logrun -s -v make cross-in-a-container KUBE_DOCKER_IMAGE_TAG="$version" \
|| return 1
logecho -n "Moving build _output to $BUILD_OUTPUT-$version: "
logrun -s mv $BUILD_OUTPUT $BUILD_OUTPUT-$version || return 1
}
##############################################################################
# Build the Kubernetes tree
# @param label - The label to process
PROGSTEP[build_tree]="BUILD TREE"
build_tree () {
local label=$1
local version=${RELEASE_VERSION[$label]}
local branch
checkout_object $label
branch=$(gitlib::current_branch)
# Convert a detached head state to something more readable
[[ "$branch" == HEAD ]] && branch="master (detached head)"
# For official releases we need to build BOTH the official and the beta and
# push those releases.
logecho -n "Building Kubernetes $version on $branch: "
if ((FLAGS_gcb)); then
# make_cross done separately
# OUT_DIR works in this context. Woohoo!
# KUBE_DOCKER_IMAGE_TAG needed here due to references deep in
# build/lib/release.sh
logrun -s -v make package-tarballs KUBE_DOCKER_IMAGE_TAG="$version" \
OUT_DIR=$OUT_DIR-$version || return 1
else
# TODO: Ideally we update LOCAL_OUTPUT_ROOT in build/common.sh to be
# modifiable. In the meantime just mv the dir after it's done
# Not until https://github.com/kubernetes/kubernetes/issues/23839
#logrun -s make release OUT_DIR=$BUILD_OUTPUT-${RELEASE_VERSION[$label]}
logrun -s make release KUBE_DOCKER_IMAGE_TAG="$version" || return 1
logecho -n "Moving build _output to $BUILD_OUTPUT-$version: "
logrun -s mv $BUILD_OUTPUT $BUILD_OUTPUT-$version || return 1
fi
}
##############################################################################
# Look for binary artifacts in the known loctions
# @param base - The base directory
# @param version - Version
# return 1 on failure
binary_artifacts_exist () {
local base=$1
local version=$2
local gsutil
# Detect GSC paths
[[ $base =~ ^gs:// ]] && gsutil="$GSUTIL -q"
logecho -n "Searching for artifacts at $base: "
if logrun $gsutil ls $base/gcs-stage/$version/*.tar.gz* >/dev/null 2>&1 && \
logrun $gsutil ls $base/release-images/*/*.tar >/dev/null 2>&1; then
logecho "$FOUND"
else
logecho "$NOTFOUND"
return 1
fi
}
##############################################################################
# Can be one of
# * Local disk src - symlink it
# * src.tar.gz, gcs and gcr artifacts on GCS
# Sets global STAGED_LOCATION={local,gcs}
# Sets global STAGED_BUCKET
# Modifies global RELEASE_GB
# returns 1 on failure
found_staged_location () {
local release_versions="$*"
local bucket
local jbv="$JENKINS_BUILD_VERSION"
local local_root="$BASEDIR/$PROG-$jbv/src"
local k8s_root="$local_root/k8s.io/kubernetes/_output"
local gs_stage_root
local tmp_tar_archive=$TMPDIR/$PROG-$$-src.tar.gz
logecho
# first look locally
for version in $release_versions; do
if binary_artifacts_exist $k8s_root-$version $version; then
STAGED_LOCATION="local"
else
STAGED_LOCATION=""
break
fi
done
# NOTE: We could also look for artifacts on GCS even when found local
# in order to do a bucket-to-bucket copy and save 2 minutes.
# But this isn't a typical use-case. Normally staging will happen
# exclusively on GCB and then 'release' will happen either on the desktop
# or GCB which will never find anything 'local' anyway.
if [[ $STAGED_LOCATION == "local" ]]; then
# Enjoy the local filesystem
logecho -n "Symlinking $local_root to $WORKDIR/src: "
logrun mkdir -p $WORKDIR
logrun -s ln -nsf $local_root $WORKDIR/src || return 1
RELEASE_GB="5"
return 0
fi
# If that didn't work out, look for source archive
# and artifacts on all READ_RELEASE_BUCKETS on GCS
for bucket in ${READ_RELEASE_BUCKETS[*]}; do
logecho -n "Searching for staged src.tar.gz on $bucket: "
if logrun $GSUTIL -m cp gs://$bucket/stage/$jbv/src.tar.gz \
$tmp_tar_archive; then
logecho "$FOUND"
# Set a global to skip other build steps in workflow
STAGED_LOCATION="gcs"
STAGED_BUCKET="$bucket"
break
else
logecho "$NOTFOUND"
fi
done
# If we get here and STAGED_LOCATION isn't set, give up
[[ -z "$STAGED_LOCATION" ]] && return 1
# Now ensure binary artifacts exist in the same location, or give up
for version in $release_versions; do
gs_stage_root="gs://$bucket/stage/$jbv/$version"
if ! binary_artifacts_exist $gs_stage_root $version; then
# Ensure these are unset in this case
unset STAGED_LOCATION STAGED_BUCKET
return 1
fi
done
logecho -n "Extracting $tmp_tar_archive: "
logrun -s tar xfz $tmp_tar_archive -C $WORKDIR || return 1
logrun rm -f $tmp_tar_archive
# Set disk requirements when everything's on GCS
RELEASE_GB="10"
}
##############################################################################
# Copy artifacts from GCS and between buckets as needed
# @param label - The ${RELEASE_VERSION{index}]
# @param staged_bucket - The STAGED_BUCKET
# @param release_bucket - The RELEASE_BUCKET
# return 1 on failure
copy_staged_from_gcs () {
local label=$1
local staged_bucket=$2
local release_bucket=$3
local jbv="$JENKINS_BUILD_VERSION"
local version="${RELEASE_VERSION[$label]}"
local gs_stage_root="gs://$staged_bucket/stage/$jbv/$version"
local gs_release_root="gs://$release_bucket/release/$version"
local outdir="$TREE_ROOT/_output-$version"
local type
# cp the /stage/ tarballs to /release/ on GCS directly
logecho -n "Bucket-to-bucket copy $gs_stage_root/gcs-stage artifacts" \
"to $gs_release_root: "
logrun -s $GSUTIL -mq cp -rc $gs_stage_root/gcs-stage/$version/* \
$gs_release_root/ || return 1
logrun mkdir -p $outdir/gcs-stage/$version
logecho -n "Copy staged kubernetes.tar.gz to $outdir/gcs-stage/$version: "
logrun -s $GSUTIL -q \
cp -c $gs_stage_root/gcs-stage/$version/kubernetes.tar.gz \
$outdir/gcs-stage/$version || return 1
# Copy docker images back to _output trees for later pushing
# TODO: Can we somehow stage these on GCR.IO using docker or even
# the GCS backend to eliminate the VERY EXPENSIVE --12 minutes--
# it takes to push these containers from local disk?
logrun mkdir -p $outdir/release-images
logecho -n "Copy staged docker images to $outdir/release-images: "
logrun -s $GSUTIL -mq cp -cr $gs_stage_root/release-images/* \
$outdir/release-images/ || return 1
}
##############################################################################
# Push git objects to github
# NOTES:
# * alpha is alone, pushes tags only
# * beta is alone, pushes branch and tags
# * rc is alone, pushes branch and tags
# * official pushes both official and beta items - branch and tags
# * New branch tags a new alpha on master, new beta on new branch and pushes
# new branch and tags on both
PROGSTEP[push_git_objects]="PUSH GIT OBJECTS"
push_git_objects () {
local b
local dryrun_flag=" --dry-run"
# The real deal?
((FLAGS_nomock)) && dryrun_flag=""
((FLAGS_yes)) \
|| common::askyorn -e "Pausing here. Confirm push$dryrun_flag of tags" \
"and bits" \
|| common::exit 1 "Exiting..."
logecho -n "Checkout master branch to push objects: "
logrun -s git checkout master || return 1
logecho "Pushing$dryrun_flag tags"
for b in ${!RELEASE_VERSION[@]}; do
logecho -n "* ${RELEASE_VERSION[$b]}: "
logrun -s git push$dryrun_flag origin ${RELEASE_VERSION[$b]} || return 1
done
if [[ "$RELEASE_BRANCH" =~ release- ]]; then
logecho -n "Pushing$dryrun_flag $RELEASE_BRANCH branch: "
logrun -s git push$dryrun_flag origin $RELEASE_BRANCH || return 1
# Additionally push the parent branch if a branch of branch
if [[ "$PARENT_BRANCH" =~ release- ]]; then
logecho -n "Pushing$dryrun_flag $PARENT_BRANCH branch: "
logrun -s git push$dryrun_flag origin $PARENT_BRANCH || return 1
fi
fi
# For files created on master with new branches and
# for $CHANGELOG_FILE, update the master
gitlib::push_master
}
###############################################################################
# generate the announcement text to be mailed and published
branch_announcement_text () {
cat <<EOF
Kubernetes team,
<P>
Kubernetes' $RELEASE_BRANCH branch has been created.
<P>
The release owner will be sending updates on how to interact with this branch shortly. The <A HREF=https://git.k8s.io/community/contributors/devel/sig-release/cherry-picks.md>Cherrypick Guide</A> has some general guidance on how things will proceed.
<P>
Announced by <A HREF=https://github.com/kubernetes/release>$PROG</A>, the Kubernetes Release Tool
EOF
}
###############################################################################
# generate the announcement text to be mailed and published
release_announcement_text () {
cat <<EOF
Kubernetes team,
<P>
Kubernetes $RELEASE_VERSION_PRIME has been built and pushed.
<P>
The release notes have been updated in <A HREF=https://github.com/kubernetes/kubernetes/blob/master/$CHANGELOG_FILE/#${RELEASE_VERSION_PRIME//\./}>$CHANGELOG_FILE</A> with a pointer to it on <A HREF=https://github.com/kubernetes/kubernetes/releases/tag/$RELEASE_VERSION_PRIME>github</A>:
<P>
<HR>
$(cat $RELEASE_NOTES_HTML)
<HR>
<P><BR>
Leads, the <A HREF=https://github.com/kubernetes/kubernetes/blob/master/$CHANGELOG_FILE/#${RELEASE_VERSION_PRIME//\./}>$CHANGELOG_FILE</A> has been bootstrapped with $RELEASE_VERSION_PRIME release notes and you may edit now as needed.
<P><BR>
Published by <A HREF=https://github.com/kubernetes/release>$PROG</A>, the Kubernetes Release Tool