Skip to content

Commit dc0122c

Browse files
authored
Merge pull request kubernetes#92286 from wojtek-t/migrate_if_needed_golang
Merge migrate-if-needed etcd bash script with golang binary
2 parents b0e974e + 2a7978e commit dc0122c

File tree

9 files changed

+484
-194
lines changed

9 files changed

+484
-194
lines changed

build/dependencies.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ dependencies:
6565
refPaths:
6666
- path: cluster/images/etcd/Makefile
6767
match: BUNDLED_ETCD_VERSIONS\?|LATEST_ETCD_VERSION\?
68-
- path: cluster/images/etcd/migrate-if-needed.sh
69-
match: BUNDLED_VERSIONS=
68+
- path: cluster/images/etcd/migrate/options.go
7069

7170
- name: "golang"
7271
version: 1.14.4

cluster/images/etcd/migrate-if-needed.sh

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -14,95 +14,13 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
# NOTES
18-
# This script performs etcd upgrade based on the following environmental
19-
# variables:
20-
# TARGET_STORAGE - API of etcd to be used (supported: 'etcd3')
21-
# TARGET_VERSION - etcd release to be used (supported: '3.0.17', '3.1.12',
22-
# '3.2.24', "3.3.17", "3.4.9")
23-
# DATA_DIRECTORY - directory with etcd data
24-
#
25-
# The current etcd version and storage format is detected based on the
26-
# contents of "${DATA_DIRECTORY}/version.txt" file (if the file doesn't
27-
# exist, we default it to "3.0.17/etcd2".
28-
#
29-
# The update workflow support the following upgrade steps:
30-
# - 3.0.17/etcd3 -> 3.1.12/etcd3
31-
# - 3.1.12/etcd3 -> 3.2.24/etcd3
32-
# - 3.2.24/etcd3 -> 3.3.17/etcd3
33-
# - 3.3.17/etcd3 -> 3.4.9/etcd3
34-
#
35-
# NOTE: The releases supported in this script has to match release binaries
36-
# present in the etcd image (to make this script work correctly).
37-
#
38-
# Based on the current etcd version and storage format we detect what
39-
# upgrade step from this should be done to get reach target configuration
17+
18+
# DEPRECATED:
19+
# The functionality has been moved to migrate binary and this script
20+
# if left for backward compatibility with previous manifests. It will be
21+
# removed in the future.
4022

4123
set -o errexit
4224
set -o nounset
4325

