vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

View File

@ -0,0 +1,19 @@
# Copyright 2016 The Kubernetes Authors.
#
# 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.
FROM BASEIMAGE
EXPOSE 2379 2380 4001 7001
COPY etcd* etcdctl* /usr/local/bin/
COPY migrate-if-needed.sh attachlease rollback /usr/local/bin/

117
vendor/k8s.io/kubernetes/cluster/images/etcd/Makefile generated vendored Normal file
View File

@ -0,0 +1,117 @@
# Copyright 2016 The Kubernetes Authors.
#
# 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.
# Build the etcd image
#
# Usage:
# [TAGS=2.2.1 2.3.7 3.0.17 3.1.10] [REGISTRY=gcr.io/google_containers] [ARCH=amd64] [BASEIMAGE=busybox] make (build|push)
# The image contains different etcd versions to simplify
# upgrades. Thus be careful when removing any tag from here.
#
# NOTE: The etcd upgrade rules are that you can upgrade only 1 minor
# version at a time, and patch release don't matter.
#
# Except from etcd-$(tag) and etcdctl-$(tag) binaries, we also
# need etcd and etcdctl binaries for backward compatibility reasons.
# That binary will be set to the last tag from $(TAGS).
TAGS?=2.2.1 2.3.7 3.0.17 3.1.10
REGISTRY_TAG?=3.1.10
ARCH?=amd64
REGISTRY?=gcr.io/google_containers
GOLANG_VERSION?=1.7.6
GOARM=7
TEMP_DIR:=$(shell mktemp -d)
ifeq ($(ARCH),amd64)
BASEIMAGE?=busybox
endif
ifeq ($(ARCH),arm)
BASEIMAGE?=arm32v7/busybox
endif
ifeq ($(ARCH),arm64)
BASEIMAGE?=arm64v8/busybox
endif
ifeq ($(ARCH),ppc64le)
BASEIMAGE?=ppc64le/busybox
endif
ifeq ($(ARCH),s390x)
BASEIMAGE?=s390x/busybox
endif
build:
# Copy the content in this dir to the temp dir,
# without copying the subdirectories.
find ./ -maxdepth 1 -type f | xargs cp -t $(TEMP_DIR)
# Compile attachlease
docker run -i -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes -v $(TEMP_DIR):/build -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \
/bin/bash -c "CGO_ENABLED=0 go build -o /build/attachlease k8s.io/kubernetes/cluster/images/etcd/attachlease"
# Compile rollback
docker run -i -v $(shell pwd)/../../../:/go/src/k8s.io/kubernetes -v $(TEMP_DIR):/build -e GOARCH=$(ARCH) golang:$(GOLANG_VERSION) \
/bin/bash -c "CGO_ENABLED=0 go build -o /build/rollback k8s.io/kubernetes/cluster/images/etcd/rollback"
ifeq ($(ARCH),amd64)
# Do not compile if we should make an image for amd64, use the official etcd binaries instead
# For each release create a tmp dir 'etcd_release_tmp_dir' and unpack the release tar there.
for tag in $(TAGS); do \
etcd_release_tmp_dir=$(shell mktemp -d); \
curl -sSL --retry 5 https://github.com/coreos/etcd/releases/download/v$$tag/etcd-v$$tag-linux-amd64.tar.gz | tar -xz -C $$etcd_release_tmp_dir --strip-components=1; \
cp $$etcd_release_tmp_dir/etcd $$etcd_release_tmp_dir/etcdctl $(TEMP_DIR)/; \
cp $(TEMP_DIR)/etcd $(TEMP_DIR)/etcd-$$tag; \
cp $(TEMP_DIR)/etcdctl $(TEMP_DIR)/etcdctl-$$tag; \
done
else
# Download etcd in a golang container and cross-compile it statically
# For each release create a tmp dir 'etcd_release_tmp_dir' and unpack the release tar there.
for tag in $(TAGS); do \
etcd_release_tmp_dir=$(shell mktemp -d); \
docker run -i -v $$etcd_release_tmp_dir:/etcdbin golang:$(GOLANG_VERSION) /bin/bash -c \
"git clone https://github.com/coreos/etcd /go/src/github.com/coreos/etcd \
&& cd /go/src/github.com/coreos/etcd \
&& git checkout v$$tag \
&& GOARM=$(GOARM) GOARCH=$(ARCH) ./build \
&& cp -f bin/$(ARCH)/etcd* bin/etcd* /etcdbin; echo 'done'"; \
cp $$etcd_release_tmp_dir/etcd $$etcd_release_tmp_dir/etcdctl $(TEMP_DIR)/; \
cp $(TEMP_DIR)/etcd $(TEMP_DIR)/etcd-$$tag; \
cp $(TEMP_DIR)/etcdctl $(TEMP_DIR)/etcdctl-$$tag; \
done
# Add this ENV variable in order to workaround an unsupported arch blocker
# The multiarch feature is in an limited and experimental state right now, and etcd should work fine on arm64
# On arm (which is 32-bit), it can't handle >1GB data in-memory, but it is very unlikely someone tinkering with their limited arm devices would reach such a high usage
# ppc64le is still quite untested, but compiles and is probably in the process of being validated by IBM.
cd $(TEMP_DIR) && echo "ENV ETCD_UNSUPPORTED_ARCH=$(ARCH)" >> Dockerfile
endif
# Replace BASEIMAGE with the real base image
cd $(TEMP_DIR) && sed -i.bak 's|BASEIMAGE|$(BASEIMAGE)|g' Dockerfile
# And build the image
docker build --pull -t $(REGISTRY)/etcd-$(ARCH):$(REGISTRY_TAG) $(TEMP_DIR)
push: build
gcloud docker -- push $(REGISTRY)/etcd-$(ARCH):$(REGISTRY_TAG)
ifeq ($(ARCH),amd64)
# Backward compatibility. TODO: deprecate this image tag
docker tag $(REGISTRY)/etcd-$(ARCH):$(REGISTRY_TAG) $(REGISTRY)/etcd:$(REGISTRY_TAG)
gcloud docker -- push $(REGISTRY)/etcd:$(REGISTRY_TAG)
endif
all: build
.PHONY: build push

