This repository has been archived by the owner on Mar 4, 2024. It is now read-only.
forked from kubernetes/release
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathanago
executable file
·1021 lines (897 loc) · 37.9 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
#!/bin/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 [--yes] [--nomock] [--noclean] [--rc] [--official]
#+ [--buildversion=<jenkins build version>]
#+ [--basedir=<alt base work dir>] <branch>
#+ [--security_layer=/path/to/pointer/to/script]
#+ $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.
#+ ACLs control who can actually *push*.
#+
#+ 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:
#+
#+ 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_REPO and RELEASE_BUCKET are the publish locations
#+
#+
#+ OPTIONS
#+ [--yes] - Assume 'yes' to all queries
#+ [--nomock] - Complete an actual release with upstream
#+ pushes
#+ [--noclean] - Attempt to work in existing workspace
#+ Not always possible and ignored when --nomock
#+ is set
#+ [--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
#+ [--basedir=dir] - Specify an alternate base directory
#+ (default: /usr/local/google/$USER)
#+ [--security_layer=] - A file containing a path to a script to
#+ source/include:
#+ FLAGS_security_layer=/path/to/script
#+ Default: $HOME/.kubernetes-releaserc
#+ [--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
#+
#+ 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
#+ * Add statefulness and re-entrancy if the need develops
#+ * Allow --buildversion to specify the absolute version that
#+ release::set_build_version() must wait for
#+ - useful when targeting a specific hash for a branch release
#+
########################################################################
# If NO ARGUMENTS should return usage, uncomment the following line:
usage=${1:-yes}
source $(dirname $(readlink -ne $BASH_SOURCE))/lib/common.sh
source $TOOL_LIB_PATH/gitlib.sh
source $TOOL_LIB_PATH/releaselib.sh
# 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
else
if [[ -n ${BASH_REMATCH[4]} ]]; then
common::exit 1 "Only --official releases are allowed on 3-part branches!"
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 () {
tput cnorm
local f
logrun rm -rf $LOCAL_CACHE
if [[ -d $WORKDIR ]]; then
for f in $(ls $LOGFILE{,.[0-9]} 2>/dev/null); do
common::strip_control_characters $f
done
logecho -n "Copying $LOGFILE{,.[0-9]} to $WORKDIR: "
logrun -s cp -f $LOGFILE{,.[0-9]} $WORKDIR
fi
common::timestamp end
exit ${1:-0}
}
###############################################################################
# Simple ACL check to limit nomock runs to a short list of release folks
check_acls () {
if ! (echo "$USER" | egrep -q -w "$ACL_LIST"); then
echo "Live releases restricted to certain users!"
return 1
fi
}
###############################################################################
# Checks release-specific prereqs
# Sets globals GCLOUD_ACCOUNT GCLOUD_PROJECT
# @param package - A space separated list of packages to verify exist
#
check_prerequisites () {
local userat="$USER@$DOMAIN_NAME"
local tempfile=/tmp/$PROG-cp.$$
security_layer::auth_check 2
if ! common::set_cloud_binaries; then
logecho "Releasing Kubernetes requires gsutil and gcloud. Please download,"
logecho "install and authorize through the Google Cloud SDK:"
logecho
logecho "https://developers.google.com/cloud/sdk/"
return 1
fi
# TODO: Users outside google? Ask/derive domain?
# TODO: The real test here is to verify that whatever auth has access to
# do releasey things
gcloud_auth_list=$($GCLOUD auth list 2>/dev/null)
for user in $G_AUTH_USER $userat; do
logecho -n "Checking cloud account/auth $user: "
if [[ "$gcloud_auth_list" =~ -\ $user ]] && \
(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
# Ensure $USER is active to start
# The loop above finishes on $userat
gcloud_auth_list=$($GCLOUD auth list 2>/dev/null)
if ! [[ "$gcloud_auth_list" =~ -\ $userat\ ACTIVE ]]; then
logecho "$userat is not the active gcloud user!"
logecho "Set with:"
logecho
logecho "$ gcloud config set account $userat"
return 1
fi
GCLOUD_ACCOUNT=$user
# Verify write access to $RELEASE_BUCKET
# Insufficient for checking actual writability, but useful:
# ganpati list-members -g cloud-kubernetes-release -u $USER|fgrep -wq $USER
logecho -n "Checking writability and ACLs on $RELEASE_BUCKET: "
if release::gcs::ensure_release_bucket $RELEASE_BUCKET && \
logrun touch $tempfile && \
logrun $GSUTIL cp $tempfile gs://$RELEASE_BUCKET && \
logrun $GSUTIL rm gs://$RELEASE_BUCKET/${tempfile##*/} && \
logrun rm -f $tempfile; then
logecho -r "$OK"
else
logecho -r "$FAILED"
return 1
fi
logecho -n "Checking cloud project state: "
GCLOUD_PROJECT=$($GCLOUD config list project 2>/dev/null |\
awk '{project = $3} END {print project}')
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"
}
###############################################################################
# Updates pkg/version/base.go with RELEASE_VERSION
# Uses the RELEASE_VERSION global dict
# @param label - label index to RELEASE_VERSION
rev_version_base () {
local label=$1
local version_file="pkg/version/base.go"
local minor_plus
local gitmajor
local gitminor
logecho "Updating $version_file to ${RELEASE_VERSION[$label]}..."
if [[ ${RELEASE_VERSION[$label]} =~ ${VER_REGEX[release]} ]]; then
gitmajor=${BASH_REMATCH[1]}
gitminor=${BASH_REMATCH[2]}
fi
[[ "$label" =~ ^beta || "$label" =~ ^rc ]] && minor_plus="+"
sed -i -e "s/\(gitMajor *string = \)\"[^\"]*\"/\1\"$gitmajor\"/g" \
-e "s/\(gitMinor *string = \)\"[^\"]*\"/\1\"$gitminor$minor_plus\"/g" \
-e "s/\(gitVersion *string = \)\"[^\"]*\"/\1\"${RELEASE_VERSION[$label]}+\$Format:%h\$\"/g" $version_file
logecho -n "Formatting $version_file: "
logrun -s gofmt -s -w $version_file
logrun git add $version_file
logecho -n "Committing $version_file: "
logrun -s git commit -m "Kubernetes version ${RELEASE_VERSION[$label]}"
}
###############################################################################
# Update CHANGELOG on master
generate_release_notes() {
local release_tars=$TREE_ROOT/_output-$RELEASE_VERSION_PRIME/release-tars
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
logecho "Insert $RELEASE_VERSION_PRIME notes into CHANGELOG.md..."
# 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.md | logrun
logecho -n "Update CHANGELOG.md TOC: "
common::mdtoc CHANGELOG.md || return 1
logecho -n "Committing CHANGELOG.md: "
logrun -s git commit -am "Update CHANGELOG.md for $RELEASE_VERSION_PRIME." \
|| return 1
}
##############################################################################
# Prepare sources for building for a given label
# @param label - The label to process
prepare_tree () {
local label=$1
local label_common=$label
local tree_object="$RELEASE_BRANCH"
local branch_arg
local branch_point
local current_branch
# Check for tag first
if git rev-parse "${RELEASE_VERSION[$label]}" >/dev/null 2>&1; then
if ((FLAGS_noclean)); then
logecho "$ATTENTION: Found existing tag ${RELEASE_VERSION[$label]} in" \
"unclean tree during --noclean run"
logecho -n "Checking out ${RELEASE_VERSION[$label]}: "
logrun -s git checkout ${RELEASE_VERSION[$label]} || return 1
return 0
else
logecho "The ${RELEASE_VERSION[$label]} tag already exists!"
logecho "Possible reasons for this:"
logecho "* --buildversion is old."
logecho "* $WORKDIR is unclean"
return 1
fi
fi
# if this is a new branch, checkout -B
if [[ -n "$PARENT_BRANCH" ]]; then
if [[ $RELEASE_VERSION_PRIME == ${RELEASE_VERSION[$label]} ]]; then
branch_arg="-B"
branch_point=$BRANCH_POINT
else
# if this is not the PRIMary version on the named RELEASE_BRANCH, use the
# parent
tree_object=$PARENT_BRANCH
fi
elif [[ $label == "alpha" ]]; then
if ! [[ $JENKINS_BUILD_VERSION =~ ${VER_REGEX[build]} ]]; then
logecho "Unable to set checkout point for alpha release!" \
"Invalid JENKINS_BUILD_VERSION=$JENKINS_BUILD_VERSION"
return 1
fi
tree_object=${BASH_REMATCH[2]}
fi
# Checkout location
logecho -n "Checking out $tree_object: "
logrun -s git checkout $branch_arg $tree_object $branch_point || return 1
# Now set the current_branch we're on
current_branch=$(gitlib::current_branch)
# rev base.go
case $label in
beta*|rc|official) rev_version_base $label ;;
esac
# 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" && "$current_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 git commit -am \
"Generating docs for ${RELEASE_VERSION[$label]} on $current_branch."
fi
# 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 $current_branch: "
logrun -s git tag -a -m "$commit_string" "${RELEASE_VERSION[$label]}"
}
##############################################################################
# Build the Kubernetes tree
# @param version - The kubernetes version to build
build_tree () {
local version=$1
local 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
logecho -n "Building Kubernetes $version on $branch: "
# Should be an arg at some point
export KUBE_DOCKER_IMAGE_TAG="$version"
# 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 || common::exit 1
logecho -n "Moving build _output to $BUILD_OUTPUT-$version: "
logrun -s mv $BUILD_OUTPUT $BUILD_OUTPUT-$version
}
##############################################################################
# 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
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 "Rebase $RELEASE_BRANCH branch: "
#logrun git fetch origin/$RELEASE_BRANCH || return 1
#logrun git rebase origin/$RELEASE_BRANCH || return 1
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 new branches and for CHANGELOG, update the master
gitlib::push_master
}
###############################################################################
# generate the announcement text to be mailed and published
create_branch_announcement () {
cat <<+
Kubernetes team,
<P>
Kubernetes' $RELEASE_BRANCH branch has been created and is ready to accept patches.
<P>
Refer to the <A HREF=https://github.com/kubernetes/kubernetes/blob/master/docs/devel/cherry-picks.md>Cherrypick Guide</A> for instructions.
<P>
Announced by <A HREF=https://github.com/kubernetes/release>$PROG</A>, the Kubernetes Release Tool
+
}
###############################################################################
# generate the announcement text to be mailed and published
create_announcement () {
cat <<+
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.md/#${RELEASE_VERSION_PRIME//\./}>CHANGELOG.md</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.md/#${RELEASE_VERSION_PRIME//\./}>CHANGELOG.md</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
+
}
###############################################################################
# Mail out the announcement
# @param subject - the subject for the email notification
announce () {
local mailto="cloud-kubernetes-team@google.com"
mailto+=",kubernetes-dev@googlegroups.com"
mailto+=",kubernetes-announce@googlegroups.com"
local subject="$*"
local announcement_text=/tmp/$PROG-announce.$$
((FLAGS_nomock)) || mailto=$USER@$DOMAIN_NAME
if [[ -n "$PARENT_BRANCH" ]]; then
create_branch_announcement
else
create_announcement
fi > $announcement_text
logecho "Announcing k8s $RELEASE_VERSION_PRIME to $mailto..."
# Always cc invoker
# Due to announcements landing on public mailing lists requiring membership,
# post from the invoking user (for now until this is productionized further)
# and use reply-to to ensure replies go to the right place.
common::sendmail -h "$mailto" "K8s-Anago<$USER@$DOMAIN_NAME>" \
"K8s-Anago<cloud-kubernetes-release@google.com>" \
"$subject" "$USER@$DOMAIN_NAME" \
"$announcement_text" --html || return 1
logrun rm -f $announcement_text
}
###############################################################################
# Update the releases page on github
update_github_release () {
local release_id
local id_suffix
local release_verb="Posting"
local prerelease="true"
local draft="true"
local tarball="$TREE_ROOT/_output-$RELEASE_VERSION_PRIME/release-tars"
tarball+="/kubernetes.tar.gz"
local sha_hash=$(common::sha $tarball 256)
((FLAGS_official)) && prerelease="false"
if ((FLAGS_nomock)); then
draft="false"
# Check to see that a tag exists.
# non-draft release posts to github create a tag. We don't want to
# create any tags on the repo this way. The tag should already exist
# as a result of the release process.
if ! $GHCURL $K8S_GITHUB_API/git/refs/tags |jq -r '.[] | .ref' |\
egrep -q "^refs/tags/$RELEASE_VERSION_PRIME$"; then
logecho
logecho "$FATAL: How did we get here?"
logecho "The $RELEASE_VERSION_PRIME tag doesn't exist yet on github." \
"That can't be good."
logecho "We certainly cannot publish a release without a tag."
return 1
fi
fi
# Does the release exist yet?
release_id=$($GHCURL $K8S_GITHUB_API/releases/tags/$RELEASE_VERSION_PRIME |\
jq -r '.id')
if [[ -n "$release_id" ]]; then
logecho "The $RELEASE_VERSION_PRIME is already published on github."
if ((FLAGS_yes)) || common::askyorn -e "Would you like to update it"; then
logecho "Setting post data id to $release_id to update existing release."
id_suffix="/$release_id"
release_verb="Updating"
else
logecho "Existing release (id #$release_id) left intact."
return 1
fi
fi
# post release data
logecho "$release_verb the $RELEASE_VERSION_PRIME release on github..."
local changelog_url="$K8S_GITHUB_URL/blob/master/CHANGELOG.md"
release_id=$($GHCURL $K8S_GITHUB_API/releases$id_suffix --data \
'{
"tag_name": "'$RELEASE_VERSION_PRIME'",
"target_commitish": "'$RELEASE_BRANCH'",
"name": "'$RELEASE_VERSION_PRIME'",
"body": "See [kubernetes-announce@](https://groups.google.com/forum/#!forum/kubernetes-announce) and [CHANGELOG]('$changelog_url'#'${RELEASE_VERSION_PRIME//\./}') for details.\n\nSHA256 for `kubernetes.tar.gz`: `'$sha_hash'`\n\nAdditional binary downloads are linked in the [CHANGELOG]('$changelog_url'#downloads-for-'${RELEASE_VERSION_PRIME//\./}').",
"draft": '$draft',
"prerelease": '$prerelease'
}' |jq -r '.id')
# verify it was created
if [[ -z "$release_id" ]]; then
logecho
logecho -r "$FAILED to create the $RELEASE_VERSION_PRIME release on github!"
return 1
fi
# publish binary
logecho -n "Uploading binary to github: "
logrun -s $GHCURL -H "Content-Type:application/x-compressed" \
--data-binary @$tarball \
"${K8S_GITHUB_API/api\./uploads\.}/releases/$release_id/assets?name=${tarball##*/}"
if $draft; then
logecho
logecho "$ATTENTION: A draft release of $RELEASE_VERSION_PRIME was" \
"created at $K8S_GITHUB_URL/releases."
logecho
# delete it
if ((FLAGS_yes)) || \
common::askyorn -y "Delete draft release (id #$release_id) now"; then
logecho -n "Deleting the draft release (id #$release_id): "
logrun $GHCURL -X DELETE $K8S_GITHUB_API/releases/$release_id
fi
# verify it was deleted
release_id=$($GHCURL $K8S_GITHUB_API/releases/$release_id | jq -r '.id')
if [[ -n "$release_id" ]]; then
logecho -r $FAILED
logecho "The draft release (id #$release_id) was NOT deleted." \
"Deal with it by hand"
logecho "$K8S_GITHUB_URL/releases/$RELEASE_VERSION_PRIME"
else
logecho -r $OK
fi
fi
}
##############################################################################
# Calls into Jenkins looking for a build to use for release
# Sets global PARENT_BRANCH when a new branch is created
# And global BRANCH_POINT when new branch is created from an existing tag
get_build_candidate () {
local testing_branch
# Are we branching to a new branch?
if gitlib::branch_exists $RELEASE_BRANCH; then
# If the branch is a 3-part branch (ie. release-1.2.3)
if [[ $RELEASE_BRANCH =~ $BRANCH_REGEX ]] && \
[[ -n ${BASH_REMATCH[4]} ]]; then
# The 'missing' . here between 2 and 3 is intentional. It's part of the
# optional regex.
BRANCH_POINT=v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}${BASH_REMATCH[3]}
fi
testing_branch=$RELEASE_BRANCH
else
[[ $RELEASE_BRANCH =~ $BRANCH_REGEX ]]
# Not a 3 part branch
if [[ -z "${BASH_REMATCH[4]}" ]]; then
if ((FLAGS_official)); then
common::exit 1 "Can't do official releases when creating a new branch!"
fi
PARENT_BRANCH=master
testing_branch=$PARENT_BRANCH
# if 3 part branch name, check parent exists
elif gitlib::branch_exists ${RELEASE_BRANCH%.*}; then
PARENT_BRANCH=${RELEASE_BRANCH%.*}
# The 'missing' . here between 2 and 3 is intentional. It's part of the
# optional regex.
BRANCH_POINT=v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}${BASH_REMATCH[3]}
testing_branch=$PARENT_BRANCH
else
common::exit 1 "$FATAL! We should never get here! branch=$RELEASE_BRANCH"
fi
fi
if [[ -z $BRANCH_POINT ]]; then
if [[ -n $JENKINS_BUILD_VERSION ]]; then
logecho -r "$ATTENTION: Using --buildversion=$JENKINS_BUILD_VERSION"
else
logecho "Asking Jenkins for a good build (this may take some time)..."
FLAGS_verbose=1 release::set_build_version $testing_branch || return 1
fi
# The RELEASE_BRANCH should always match with the JENKINS_BUILD_VERSION
if [[ $RELEASE_BRANCH =~ release- ]] && \
[[ ! $JENKINS_BUILD_VERSION =~ ^v${RELEASE_BRANCH/release-/} ]]; then
logecho
logecho "$FATAL! branch/build mismatch!"
logecho "buildversion=$JENKINS_BUILD_VERSION branch=$RELEASE_BRANCH"
common::exit 1
fi
fi
FLAGS_verbose=1 \
release::set_release_version ${BRANCH_POINT:-$JENKINS_BUILD_VERSION} \
$RELEASE_BRANCH \
$PARENT_BRANCH \
|| return 1
# Check that this tag doesn't exist which may occur if $PROG ran partially
if [[ "$($GHCURL $K8S_GITHUB_API/tags |jq -r '.[] .name')" =~ \
$'\n'$RELEASE_VERSION_PRIME$'\n' ]]; then
logecho
logecho "The tag $RELEASE_VERSION_PRIME already exists on github."
logecho "* An old --buildversion was specified on the command-line"
logecho "* An incomplete $PROG run is being rerun. anago is not fully" \
"reentrant yet. COMING SOON."
return 1
fi
}
##############################################################################
# Prepare the workspace and sync the tree
prepare_workspace () {
local outdir
# Clean up or not
if ((FLAGS_noclean)); then
logecho "Working in existing workspace..."
else
if [[ -d $TREE_ROOT ]]; then
logecho "Checking for _output directories..."
logrun cd $TREE_ROOT
# set -e sillyness - yes that's a ||true there that would otherwise not
# be needed except for 'set -e' in all its glory
for outdir in $(ls -1d _output* 2>/dev/null ||true); do
# This craziness due to
# https://github.com/kubernetes/kubernetes/issues/23839
if [[ $outdir != "_output" ]]; then
logrun mv $outdir _output
fi
logecho -n "make clean for $outdir: "
logrun -s make clean || return 1
logecho -n "Removing _output: "
logrun -s rm -rf _output || return 1
done
fi
logecho -n "Removing/recreating $WORKDIR: "
logrun cd /tmp
logrun -s rm -rf $WORKDIR || return 1
fi
# Sync the tree
gitlib::sync_repo $K8S_GITHUB_SSH $TREE_ROOT || return 1
logrun cd $TREE_ROOT
}
##############################################################################
# Archive the release on GS
archive_release () {
local archive_bucket=gs://$RELEASE_BUCKET/releases
((FLAGS_yes)) || common::askyorn -y "Archive this release on GS" || return
logecho -n "Copy $WORKDIR to $archive_bucket: "
logrun -s $GSUTIL -mq cp -r $WORKDIR $archive_bucket || return 1
}
###############################################################################
# MAIN
###############################################################################
# Default mode is a mocked release workflow
: ${FLAGS_nomock:=0}
: ${FLAGS_noclean:=0}
: ${FLAGS_rc:=0}
: ${FLAGS_official:=0}
# Set with --buildversion or set it later in release::set_build_version()
JENKINS_BUILD_VERSION=$FLAGS_buildversion
RELEASE_BUCKET="kubernetes-release"
if ((FLAGS_nomock)); then
# Override any --noclean setting if nomock
FLAGS_noclean=0
GCRIO_REPO="google_containers"
else
# This is passed to logrun() where appropriate when we want to mock
# specific activities like pushes
LOGRUN_MOCK="-m"
# Point to a $USER playground
RELEASE_BUCKET+=-$USER
GCRIO_REPO="kubernetes-release-test"
fi
BASEDIR=${FLAGS_basedir:-"/usr/local/google/$USER"}
# TODO:
# These KUBE_ globals extend beyond the scope of the new release refactored
# tooling so to pass these through as flags will require fixes across
# kubernetes/kubernetes and kubernetes/release which we can do at a later time
export KUBE_DOCKER_REGISTRY="gcr.io/$GCRIO_REPO"
export KUBE_RELEASE_RUN_TESTS=n
export KUBE_SKIP_CONFIRMATIONS=y
##############################################################################
# Initialize logs
##############################################################################
# Initialize and save up to 10 (rotated logs)
LOGFILE=/tmp/$PROG.log
common::logfileinit $LOGFILE 10
# BEGIN script
common::timestamp begin
# Check tool repo
gitlib::repo_state || common::exit 1
# Additional functionality
common::security_layer
##############################################################################
common::stepheader "CHECK GITHUB AUTH"
##############################################################################
gitlib::github_api_token
gitlib::ssh_auth
# Domain check
if [[ "$HOSTNAME" =~ \.([^\.]+\.com)$ ]]; then
DOMAIN_NAME=${FLAGS_domain:-${BASH_REMATCH[1]}}
if ! [[ -n $DOMAIN_NAME ]]; then
common::exit 1 "Unable to determine your domain." \
"Pass it in on the command-line" \
"with --domain=<yourdomain.com>"
fi
fi
# Simple check to validate who can do actual releases
((FLAGS_nomock)) && check_acls
##############################################################################
common::stepheader "CHECK PREREQUISITES"
##############################################################################
common::check_packages jq docker-engine pandoc ${PREREQUISITE_PACKAGES[*]} \
|| common::exit 1 "Exiting..."
check_prerequisites || common::exit 1 "Exiting..."
##############################################################################
common::stepheader "SET BUILD CANDIDATE"
##############################################################################
common::runstep get_build_candidate || common::exit 1 "Exiting..."
# WORK/BUILD area
WORKDIR=$BASEDIR/$PROG-$RELEASE_VERSION_PRIME
# Go tools expect the kubernetes src to be under $GOPATH
export GOPATH=$WORKDIR
# TOOL_ROOT is release/
# TREE_ROOT is working branch/tree
TREE_ROOT=$WORKDIR/src/k8s.io/kubernetes
BUILD_OUTPUT=$TREE_ROOT/_output
RELEASE_NOTES_MD=$WORKDIR/release-notes.md
RELEASE_NOTES_HTML=$WORKDIR/release-notes.html
# Ensure the WORKDIR exists
logrun mkdir -p $WORKDIR
##############################################################################
common::stepheader "DISK SPACE CHECK"
##############################################################################
# 18G per build
common::disk_space_check $BASEDIR $((30*${#RELEASE_VERSION[*]})) ||\
common::exit 1 "Exiting..."
if [[ $RELEASE_BRANCH =~ release- ]] &&
gitlib::branch_exists $RELEASE_BRANCH; then
##############################################################################
common::stepheader "PENDING PRS ON THE $RELEASE_BRANCH BRANCH"
##############################################################################
gitlib::pending_prs $RELEASE_BRANCH
fi
##############################################################################
common::stepheader "SESSION VALUES"
##############################################################################
# Show versions and ask for confirmation to continue
# Pass in the indexed RELEASE_VERSION dict key by key
ALL_RELEASE_VERSIONS=($(for key in ${!RELEASE_VERSION[@]}; do
echo RELEASE_VERSION[$key]; done))
# Depending on the type of operation being performed one of these will be set
if [[ -n $BRANCH_POINT ]]; then
DISPLAY_VERSION="BRANCH_POINT"
else
DISPLAY_VERSION="JENKINS_BUILD_VERSION"
fi
common::printvars -p WORKDIR WORKDIR TREE_ROOT $DISPLAY_VERSION \
RELEASE_VERSION_PRIME ${ALL_RELEASE_VERSIONS[@]} \
RELEASE_BRANCH GCRIO_REPO RELEASE_BUCKET \
FLAGS_nomock FLAGS_noclean FLAGS_rc FLAGS_official \
LOGFILE
if [[ -n "$PARENT_BRANCH" ]]; then
logecho
logecho "$ATTENTION: $RELEASE_BRANCH is a NEW branch off $PARENT_BRANCH!"
fi
logecho
((FLAGS_yes)) || common::askyorn -e "Do these values look ok for a release" \
|| common::exit 1 "Exiting..."
logecho
logecho -r "${TPUT[BOLD]}>>>>>>>>${TPUT[OFF]}" \
"View detailed session output with: tailf $LOGFILE"
logecho -r "${TPUT[BOLD]}>>>>>>>>${TPUT[OFF]}" \
"(Previous logs can be found in $LOGFILE.{1..10})"
##############################################################################
common::stepheader "PREPARE WORKSPACE"
##############################################################################
common::runstep prepare_workspace || common::exit 1 "Exiting..."
# Need to check git push direct credentials here, deeper into the process
# than I'd optimally like - preferably this is done way earlier up where the
# other prerequisites are checked, but the nature of the check requires
# an actual git repo.
##############################################################################
common::stepheader "CHECK GIT PUSH ACCESS"
##############################################################################
# TODO: capture state of access without forcing us into a prompt I have to
# expose.
logecho "Checking git push access - verbosely to accept password if needed..."
logecho "(NOTE: If using 2factor, enter a token for password)"
logrun -v git push -q --dry-run origin master || common::exit 1 "Exiting..."
# Iterate over session release versions for setup, tagging and building
for label in ${!RELEASE_VERSION[@]}; do
##############################################################################
common::stepheader "TAG AND BUILD ${RELEASE_VERSION[$label]}"
##############################################################################
# Prepare the tree for each set of actions (keys of RELEASE_VERSION)
common::runstep prepare_tree $label || common::exit 1 "Exiting..."
common::runstep build_tree ${RELEASE_VERSION[$label]}
done
# No release notes for X.Y.Z-beta.0 releases
if [[ -z "$PARENT_BRANCH" ]]; then
##############################################################################
common::stepheader "GENERATE RELEASE NOTES"
##############################################################################
common::runstep generate_release_notes || common::exit 1 "Exiting..."
fi
##############################################################################
common::stepheader "PUSH GIT OBJECTS"
##############################################################################
common::runstep push_git_objects || common::exit 1 "Exiting..."
# Get back on $RELEASE_BRANCH because we source branch-specific
# hack/lib/golang.sh in release::docker::release()
if [[ $RELEASE_BRANCH =~ release- ]]; then
logecho -n "Checkout $RELEASE_BRANCH branch to continue: "
logrun -s git checkout $RELEASE_BRANCH || common::exit 1 "Exiting..."
fi
# Set branch-specific KUBE_SERVER_PLATFORMS from current tree
# Used in release::docker::release()
source $TREE_ROOT/hack/lib/golang.sh
# Push for each release version of this session
for label in ${!RELEASE_VERSION[@]}; do
##############################################################################
common::stepheader "PUSH ${RELEASE_VERSION[$label]} IMAGES"
##############################################################################
common::runstep release::gcs::copy_release_artifacts \
release ${RELEASE_VERSION[$label]} \
$BUILD_OUTPUT-${RELEASE_VERSION[$label]} \
$RELEASE_BUCKET \
|| common::exit 1 "Exiting..."
common::runstep release::docker::release \
$KUBE_DOCKER_REGISTRY \
${RELEASE_VERSION[$label]} \
|| common::exit 1 "Exiting..."
common::runstep release::gcs::publish_version \
release \
${RELEASE_VERSION[$label]} \
$BUILD_OUTPUT-${RELEASE_VERSION[$label]} \
$RELEASE_BUCKET \
|| common::exit 1 "Exiting..."
done
if [[ -n "$PARENT_BRANCH" ]]; then
##############################################################################
common::stepheader "ANNOUNCE NEW BRANCH"
##############################################################################
common::runstep announce "k8s $RELEASE_BRANCH branch has been created" \