-
Notifications
You must be signed in to change notification settings - Fork 502
/
anago
executable file
·1741 lines (1541 loc) · 63.3 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]
#+ [--type=[alpha|beta|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>]
#+ [--tmpdir=</alt/tmp>]
#+ $PROG [--helpshort|--usage|-?]
#+ $PROG [--help|-man]
#+
#+ DESCRIPTION
#+ $PROG produces Kubernetes releases.
#+
#+ Driven by the named source <branch> and an [--type] 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 [--type] 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 if --type=alpha
#+ master beta if --type=beta
#+ master X N/A
#+ master X N/A
#+ release-* X release candidate if --type=rc
#+ release-* X official if --type=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
#+ [--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.
#+ [--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
#+ [--type] - The release type to be built, must be one of:
#+ 'alpha', 'beta', 'rc' or 'official'
#+ [--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 --type=rc release-1.1
#+ - Do a mock release candidate from release-1.1
#+ $PROG --type=official release-1.1
#+ - Do a mock official release from release-1.1
#+ $PROG --nomock --type=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}
# gitlib.sh and common.sh will still use the FLAGS_gcb because they're called
# by other, manually executed scripts, too
export FLAGS_gcb=1
# 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..."
if [[ ! $FLAGS_type =~ ^(alpha|beta|rc|official)$ ]]; then
common::exit 1 "--type must be either 'alpha', 'beta', 'rc' or 'official'"
fi
# Set positional args
RELEASE_BRANCH=${POSITIONAL_ARGV[0]}
# Check branch format
[[ $RELEASE_BRANCH =~ $BRANCH_REGEX ]] \
|| common::exit 1 "Invalid branch name!"
###############################################################################
# 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
if [[ -d $WORKDIR ]]; then
# Just copy the logs and files up. A completed release copies everything
archive_release --files-only
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 we have write access to a specified registry
# @param registry - A registry to check the ACLs for
#
ensure_registry_acls () {
local registry="$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 -v 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.
r=${registry//_/-}
# When we are no-mock mode we need to perform an image promotion, so it's
# unnecessary to check for write access to the production container registry.
if ((FLAGS_nomock)); then
logecho -n "Skipping container registry ACL check on $GCRIO_PATH_PROD in no-mock mode: "
logecho $OK
return 0
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 -v $GSUTIL cp $emptyfile $gs_path && \
logrun -v $GSUTIL rm $gs_path/${emptyfile##*/}; then
logecho $OK
else
logecho $FAILED
((retcode++))
fi
logrun -v 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"
# Verify write access to all container registries that might be used
# to ensure both mock and --nomock runs will work.
# TEMPORARILY DISABLE ACL CHECK FOR VDF
#ensure_registry_acls "$GCRIO_PATH" || 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
logecho -n "Checking the availability of krel: "
logrun -v -s krel version || return 1
}
###############################################################################
# Update $CHANGELOG_FILEPATH on master
PROGSTEP[generate_release_notes]="GENERATE RELEASE NOTES"
generate_release_notes () {
local release_tars=$TREE_ROOT/_output-$RELEASE_VERSION_PRIME/release-tars
local git_status_output
local repo_state
logrun -v krel changelog \
--repo "$TREE_ROOT" \
--branch="${PARENT_BRANCH:-$RELEASE_BRANCH}" \
--tars "$release_tars" \
--tag "$RELEASE_VERSION_PRIME" \
--html-file "$RELEASE_NOTES_HTML" \
--bucket="$RELEASE_BUCKET" \
|| return 1
git_status_output=$(git status -s)
repo_state=$([[ -z $git_status_output ]] || echo "dirty")
if [[ $repo_state == "dirty" ]]; then
logecho "Repo state was dirty after generating release notes. Cannot continue."
logecho "'git status -s' output:"
logecho "$git_status_output"
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_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" || $label == "beta" ]] && 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
}
##############################################################################
# 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="${KUBE_CROSS_IMAGE}"
version="$(cat "${TREE_ROOT}/${KUBE_CROSS_CONFIG_LOCATION}/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}/${KUBE_CROSS_CONFIG_LOCATION}/"
fi
}
##############################################################################
# Prepare sources for building for a given label
# @param label - The label to process (eg: "beta" or "official")
PROGSTEP[prepare_tree]="PREPARE AND TAG TREE"
prepare_tree () {
local label=$1
local label_common="$label"
local branch
local commit_string
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)
# Clear all CHANGELOG-N.NN.md files on the branch and insert the
# generated one, if # $PARENT_BRANCH==master, which seems to be in the
# weeks between branch creation the first official release of the branch.
# TODO: can this condition on whether the branch is "beta" or "rc" and be
# more simply readable to a maintainer?
if [[ "$PARENT_BRANCH" == "master" && "$branch" != "master" ]]; then
logecho -n "Remove any previous CHANGELOG-*.md files: "
logrun -s git rm -f "$CHANGELOG_DIR"/CHANGELOG-*.md || return 1
logecho -n "Copy master $CHANGELOG_FILEPATH to $branch branch: "
logrun -s git checkout master -- $CHANGELOG_FILEPATH || return 1
logecho -n "Committing deleted CHANGELOG-*.md files: "
logrun -s git commit -am "Delete extraneous CHANGELOG-*.md files on branch." || return 1
fi
# This appears to be a bug fix for some historical bug. Unclear if/why
# this would ever result in label != label_common.
# Ensure a common name for label in case we're using the special beta indexes
[[ "$label" =~ ^beta ]] && label_common="beta"
# For release branches, we create an empty release commit to avoid potential
# ambiguous 'git describe' logic between the official release, 'x.y.z' and the
# next beta of that release branch, 'x.y.(z+1)-beta.0'.
#
# We avoid doing this empty release commit on 'master', as:
# - there is a potential for branch conflicts as upstream/master moves ahead
# - we're checking out a git ref, as opposed to a branch, which means the tag
# will detached from 'upstream/master'
#
# A side-effect of the tag being detached from 'master' is the primary build job
# (ci-kubernetes-build) will build as the previous alpha, instead of the assumed
# tag. This causes the next anago run against 'master' to fail due to an old
# build version.
#
# Example: 'v1.18.0-alpha.2.663+df908c3aad70be'
# (should instead be 'v1.18.0-alpha.3.<commits-since-tag>+<commit-ish>')
#
# ref:
# - https://github.com/kubernetes/release/issues/1020
# - https://github.com/kubernetes/release/pull/1030
# - https://github.com/kubernetes/release/issues/1080
# - https://github.com/kubernetes/kubernetes/pull/88074
if [[ "$branch" =~ ^release- ]]; then
logecho "Creating an empty release commit for tag ${RELEASE_VERSION[$label]}"
logrun git commit --allow-empty -m "Release commit for Kubernetes ${RELEASE_VERSION[$label]}" \
|| return 1
fi
# 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]}" || return 1
}
##############################################################################
# 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: "
# 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
}
##############################################################################
# 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
logecho "unsetting STAGED_LOCATION STAGED_BUCKET"
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 tags only
# * rc is alone, pushes branch and tags
# * official pushes both official and RC items - branch and tags
# * New branch tags a new alpha on master, new RC 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 release_type in "${ORDERED_RELEASE_KEYS[@]}"; do
logecho -n "Pushing ${RELEASE_VERSION[$release_type]} tag: "
logrun -v -s git push $dryrun_flag origin ${RELEASE_VERSION[$release_type]} || return 1
done
if [[ "$RELEASE_BRANCH" =~ release- ]]; then
logecho -n "Pushing$dryrun_flag $RELEASE_BRANCH branch: "
logrun -v -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 -v -s git push $dryrun_flag origin $PARENT_BRANCH || return 1
fi
fi
# For files created on master with new branches and
# for $CHANGELOG_FILEPATH, update the master
gitlib::push_master
}
###############################################################################
# generate the announcement text to be mailed and published
branch_announcement_text () {
cat <<EOF
Kubernetes Community,
<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 your <A HREF=https://git.k8s.io/sig-release/release-managers.md>Kubernetes Release Managers</A>.
EOF
}
###############################################################################
# generate the announcement text to be mailed and published
release_announcement_text () {
cat <<EOF
Kubernetes Community,
<P>
Kubernetes $RELEASE_VERSION_PRIME has been built and pushed.
<P>
The release notes have been updated in <A HREF=https://git.k8s.io/kubernetes/$CHANGELOG_FILEPATH/#${RELEASE_VERSION_PRIME//\./}>$CHANGELOG_FILE</A>, with a pointer to them on <A HREF=https://github.com/kubernetes/kubernetes/releases/tag/$RELEASE_VERSION_PRIME>github</A>:
<P>
<HR>
$(cat $RELEASE_NOTES_HTML)
<HR>
<P><BR>
Contributors, the <A HREF=https://git.k8s.io/kubernetes/$CHANGELOG_FILEPATH/#${RELEASE_VERSION_PRIME//\./}>$CHANGELOG_FILE</A> has been bootstrapped with $RELEASE_VERSION_PRIME release notes and you may edit now as needed.
<P><BR><BR>
Published by your <A HREF=https://git.k8s.io/sig-release/release-managers.md>Kubernetes Release Managers</A>.
EOF
}
###############################################################################
# Construct META for the announcement and send it out
# @optparam --branch - Default announcement type is 'release' or --branch.
PROGSTEP[announce]="ANNOUNCE BRANCH OR RELEASE"
announce () {
local arg="$1"
local announcement_file="${WORKDIR}/announcement.html"
local subject_file="${WORKDIR}/announcement-subject.txt"
local nomock_flag=""
local pubotRc=0
local pubotIssue=''
logecho "Creating k8s ${RELEASE_VERSION_PRIME} announcement in ${WORKDIR} ..."
if [[ "$arg" == "--branch" ]]; then
echo "Kubernetes ${RELEASE_BRANCH} branch has been created" > "$subject_file"
branch_announcement_text > "$announcement_file"
logecho "Kubernetes ${RELEASE_BRANCH} branch creation announcement created."
# When we create a new branch, we notify the publishing-bot folks by
# creating an issue for them
pubotIssue="$( gitlib::create_publishing_bot_issue "$RELEASE_BRANCH" | gitlib::get_issue_url )" || pubotRc=$?
if [ "$pubotRc" != '0' ]; then
logecho "${WARNING}: Could not create issue for the publishing-bot update"
else
logecho "${OK}: publishing-bot update issue created: ${pubotIssue}"
fi
else
echo "Kubernetes ${RELEASE_VERSION_PRIME} is live!" > "$subject_file"
release_announcement_text > "$announcement_file"
logecho "Kubernetes ${RELEASE_VERSION_PRIME} release announcement created."
fi
logecho "$WARNING: Emails cannot be sent from GCB. Execute the following" \
"after a completed release to send email notification:"
logecho
# for an explanation of this construct look at
#
# http://tldp.org/LDP/abs/html/testconstructs.html
#
# or for the tl;dr check out
#
# https://www.tldp.org/LDP/abs/html/dblparens.html
#
# what we're doing here is setting nomock_flag="--nomock"
# if FLAGS_nomock evaluates to true...it's concise and consistent
# with the rest of anago but replicate the pattern at your peril
# definitely understand what's happening when you do
#
# myVar="OG"; ((0)) && myVar="changed"; echo $myVar
#
# vs
#
# myVar="OG"; ((1)) && myVar="changed"; echo $myVar
# note also that the values for FLAGS_x are either 0 (not set)
# or 1 (set) so that this works. This is not Ruby or similar
# where "--some-flag" would be truthy but it is BASH
# so values > 0 are actually truthy
((FLAGS_nomock)) && nomock_flag="--nomock"
logecho "$ krel announce ${nomock_flag} --tag ${RELEASE_VERSION_PRIME}"
logecho
}
###############################################################################
# Update the releases page on github
# return 1 on failure
PROGSTEP[update_github_release]="UPDATE GITHUB RELEASES PAGE"
update_github_release () {
local release_id
local id_suffix
local release_verb="Posting"
local prerelease="true"
local draft="true"
local staging_dir="${TREE_ROOT}/_output-${RELEASE_VERSION_PRIME}/gcs-stage/${RELEASE_VERSION_PRIME}"
local tarball="${staging_dir}/kubernetes.tar.gz"
local sha256_hash
local sha512_hash
local sha256sums_file="${staging_dir}/SHA256SUMS"
local sha512sums_file="${staging_dir}/SHA512SUMS"
local body_fmt
local body
local query_tmpl
local query_body
sha256_hash=$(common::sha "$tarball" 256)
sha512_hash=$(common::sha "$tarball" 512)
[[ "$FLAGS_type" == 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
else
# Return unless explicitly requested
if ! ((FLAGS_github_release_draft)); then
logecho "Mock run - Skipping. Use --github-release-draft to force."
return
fi
fi