32
vendor/k8s.io/kubernetes/cluster/images/etcd/README.md generated vendored Normal file
View File

@ -0,0 +1,32 @@
### etcd
This is a small etcd image used in Kubernetes setups where `etcd` is deployed as a docker image.
For `amd64`, official `etcd` and `etcdctl` binaries are downloaded from Github to maintain official support.
For other architectures, `etcd` is cross-compiled from source. Arch-specific `busybox` images serve as base images.
#### How to release
```console
# Build for linux/amd64 (default)
$ make push ARCH=amd64
# ---> gcr.io/google_containers/etcd-amd64:TAG
# ---> gcr.io/google_containers/etcd:TAG
$ make push ARCH=arm
# ---> gcr.io/google_containers/etcd-arm:TAG
$ make push ARCH=arm64
# ---> gcr.io/google_containers/etcd-arm64:TAG
$ make push ARCH=ppc64le
# ---> gcr.io/google_containers/etcd-ppc64le:TAG
$ make push ARCH=s390x
# ---> gcr.io/google_containers/etcd-s390x:TAG
```
If you don't want to push the images, run `make` or `make build` instead
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/cluster/images/etcd/README.md?pixel)]()

View File

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "attachlease",
importpath = "k8s.io/kubernetes/cluster/images/etcd/attachlease",
library = ":go_default_library",
)
go_library(
name = "go_default_library",
srcs = ["attachlease.go"],
importpath = "k8s.io/kubernetes/cluster/images/etcd/attachlease",
deps = [
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,70 @@
/*
Copyright 2016 The Kubernetes Authors.
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.
*/
package main
import (
"flag"
"strings"
"time"
"github.com/coreos/etcd/clientv3"
"github.com/golang/glog"
"golang.org/x/net/context"
)
var (
etcdAddress = flag.String("etcd-address", "", "Etcd address")
ttlKeysPrefix = flag.String("ttl-keys-prefix", "", "Prefix for TTL keys")
leaseDuration = flag.Duration("lease-duration", time.Hour, "Lease duration (seconds granularity)")
)
func main() {
flag.Parse()
if *etcdAddress == "" {
glog.Fatalf("--etcd-address flag is required")
}
client, err := clientv3.New(clientv3.Config{Endpoints: []string{*etcdAddress}})
if err != nil {
glog.Fatalf("Error while creating etcd client: %v", err)
}
// Make sure that ttlKeysPrefix is ended with "/" so that we only get children "directories".
if !strings.HasSuffix(*ttlKeysPrefix, "/") {
*ttlKeysPrefix += "/"
}
ctx := context.Background()
objectsResp, err := client.KV.Get(ctx, *ttlKeysPrefix, clientv3.WithPrefix())
if err != nil {
glog.Fatalf("Error while getting objects to attach to the lease")
}
lease, err := client.Lease.Grant(ctx, int64(*leaseDuration/time.Second))
if err != nil {
glog.Fatalf("Error while creating lease: %v", err)
}
glog.Infof("Lease with TTL: %v created", lease.TTL)
glog.Infof("Attaching lease to %d entries", len(objectsResp.Kvs))
for _, kv := range objectsResp.Kvs {
_, err := client.KV.Put(ctx, string(kv.Key), string(kv.Value), clientv3.WithLease(lease.ID))
if err != nil {
glog.Errorf("Error while attaching lease to: %s", string(kv.Key))
}
}
}

View File

@ -0,0 +1,266 @@
#!/bin/sh
# Copyright 2016 The Kubernetes Authors.
#
# 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.
# NOTES
# This script performs etcd upgrade based on the following environmental
# variables:
# TARGET_STORAGE - API of etcd to be used (supported: 'etcd2', 'etcd3')
# TARGET_VERSION - etcd release to be used (supported: '2.2.1', '2.3.7', '3.0.17')
# DATA_DIRECTORY - directory with etcd data
#
# The current etcd version and storage format is detected based on the
# contents of "${DATA_DIRECTORY}/version.txt" file (if the file doesn't
# exist, we default it to "2.2.1/etcd2".
#
# The update workflow support the following upgrade steps:
# - 2.2.1/etcd2 -> 2.3.7/etcd2
# - 2.3.7/etcd2 -> 3.0.17/etcd2
# - 3.0.17/etcd2 -> 3.0.17/etcd3
#
# NOTE: The releases supported in this script has to match release binaries
# present in the etcd image (to make this script work correctly).
#
# Based on the current etcd version and storage format we detect what
# upgrade step from this should be done to get reach target configuration
set -o errexit
set -o nounset
if [ -z "${TARGET_STORAGE:-}" ]; then
echo "TARGET_STORAGE variable unset - unexpected failure"
exit 1
fi
if [ -z "${TARGET_VERSION:-}" ]; then
echo "TARGET_VERSION variable unset - unexpected failure"
exit 1
fi
if [ -z "${DATA_DIRECTORY:-}" ]; then
echo "DATA_DIRECTORY variable unset - unexpected failure"
exit 1
fi
echo "$(date +'%Y-%m-%d %H:%M:%S') Detecting if migration is needed"
if [ "${TARGET_STORAGE}" != "etcd2" -a "${TARGET_STORAGE}" != "etcd3" ]; then
echo "Not supported version of storage: ${TARGET_STORAGE}"
exit 1
fi
# Correctly support upgrade and rollback to non-default version.
if [ "${DO_NOT_MOVE_BINARIES:-}" != "true" ]; then
cp "/usr/local/bin/etcd-${TARGET_VERSION}" "/usr/local/bin/etcd"
cp "/usr/local/bin/etcdctl-${TARGET_VERSION}" "/usr/local/bin/etcdctl"
fi
# NOTE: SUPPORTED_VERSION has to match release binaries present in the
# etcd image (to make this script work correctly).
# We cannot use array since sh doesn't support it.
SUPPORTED_VERSIONS_STRING="2.2.1 2.3.7 3.0.17"
SUPPORTED_VERSIONS=$(echo "${SUPPORTED_VERSIONS_STRING}" | tr " " "\n")
VERSION_FILE="version.txt"
CURRENT_STORAGE="etcd2"
CURRENT_VERSION="2.2.1"
if [ -e "${DATA_DIRECTORY}/${VERSION_FILE}" ]; then
VERSION_CONTENTS="$(cat ${DATA_DIRECTORY}/${VERSION_FILE})"
# Example usage: if contents of VERSION_FILE is 2.3.7/etcd2, then
# - CURRENT_VERSION would be '2.3.7'
# - CURRENT_STORAGE would be 'etcd2'
CURRENT_VERSION="$(echo $VERSION_CONTENTS | cut -d '/' -f 1)"
CURRENT_STORAGE="$(echo $VERSION_CONTENTS | cut -d '/' -f 2)"
fi
ETCD_DATA_PREFIX="${ETCD_DATA_PREFIX:-/registry}"
# If there is no data in DATA_DIRECTORY, this means that we are
# starting etcd from scratch. In that case, we don't need to do
# any migration.
if [ ! -d "${DATA_DIRECTORY}" ]; then
mkdir -p "${DATA_DIRECTORY}"
fi
if [ -z "$(ls -A ${DATA_DIRECTORY})" ]; then
echo "${DATA_DIRECTORY} is empty - skipping migration"
echo "${TARGET_VERSION}/${TARGET_STORAGE}" > "${DATA_DIRECTORY}/${VERSION_FILE}"
exit 0
fi
# Starts 'etcd' version ${START_VERSION} and writes to it:
# 'etcd_version' -> "${START_VERSION}"
# Successful write confirms that etcd is up and running.
# Sets ETCD_PID at the end.
# Returns 0 if etcd was successfully started, non-0 otherwise.
start_etcd() {
# Use random ports, so that apiserver cannot connect to etcd.
ETCD_PORT=18629
ETCD_PEER_PORT=2380
# Avoid collisions between etcd and event-etcd.
case "${DATA_DIRECTORY}" in
*event*)
ETCD_PORT=18631
ETCD_PEER_PORT=2381
;;
esac
local ETCD_CMD="${ETCD:-/usr/local/bin/etcd-${START_VERSION}}"
local ETCDCTL_CMD="${ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}}"
local API_VERSION="$(echo ${START_STORAGE} | cut -c5-5)"
if [ "${API_VERSION}" = "2" ]; then
ETCDCTL_CMD="${ETCDCTL_CMD} --debug --endpoint=http://127.0.0.1:${ETCD_PORT} set"
else
ETCDCTL_CMD="${ETCDCTL_CMD} --endpoints=http://127.0.0.1:${ETCD_PORT} put"
fi
${ETCD_CMD} \
--name="etcd-$(hostname)" \
--debug \
--data-dir=${DATA_DIRECTORY} \
--listen-client-urls http://127.0.0.1:${ETCD_PORT} \
--advertise-client-urls http://127.0.0.1:${ETCD_PORT} \
--listen-peer-urls http://127.0.0.1:${ETCD_PEER_PORT} \
--initial-advertise-peer-urls http://127.0.0.1:${ETCD_PEER_PORT} &
ETCD_PID=$!
# Wait until we can write to etcd.
for i in $(seq 240); do
sleep 0.5
ETCDCTL_API="${API_VERSION}" ${ETCDCTL_CMD} 'etcd_version' ${START_VERSION}
if [ "$?" -eq "0" ]; then
echo "Etcd on port ${ETCD_PORT} is up."
return 0
fi
done
echo "Timeout while waiting for etcd on port ${ETCD_PORT}"
return 1
}
# Stops etcd with ${ETCD_PID} pid.
stop_etcd() {
kill "${ETCD_PID-}" >/dev/null 2>&1 || :
wait "${ETCD_PID-}" >/dev/null 2>&1 || :
}
ATTACHLEASE="${ATTACHLEASE:-/usr/local/bin/attachlease}"
ROLLBACK="${ROLLBACK:-/usr/local/bin/rollback}"
# If we are upgrading from 2.2.1 and this is the first try for upgrade,
# do the backup to allow restoring from it in case of failed upgrade.
BACKUP_DIR="${DATA_DIRECTORY}/migration-backup"
if [ "${CURRENT_VERSION}" = "2.2.1" -a "${CURRENT_VERSION}" != "${TARGET_VERSION}" -a ! -d "${BACKUP_DIR}" ]; then
echo "Backup etcd before starting migration"
mkdir ${BACKUP_DIR}
ETCDCTL_CMD="/usr/local/bin/etcdctl-2.2.1"
ETCDCTL_API=2 ${ETCDCTL_CMD} --debug backup --data-dir=${DATA_DIRECTORY} \
--backup-dir=${BACKUP_DIR}
echo "Backup done in ${BACKUP_DIR}"
fi
# Do the roll-forward migration if needed.
# The migration goes as following:
# 1. for all versions starting one after the current version of etcd
# we do "start, wait until healthy and stop etcd". This is the
# procedure that etcd documentation suggests for upgrading binaries.
# 2. For the first 3.0.x version that we encounter, if we are still in
# v2 API, we do upgrade to v3 API using the "etcdct migrate" and
# attachlease commands.
SKIP_STEP=true
for step in ${SUPPORTED_VERSIONS}; do
if [ "${step}" = "${CURRENT_VERSION}" ]; then
SKIP_STEP=false
elif [ "${SKIP_STEP}" != "true" ]; then
# Do the migration step, by just starting etcd in this version.
START_VERSION="${step}"
START_STORAGE="${CURRENT_STORAGE}"
if ! start_etcd; then
# Migration failed.
echo "Starting etcd ${step} failed"
exit 1
fi
# Kill etcd and wait until this is down.
stop_etcd
CURRENT_VERSION=${step}
echo "${CURRENT_VERSION}/${CURRENT_STORAGE}" > "${DATA_DIRECTORY}/${VERSION_FILE}"
fi
if [ "$(echo ${CURRENT_VERSION} | cut -c1-2)" = "3." -a "${CURRENT_VERSION}" = "${step}" -a "${CURRENT_STORAGE}" = "etcd2" -a "${TARGET_STORAGE}" = "etcd3" ]; then
# If it is the first 3.x release in the list and we are migrating
# also from 'etcd2' to 'etcd3', do the migration now.
echo "Performing etcd2 -> etcd3 migration"
START_VERSION="${step}"
START_STORAGE="etcd3"
ETCDCTL_CMD="${ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}}"
ETCDCTL_API=3 ${ETCDCTL_CMD} migrate --data-dir=${DATA_DIRECTORY}
echo "Attaching leases to TTL entries"
# Now attach lease to all keys.
# To do it, we temporarily start etcd on a random port (so that
# apiserver actually cannot access it).
if ! start_etcd; then
echo "Starting etcd ${step} in v3 mode failed"
exit 1
fi
# Create a lease and attach all keys to it.
${ATTACHLEASE} \
--etcd-address http://127.0.0.1:${ETCD_PORT} \
--ttl-keys-prefix "${TTL_KEYS_DIRECTORY:-${ETCD_DATA_PREFIX}/events}" \
--lease-duration 1h
# Kill etcd and wait until this is down.
stop_etcd
CURRENT_STORAGE="etcd3"
echo "${CURRENT_VERSION}/${CURRENT_STORAGE}" > "${DATA_DIRECTORY}/${VERSION_FILE}"
fi
if [ "$(echo ${CURRENT_VERSION} | cut -c1-4)" = "3.1." -a "${CURRENT_VERSION}" = "${step}" -a "${CURRENT_STORAGE}" = "etcd3" ]; then
# If we are upgrading to 3.1.* release, if the cluster was migrated
# from v2 version, the v2 data may still be around. So now is the
# time to actually remove them.
echo "Remove stale v2 data"
START_VERSION="${step}"
START_STORAGE="etcd3"
ETCDCTL_CMD="${ETCDCTL:-/usr/local/bin/etcdctl-${START_VERSION}}"
if ! start_etcd; then
echo "Starting etcd ${step} in v3 mode failed"
exit 1
fi
${ETCDCTL_CMD} rm --recursive "${ETCD_DATA_PREFIX}"
# Kill etcd and wait until this is down.
stop_etcd
echo "Successfully remove v2 data"
# Also remove backup from v2->v3 migration.
rm -rf "${BACKUP_DIR}"
fi
if [ "${CURRENT_VERSION}" = "${TARGET_VERSION}" -a "${CURRENT_STORAGE}" = "${TARGET_STORAGE}" ]; then
break
fi
done
# Do the rollback of needed.
# NOTE: Rollback is only supported from "3.0.x" version in 'etcd3' mode to
# "2.2.1" version in 'etcd2' mode.
if [ "${CURRENT_STORAGE}" = "etcd3" -a "${TARGET_STORAGE}" = "etcd2" ]; then
if [ "$(echo ${CURRENT_VERSION} | cut -c1-4)" != "3.0." -o "${TARGET_VERSION}" != "2.2.1" ]; then
echo "etcd3 -> etcd2 downgrade is supported only between 3.0.x and 2.2.1"
return 0
fi
echo "Backup and remove all existing v2 data"
ROLLBACK_BACKUP_DIR="${DATA_DIRECTORY}.bak"
rm -rf "${ROLLBACK_BACKUP_DIR}"
mkdir -p "${ROLLBACK_BACKUP_DIR}"
cp -r "${DATA_DIRECTORY}" "${ROLLBACK_BACKUP_DIR}"
echo "Performing etcd3 -> etcd2 rollback"
${ROLLBACK} --data-dir "${DATA_DIRECTORY}"
if [ "$?" -ne "0" ]; then
echo "Rollback to etcd2 failed"
exit 1
fi
CURRENT_STORAGE="etcd2"
CURRENT_VERSION="2.2.1"
echo "${CURRENT_VERSION}/${CURRENT_STORAGE}" > "${DATA_DIRECTORY}/${VERSION_FILE}"
fi
echo "$(date +'%Y-%m-%d %H:%M:%S') Migration finished"

View File

@ -0,0 +1,49 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "rollback",
importpath = "k8s.io/kubernetes/cluster/images/etcd/rollback",
library = ":go_default_library",
)
go_library(
name = "go_default_library",
srcs = ["rollback.go"],
importpath = "k8s.io/kubernetes/cluster/images/etcd/rollback",
deps = [
"//third_party/forked/etcd221/wal:go_default_library",
"//vendor/github.com/coreos/etcd/etcdserver:go_default_library",
"//vendor/github.com/coreos/etcd/etcdserver/etcdserverpb:go_default_library",
"//vendor/github.com/coreos/etcd/etcdserver/membership:go_default_library",
"//vendor/github.com/coreos/etcd/mvcc/backend:go_default_library",
"//vendor/github.com/coreos/etcd/mvcc/mvccpb:go_default_library",
"//vendor/github.com/coreos/etcd/pkg/pbutil:go_default_library",
"//vendor/github.com/coreos/etcd/pkg/types:go_default_library",
"//vendor/github.com/coreos/etcd/raft/raftpb:go_default_library",
"//vendor/github.com/coreos/etcd/snap:go_default_library",
"//vendor/github.com/coreos/etcd/store:go_default_library",
"//vendor/github.com/coreos/etcd/wal:go_default_library",
"//vendor/github.com/coreos/etcd/wal/walpb:go_default_library",
"//vendor/github.com/coreos/go-semver/semver:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,45 @@
# Rollback workflow
Build it in this directory.
Make sure you have etcd dependency ready. Last time we use etcd v3.0.7.
```
$ go build .
```
Run it:
```
$ ./rollback2 --data-dir $ETCD_DATA_DIR --ttl 1h
```
This will rollback KV pairs from v3 into v2.
If a key was attached to a lease before, it will be created with given TTL (default to 1h).
On success, it will print at the end:
```
Finished successfully
```
Repeat this on all etcd members.
You can do simple check on keys (if any exists):
```
etcdctl ls /
```
Important Note
------
This tool isn't recommended to use if any problem comes up in etcd3 backend.
Please report bugs and we will fix it soon.
If it's still preferred to run this tool, please backup all your data beforehand.
This tool will also back up datadir to same path with ".rollback.backup" suffix.
Caveats:
- The tool doesn't preserve versions of keys.
- If any v2 data exists before rollback, they will be wiped out.
- v3 data only exists in the backup after successful rollback.
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/cluster/images/etcd/rollback/README.md?pixel)]()