44-
# NOTE: BUNDLED_VERSION has to match release binaries present in the
45-
# etcd image (to make this script work correctly).
46-
BUNDLED_VERSIONS="3.0.17, 3.1.12, 3.2.24, 3.3.17, 3.4.9"
47-
48-
# shellcheck disable=SC2039
49-
# NOTE: Make sure the resulted ETCD_NAME agrees with --name in etcd.manifest: https://github.com/kubernetes/kubernetes/blob/e7ca64fbe16d0c4b6c7b36aecde9cd75042b2828/cluster/gce/manifests/etcd.manifest#L27
50-
ETCD_NAME="${ETCD_NAME:-etcd-${ETCD_HOSTNAME:-$HOSTNAME}}"
51-
if [ -z "${DATA_DIRECTORY:-}" ]; then
52-
echo "DATA_DIRECTORY variable unset - unexpected failure"
53-
exit 1
54-
fi
55-
56-
case "${DATA_DIRECTORY}" in
57-
*event*)
58-
ETCD_PEER_PORT=2381
59-
ETCD_CLIENT_PORT=18631
60-
;;
61-
*)
62-
ETCD_PEER_PORT=2380
63-
ETCD_CLIENT_PORT=18629
64-
;;
65-
esac
66-
67-
if [ -z "${INITIAL_CLUSTER:-}" ]; then
68-
echo "Warn: INITIAL_CLUSTER variable unset - defaulting to ${ETCD_NAME}=http://localhost:${ETCD_PEER_PORT}"
69-
INITIAL_CLUSTER="${ETCD_NAME}=http://localhost:${ETCD_PEER_PORT}"
70-
fi
71-
if [ -z "${LISTEN_PEER_URLS:-}" ]; then
72-
echo "Warn: LISTEN_PEER_URLS variable unset - defaulting to http://localhost:${ETCD_PEER_PORT}"
73-
LISTEN_PEER_URLS="http://localhost:${ETCD_PEER_PORT}"
74-
fi
75-
if [ -z "${INITIAL_ADVERTISE_PEER_URLS:-}" ]; then
76-
echo "Warn: INITIAL_ADVERTISE_PEER_URLS variable unset - defaulting to http://localhost:${ETCD_PEER_PORT}"
77-
INITIAL_ADVERTISE_PEER_URLS="http://localhost:${ETCD_PEER_PORT}"
78-
fi
79-
if [ -z "${TARGET_VERSION:-}" ]; then
80-
echo "TARGET_VERSION variable unset - unexpected failure"
81-
exit 1
82-
fi
83-
if [ -z "${TARGET_STORAGE:-}" ]; then
84-
echo "TARGET_STORAGE variable unset - unexpected failure"
85-
exit 1
86-
fi
87-
ETCD_DATA_PREFIX="${ETCD_DATA_PREFIX:-/registry}"
88-
ETCD_CREDS="${ETCD_CREDS:-}"
89-
90-
# Correctly support upgrade and rollback to non-default version.
91-
if [ "${DO_NOT_MOVE_BINARIES:-}" != "true" ]; then
92-
/bin/cp "/usr/local/bin/etcd-${TARGET_VERSION}" "/usr/local/bin/etcd"
93-
/bin/cp "/usr/local/bin/etcdctl-${TARGET_VERSION}" "/usr/local/bin/etcdctl"
94-
fi
95-
96-
/usr/local/bin/migrate \
97-
--name "${ETCD_NAME}" \
98-
--port "${ETCD_CLIENT_PORT}" \
99-
--listen-peer-urls "${LISTEN_PEER_URLS}" \
100-
--initial-advertise-peer-urls "${INITIAL_ADVERTISE_PEER_URLS}" \
101-
--data-dir "${DATA_DIRECTORY}" \
102-
--bundled-versions "${BUNDLED_VERSIONS}" \
103-
--initial-cluster "${INITIAL_CLUSTER}" \
104-
--target-version "${TARGET_VERSION}" \
105-
--target-storage "${TARGET_STORAGE}" \
106-
--etcd-data-prefix "${ETCD_DATA_PREFIX}" \
107-
--ttl-keys-directory "${TTL_KEYS_DIRECTORY:-${ETCD_DATA_PREFIX}/events}" \
108-
--etcd-server-extra-args "${ETCD_CREDS}"
26+
/usr/local/bin/migrate

cluster/images/etcd/migrate/BUILD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
33
go_library(
44
name = "go_default_library",
55
srcs = [
6+
"copy_file.go",
67
"data_dir.go",
78
"migrate.go",
89
"migrate_client.go",
910
"migrate_server.go",
1011
"migrator.go",
12+
"options.go",
1113
"util_others.go",
1214
"utils_windows.go",
1315
"versions.go",
@@ -17,6 +19,7 @@ go_library(
1719
deps = [
1820
"//vendor/github.com/blang/semver:go_default_library",
1921
"//vendor/github.com/spf13/cobra:go_default_library",
22+
"//vendor/github.com/spf13/pflag:go_default_library",
2023
"//vendor/go.etcd.io/etcd/client:go_default_library",
2124
"//vendor/go.etcd.io/etcd/clientv3:go_default_library",
2225
"//vendor/google.golang.org/grpc:go_default_library",
@@ -69,6 +72,7 @@ go_test(
6972
name = "go_default_test",
7073
srcs = [
7174
"data_dir_test.go",
75+
"options_test.go",
7276
"versions_test.go",
7377
],
7478
data = glob(["testdata/**"]),
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"fmt"
21+
"io"
22+
"os"
23+
"path/filepath"
24+
)
25+
26+
func copyFile(source, dest string) error {
27+
sf, err := os.Open(source)
28+
if err != nil {
29+
return fmt.Errorf("unable to open source file [%s]: %q", source, err)
30+
}
31+
defer sf.Close()
32+
fi, err := sf.Stat()
33+
if err != nil {
34+
return fmt.Errorf("unable to stat source file [%s]: %q", source, err)
35+
}
36+
37+
dir := filepath.Dir(dest)
38+
if err := os.MkdirAll(dir, 0755); err != nil {
39+
return fmt.Errorf("unable to create directory [%s]: %q", dir, err)
40+
}
41+
df, err := os.Create(dest)
42+
if err != nil {
43+
return fmt.Errorf("unable to create destination file [%s]: %q", dest, err)
44+
}
45+
defer df.Close()
46+
47+
_, err = io.Copy(df, sf)
48+
if err != nil {
49+
return fmt.Errorf("unable to copy [%s] to [%s]: %q", source, dest, err)
50+
}
51+
52+
if err := os.Chmod(dest, fi.Mode()); err != nil {
53+
return fmt.Errorf("unable to close destination file: %q", err)
54+
}
55+
return nil
56+
}

cluster/images/etcd/migrate/integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242
)
4343

4444
var (
45-
testSupportedVersions = MustParseSupportedVersions("3.0.17, 3.1.12")
45+
testSupportedVersions = MustParseSupportedVersions([]string{"3.0.17", "3.1.12"})
4646
testVersionPrevious = &EtcdVersion{semver.MustParse("3.0.17")}
4747
testVersionLatest = &EtcdVersion{semver.MustParse("3.1.12")}
4848
)

cluster/images/etcd/migrate/migrate.go

Lines changed: 27 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ package main
1818

1919
import (
2020
"fmt"
21-
"os"
2221
"path/filepath"
2322

2423
"github.com/spf13/cobra"
2524
"k8s.io/klog/v2"
2625
)
2726

2827
const (
29-
versionFilename = "version.txt"
30-
defaultPort uint64 = 18629
28+
versionFilename = "version.txt"
3129
)
3230

3331
var (
@@ -46,94 +44,43 @@ a target etcd version, this tool will upgrade or downgrade the etcd data from th
4644
opts = migrateOpts{}
4745
)
4846

49-
type migrateOpts struct {
50-
name string
51-
port uint64
52-
peerListenUrls string
53-
peerAdvertiseUrls string
54-
binDir string
55-
dataDir string
56-
bundledVersionString string
57-
etcdDataPrefix string
58-
ttlKeysDirectory string
59-
initialCluster string
60-
targetVersion string
61-
targetStorage string
62-
etcdServerArgs string
63-
}
64-
6547
func main() {
66-
flags := migrateCmd.Flags()
67-
flags.StringVar(&opts.name, "name", "", "etcd cluster member name. Defaults to etcd-{hostname}")
68-
flags.Uint64Var(&opts.port, "port", defaultPort, "etcd client port to use during migration operations. This should be a different port than typically used by etcd to avoid clients accidentally connecting during upgrade/downgrade operations.")
69-
flags.StringVar(&opts.peerListenUrls, "listen-peer-urls", "", "etcd --listen-peer-urls flag, required for HA clusters")
70-
flags.StringVar(&opts.peerAdvertiseUrls, "initial-advertise-peer-urls", "", "etcd --initial-advertise-peer-urls flag, required for HA clusters")
71-
flags.StringVar(&opts.binDir, "bin-dir", "/usr/local/bin", "directory of etcd and etcdctl binaries, must contain etcd-<version> and etcdctl-<version> for each version listed in bindled-versions")
72-
flags.StringVar(&opts.dataDir, "data-dir", "", "etcd data directory of etcd server to migrate")
73-
flags.StringVar(&opts.bundledVersionString, "bundled-versions", "", "comma separated list of etcd binary versions present under the bin-dir")
74-
flags.StringVar(&opts.etcdDataPrefix, "etcd-data-prefix", "/registry", "etcd key prefix under which all objects are kept")
75-
flags.StringVar(&opts.ttlKeysDirectory, "ttl-keys-directory", "", "etcd key prefix under which all keys with TTLs are kept. Defaults to {etcd-data-prefix}/events")
76-
flags.StringVar(&opts.initialCluster, "initial-cluster", "", "comma separated list of name=endpoint pairs. Defaults to etcd-{hostname}=http://localhost:2380")
77-
flags.StringVar(&opts.targetVersion, "target-version", "", "version of etcd to migrate to. Format must be '<major>.<minor>.<patch>'")
78-
flags.StringVar(&opts.targetStorage, "target-storage", "", "storage version of etcd to migrate to, one of: etcd2, etcd3")
79-
flags.StringVar(&opts.etcdServerArgs, "etcd-server-extra-args", "", "additional etcd server args for starting etcd servers during migration steps, --peer-* TLS cert flags should be added for etcd clusters with more than 1 member that use mutual TLS for peer communication.")
48+
registerFlags(migrateCmd.Flags(), &opts)
8049
err := migrateCmd.Execute()
8150
if err != nil {
82-
fmt.Printf("Failed to execute migratecmd: %s", err)
51+
klog.Errorf("Failed to execute migratecmd: %s", err)
8352
}
8453
}
8554

86-
// runMigrate validates the command line flags and starts the migration.
55+
// runMigrate starts the migration.
8756
func runMigrate() {
88-
if opts.name == "" {
89-
hostname, err := os.Hostname()
90-
if err != nil {
91-
klog.Errorf("Error while getting hostname to supply default --name: %v", err)
92-
os.Exit(1)
93-
}
94-
opts.name = fmt.Sprintf("etcd-%s", hostname)
95-
}
96-
97-
if opts.ttlKeysDirectory == "" {
98-
opts.ttlKeysDirectory = fmt.Sprintf("%s/events", opts.etcdDataPrefix)
99-
}
100-
if opts.initialCluster == "" {
101-
opts.initialCluster = fmt.Sprintf("%s=http://localhost:2380", opts.name)
102-
}
103-
if opts.targetStorage == "" {
104-
klog.Errorf("--target-storage is required")
105-
os.Exit(1)
106-
}
107-
if opts.targetVersion == "" {
108-
klog.Errorf("--target-version is required")
109-
os.Exit(1)
110-
}
111-
if opts.dataDir == "" {
112-
klog.Errorf("--data-dir is required")
113-
os.Exit(1)
114-
}
115-
if opts.bundledVersionString == "" {
116-
klog.Errorf("--bundled-versions is required")
117-
os.Exit(1)
118-
}
119-
120-
bundledVersions, err := ParseSupportedVersions(opts.bundledVersionString)
121-
if err != nil {
122-
klog.Errorf("Failed to parse --supported-versions: %v", err)
123-
}
124-
err = validateBundledVersions(bundledVersions, opts.binDir)
125-
if err != nil {
126-
klog.Errorf("Failed to validate that 'etcd-<version>' and 'etcdctl-<version>' binaries exist in --bin-dir '%s' for all --bundled-versions '%s': %v",
127-
opts.binDir, opts.bundledVersionString, err)
128-
os.Exit(1)
57+
if err := opts.validateAndDefault(); err != nil {
58+
klog.Fatalf("%v", err)
12959
}
60+
copyBinaries()
13061

13162
target := &EtcdVersionPair{
13263
version: MustParseEtcdVersion(opts.targetVersion),
13364
storageVersion: MustParseEtcdStorageVersion(opts.targetStorage),
13465
}
13566

136-
migrate(opts.name, opts.port, opts.peerListenUrls, opts.peerAdvertiseUrls, opts.binDir, opts.dataDir, opts.etcdDataPrefix, opts.ttlKeysDirectory, opts.initialCluster, target, bundledVersions, opts.etcdServerArgs)
67+
migrate(
68+
opts.name, opts.port, opts.peerListenUrls, opts.peerAdvertiseUrls, opts.binDir,
69+
opts.dataDir, opts.etcdDataPrefix, opts.ttlKeysDirectory, opts.initialCluster,
70+
target, opts.supportedVersions, opts.etcdServerArgs)
71+
}
72+
73+
func copyBinaries() {
74+
if val, err := lookupEnv("DO_NOT_MOVE_BINARIES"); err != nil || val != "true" {
75+
etcdVersioned := fmt.Sprintf("etcd-%s", opts.targetVersion)
76+
etcdctlVersioned := fmt.Sprintf("etcdctl-%s", opts.targetVersion)
77+
if err := copyFile(filepath.Join(opts.binDir, etcdVersioned), filepath.Join(opts.binDir, "etcd")); err != nil {
78+
klog.Fatalf("Failed to copy %s: %v", etcdVersioned, err)
79+
}
80+
if err := copyFile(filepath.Join(opts.binDir, etcdctlVersioned), filepath.Join(opts.binDir, "etcdctl")); err != nil {
81+
klog.Fatalf("Failed to copy %s: %v", etcdctlVersioned, err)
82+
}
83+
}
13784
}
13885

13986
// migrate opens or initializes the etcd data directory, configures the migrator, and starts the migration.
@@ -142,8 +89,7 @@ func migrate(name string, port uint64, peerListenUrls string, peerAdvertiseUrls
14289

14390
dataDir, err := OpenOrCreateDataDirectory(dataDirPath)
14491
if err != nil {
145-
klog.Errorf("Error opening or creating data directory %s: %v", dataDirPath, err)
146-
os.Exit(1)
92+
klog.Fatalf("Error opening or creating data directory %s: %v", dataDirPath, err)
14793
}
14894

14995
cfg := &EtcdMigrateCfg{
@@ -161,31 +107,14 @@ func migrate(name string, port uint64, peerListenUrls string, peerAdvertiseUrls
161107
}
162108
client, err := NewEtcdMigrateClient(cfg)
163109
if err != nil {
164-
klog.Errorf("Migration failed: %v", err)
165-
os.Exit(1)
110+
klog.Fatalf("Migration failed: %v", err)
166111
}
167112
defer client.Close()
168113

169114
migrator := &Migrator{cfg, dataDir, client}
170115

171116
err = migrator.MigrateIfNeeded(target)
172117
if err != nil {
173-
klog.Errorf("Migration failed: %v", err)
174-
os.Exit(1)
175-
}
176-
}
177-
178-
// validateBundledVersions checks that 'etcd-<version>' and 'etcdctl-<version>' binaries exist in the binDir
179-
// for each version in the bundledVersions list.
180-
func validateBundledVersions(bundledVersions SupportedVersions, binDir string) error {
181-
for _, v := range bundledVersions {
182-
for _, binaryName := range []string{"etcd", "etcdctl"} {
183-
fn := filepath.Join(binDir, fmt.Sprintf("%s-%s", binaryName, v))
184-
if _, err := os.Stat(fn); err != nil {
185-
return fmt.Errorf("failed to validate '%s' binary exists for bundled-version '%s': %v", fn, v, err)
186-
}
187-
188-
}
118+
klog.Fatalf("Migration failed: %v", err)
189119
}
190-
return nil
191120
}

0 commit comments

Comments
 (0)