View File

@ -0,0 +1,336 @@
/*
Copyright 2016 The Kubernetes Authors.
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.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
// Uncomment when you want to rollback to 2.2.1 version.
oldwal "k8s.io/kubernetes/third_party/forked/etcd221/wal"
// Uncomment when you want to rollback to 2.3.7 version.
// oldwal "k8s.io/kubernetes/third_party/forked/etcd237/wal"
"github.com/coreos/etcd/etcdserver"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/etcdserver/membership"
"github.com/coreos/etcd/mvcc/backend"
"github.com/coreos/etcd/mvcc/mvccpb"
"github.com/coreos/etcd/pkg/pbutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/snap"
"github.com/coreos/etcd/store"
"github.com/coreos/etcd/wal"
"github.com/coreos/etcd/wal/walpb"
"github.com/coreos/go-semver/semver"
"github.com/golang/glog"
)
const rollbackVersion = "2.2.0"
var (
migrateDatadir = flag.String("data-dir", "", "Path to the data directory")
ttl = flag.Duration("ttl", time.Hour, "TTL of event keys (default 1 hour)")
)
func main() {
flag.Parse()
if len(*migrateDatadir) == 0 {
glog.Fatal("need to set '--data-dir'")
}
dbpath := path.Join(*migrateDatadir, "member", "snap", "db")
// etcd3 store backend. We will use it to parse v3 data files and extract information.
be := backend.NewDefaultBackend(dbpath)
tx := be.BatchTx()
// etcd2 store backend. We will use v3 data to update this and then save snapshot to disk.
st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
expireTime := time.Now().Add(*ttl)
tx.Lock()
err := tx.UnsafeForEach([]byte("key"), func(k, v []byte) error {
kv := &mvccpb.KeyValue{}
kv.Unmarshal(v)
// This is compact key.
if !strings.HasPrefix(string(kv.Key), "/") {
return nil
}
ttlOpt := store.TTLOptionSet{}
if kv.Lease != 0 {
ttlOpt = store.TTLOptionSet{ExpireTime: expireTime}
}
if !isTombstone(k) {
sk := path.Join(strings.Trim(etcdserver.StoreKeysPrefix, "/"), string(kv.Key))
_, err := st.Set(sk, false, string(kv.Value), ttlOpt)
if err != nil {
return err
}
} else {
st.Delete(string(kv.Key), false, false)
}
return nil
})
if err != nil {
glog.Fatal(err)
}
tx.Unlock()
if err := traverseAndDeleteEmptyDir(st, "/"); err != nil {
glog.Fatal(err)
}
// rebuild cluster state.
metadata, hardstate, oldSt, err := rebuild(*migrateDatadir)
if err != nil {
glog.Fatal(err)
}
// In the following, it's low level logic that saves metadata and data into v2 snapshot.
backupPath := *migrateDatadir + ".rollback.backup"
if err := os.Rename(*migrateDatadir, backupPath); err != nil {
glog.Fatal(err)
}
if err := os.MkdirAll(path.Join(*migrateDatadir, "member", "snap"), 0700); err != nil {
glog.Fatal(err)
}
walDir := path.Join(*migrateDatadir, "member", "wal")
w, err := oldwal.Create(walDir, metadata)
if err != nil {
glog.Fatal(err)
}
err = w.SaveSnapshot(walpb.Snapshot{Index: hardstate.Commit, Term: hardstate.Term})
if err != nil {
glog.Fatal(err)
}
w.Close()
event, err := oldSt.Get(etcdserver.StoreClusterPrefix, true, false)
if err != nil {
glog.Fatal(err)
}
// nodes (members info) for ConfState
nodes := []uint64{}
traverseMetadata(event.Node, func(n *store.NodeExtern) {
if n.Key != etcdserver.StoreClusterPrefix {
// update store metadata
v := ""
if !n.Dir {
v = *n.Value
}
if n.Key == path.Join(etcdserver.StoreClusterPrefix, "version") {
v = rollbackVersion
}
if _, err := st.Set(n.Key, n.Dir, v, store.TTLOptionSet{}); err != nil {
glog.Fatal(err)
}
// update nodes
fields := strings.Split(n.Key, "/")
if len(fields) == 4 && fields[2] == "members" {
nodeID, err := strconv.ParseUint(fields[3], 16, 64)
if err != nil {
glog.Fatalf("failed to parse member ID (%s): %v", fields[3], err)
}
nodes = append(nodes, nodeID)
}
}
})
data, err := st.Save()
if err != nil {
glog.Fatal(err)
}
raftSnap := raftpb.Snapshot{
Data: data,
Metadata: raftpb.SnapshotMetadata{
Index: hardstate.Commit,
Term: hardstate.Term,
ConfState: raftpb.ConfState{
Nodes: nodes,
},
},
}
snapshotter := snap.New(path.Join(*migrateDatadir, "member", "snap"))
if err := snapshotter.SaveSnap(raftSnap); err != nil {
glog.Fatal(err)
}
fmt.Println("Finished successfully")
}
func traverseMetadata(head *store.NodeExtern, handleFunc func(*store.NodeExtern)) {
q := []*store.NodeExtern{head}
for len(q) > 0 {
n := q[0]
q = q[1:]
handleFunc(n)
for _, next := range n.Nodes {
q = append(q, next)
}
}
}
const (
revBytesLen = 8 + 1 + 8
markedRevBytesLen = revBytesLen + 1
markBytePosition = markedRevBytesLen - 1
markTombstone byte = 't'
)
func isTombstone(b []byte) bool {
return len(b) == markedRevBytesLen && b[markBytePosition] == markTombstone
}
func traverseAndDeleteEmptyDir(st store.Store, dir string) error {
e, err := st.Get(dir, true, false)
if err != nil {
return err
}
if len(e.Node.Nodes) == 0 {
st.Delete(dir, true, true)
return nil
}
for _, node := range e.Node.Nodes {
if !node.Dir {
glog.V(2).Infof("key: %s", node.Key[len(etcdserver.StoreKeysPrefix):])
} else {
err := traverseAndDeleteEmptyDir(st, node.Key)
if err != nil {
return err
}
}
}
return nil
}
func rebuild(datadir string) ([]byte, *raftpb.HardState, store.Store, error) {
waldir := path.Join(datadir, "member", "wal")
snapdir := path.Join(datadir, "member", "snap")
ss := snap.New(snapdir)
snapshot, err := ss.Load()
if err != nil && err != snap.ErrNoSnapshot {
return nil, nil, nil, err
}
var walsnap walpb.Snapshot
if snapshot != nil {
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
}
w, err := wal.OpenForRead(waldir, walsnap)
if err != nil {
return nil, nil, nil, err
}
defer w.Close()
meta, hardstate, ents, err := w.ReadAll()
if err != nil {
return nil, nil, nil, err
}
st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
if snapshot != nil {
err := st.Recovery(snapshot.Data)
if err != nil {
return nil, nil, nil, err
}
}
cluster := membership.NewCluster("")
cluster.SetStore(st)
cluster.Recover(func(*semver.Version) {})
applier := etcdserver.NewApplierV2(st, cluster)
for _, ent := range ents {
if ent.Type == raftpb.EntryConfChange {
var cc raftpb.ConfChange
pbutil.MustUnmarshal(&cc, ent.Data)
switch cc.Type {
case raftpb.ConfChangeAddNode:
m := new(membership.Member)
if err := json.Unmarshal(cc.Context, m); err != nil {
return nil, nil, nil, err
}
cluster.AddMember(m)
case raftpb.ConfChangeRemoveNode:
id := types.ID(cc.NodeID)
cluster.RemoveMember(id)
case raftpb.ConfChangeUpdateNode:
m := new(membership.Member)
if err := json.Unmarshal(cc.Context, m); err != nil {
return nil, nil, nil, err
}
cluster.UpdateRaftAttributes(m.ID, m.RaftAttributes)
}
continue
}
var raftReq pb.InternalRaftRequest
if !pbutil.MaybeUnmarshal(&raftReq, ent.Data) { // backward compatible
var r pb.Request
pbutil.MustUnmarshal(&r, ent.Data)
applyRequest(&r, applier)
} else {
if raftReq.V2 != nil {
req := raftReq.V2
applyRequest(req, applier)
}
}
}
return meta, &hardstate, st, nil
}
func toTTLOptions(r *pb.Request) store.TTLOptionSet {
refresh, _ := pbutil.GetBool(r.Refresh)
ttlOptions := store.TTLOptionSet{Refresh: refresh}
if r.Expiration != 0 {
ttlOptions.ExpireTime = time.Unix(0, r.Expiration)
}
return ttlOptions
}
func applyRequest(r *pb.Request, applyV2 etcdserver.ApplierV2) {
toTTLOptions(r)
switch r.Method {
case "PUT":
applyV2.Put(r)
case "DELETE":
applyV2.Delete(r)
case "POST", "QGET", "SYNC":
return
default:
glog.Fatal("unknown command")
}
}