Merge remote-tracking branch 'origin/master' into release-1.14. Deleting CHANGELOG-1.12.md

pull/564/head
Hannes Hoerl 2019-02-27 09:49:00 +00:00
commit e5777aab95
274 changed files with 10233 additions and 1834 deletions

View File

@ -223,6 +223,11 @@
"sideEffects": {
"description": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.",
"type": "string"
},
"timeoutSeconds": {
"description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.",
"format": "int32",
"type": "integer"
}
},
"required": [

View File

@ -17,7 +17,7 @@ COPY elasticsearch_logging_discovery.go go.mod go.sum /
RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -a -ldflags "-w" -o /elasticsearch_logging_discovery /elasticsearch_logging_discovery.go
FROM docker.elastic.co/elasticsearch/elasticsearch-oss:6.3.2
FROM docker.elastic.co/elasticsearch/elasticsearch-oss:6.6.1
VOLUME ["/data"]
EXPOSE 9200 9300

View File

@ -16,7 +16,7 @@
PREFIX = gcr.io/fluentd-elasticsearch
IMAGE = elasticsearch
TAG = v6.3.0
TAG = v6.6.1
build:
gcloud builds submit --tag ${PREFIX}/${IMAGE}:${TAG}
gcloud builds submit --tag ${PREFIX}/${IMAGE}:${TAG}

View File

@ -54,7 +54,7 @@ metadata:
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
version: v6.3.0
version: v6.6.1
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
spec:
@ -63,17 +63,17 @@ spec:
selector:
matchLabels:
k8s-app: elasticsearch-logging
version: v6.3.0
version: v6.6.1
template:
metadata:
labels:
k8s-app: elasticsearch-logging
version: v6.3.0
version: v6.6.1
kubernetes.io/cluster-service: "true"
spec:
serviceAccountName: elasticsearch-logging
containers:
- image: k8s.gcr.io/elasticsearch:v6.3.0
- image: gcr.io/fluentd-elasticsearch/elasticsearch:v6.6.1
name: elasticsearch-logging
resources:
# need more cpu upon initialization, therefore burstable class

View File

@ -21,7 +21,7 @@ spec:
spec:
containers:
- name: kibana-logging
image: docker.elastic.co/kibana/kibana-oss:6.3.2
image: docker.elastic.co/kibana/kibana-oss:6.6.1
resources:
# need more cpu upon initialization, therefore burstable class
limits:

View File

@ -66,19 +66,19 @@ spec:
- '-c'
- >
LIVENESS_THRESHOLD_SECONDS=${LIVENESS_THRESHOLD_SECONDS:-300};
STUCK_THRESHOLD_SECONDS=${LIVENESS_THRESHOLD_SECONDS:-900};
STUCK_THRESHOLD_SECONDS=${STUCK_THRESHOLD_SECONDS:-900};
if [ ! -e /var/log/fluentd-buffers ];
then
exit 1;
fi;
touch -d "${STUCK_THRESHOLD_SECONDS} seconds ago" /tmp/marker-stuck;
if [[ -z "$(find /var/log/fluentd-buffers -type f -newer /tmp/marker-stuck -print -quit)" ]];
if [ -z "$(find /var/log/fluentd-buffers -type d -newer /tmp/marker-stuck -print -quit)" ];
then
rm -rf /var/log/fluentd-buffers;
exit 1;
fi;
touch -d "${LIVENESS_THRESHOLD_SECONDS} seconds ago" /tmp/marker-liveness;
if [[ -z "$(find /var/log/fluentd-buffers -type f -newer /tmp/marker-liveness -print -quit)" ]];
if [ -z "$(find /var/log/fluentd-buffers -type d -newer /tmp/marker-liveness -print -quit)" ];
then
exit 1;
fi;

View File

@ -267,7 +267,7 @@ function prepare-node-upgrade() {
# TODO(zmerlynn): Get configure-vm script from ${version}. (Must plumb this
# through all create-linux-node-instance-template implementations).
local template_name=$(get-template-name-from-version ${SANITIZED_VERSION})
local template_name=$(get-template-name-from-version ${SANITIZED_VERSION} ${NODE_INSTANCE_PREFIX})
create-linux-node-instance-template "${template_name}"
# The following is echo'd so that callers can get the template name.
echo "Instance template name: ${template_name}"
@ -373,7 +373,7 @@ function do-node-upgrade() {
# Do the actual upgrade.
# NOTE(zmerlynn): If you are changing this gcloud command, update
# test/e2e/cluster_upgrade.go to match this EXACTLY.
local template_name=$(get-template-name-from-version ${SANITIZED_VERSION})
local template_name=$(get-template-name-from-version ${SANITIZED_VERSION} ${NODE_INSTANCE_PREFIX})
local old_templates=()
local updates=()
for group in ${INSTANCE_GROUPS[@]}; do

View File

@ -112,7 +112,10 @@ if [[ "${ENABLE_CLUSTER_AUTOSCALER}" == "true" ]]; then
fi
fi
# These prefixes must not be prefixes of each other, so that they can be used to
# detect mutually exclusive sets of nodes.
NODE_INSTANCE_PREFIX=${NODE_INSTANCE_PREFIX:-"${INSTANCE_PREFIX}-minion"}
WINDOWS_NODE_INSTANCE_PREFIX=${WINDOWS_NODE_INSTANCE_PREFIX:-"${INSTANCE_PREFIX}-windows-node"}
NODE_TAGS="${NODE_TAG}"
@ -373,9 +376,12 @@ function upload-tars() {
#
# Assumed vars:
# NODE_INSTANCE_PREFIX
# WINDOWS_NODE_INSTANCE_PREFIX
# Vars set:
# NODE_NAMES
# INSTANCE_GROUPS
# WINDOWS_NODE_NAMES
# WINDOWS_INSTANCE_GROUPS
function detect-node-names() {
detect-project
INSTANCE_GROUPS=()
@ -383,6 +389,12 @@ function detect-node-names() {
--project "${PROJECT}" \
--filter "name ~ '${NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
--format='value(name)' || true))
WINDOWS_INSTANCE_GROUPS=()
WINDOWS_INSTANCE_GROUPS+=($(gcloud compute instance-groups managed list \
--project "${PROJECT}" \
--filter "name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
--format='value(name)' || true))
NODE_NAMES=()
if [[ -n "${INSTANCE_GROUPS[@]:-}" ]]; then
for group in "${INSTANCE_GROUPS[@]}"; do
@ -395,6 +407,14 @@ function detect-node-names() {
if [[ -n "${HEAPSTER_MACHINE_TYPE:-}" ]]; then
NODE_NAMES+=("${NODE_INSTANCE_PREFIX}-heapster")
fi
WINDOWS_NODE_NAMES=()
if [[ -n "${WINDOWS_INSTANCE_GROUPS[@]:-}" ]]; then
for group in "${WINDOWS_INSTANCE_GROUPS[@]}"; do
WINDOWS_NODE_NAMES+=($(gcloud compute instance-groups managed \
list-instances "${group}" --zone "${ZONE}" --project "${PROJECT}" \
--format='value(instance)'))
done
fi
echo "INSTANCE_GROUPS=${INSTANCE_GROUPS[*]:-}" >&2
echo "NODE_NAMES=${NODE_NAMES[*]:-}" >&2
@ -1403,6 +1423,7 @@ function build-windows-kube-env {
build-linux-kube-env false $file
cat >>$file <<EOF
WINDOWS_NODE_INSTANCE_PREFIX: $(yaml-quote ${WINDOWS_NODE_INSTANCE_PREFIX})
NODE_BINARY_TAR_URL: $(yaml-quote ${NODE_BINARY_TAR_URL})
NODE_BINARY_TAR_HASH: $(yaml-quote ${NODE_BINARY_TAR_HASH})
K8S_DIR: $(yaml-quote ${WINDOWS_K8S_DIR})
@ -1852,9 +1873,13 @@ function make-gcloud-network-argument() {
}
# $1: version (required)
# $2: Prefix for the template name, i.e. NODE_INSTANCE_PREFIX or
# WINDOWS_NODE_INSTANCE_PREFIX.
function get-template-name-from-version() {
local -r version=${1}
local -r template_prefix=${2}
# trim template name to pass gce name validation
echo "${NODE_INSTANCE_PREFIX}-template-${1}" | cut -c 1-63 | sed 's/[\.\+]/-/g;s/-*$//g'
echo "${template_prefix}-template-${version}" | cut -c 1-63 | sed 's/[\.\+]/-/g;s/-*$//g'
}
# validates the NODE_LOCAL_SSDS_EXT variable
@ -2627,9 +2652,8 @@ function create-nodes-template() {
# NOTE: these template names and their format must match
# create-[linux,windows]-nodes() as well as get-template()!
# TODO(pjh): find a better way to manage these (get-template() is annoying).
local linux_template_name="${NODE_INSTANCE_PREFIX}-template"
local windows_template_name="${NODE_INSTANCE_PREFIX}-template-windows"
local windows_template_name="${WINDOWS_NODE_INSTANCE_PREFIX}-template"
create-linux-node-instance-template $linux_template_name
create-windows-node-instance-template $windows_template_name "${scope_flags[*]}"
}
@ -2700,22 +2724,22 @@ function create-linux-nodes() {
# Assumes:
# - NUM_WINDOWS_MIGS
# - NODE_INSTANCE_PREFIX
# - WINDOWS_NODE_INSTANCE_PREFIX
# - NUM_WINDOWS_NODES
# - PROJECT
# - ZONE
function create-windows-nodes() {
local template_name="${NODE_INSTANCE_PREFIX}-template-windows"
local template_name="${WINDOWS_NODE_INSTANCE_PREFIX}-template"
local -r nodes="${NUM_WINDOWS_NODES}"
local instances_left=${nodes}
for ((i=1; i<=${NUM_WINDOWS_MIGS}; i++)); do
local group_name="${NODE_INSTANCE_PREFIX}-windows-group-$i"
local group_name="${WINDOWS_NODE_INSTANCE_PREFIX}-group-$i"
if [[ $i == ${NUM_WINDOWS_MIGS} ]]; then
# TODO: We don't add a suffix for the last group to keep backward compatibility when there's only one MIG.
# We should change it at some point, but note #18545 when changing this.
group_name="${NODE_INSTANCE_PREFIX}-windows-group"
group_name="${WINDOWS_NODE_INSTANCE_PREFIX}-group"
fi
# Spread the remaining number of nodes evenly
this_mig_size=$((${instances_left} / (${NUM_WINDOWS_MIGS}-${i}+1)))
@ -2946,6 +2970,7 @@ function remove-replica-from-etcd() {
# Assumed vars:
# MASTER_NAME
# NODE_INSTANCE_PREFIX
# WINDOWS_NODE_INSTANCE_PREFIX
# ZONE
# This function tears down cluster resources 10 at a time to avoid issuing too many
# API calls and exceeding API quota. It is important to bring down the instances before bringing
@ -2954,7 +2979,7 @@ function kube-down() {
local -r batch=200
detect-project
detect-node-names # For INSTANCE_GROUPS
detect-node-names # For INSTANCE_GROUPS and WINDOWS_INSTANCE_GROUPS
echo "Bringing down cluster"
set +e # Do not stop on error
@ -2965,7 +2990,8 @@ function kube-down() {
# change during a cluster upgrade.)
local templates=$(get-template "${PROJECT}")
for group in ${INSTANCE_GROUPS[@]:-}; do
local all_instance_groups=(${INSTANCE_GROUPS[@]} ${WINDOWS_INSTANCE_GROUPS[@]})
for group in ${all_instance_groups[@]:-}; do
if gcloud compute instance-groups managed describe "${group}" --project "${PROJECT}" --zone "${ZONE}" &>/dev/null; then
gcloud compute instance-groups managed delete \
--project "${PROJECT}" \
@ -3087,7 +3113,7 @@ function kube-down() {
local -a minions
minions=( $(gcloud compute instances list \
--project "${PROJECT}" \
--filter="name ~ '${NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
--filter="(name ~ '${NODE_INSTANCE_PREFIX}-.+' OR name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+') AND zone:(${ZONE})" \
--format='value(name)') )
# If any minions are running, delete them in batches.
while (( "${#minions[@]}" > 0 )); do
@ -3242,15 +3268,19 @@ function set-replica-name() {
REPLICA_NAME="${MASTER_NAME}-${suffix}"
}
# Gets the instance template for given NODE_INSTANCE_PREFIX. It echos the template name so that the function
# output can be used.
# Gets the instance templates in use by the cluster. It echos the template names
# so that the function output can be used.
# Assumed vars:
# NODE_INSTANCE_PREFIX
# WINDOWS_NODE_INSTANCE_PREFIX
#
# $1: project
function get-template() {
local linux_filter="${NODE_INSTANCE_PREFIX}-template(-(${KUBE_RELEASE_VERSION_DASHED_REGEX}|${KUBE_CI_VERSION_DASHED_REGEX}))?"
local windows_filter="${WINDOWS_NODE_INSTANCE_PREFIX}-template(-(${KUBE_RELEASE_VERSION_DASHED_REGEX}|${KUBE_CI_VERSION_DASHED_REGEX}))?"
gcloud compute instance-templates list \
--filter="name ~ '${NODE_INSTANCE_PREFIX}-template(-(${KUBE_RELEASE_VERSION_DASHED_REGEX}|${KUBE_CI_VERSION_DASHED_REGEX}))?'" \
--filter="name ~ '${linux_filter}' OR name ~ '${windows_filter}'" \
--project="${1}" --format='value(name)'
}
@ -3259,6 +3289,7 @@ function get-template() {
# Assumed vars:
# MASTER_NAME
# NODE_INSTANCE_PREFIX
# WINDOWS_NODE_INSTANCE_PREFIX
# ZONE
# REGION
# Vars set:
@ -3274,11 +3305,19 @@ function check-resources() {
KUBE_RESOURCE_FOUND="Managed instance groups ${INSTANCE_GROUPS[@]}"
return 1
fi
if [[ -n "${WINDOWS_INSTANCE_GROUPS[@]:-}" ]]; then
KUBE_RESOURCE_FOUND="Managed instance groups ${WINDOWS_INSTANCE_GROUPS[@]}"
return 1
fi
if gcloud compute instance-templates describe --project "${PROJECT}" "${NODE_INSTANCE_PREFIX}-template" &>/dev/null; then
KUBE_RESOURCE_FOUND="Instance template ${NODE_INSTANCE_PREFIX}-template"
return 1
fi
if gcloud compute instance-templates describe --project "${PROJECT}" "${WINDOWS_NODE_INSTANCE_PREFIX}-template" &>/dev/null; then
KUBE_RESOURCE_FOUND="Instance template ${WINDOWS_NODE_INSTANCE_PREFIX}-template"
return 1
fi
if gcloud compute instances describe --project "${PROJECT}" "${MASTER_NAME}" --zone "${ZONE}" &>/dev/null; then
KUBE_RESOURCE_FOUND="Kubernetes master ${MASTER_NAME}"
@ -3294,10 +3333,10 @@ function check-resources() {
local -a minions
minions=( $(gcloud compute instances list \
--project "${PROJECT}" \
--filter="name ~ '${NODE_INSTANCE_PREFIX}-.+' AND zone:(${ZONE})" \
--filter="(name ~ '${NODE_INSTANCE_PREFIX}-.+' OR name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}-.+') AND zone:(${ZONE})" \
--format='value(name)') )
if (( "${#minions[@]}" > 0 )); then
KUBE_RESOURCE_FOUND="${#minions[@]} matching matching ${NODE_INSTANCE_PREFIX}-.+"
KUBE_RESOURCE_FOUND="${#minions[@]} matching ${NODE_INSTANCE_PREFIX}-.+ or ${WINDOWS_NODE_INSTANCE_PREFIX}-.+"
return 1
fi

View File

@ -250,11 +250,12 @@ function Disable-WindowsDefender {
# Creates directories where other functions in this module will read and write
# data.
# Note: C:\tmp is required for running certain kubernetes tests.
function Create-Directories {
Log-Output "Creating ${env:K8S_DIR} and its subdirectories."
ForEach ($dir in ("${env:K8S_DIR}", "${env:NODE_DIR}", "${env:LOGS_DIR}",
"${env:CNI_DIR}", "${env:CNI_CONFIG_DIR}", "${env:MANIFESTS_DIR}",
"${env:PKI_DIR}")) {
"${env:PKI_DIR}"), "C:\tmp") {
mkdir -Force $dir
}
}

View File

@ -52,6 +52,10 @@ readonly initd_logfiles="docker/log"
readonly supervisord_logfiles="kubelet.log supervisor/supervisord.log supervisor/kubelet-stdout.log supervisor/kubelet-stderr.log supervisor/docker-stdout.log supervisor/docker-stderr.log"
readonly systemd_services="kubelet kubelet-monitor kube-container-runtime-monitor ${LOG_DUMP_SYSTEMD_SERVICES:-docker}"
readonly dump_systemd_journal="${LOG_DUMP_SYSTEMD_JOURNAL:-false}"
# Log files found in WINDOWS_LOGS_DIR on Windows nodes:
readonly windows_node_logfiles="kubelet.log kube-proxy.log docker.log"
# Log files found in other directories on Windows nodes:
readonly windows_node_otherfiles="C:\\Windows\\MEMORY.dmp"
# Limit the number of concurrent node connections so that we don't run out of
# file descriptors for large clusters.
@ -195,6 +199,66 @@ function save-logs() {
copy-logs-from-node "${node_name}" "${dir}" "${files}"
}
# Saves a copy of the Windows Docker event log to ${WINDOWS_LOGS_DIR}\docker.log
# on node $1.
function export-windows-docker-event-log() {
local -r node="${1}"
local -r powershell_cmd="powershell.exe -Command \$log=\$(Get-EventLog -LogName Application -Source Docker); Set-Content '${WINDOWS_LOGS_DIR}\\docker.log' \$log.Message"
# Retry up to 3 times to allow ssh keys to be properly propagated and
# stored.
for retry in {1..3}; do
if gcloud compute ssh --project "${PROJECT}" --zone "${ZONE}" "${node}" \
--command "$powershell_cmd"; then
break
else
sleep 10
fi
done
}
# Save log files and serial console output from Windows node $1 into local
# directory $2.
# This function shouldn't ever trigger errexit.
function save-logs-windows() {
local -r node="${1}"
local -r dest_dir="${2}"
if [[ ! "${gcloud_supported_providers}" =~ "${KUBERNETES_PROVIDER}" ]]; then
echo "Not saving logs for ${node}, Windows log dumping requires gcloud support"
return
fi
export-windows-docker-event-log "${node}"
local remote_files=()
for file in ${windows_node_logfiles[@]}; do
remote_files+=( "${WINDOWS_LOGS_DIR}\\${file}" )
done
remote_files+=( "${windows_node_otherfiles[@]}" )
# TODO(pjh, yujuhong): handle rotated logs and copying multiple files at the
# same time.
for remote_file in ${remote_files[@]}; do
# Retry up to 3 times to allow ssh keys to be properly propagated and
# stored.
for retry in {1..3}; do
if gcloud compute scp --recurse --project "${PROJECT}" \
--zone "${ZONE}" "${node}:${remote_file}" "${dest_dir}" \
> /dev/null; then
break
else
sleep 10
fi
done
done
# Serial port 1 contains the Windows console output.
gcloud compute instances get-serial-port-output --project "${PROJECT}" \
--zone "${ZONE}" --port 1 "${node}" > "${dest_dir}/serial-1.log" || true
}
# Execute a command in container $2 on node $1.
# Uses docker because the container may not ordinarily permit direct execution.
function run-in-docker-container() {
@ -247,8 +311,13 @@ function dump_masters() {
fi
}
# Dumps logs from nodes in the cluster. Linux nodes to dump logs from can be
# specified via $1 or $use_custom_instance_list. If not specified then the nodes
# to dump logs for will be detected using detect-node-names(); if Windows nodes
# are present then they will be detected and their logs will be dumped too.
function dump_nodes() {
local node_names=()
local windows_node_names=()
if [[ -n "${1:-}" ]]; then
echo "Dumping logs for nodes provided as args to dump_nodes() function"
node_names=( "$@" )
@ -264,9 +333,12 @@ function dump_nodes() {
if [[ -n "${NODE_NAMES:-}" ]]; then
node_names=( "${NODE_NAMES[@]}" )
fi
if [[ -n "${WINDOWS_NODE_NAMES:-}" ]]; then
windows_node_names=( "${WINDOWS_NODE_NAMES[@]}" )
fi
fi
if [[ "${#node_names[@]}" == 0 ]]; then
if [[ "${#node_names[@]}" == 0 && "${#windows_node_names[@]}" == 0 ]]; then
echo "No nodes found!"
return
fi
@ -276,24 +348,31 @@ function dump_nodes() {
node_logfiles_all="${node_logfiles_all} ${hollow_node_logfiles}"
fi
nodes_selected_for_logs=()
linux_nodes_selected_for_logs=()
if [[ -n "${LOGDUMP_ONLY_N_RANDOM_NODES:-}" ]]; then
# We randomly choose 'LOGDUMP_ONLY_N_RANDOM_NODES' many nodes for fetching logs.
for index in `shuf -i 0-$(( ${#node_names[*]} - 1 )) -n ${LOGDUMP_ONLY_N_RANDOM_NODES}`
do
nodes_selected_for_logs+=("${node_names[$index]}")
linux_nodes_selected_for_logs+=("${node_names[$index]}")
done
else
nodes_selected_for_logs=( "${node_names[@]}" )
linux_nodes_selected_for_logs=( "${node_names[@]}" )
fi
all_selected_nodes=( "${linux_nodes_selected_for_logs[@]}" )
all_selected_nodes+=( "${windows_node_names[@]}" )
proc=${max_dump_processes}
for node_name in "${nodes_selected_for_logs[@]}"; do
for i in "${!all_selected_nodes[@]}"; do
node_name="${all_selected_nodes[$i]}"
node_dir="${report_dir}/${node_name}"
mkdir -p "${node_dir}"
# Save logs in the background. This speeds up things when there are
# many nodes.
save-logs "${node_name}" "${node_dir}" "${node_logfiles_all}" "${node_systemd_services}" &
if [[ "${i}" -lt "${#linux_nodes_selected_for_logs[@]}" ]]; then
# Save logs in the background. This speeds up things when there are
# many nodes.
save-logs "${node_name}" "${node_dir}" "${node_logfiles_all}" "${node_systemd_services}" &
else
save-logs-windows "${node_name}" "${node_dir}" &
fi
# We don't want to run more than ${max_dump_processes} at a time, so
# wait once we hit that many nodes. This isn't ideal, since one might
@ -311,6 +390,9 @@ function dump_nodes() {
}
# Collect names of nodes which didn't run logexporter successfully.
# This function examines NODE_NAMES but not WINDOWS_NODE_NAMES since logexporter
# does not run on Windows nodes.
#
# Note: This step is O(#nodes^2) as we check if each node is present in the list of succeeded nodes.
# Making it linear would add code complexity without much benefit (as it just takes ~1s for 5k nodes).
# Assumes:
@ -328,6 +410,8 @@ function find_non_logexported_nodes() {
done
}
# This function examines NODE_NAMES but not WINDOWS_NODE_NAMES since logexporter
# does not run on Windows nodes.
function dump_nodes_with_logexporter() {
if [[ -n "${use_custom_instance_list}" ]]; then
echo "Dumping logs for nodes provided by log_dump_custom_get_instances() function"
@ -446,10 +530,16 @@ function detect_node_failures() {
fi
detect-node-names
if [ -z "${INSTANCE_GROUPS:-}" ]; then
if [[ "${KUBERNETES_PROVIDER}" == "gce" ]]; then
local all_instance_groups=(${INSTANCE_GROUPS[@]} ${WINDOWS_INSTANCE_GROUPS[@]})
else
local all_instance_groups=(${INSTANCE_GROUPS[@]})
fi
if [ -z "${all_instance_groups:-}" ]; then
return
fi
for group in "${INSTANCE_GROUPS[@]}"; do
for group in "${all_instance_groups[@]}"; do
local creation_timestamp=$(gcloud compute instance-groups managed describe \
"${group}" \
--project "${PROJECT}" \

View File

@ -56,7 +56,7 @@ if [[ "${KUBERNETES_PROVIDER:-}" == "gce" ]]; then
# In multizone mode we need to add instances for all nodes in the region.
if [[ "${MULTIZONE:-}" == "true" ]]; then
EXPECTED_NUM_NODES=$(gcloud -q compute instances list --project="${PROJECT}" --format=[no-heading] \
--filter="name ~ '${NODE_INSTANCE_PREFIX}.*' AND zone:($(gcloud -q compute zones list --project="${PROJECT}" --filter=region=${REGION} --format=csv[no-heading]\(name\) | tr "\n" "," | sed "s/,$//"))" | wc -l)
--filter="(name ~ '${NODE_INSTANCE_PREFIX}.*' OR name ~ '${WINDOWS_NODE_INSTANCE_PREFIX}.*') AND zone:($(gcloud -q compute zones list --project="${PROJECT}" --filter=region=${REGION} --format=csv[no-heading]\(name\) | tr "\n" "," | sed "s/,$//"))" | wc -l)
echo "Computing number of nodes, NODE_INSTANCE_PREFIX=${NODE_INSTANCE_PREFIX}, REGION=${REGION}, EXPECTED_NUM_NODES=${EXPECTED_NUM_NODES}"
fi
else

View File

@ -41,6 +41,7 @@ filegroup(
"//cmd/kubeadm/app/phases/bootstraptoken/node:all-srcs",
"//cmd/kubeadm/app/phases/certs:all-srcs",
"//cmd/kubeadm/app/phases/controlplane:all-srcs",
"//cmd/kubeadm/app/phases/copycerts:all-srcs",
"//cmd/kubeadm/app/phases/etcd:all-srcs",
"//cmd/kubeadm/app/phases/kubeconfig:all-srcs",
"//cmd/kubeadm/app/phases/kubelet:all-srcs",
@ -48,7 +49,6 @@ filegroup(
"//cmd/kubeadm/app/phases/patchnode:all-srcs",
"//cmd/kubeadm/app/phases/selfhosting:all-srcs",
"//cmd/kubeadm/app/phases/upgrade:all-srcs",
"//cmd/kubeadm/app/phases/uploadcerts:all-srcs",
"//cmd/kubeadm/app/phases/uploadconfig:all-srcs",
"//cmd/kubeadm/app/preflight:all-srcs",
"//cmd/kubeadm/app/util:all-srcs",

View File

@ -147,7 +147,7 @@ type APIEndpoint struct {
// NodeRegistrationOptions holds fields that relate to registering a new control-plane or node to the cluster, either via "kubeadm init" or "kubeadm join"
type NodeRegistrationOptions struct {
// Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm joiń` operation.
// Name is the `.Metadata.Name` field of the Node API object that will be created in this `kubeadm init` or `kubeadm join` operation.
// This field is also used in the CommonName field of the kubelet's client certificate to the API server.
// Defaults to the hostname of the node if not provided.
Name string `json:"name,omitempty"`

View File

@ -428,6 +428,7 @@ func isAllowedFlag(flagName string) bool {
kubeadmcmdoptions.NodeCRISocket,
kubeadmcmdoptions.KubeconfigDir,
kubeadmcmdoptions.UploadCerts,
kubeadmcmdoptions.CertificateKey,
"print-join-command", "rootfs", "v")
if knownFlags.Has(flagName) {
return true

View File

@ -84,6 +84,7 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/options:go_default_library",

View File

@ -127,6 +127,7 @@ type joinOptions struct {
controlPlane bool
ignorePreflightErrors []string
externalcfg *kubeadmapiv1beta1.JoinConfiguration
certificateKey string
}
// compile-time assert that the local data object satisfies the phases data interface.
@ -142,6 +143,7 @@ type joinData struct {
clientSets map[string]*clientset.Clientset
ignorePreflightErrors sets.String
outputWriter io.Writer
certificateKey string
}
// NewCmdJoin returns "kubeadm join" command.
@ -192,7 +194,7 @@ func NewCmdJoin(out io.Writer, joinOptions *joinOptions) *cobra.Command {
}
addJoinConfigFlags(cmd.Flags(), joinOptions.externalcfg)
addJoinOtherFlags(cmd.Flags(), &joinOptions.cfgPath, &joinOptions.ignorePreflightErrors, &joinOptions.controlPlane, &joinOptions.token)
addJoinOtherFlags(cmd.Flags(), &joinOptions.cfgPath, &joinOptions.ignorePreflightErrors, &joinOptions.controlPlane, &joinOptions.token, &joinOptions.certificateKey)
joinRunner.AppendPhase(phases.NewPreflightPhase())
joinRunner.AppendPhase(phases.NewControlPlanePreparePhase())
@ -254,7 +256,14 @@ func addJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfig
}
// addJoinOtherFlags adds join flags that are not bound to a configuration file to the given flagset
func addJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, ignorePreflightErrors *[]string, controlPlane *bool, token *string) {
func addJoinOtherFlags(
flagSet *flag.FlagSet,
cfgPath *string,
ignorePreflightErrors *[]string,
controlPlane *bool,
token *string,
certificateKey *string,
) {
flagSet.StringVar(
cfgPath, options.CfgPath, *cfgPath,
"Path to kubeadm config file.",
@ -271,6 +280,10 @@ func addJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, ignorePreflightEr
controlPlane, options.ControlPlane, *controlPlane,
"Create a new control plane instance on this node",
)
flagSet.StringVar(
certificateKey, options.CertificateKey, "",
"Use this key to decrypt the certificate secrets uploaded by init.",
)
}
// newJoinOptions returns a struct ready for being used for creating cmd join flags.
@ -375,9 +388,15 @@ func newJoinData(cmd *cobra.Command, args []string, options *joinOptions, out io
clientSets: map[string]*clientset.Clientset{},
ignorePreflightErrors: ignorePreflightErrorsSet,
outputWriter: out,
certificateKey: options.certificateKey,
}, nil
}
// CertificateKey returns the key used to encrypt the certs.
func (j *joinData) CertificateKey() string {
return j.certificateKey
}
// Cfg returns the JoinConfiguration.
func (j *joinData) Cfg() *kubeadmapi.JoinConfiguration {
return j.cfg

View File

@ -121,4 +121,7 @@ const (
// UploadCerts flag instruct kubeadm to upload certificates
UploadCerts = "experimental-upload-certs"
// CertificateKey flag sets the key used to encrypt and decrypt certificate secrets
CertificateKey = "certificate-key"
)

View File

@ -33,12 +33,12 @@ go_library(
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/copycerts:go_default_library",
"//cmd/kubeadm/app/phases/etcd:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/kubelet:go_default_library",
"//cmd/kubeadm/app/phases/markcontrolplane:go_default_library",
"//cmd/kubeadm/app/phases/patchnode:go_default_library",
"//cmd/kubeadm/app/phases/uploadcerts:go_default_library",
"//cmd/kubeadm/app/phases/uploadconfig:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",

View File

@ -26,7 +26,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadcerts"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/copycerts"
)
// NewUploadCertsPhase returns the uploadCerts phase
@ -59,14 +59,14 @@ func runUploadCerts(c workflow.RunData) error {
}
if len(data.CertificateKey()) == 0 {
certificateKey, err := uploadcerts.CreateCertificateKey()
certificateKey, err := copycerts.CreateCertificateKey()
if err != nil {
return err
}
data.SetCertificateKey(certificateKey)
}
if err := uploadcerts.UploadCerts(client, data.Cfg(), data.CertificateKey()); err != nil {
if err := copycerts.UploadCerts(client, data.Cfg(), data.CertificateKey()); err != nil {
return errors.Wrap(err, "error uploading certs")
}
return nil

View File

@ -20,6 +20,7 @@ go_library(
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/copycerts:go_default_library",
"//cmd/kubeadm/app/phases/etcd:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/kubelet:go_default_library",

View File

@ -21,13 +21,17 @@ import (
"github.com/pkg/errors"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/copycerts"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
// NewControlPlanePreparePhase creates a kubeadm workflow phase that implements the preparation of the node to serve a control plane
@ -35,7 +39,6 @@ func NewControlPlanePreparePhase() workflow.Phase {
return workflow.Phase{
Name: "control-plane-prepare",
Short: "Prepares the machine for serving a control plane.",
Long: cmdutil.MacroCommandLongDescription,
Phases: []workflow.Phase{
{
Name: "all",
@ -43,6 +46,7 @@ func NewControlPlanePreparePhase() workflow.Phase {
InheritFlags: getControlPlanePreparePhaseFlags(),
RunAllSiblings: true,
},
newControlPlanePrepareDownloadCertsSubphase(),
newControlPlanePrepareCertsSubphase(),
newControlPlanePrepareKubeconfigSubphase(),
newControlPlanePrepareManifestsSubphases(),
@ -60,6 +64,20 @@ func getControlPlanePreparePhaseFlags() []string {
options.TokenDiscovery,
options.TokenDiscoveryCAHash,
options.TokenDiscoverySkipCAHash,
options.CertificateKey,
}
}
func newControlPlanePrepareDownloadCertsSubphase() workflow.Phase {
return workflow.Phase{
Name: "download-certs",
Short: fmt.Sprintf("Download certificates from %s", kubeadmconstants.KubeadmCertsSecret),
Long: cmdutil.MacroCommandLongDescription,
Run: runControlPlanePrepareDownloadCertsPhaseLocal,
InheritFlags: []string{
options.CfgPath,
options.CertificateKey,
},
}
}
@ -110,6 +128,33 @@ func runControlPlanePrepareManifestsSubphase(c workflow.RunData) error {
return controlplane.CreateInitStaticPodManifestFiles(kubeadmconstants.GetStaticPodDirectory(), cfg)
}
func runControlPlanePrepareDownloadCertsPhaseLocal(c workflow.RunData) error {
data, ok := c.(JoinData)
if !ok {
return errors.New("download-certs phase invoked with an invalid data struct")
}
if data.Cfg().ControlPlane == nil || len(data.CertificateKey()) == 0 {
klog.V(1).Infoln("[download-certs] Skipping certs download")
return nil
}
cfg, err := data.InitCfg()
if err != nil {
return err
}
client, err := bootstrapClient(data)
if err != nil {
return err
}
if err := copycerts.DownloadCerts(client, cfg, data.CertificateKey()); err != nil {
return errors.Wrap(err, "error downloading certs")
}
return nil
}
func runControlPlanePrepareCertsPhaseLocal(c workflow.RunData) error {
data, ok := c.(JoinData)
if !ok {
@ -157,3 +202,15 @@ func runControlPlanePrepareKubeconfigPhaseLocal(c workflow.RunData) error {
return nil
}
func bootstrapClient(data JoinData) (clientset.Interface, error) {
tlsBootstrapCfg, err := data.TLSBootstrapCfg()
if err != nil {
return nil, errors.Wrap(err, "unable to access the cluster")
}
client, err := kubeconfigutil.ToClientSet(tlsBootstrapCfg)
if err != nil {
return nil, errors.Wrap(err, "unable to access the cluster")
}
return client, nil
}

View File

@ -28,6 +28,7 @@ import (
// JoinData is the interface to use for join phases.
// The "joinData" type from "cmd/join.go" must satisfy this interface.
type JoinData interface {
CertificateKey() string
Cfg() *kubeadmapi.JoinConfiguration
KubeConfigPath() string
TLSBootstrapCfg() (*clientcmdapi.Config, error)

View File

@ -31,6 +31,7 @@ type testJoinData struct{}
// testJoinData must satisfy JoinData.
var _ JoinData = &testJoinData{}
func (j *testJoinData) CertificateKey() string { return "" }
func (j *testJoinData) Cfg() *kubeadmapi.JoinConfiguration { return nil }
func (j *testJoinData) KubeConfigPath() string { return "" }
func (j *testJoinData) TLSBootstrapCfg() (*clientcmdapi.Config, error) { return nil, nil }

View File

@ -39,7 +39,7 @@ var (
kubeadm join phase preflight --config kubeadm-config.yml
`)
notReadyToJoinControPlaneTemp = template.Must(template.New("join").Parse(dedent.Dedent(`
notReadyToJoinControlPlaneTemp = template.Must(template.New("join").Parse(dedent.Dedent(`
One or more conditions for hosting a new control plane instance is not satisfied.
{{.Error}}
@ -105,14 +105,15 @@ func runPreflight(c workflow.RunData) error {
if j.Cfg().ControlPlane != nil {
// Checks if the cluster configuration supports
// joining a new control plane instance and if all the necessary certificates are provided
if err := checkIfReadyForAdditionalControlPlane(&initCfg.ClusterConfiguration); err != nil {
hasCertificateKey := len(j.CertificateKey()) > 0
if err := checkIfReadyForAdditionalControlPlane(&initCfg.ClusterConfiguration, hasCertificateKey); err != nil {
// outputs the not ready for hosting a new control plane instance message
ctx := map[string]string{
"Error": err.Error(),
}
var msg bytes.Buffer
notReadyToJoinControPlaneTemp.Execute(&msg, ctx)
notReadyToJoinControlPlaneTemp.Execute(&msg, ctx)
return errors.New(msg.String())
}
@ -134,15 +135,17 @@ func runPreflight(c workflow.RunData) error {
// checkIfReadyForAdditionalControlPlane ensures that the cluster is in a state that supports
// joining an additional control plane instance and if the node is ready to preflight
func checkIfReadyForAdditionalControlPlane(initConfiguration *kubeadmapi.ClusterConfiguration) error {
func checkIfReadyForAdditionalControlPlane(initConfiguration *kubeadmapi.ClusterConfiguration, hasCertificateKey bool) error {
// blocks if the cluster was created without a stable control plane endpoint
if initConfiguration.ControlPlaneEndpoint == "" {
return errors.New("unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address")
}
// checks if the certificates that must be equal across contolplane instances are provided
if ret, err := certs.SharedCertificateExists(initConfiguration); !ret {
return err
if !hasCertificateKey {
// checks if the certificates that must be equal across controlplane instances are provided
if ret, err := certs.SharedCertificateExists(initConfiguration); !ret {
return err
}
}
return nil

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
@ -68,15 +69,20 @@ func NewCmdReset(in io.Reader, out io.Writer) *cobra.Command {
kubeadmutil.CheckErr(err)
}
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
if err != nil {
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap from cluster: %v", err)
}
if criSocketPath == "" {
criSocketPath, err = resetDetectCRISocket(client)
criSocketPath, err = resetDetectCRISocket(cfg)
kubeadmutil.CheckErr(err)
klog.V(1).Infof("[reset] detected and using CRI socket: %s", criSocketPath)
}
r, err := NewReset(in, ignorePreflightErrorsSet, forceReset, certsDir, criSocketPath)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(r.Run(out, client))
kubeadmutil.CheckErr(r.Run(out, client, cfg))
},
}
@ -131,17 +137,19 @@ func NewReset(in io.Reader, ignorePreflightErrors sets.String, forceReset bool,
}
// Run reverts any changes made to this host by "kubeadm init" or "kubeadm join".
func (r *Reset) Run(out io.Writer, client clientset.Interface) error {
func (r *Reset) Run(out io.Writer, client clientset.Interface, cfg *kubeadmapi.InitConfiguration) error {
var dirsToClean []string
// Only clear etcd data when using local etcd.
etcdManifestPath := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName, "etcd.yaml")
klog.V(1).Infof("[reset] checking for etcd config")
etcdDataDir, err := getEtcdDataDir(etcdManifestPath, client)
etcdDataDir, err := getEtcdDataDir(etcdManifestPath, cfg)
if err == nil {
dirsToClean = append(dirsToClean, etcdDataDir)
if err := removeEtcdMember(client); err != nil {
klog.Warningf("[reset] failed to remove etcd member: %v\n.Please manually remove this etcd member using etcdctl", err)
if cfg != nil {
if err := etcdphase.RemoveStackedEtcdMemberFromCluster(client, cfg); err != nil {
klog.Warningf("[reset] failed to remove etcd member: %v\n.Please manually remove this etcd member using etcdctl", err)
}
}
} else {
fmt.Println("[reset] no etcd config found. Assuming external etcd")
@ -209,25 +217,14 @@ func (r *Reset) Run(out io.Writer, client clientset.Interface) error {
return nil
}
func removeEtcdMember(client clientset.Interface) error {
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
if err != nil {
return err
}
return etcdphase.RemoveStackedEtcdMemberFromCluster(client, cfg)
}
func getEtcdDataDir(manifestPath string, client clientset.Interface) (string, error) {
func getEtcdDataDir(manifestPath string, cfg *kubeadmapi.InitConfiguration) (string, error) {
const etcdVolumeName = "etcd-data"
var dataDir string
if client != nil {
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
if err == nil && cfg.Etcd.Local != nil {
return cfg.Etcd.Local.DataDir, nil
}
klog.Warningf("[reset] Unable to fetch the kubeadm-config ConfigMap, using etcd pod spec as fallback: %v", err)
if cfg != nil && cfg.Etcd.Local != nil {
return cfg.Etcd.Local.DataDir, nil
}
klog.Warningln("[reset] No kubeadm config, using etcd pod spec to get data directory")
etcdPod, err := utilstaticpod.ReadStaticPodFromDisk(manifestPath)
if err != nil {
@ -311,13 +308,10 @@ func resetConfigDir(configPathDir, pkiPathDir string) {
}
}
func resetDetectCRISocket(client clientset.Interface) (string, error) {
if client != nil {
// first try to connect to the cluster for the CRI socket
cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "reset", false)
if err == nil {
return cfg.NodeRegistration.CRISocket, nil
}
func resetDetectCRISocket(cfg *kubeadmapi.InitConfiguration) (string, error) {
if cfg != nil {
// first try to get the CRI socket from the cluster configuration
return cfg.NodeRegistration.CRISocket, nil
}
// if this fails, try to detect it

View File

@ -25,7 +25,7 @@ import (
"github.com/lithammer/dedent"
clientsetfake "k8s.io/client-go/kubernetes/fake"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
@ -275,38 +275,38 @@ func TestGetEtcdDataDir(t *testing.T) {
podYaml string
expectErr bool
writeManifest bool
validClient bool
validConfig bool
}{
"non-existent file returns error": {
expectErr: true,
writeManifest: false,
validClient: true,
validConfig: true,
},
"return etcd data dir": {
dataDir: "/path/to/etcd",
podYaml: etcdPod,
expectErr: false,
writeManifest: true,
validClient: true,
validConfig: true,
},
"invalid etcd pod": {
podYaml: etcdPodInvalid,
expectErr: true,
writeManifest: true,
validClient: true,
validConfig: true,
},
"etcd pod spec without data volume": {
podYaml: etcdPodWithoutDataVolume,
expectErr: true,
writeManifest: true,
validClient: true,
validConfig: true,
},
"kubeconfig file doesn't exist": {
dataDir: "/path/to/etcd",
podYaml: etcdPod,
expectErr: false,
writeManifest: true,
validClient: false,
validConfig: false,
},
}
@ -325,9 +325,9 @@ func TestGetEtcdDataDir(t *testing.T) {
var dataDir string
var err error
if test.validClient {
client := clientsetfake.NewSimpleClientset()
dataDir, err = getEtcdDataDir(manifestPath, client)
if test.validConfig {
cfg := &kubeadmapi.InitConfiguration{}
dataDir, err = getEtcdDataDir(manifestPath, cfg)
} else {
dataDir, err = getEtcdDataDir(manifestPath, nil)
}

View File

@ -22,7 +22,7 @@ import (
)
const (
testUpgradeDiffConfig = `testdata/diff_master_config.yaml`
testUpgradeDiffConfig = `testdata/diff_controlplane_config.yaml`
testUpgradeDiffManifest = `testdata/diff_dummy_manifest.yaml`
)

View File

@ -30,7 +30,7 @@ import (
)
var joinCommandTemplate = template.Must(template.New("join").Parse(`` +
`kubeadm join {{.MasterHostPort}} --token {{.Token}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}}{{if .UploadCerts}} --certificate-key {{.CertificateKey}}{{end}}`,
`kubeadm join {{.ControlPlaneHostPort}} --token {{.Token}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}}{{if .UploadCerts}} --certificate-key {{.CertificateKey}}{{end}}`,
))
// GetJoinCommand returns the kubeadm join command for a given token and
@ -71,11 +71,11 @@ func GetJoinCommand(kubeConfigFile, token, key string, skipTokenPrint, uploadCer
}
ctx := map[string]interface{}{
"Token": token,
"CAPubKeyPins": publicKeyPins,
"MasterHostPort": strings.Replace(clusterConfig.Server, "https://", "", -1),
"UploadCerts": uploadCerts,
"CertificateKey": key,
"Token": token,
"CAPubKeyPins": publicKeyPins,
"ControlPlaneHostPort": strings.Replace(clusterConfig.Server, "https://", "", -1),
"UploadCerts": uploadCerts,
"CertificateKey": key,
}
if skipTokenPrint {

View File

@ -172,16 +172,16 @@ func RetrieveValidatedConfigInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmda
// buildInsecureBootstrapKubeConfig makes a kubeconfig object that connects insecurely to the API Server for bootstrapping purposes
func buildInsecureBootstrapKubeConfig(endpoint, clustername string) *clientcmdapi.Config {
masterEndpoint := fmt.Sprintf("https://%s", endpoint)
bootstrapConfig := kubeconfigutil.CreateBasic(masterEndpoint, clustername, BootstrapUser, []byte{})
controlPlaneEndpoint := fmt.Sprintf("https://%s", endpoint)
bootstrapConfig := kubeconfigutil.CreateBasic(controlPlaneEndpoint, clustername, BootstrapUser, []byte{})
bootstrapConfig.Clusters[clustername].InsecureSkipTLSVerify = true
return bootstrapConfig
}
// buildSecureBootstrapKubeConfig makes a kubeconfig object that connects securely to the API Server for bootstrapping purposes (validating with the specified CA)
func buildSecureBootstrapKubeConfig(endpoint string, caCert []byte, clustername string) *clientcmdapi.Config {
masterEndpoint := fmt.Sprintf("https://%s", endpoint)
bootstrapConfig := kubeconfigutil.CreateBasic(masterEndpoint, clustername, BootstrapUser, caCert)
controlPlaneEndpoint := fmt.Sprintf("https://%s", endpoint)
bootstrapConfig := kubeconfigutil.CreateBasic(controlPlaneEndpoint, clustername, BootstrapUser, caCert)
return bootstrapConfig
}

View File

@ -2,8 +2,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["uploadcerts.go"],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadcerts",
srcs = ["copycerts.go"],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/phases/copycerts",
visibility = ["//visibility:public"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
@ -14,9 +14,12 @@ go_library(
"//pkg/apis/rbac/v1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
"//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
@ -38,7 +41,7 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["uploadcerts_test.go"],
srcs = ["copycerts_test.go"],
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package uploadcerts
package copycerts
import (
"encoding/hex"
@ -28,9 +28,12 @@ import (
v1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
clientset "k8s.io/client-go/kubernetes"
certutil "k8s.io/client-go/util/cert"
keyutil "k8s.io/client-go/util/keyutil"
bootstraputil "k8s.io/cluster-bootstrap/token/util"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
@ -92,7 +95,7 @@ func UploadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration,
return err
}
secretData, err := getSecretData(cfg, decodedKey)
secretData, err := getDataFromDisk(cfg, decodedKey)
if err != nil {
return err
}
@ -169,7 +172,7 @@ func loadAndEncryptCert(certPath string, key []byte) ([]byte, error) {
return cryptoutil.EncryptBytes(cert, key)
}
func certsToUpload(cfg *kubeadmapi.InitConfiguration) map[string]string {
func certsToTransfer(cfg *kubeadmapi.InitConfiguration) map[string]string {
certsDir := cfg.CertificatesDir
certs := map[string]string{
kubeadmconstants.CACertName: path.Join(certsDir, kubeadmconstants.CACertName),
@ -191,15 +194,85 @@ func certsToUpload(cfg *kubeadmapi.InitConfiguration) map[string]string {
return certs
}
func getSecretData(cfg *kubeadmapi.InitConfiguration, key []byte) (map[string][]byte, error) {
func getDataFromDisk(cfg *kubeadmapi.InitConfiguration, key []byte) (map[string][]byte, error) {
secretData := map[string][]byte{}
for certName, certPath := range certsToUpload(cfg) {
for certName, certPath := range certsToTransfer(cfg) {
cert, err := loadAndEncryptCert(certPath, key)
if err == nil || (err != nil && os.IsNotExist(err)) {
secretData[strings.Replace(certName, "/", "-", -1)] = cert
secretData[certOrKeyNameToSecretName(certName)] = cert
} else {
return nil, err
}
}
return secretData, nil
}
// DownloadCerts downloads the certificates needed to join a new control plane.
func DownloadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, key string) error {
fmt.Printf("[download-certs] downloading the certificates in Secret %q in the %q Namespace\n", kubeadmconstants.KubeadmCertsSecret, metav1.NamespaceSystem)
decodedKey, err := hex.DecodeString(key)
if err != nil {
return errors.Wrap(err, "error decoding certificate key")
}
secret, err := getSecret(client)
if err != nil {
return errors.Wrap(err, "error downloading the secret")
}
secretData, err := getDataFromSecret(secret, decodedKey)
if err != nil {
return errors.Wrap(err, "error decoding secret data with provided key")
}
for certOrKeyName, certOrKeyPath := range certsToTransfer(cfg) {
certOrKeyData, found := secretData[certOrKeyNameToSecretName(certOrKeyName)]
if !found {
return errors.New("couldn't find required certificate or key in Secret")
}
if err := writeCertOrKey(certOrKeyPath, certOrKeyData); err != nil {
return err
}
}
return nil
}
func writeCertOrKey(certOrKeyPath string, certOrKeyData []byte) error {
if _, err := keyutil.ParsePublicKeysPEM(certOrKeyData); err == nil {
return keyutil.WriteKey(certOrKeyPath, certOrKeyData)
} else if _, err := certutil.ParseCertsPEM(certOrKeyData); err == nil {
return certutil.WriteCert(certOrKeyPath, certOrKeyData)
}
return errors.New("unknown data found in Secret entry")
}
func getSecret(client clientset.Interface) (*v1.Secret, error) {
secret, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Get(kubeadmconstants.KubeadmCertsSecret, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil, errors.Errorf("Secret %q was not found in the %q Namespace. This Secret might have expired. Please, run `kubeadm init phase upload-certs` on a control plane to generate a new one", kubeadmconstants.KubeadmCertsSecret, metav1.NamespaceSystem)
}
return nil, err
}
return secret, nil
}
func getDataFromSecret(secret *v1.Secret, key []byte) (map[string][]byte, error) {
secretData := map[string][]byte{}
for certName, encryptedCert := range secret.Data {
cert, err := cryptoutil.DecryptBytes(encryptedCert, key)
if err != nil {
// If any of the decrypt operations fail do not return a partial result,
// return an empty result immediately
return map[string][]byte{}, err
}
secretData[certName] = cert
}
return secretData, nil
}
func certOrKeyNameToSecretName(certOrKeyName string) string {
return strings.Replace(certOrKeyName, "/", "-", -1)
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package uploadcerts
package copycerts
import (
"encoding/hex"
@ -38,7 +38,7 @@ func TestUploadCerts(t *testing.T) {
}
//teste cert name, teste cert can be decrypted
func TestGetSecretData(t *testing.T) {
func TestGetDataFromInitConfig(t *testing.T) {
certData := []byte("cert-data")
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
@ -58,14 +58,14 @@ func TestGetSecretData(t *testing.T) {
t.Fatalf(dedent.Dedent("failed to create etcd cert dir.\nfatal error: %v"), err)
}
certs := certsToUpload(cfg)
certs := certsToTransfer(cfg)
for name, path := range certs {
if err := ioutil.WriteFile(path, certData, 0644); err != nil {
t.Fatalf(dedent.Dedent("failed to write cert: %s\nfatal error: %v"), name, err)
}
}
secretData, err := getSecretData(cfg, decodedKey)
secretData, err := getDataFromDisk(cfg, decodedKey)
if err != nil {
t.Fatalf("failed to get secret data. fatal error: %v", err)
}
@ -83,29 +83,44 @@ func TestGetSecretData(t *testing.T) {
}
}
func TestCertsToUpload(t *testing.T) {
func TestCertsToTransfer(t *testing.T) {
localEtcdCfg := &kubeadmapi.InitConfiguration{}
externalEtcdCfg := &kubeadmapi.InitConfiguration{}
externalEtcdCfg.Etcd = kubeadmapi.Etcd{}
externalEtcdCfg.Etcd.External = &kubeadmapi.ExternalEtcd{}
commonExpectedCerts := []string{
kubeadmconstants.CACertName,
kubeadmconstants.CAKeyName,
kubeadmconstants.FrontProxyCACertName,
kubeadmconstants.FrontProxyCAKeyName,
kubeadmconstants.ServiceAccountPublicKeyName,
kubeadmconstants.ServiceAccountPrivateKeyName,
}
tests := map[string]struct {
config *kubeadmapi.InitConfiguration
expectedCerts []string
}{
"local etcd": {
config: localEtcdCfg,
expectedCerts: []string{kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName},
config: localEtcdCfg,
expectedCerts: append(
[]string{kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName},
commonExpectedCerts...,
),
},
"external etcd": {
config: externalEtcdCfg,
expectedCerts: []string{externalEtcdCA, externalEtcdCert, externalEtcdKey},
config: externalEtcdCfg,
expectedCerts: append(
[]string{externalEtcdCA, externalEtcdCert, externalEtcdKey},
commonExpectedCerts...,
),
},
}
for name, test := range tests {
t.Run(name, func(t2 *testing.T) {
certList := certsToUpload(test.config)
certList := certsToTransfer(test.config)
for _, cert := range test.expectedCerts {
if _, found := certList[cert]; !found {
t2.Fatalf(dedent.Dedent("failed to get list of certs to upload\ncert %s not found"), cert)
@ -114,3 +129,30 @@ func TestCertsToUpload(t *testing.T) {
})
}
}
func TestCertOrKeyNameToSecretName(t *testing.T) {
tests := []struct {
keyName string
expectedSecretName string
}{
{
keyName: "apiserver-kubelet-client.crt",
expectedSecretName: "apiserver-kubelet-client.crt",
},
{
keyName: "etcd/ca.crt",
expectedSecretName: "etcd-ca.crt",
},
{
keyName: "etcd/healthcheck-client.crt",
expectedSecretName: "etcd-healthcheck-client.crt",
},
}
for _, tc := range tests {
secretName := certOrKeyNameToSecretName(tc.keyName)
if secretName != tc.expectedSecretName {
t.Fatalf("secret name %s didn't match expected name %s", secretName, tc.expectedSecretName)
}
}
}

View File

@ -23,6 +23,7 @@ go_library(
"//cmd/kubeadm/app/images:go_default_library",
"//cmd/kubeadm/app/util/runtime:go_default_library",
"//cmd/kubeadm/app/util/system:go_default_library",
"//pkg/master/ports:go_default_library",
"//pkg/registry/core/service/ipallocator:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/util/ipvs:go_default_library",

View File

@ -46,6 +46,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/images"
utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
"k8s.io/kubernetes/cmd/kubeadm/app/util/system"
"k8s.io/kubernetes/pkg/master/ports"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
"k8s.io/kubernetes/pkg/util/initsystem"
ipvsutil "k8s.io/kubernetes/pkg/util/ipvs"
@ -882,10 +883,10 @@ func RunInitNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigura
checks := []Checker{
NumCPUCheck{NumCPU: kubeadmconstants.ControlPlaneNumCPU},
KubernetesVersionCheck{KubernetesVersion: cfg.KubernetesVersion, KubeadmVersion: kubeadmversion.Get().GitVersion},
FirewalldCheck{ports: []int{int(cfg.LocalAPIEndpoint.BindPort), 10250}},
FirewalldCheck{ports: []int{int(cfg.LocalAPIEndpoint.BindPort), ports.KubeletPort}},
PortOpenCheck{port: int(cfg.LocalAPIEndpoint.BindPort)},
PortOpenCheck{port: 10251},
PortOpenCheck{port: 10252},
PortOpenCheck{port: ports.InsecureSchedulerPort},
PortOpenCheck{port: ports.InsecureKubeControllerManagerPort},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeAPIServer, manifestsDir)},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeControllerManager, manifestsDir)},
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.KubeScheduler, manifestsDir)},
@ -1037,7 +1038,7 @@ func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfigurat
HostnameCheck{nodeName: cfg.GetNodeName()},
KubeletVersionCheck{KubernetesVersion: cfg.GetKubernetesVersion(), exec: execer},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
PortOpenCheck{port: 10250})
PortOpenCheck{port: ports.KubeletPort})
return checks
}

View File

@ -62,7 +62,7 @@ func TestPatchNodeNonErrorCases(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
client := fake.NewSimpleClientset()
_, err := client.Core().Nodes().Create(&tc.node)
_, err := client.CoreV1().Nodes().Create(&tc.node)
if err != nil {
t.Fatalf("failed to create node to fake client: %v", err)
}

View File

@ -34,16 +34,16 @@ import (
)
const (
masterV1alpha3YAML = "testdata/conversion/master/v1alpha3.yaml"
masterV1alpha3YAMLNonLinux = "testdata/conversion/master/v1alpha3_non_linux.yaml"
masterV1beta1YAML = "testdata/conversion/master/v1beta1.yaml"
masterV1beta1YAMLNonLinux = "testdata/conversion/master/v1beta1_non_linux.yaml"
masterInternalYAML = "testdata/conversion/master/internal.yaml"
masterInternalYAMLNonLinux = "testdata/conversion/master/internal_non_linux.yaml"
masterIncompleteYAML = "testdata/defaulting/master/incomplete.yaml"
masterDefaultedYAML = "testdata/defaulting/master/defaulted.yaml"
masterDefaultedYAMLNonLinux = "testdata/defaulting/master/defaulted_non_linux.yaml"
masterInvalidYAML = "testdata/validation/invalid_mastercfg.yaml"
controlPlaneV1alpha3YAML = "testdata/conversion/controlplane/v1alpha3.yaml"
controlPlaneV1alpha3YAMLNonLinux = "testdata/conversion/controlplane/v1alpha3_non_linux.yaml"
controlPlaneV1beta1YAML = "testdata/conversion/controlplane/v1beta1.yaml"
controlPlaneV1beta1YAMLNonLinux = "testdata/conversion/controlplane/v1beta1_non_linux.yaml"
controlPlaneInternalYAML = "testdata/conversion/controlplane/internal.yaml"
controlPlaneInternalYAMLNonLinux = "testdata/conversion/controlplane/internal_non_linux.yaml"
controlPlaneIncompleteYAML = "testdata/defaulting/controlplane/incomplete.yaml"
controlPlaneDefaultedYAML = "testdata/defaulting/controlplane/defaulted.yaml"
controlPlaneDefaultedYAMLNonLinux = "testdata/defaulting/controlplane/defaulted_non_linux.yaml"
controlPlaneInvalidYAML = "testdata/validation/invalid_controlplanecfg.yaml"
)
func diff(expected, actual []byte) string {
@ -141,15 +141,15 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
}
func TestInitConfigurationMarshallingFromFile(t *testing.T) {
masterV1alpha3YAMLAbstracted := masterV1alpha3YAML
masterV1beta1YAMLAbstracted := masterV1beta1YAML
masterInternalYAMLAbstracted := masterInternalYAML
masterDefaultedYAMLAbstracted := masterDefaultedYAML
controlPlaneV1alpha3YAMLAbstracted := controlPlaneV1alpha3YAML
controlPlaneV1beta1YAMLAbstracted := controlPlaneV1beta1YAML
controlPlaneInternalYAMLAbstracted := controlPlaneInternalYAML
controlPlaneDefaultedYAMLAbstracted := controlPlaneDefaultedYAML
if runtime.GOOS != "linux" {
masterV1alpha3YAMLAbstracted = masterV1alpha3YAMLNonLinux
masterV1beta1YAMLAbstracted = masterV1beta1YAMLNonLinux
masterInternalYAMLAbstracted = masterInternalYAMLNonLinux
masterDefaultedYAMLAbstracted = masterDefaultedYAMLNonLinux
controlPlaneV1alpha3YAMLAbstracted = controlPlaneV1alpha3YAMLNonLinux
controlPlaneV1beta1YAMLAbstracted = controlPlaneV1beta1YAMLNonLinux
controlPlaneInternalYAMLAbstracted = controlPlaneInternalYAMLNonLinux
controlPlaneDefaultedYAMLAbstracted = controlPlaneDefaultedYAMLNonLinux
}
var tests = []struct {
@ -161,32 +161,32 @@ func TestInitConfigurationMarshallingFromFile(t *testing.T) {
// and then marshals the internal object to the expected groupVersion
{ // v1alpha3 -> internal
name: "v1alpha3IsDeprecated",
in: masterV1alpha3YAMLAbstracted,
in: controlPlaneV1alpha3YAMLAbstracted,
expectedErr: true,
},
{ // v1beta1 -> internal
name: "v1beta1ToInternal",
in: masterV1beta1YAMLAbstracted,
out: masterInternalYAMLAbstracted,
in: controlPlaneV1beta1YAMLAbstracted,
out: controlPlaneInternalYAMLAbstracted,
groupVersion: kubeadm.SchemeGroupVersion,
},
{ // v1beta1 -> internal -> v1beta1
name: "v1beta1Tov1beta1",
in: masterV1beta1YAMLAbstracted,
out: masterV1beta1YAMLAbstracted,
in: controlPlaneV1beta1YAMLAbstracted,
out: controlPlaneV1beta1YAMLAbstracted,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
},
// These tests are reading one file that has only a subset of the fields populated, loading it using LoadInitConfigurationFromFile,
// and then marshals the internal object to the expected groupVersion
{ // v1beta1 -> default -> validate -> internal -> v1beta1
name: "incompleteYAMLToDefaultedv1beta1",
in: masterIncompleteYAML,
out: masterDefaultedYAMLAbstracted,
in: controlPlaneIncompleteYAML,
out: controlPlaneDefaultedYAMLAbstracted,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
},
{ // v1beta1 -> validation should fail
name: "invalidYAMLShouldFail",
in: masterInvalidYAML,
in: controlPlaneInvalidYAML,
expectedErr: true,
},
}

View File

@ -198,7 +198,7 @@ Networking:
NodeRegistration:
CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null
Name: master-1
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -197,7 +197,7 @@ Networking:
NodeRegistration:
CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null
Name: master-1
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ bootstrapTokens:
kind: InitConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ bootstrapTokens:
kind: InitConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ localAPIEndpoint:
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ localAPIEndpoint:
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -15,7 +15,7 @@ Discovery:
NodeRegistration:
CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null
Name: master-1
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ discoveryTokenUnsafeSkipCAVerification: true
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -14,7 +14,7 @@ discovery:
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ localAPIEndpoint:
bindPort: 6443
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -13,7 +13,7 @@ localAPIEndpoint:
bindPort: 6443
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: master-1
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -6,7 +6,7 @@ localAPIEndpoint:
advertiseAddress: 192.168.2.2
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: master-1
name: control-plane-1
---
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /var/lib/kubernetes/pki

View File

@ -225,14 +225,11 @@ pkg/quota/v1/evaluator/core
pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage
pkg/registry/admissionregistration/rest
pkg/registry/admissionregistration/validatingwebhookconfiguration/storage
pkg/registry/apps/daemonset
pkg/registry/apps/daemonset/storage
pkg/registry/apps/deployment
pkg/registry/apps/deployment/storage
pkg/registry/apps/replicaset
pkg/registry/apps/replicaset/storage
pkg/registry/apps/rest
pkg/registry/apps/statefulset
pkg/registry/apps/statefulset/storage
pkg/registry/auditregistration/rest
pkg/registry/authentication/rest
@ -241,10 +238,8 @@ pkg/registry/authorization/localsubjectaccessreview
pkg/registry/authorization/rest
pkg/registry/authorization/selfsubjectaccessreview
pkg/registry/authorization/subjectaccessreview
pkg/registry/autoscaling/horizontalpodautoscaler
pkg/registry/autoscaling/horizontalpodautoscaler/storage
pkg/registry/autoscaling/rest
pkg/registry/batch/cronjob
pkg/registry/batch/cronjob/storage
pkg/registry/batch/job
pkg/registry/batch/job/storage
@ -271,7 +266,6 @@ pkg/registry/core/pod/rest
pkg/registry/core/podtemplate/storage
pkg/registry/core/replicationcontroller
pkg/registry/core/replicationcontroller/storage
pkg/registry/core/resourcequota
pkg/registry/core/resourcequota/storage
pkg/registry/core/rest
pkg/registry/core/secret
@ -289,7 +283,6 @@ pkg/registry/extensions/controller/storage
pkg/registry/extensions/rest
pkg/registry/networking/networkpolicy/storage
pkg/registry/networking/rest
pkg/registry/policy/poddisruptionbudget
pkg/registry/policy/poddisruptionbudget/storage
pkg/registry/policy/rest
pkg/registry/rbac/clusterrole
@ -650,29 +643,24 @@ test/e2e/apps
test/e2e/auth
test/e2e/autoscaling
test/e2e/chaosmonkey
test/e2e/cloud
test/e2e/common
test/e2e/framework
test/e2e/framework/ingress
test/e2e/framework/providers/gce
test/e2e/framework/providers/kubemark
test/e2e/instrumentation
test/e2e/instrumentation/logging
test/e2e/instrumentation/monitoring
test/e2e/kubectl
test/e2e/lifecycle
test/e2e/lifecycle/bootstrap
test/e2e/network
test/e2e/node
test/e2e/scalability
test/e2e/scheduling
test/e2e/servicecatalog
test/e2e/storage
test/e2e/storage/drivers
test/e2e/storage/testsuites
test/e2e/storage/utils
test/e2e/storage/vsphere
test/e2e/ui
test/e2e/windows
test/e2e_kubeadm
test/e2e_node

View File

@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Good Morning!"
enableRisky: "false"

35
hack/testdata/kustomize/deployment.yaml vendored Normal file
View File

@ -0,0 +1,35 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
labels:
deployment: hello
spec:
replicas: 3
selector:
matchLabels:
deployment: hello
template:
metadata:
labels:
deployment: hello
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello",
"--port=8080",
"--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky

View File

@ -0,0 +1,5 @@
nameprefix: test-
resources:
- deployment.yaml
- service.yaml
- configmap.yaml

12
hack/testdata/kustomize/service.yaml vendored Normal file
View File

@ -0,0 +1,12 @@
kind: Service
apiVersion: v1
metadata:
name: the-service
spec:
selector:
deployment: hello
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080

View File

@ -6,7 +6,8 @@
""
],
"ForbiddenPrefixes": [
"k8s.io/kubernetes/cmd"
"k8s.io/kubernetes/cmd",
"github.com/ghodss/yaml"
]
}
]

View File

@ -24,7 +24,6 @@ filegroup(
"//pkg/api/testapi:all-srcs",
"//pkg/api/testing:all-srcs",
"//pkg/api/v1/endpoints:all-srcs",
"//pkg/api/v1/node:all-srcs",
"//pkg/api/v1/persistentvolume:all-srcs",
"//pkg/api/v1/pod:all-srcs",
"//pkg/api/v1/resource:all-srcs",

View File

@ -1,26 +0,0 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["util.go"],
importpath = "k8s.io/kubernetes/pkg/api/v1/node",
deps = ["//staging/src/k8s.io/api/core/v1: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

@ -32,6 +32,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
obj.FailurePolicy = &p
s := admissionregistration.SideEffectClassUnknown
obj.SideEffects = &s
if obj.TimeoutSeconds == nil {
i := int32(30)
obj.TimeoutSeconds = &i
}
},
}
}

View File

@ -208,6 +208,13 @@ type Webhook struct {
// sideEffects == Unknown or Some. Defaults to Unknown.
// +optional
SideEffects *SideEffectClass
// TimeoutSeconds specifies the timeout for this webhook. After the timeout passes,
// the webhook call will be ignored or the API call will fail based on the
// failure policy.
// The timeout value must be between 1 and 30 seconds.
// +optional
TimeoutSeconds *int32
}
// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make

View File

@ -40,4 +40,8 @@ func SetDefaults_Webhook(obj *admissionregistrationv1beta1.Webhook) {
unknown := admissionregistrationv1beta1.SideEffectClassUnknown
obj.SideEffects = &unknown
}
if obj.TimeoutSeconds == nil {
obj.TimeoutSeconds = new(int32)
*obj.TimeoutSeconds = 30
}
}

View File

@ -301,6 +301,7 @@ func autoConvert_v1beta1_Webhook_To_admissionregistration_Webhook(in *v1beta1.We
out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects))
out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds))
return nil
}
@ -318,6 +319,7 @@ func autoConvert_admissionregistration_Webhook_To_v1beta1_Webhook(in *admissionr
out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector))
out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects))
out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds))
return nil
}

View File

@ -171,6 +171,9 @@ func validateWebhook(hook *admissionregistration.Webhook, fldPath *field.Path) f
if hook.SideEffects != nil && !supportedSideEffectClasses.Has(string(*hook.SideEffects)) {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, supportedSideEffectClasses.List()))
}
if hook.TimeoutSeconds != nil && (*hook.TimeoutSeconds > 30 || *hook.TimeoutSeconds < 1) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("timeoutSeconds"), *hook.TimeoutSeconds, "the timeout value must be between 1 and 30 seconds"))
}
if hook.NamespaceSelector != nil {
allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...)

View File

@ -26,6 +26,8 @@ import (
func strPtr(s string) *string { return &s }
func int32Ptr(i int32) *int32 { return &i }
func newValidatingWebhookConfiguration(hooks []admissionregistration.Webhook) *admissionregistration.ValidatingWebhookConfiguration {
return &admissionregistration.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
@ -544,6 +546,63 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
}),
expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`,
},
{
name: "timeout seconds cannot be greater than 30",
config: newValidatingWebhookConfiguration(
[]admissionregistration.Webhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(31),
},
}),
expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
},
{
name: "timeout seconds cannot be smaller than 1",
config: newValidatingWebhookConfiguration(
[]admissionregistration.Webhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(0),
},
}),
expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
},
{
name: "timeout seconds must be positive",
config: newValidatingWebhookConfiguration(
[]admissionregistration.Webhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(-1),
},
}),
expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
},
{
name: "valid timeout seconds",
config: newValidatingWebhookConfiguration(
[]admissionregistration.Webhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(1),
},
{
Name: "webhook2.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(15),
},
{
Name: "webhook3.k8s.io",
ClientConfig: validClientConfig,
TimeoutSeconds: int32Ptr(30),
},
}),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {

View File

@ -257,6 +257,11 @@ func (in *Webhook) DeepCopyInto(out *Webhook) {
*out = new(SideEffectClass)
**out = **in
}
if in.TimeoutSeconds != nil {
in, out := &in.TimeoutSeconds, &out.TimeoutSeconds
*out = new(int32)
**out = **in
}
return
}

View File

@ -906,3 +906,34 @@ func TestValidatePSPRunAsGroup(t *testing.T) {
})
}
}
func TestValidatePSPSELinux(t *testing.T) {
var testCases = []struct {
name string
selinux policy.SELinuxStrategyOptions
fail bool
}{
{"SELinuxStrategyMustRunAs",
policy.SELinuxStrategyOptions{
Rule: policy.SELinuxStrategyMustRunAs,
SELinuxOptions: &api.SELinuxOptions{Level: "s9:z0,z1"}}, false},
{"SELinuxStrategyMustRunAs",
policy.SELinuxStrategyOptions{
Rule: policy.SELinuxStrategyMustRunAs,
SELinuxOptions: &api.SELinuxOptions{Level: "s0"}}, false},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
errList := validatePSPSELinux(field.NewPath("Status"), &testCase.selinux)
actualErrors := len(errList)
expectedErrors := 1
if !testCase.fail {
expectedErrors = 0
}
if actualErrors != expectedErrors {
t.Errorf("In testCase %v, expected %v errors, got %v errors", testCase.name, expectedErrors, actualErrors)
}
})
}
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package tests
import (
"context"
"net/http/httptest"
"net/url"
"testing"
@ -194,11 +195,20 @@ func (w lw) Watch(options metav1.ListOptions) (watch.Interface, error) {
func TestListWatchUntil(t *testing.T) {
fw := watch.NewFake()
go func() {
var obj *v1.Pod
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: "2",
},
}
fw.Modify(obj)
}()
listwatch := lw{
list: &v1.PodList{Items: []v1.Pod{{}}},
list: &v1.PodList{
ListMeta: metav1.ListMeta{
ResourceVersion: "1",
},
Items: []v1.Pod{{}},
},
watch: fw,
}
@ -213,8 +223,9 @@ func TestListWatchUntil(t *testing.T) {
},
}
timeout := 10 * time.Second
lastEvent, err := watchtools.ListWatchUntil(timeout, listwatch, conditions...)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
lastEvent, err := watchtools.ListWatchUntil(ctx, listwatch, conditions...)
if err != nil {
t.Fatalf("expected nil error, got %#v", err)
}

View File

@ -167,7 +167,6 @@
"AllowedPrefixes": [
"k8s.io/kubernetes/pkg/api/legacyscheme",
"k8s.io/kubernetes/pkg/api/v1/endpoints",
"k8s.io/kubernetes/pkg/api/v1/node",
"k8s.io/kubernetes/pkg/api/v1/pod",
"k8s.io/kubernetes/pkg/apis/apps/v1",
"k8s.io/kubernetes/pkg/apis/autoscaling",

View File

@ -47,7 +47,7 @@ func TestCertificateController(t *testing.T) {
Reason: "test reason",
Message: "test message",
})
_, err := client.Certificates().CertificateSigningRequests().UpdateApproval(csr)
_, err := client.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(csr)
if err != nil {
return err
}

View File

@ -15,9 +15,9 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/controller/cloud",
deps = [
"//pkg/api/v1/node:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/util/node:go_default_library",
"//pkg/kubelet/apis:go_default_library",
"//pkg/scheduler/api:go_default_library",
"//pkg/util/node:go_default_library",

View File

@ -35,8 +35,8 @@ import (
"k8s.io/client-go/tools/record"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/klog"
nodeutilv1 "k8s.io/kubernetes/pkg/api/v1/node"
"k8s.io/kubernetes/pkg/controller"
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
)
@ -133,7 +133,7 @@ func (c *CloudNodeLifecycleController) MonitorNodes() {
for _, node := range nodes {
// Default NodeReady status to v1.ConditionUnknown
status := v1.ConditionUnknown
if _, c := nodeutilv1.GetNodeCondition(&node.Status, v1.NodeReady); c != nil {
if _, c := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady); c != nil {
status = c.Status
}

View File

@ -66,7 +66,7 @@ func TestFinalizeNamespaceFunc(t *testing.T) {
},
}
d := namespacedResourcesDeleter{
nsClient: mockClient.Core().Namespaces(),
nsClient: mockClient.CoreV1().Namespaces(),
finalizerToken: v1.FinalizerKubernetes,
}
d.finalizeNamespace(testNamespace)
@ -180,7 +180,7 @@ func testSyncNamespaceThatIsTerminating(t *testing.T, versions *metav1.APIVersio
fn := func() ([]*metav1.APIResourceList, error) {
return resources, nil
}
d := NewNamespacedResourcesDeleter(mockClient.Core().Namespaces(), dynamicClient, mockClient.Core(), fn, v1.FinalizerKubernetes, true)
d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), dynamicClient, mockClient.CoreV1(), fn, v1.FinalizerKubernetes, true)
if err := d.Delete(testInput.testNamespace.Name); err != nil {
t.Errorf("scenario %s - Unexpected error when synching namespace %v", scenario, err)
}
@ -219,7 +219,7 @@ func TestRetryOnConflictError(t *testing.T) {
}
namespace := &v1.Namespace{}
d := namespacedResourcesDeleter{
nsClient: mockClient.Core().Namespaces(),
nsClient: mockClient.CoreV1().Namespaces(),
}
_, err := d.retryOnConflictError(namespace, retryOnce)
if err != nil {
@ -255,7 +255,7 @@ func TestSyncNamespaceThatIsActive(t *testing.T) {
fn := func() ([]*metav1.APIResourceList, error) {
return testResources(), nil
}
d := NewNamespacedResourcesDeleter(mockClient.Core().Namespaces(), nil, mockClient.Core(),
d := NewNamespacedResourcesDeleter(mockClient.CoreV1().Namespaces(), nil, mockClient.CoreV1(),
fn, v1.FinalizerKubernetes, true)
err := d.Delete(testNamespace.Name)
if err != nil {

View File

@ -42,7 +42,6 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/controller/nodeipam/ipam",
deps = [
"//pkg/api/v1/node:go_default_library",
"//pkg/cloudprovider/providers/gce:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/nodeipam/ipam/cidrset:go_default_library",

View File

@ -39,7 +39,6 @@ import (
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
cloudprovider "k8s.io/cloud-provider"
v1node "k8s.io/kubernetes/pkg/api/v1/node"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
"k8s.io/kubernetes/pkg/controller"
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
@ -118,7 +117,7 @@ func NewCloudCIDRAllocator(client clientset.Interface, cloud cloudprovider.Inter
// Even if PodCIDR is assigned, but NetworkUnavailable condition is
// set to true, we need to process the node to set the condition.
networkUnavailableTaint := &v1.Taint{Key: schedulerapi.TaintNodeNetworkUnavailable, Effect: v1.TaintEffectNoSchedule}
_, cond := v1node.GetNodeCondition(&newNode.Status, v1.NodeNetworkUnavailable)
_, cond := nodeutil.GetNodeCondition(&newNode.Status, v1.NodeNetworkUnavailable)
if cond == nil || cond.Status != v1.ConditionFalse || utiltaints.TaintExists(newNode.Spec.Taints, networkUnavailableTaint) {
return ca.AllocateOrOccupyCIDR(newNode)
}

View File

@ -9,7 +9,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/controller/nodelifecycle",
visibility = ["//visibility:public"],
deps = [
"//pkg/api/v1/node:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/nodelifecycle/scheduler:go_default_library",
"//pkg/controller/util/node:go_default_library",

View File

@ -52,7 +52,6 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/client-go/util/workqueue"
v1node "k8s.io/kubernetes/pkg/api/v1/node"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler"
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
@ -525,7 +524,7 @@ func (nc *Controller) doNoExecuteTaintingPass() {
// retry in 50 millisecond
return false, 50 * time.Millisecond
}
_, condition := v1node.GetNodeCondition(&node.Status, v1.NodeReady)
_, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady)
// Because we want to mimic NodeStatus.Condition["Ready"] we make "unreachable" and "not ready" taints mutually exclusive.
taintToAdd := v1.Taint{}
oppositeTaint := v1.Taint{}
@ -742,7 +741,7 @@ func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.Node
var err error
var gracePeriod time.Duration
var observedReadyCondition v1.NodeCondition
_, currentReadyCondition := v1node.GetNodeCondition(&node.Status, v1.NodeReady)
_, currentReadyCondition := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady)
if currentReadyCondition == nil {
// If ready condition is nil, then kubelet (or nodecontroller) never posted node status.
// A fake ready condition is created, where LastHeartbeatTime and LastTransitionTime is set
@ -787,10 +786,10 @@ func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.Node
var savedCondition *v1.NodeCondition
var savedLease *coordv1beta1.Lease
if found {
_, savedCondition = v1node.GetNodeCondition(savedNodeHealth.status, v1.NodeReady)
_, savedCondition = nodeutil.GetNodeCondition(savedNodeHealth.status, v1.NodeReady)
savedLease = savedNodeHealth.lease
}
_, observedCondition := v1node.GetNodeCondition(&node.Status, v1.NodeReady)
_, observedCondition := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady)
if !found {
klog.Warningf("Missing timestamp for Node %s. Assuming now as a timestamp.", node.Name)
savedNodeHealth = &nodeHealthData{
@ -885,7 +884,7 @@ func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.Node
nowTimestamp := nc.now()
for _, nodeConditionType := range remainingNodeConditionTypes {
_, currentCondition := v1node.GetNodeCondition(&node.Status, nodeConditionType)
_, currentCondition := nodeutil.GetNodeCondition(&node.Status, nodeConditionType)
if currentCondition == nil {
klog.V(2).Infof("Condition %v of node %v was never updated by kubelet", nodeConditionType, node.Name)
node.Status.Conditions = append(node.Status.Conditions, v1.NodeCondition{
@ -908,7 +907,7 @@ func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.Node
}
}
_, currentCondition := v1node.GetNodeCondition(&node.Status, v1.NodeReady)
_, currentCondition := nodeutil.GetNodeCondition(&node.Status, v1.NodeReady)
if !apiequality.Semantic.DeepEqual(currentCondition, &observedReadyCondition) {
if _, err = nc.kubeClient.CoreV1().Nodes().UpdateStatus(node); err != nil {
klog.Errorf("Error updating node %s: %v", node.Name, err)

View File

@ -665,9 +665,9 @@ func (tc *testCase) setupController(t *testing.T) (*HorizontalController, inform
defaultDownscalestabilizationWindow := 5 * time.Minute
hpaController := NewHorizontalController(
eventClient.Core(),
eventClient.CoreV1(),
testScaleClient,
testClient.Autoscaling(),
testClient.AutoscalingV1(),
testrestmapper.TestOnlyStaticRESTMapper(legacyscheme.Scheme),
metricsClient,
informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(),

View File

@ -494,9 +494,9 @@ func (tc *legacyTestCase) runTest(t *testing.T) {
defaultDownscaleStabilisationWindow := 5 * time.Minute
hpaController := NewHorizontalController(
eventClient.Core(),
eventClient.CoreV1(),
testScaleClient,
testClient.Autoscaling(),
testClient.AutoscalingV1(),
testrestmapper.TestOnlyStaticRESTMapper(legacyscheme.Scheme),
metricsClient,
informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(),

View File

@ -672,7 +672,7 @@ func TestControllerUpdateStatusWithFailure(t *testing.T) {
fakeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
return true, &apps.ReplicaSet{}, fmt.Errorf("Fake error")
})
fakeRSClient := fakeClient.Apps().ReplicaSets("default")
fakeRSClient := fakeClient.AppsV1().ReplicaSets("default")
numReplicas := int32(10)
newStatus := apps.ReplicaSetStatus{Replicas: numReplicas}
updateReplicaSetStatus(fakeRSClient, rs, newStatus)

View File

@ -14,8 +14,8 @@ go_library(
],
importpath = "k8s.io/kubernetes/pkg/controller/route",
deps = [
"//pkg/api/v1/node:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/util/node:go_default_library",
"//pkg/util/metrics:go_default_library",
"//pkg/util/node:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
@ -42,9 +42,9 @@ go_test(
srcs = ["route_controller_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/api/v1/node:go_default_library",
"//pkg/cloudprovider/providers/fake:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/controller/util/node:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",

View File

@ -40,10 +40,10 @@ import (
"k8s.io/client-go/tools/record"
clientretry "k8s.io/client-go/util/retry"
cloudprovider "k8s.io/cloud-provider"
v1node "k8s.io/kubernetes/pkg/api/v1/node"
"k8s.io/kubernetes/pkg/controller"
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
"k8s.io/kubernetes/pkg/util/metrics"
nodeutil "k8s.io/kubernetes/pkg/util/node"
utilnode "k8s.io/kubernetes/pkg/util/node"
)
const (
@ -201,7 +201,7 @@ func (rc *RouteController) reconcile(nodes []*v1.Node, routes []*cloudprovider.R
}(nodeName, nameHint, route)
} else {
// Update condition only if it doesn't reflect the current state.
_, condition := v1node.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
_, condition := nodeutil.GetNodeCondition(&node.Status, v1.NodeNetworkUnavailable)
if condition == nil || condition.Status != v1.ConditionFalse {
rc.updateNetworkingCondition(types.NodeName(node.Name), true)
}
@ -237,7 +237,7 @@ func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, ro
// patch in the retry loop.
currentTime := metav1.Now()
if routeCreated {
err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
Type: v1.NodeNetworkUnavailable,
Status: v1.ConditionFalse,
Reason: "RouteCreated",
@ -245,7 +245,7 @@ func (rc *RouteController) updateNetworkingCondition(nodeName types.NodeName, ro
LastTransitionTime: currentTime,
})
} else {
err = nodeutil.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
err = utilnode.SetNodeCondition(rc.kubeClient, nodeName, v1.NodeCondition{
Type: v1.NodeNetworkUnavailable,
Status: v1.ConditionTrue,
Reason: "NoRouteCreated",

View File

@ -29,9 +29,9 @@ import (
"k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing"
cloudprovider "k8s.io/cloud-provider"
nodeutil "k8s.io/kubernetes/pkg/api/v1/node"
fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
"k8s.io/kubernetes/pkg/controller"
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
)
func alwaysReady() bool { return true }

View File

@ -96,7 +96,7 @@ func (m *FakeNodeHandler) GetUpdatedNodesCopy() []*v1.Node {
// Core returns fake CoreInterface.
func (m *FakeNodeHandler) Core() v1core.CoreV1Interface {
return &FakeLegacyHandler{m.Clientset.Core(), m}
return &FakeLegacyHandler{m.Clientset.CoreV1(), m}
}
// CoreV1 returns fake CoreV1Interface

View File

@ -260,3 +260,17 @@ func CreateDeleteNodeHandler(f func(node *v1.Node) error) func(obj interface{})
}
}
}
// GetNodeCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetNodeCondition(status *v1.NodeStatus, conditionType v1.NodeConditionType) (int, *v1.NodeCondition) {
if status == nil {
return -1, nil
}
for i := range status.Conditions {
if status.Conditions[i].Type == conditionType {
return i, &status.Conditions[i]
}
}
return -1, nil
}

View File

@ -156,7 +156,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2
stopCh := make(chan struct{})
pods, err := fakeKubeClient.Core().Pods(v1.NamespaceAll).List(metav1.ListOptions{})
pods, err := fakeKubeClient.CoreV1().Pods(v1.NamespaceAll).List(metav1.ListOptions{})
if err != nil {
t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err)
}
@ -166,7 +166,7 @@ func attachDetachRecoveryTestCase(t *testing.T, extraPods1 []*v1.Pod, extraPods2
podInformer.GetIndexer().Add(&podToAdd)
podsNum++
}
nodes, err := fakeKubeClient.Core().Nodes().List(metav1.ListOptions{})
nodes, err := fakeKubeClient.CoreV1().Nodes().List(metav1.ListOptions{})
if err != nil {
t.Fatalf("Run failed with error. Expected: <no error> Actual: %v", err)
}

View File

@ -208,7 +208,7 @@ func (o AnnotateOptions) Validate() error {
if o.all && len(o.fieldSelector) > 0 {
return fmt.Errorf("cannot set --all and --field-selector at the same time")
}
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
if len(o.newAnnotations) < 1 && len(o.removeAnnotations) < 1 {

View File

@ -115,6 +115,9 @@ var (
# Apply the configuration in pod.json to a pod.
kubectl apply -f ./pod.json
# Apply resources from a directory containing kustomization.yaml - e.g. dir/kustomization.yaml.
kubectl apply -k dir/
# Apply the JSON passed into stdin to a pod.
cat pod.json | kubectl apply -f -
@ -152,7 +155,7 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions
o.cmdBaseName = baseName
cmd := &cobra.Command{
Use: "apply -f FILENAME",
Use: "apply (-f FILENAME | -k DIRECTORY)",
DisableFlagsInUseLine: true,
Short: i18n.T("Apply a configuration to a resource by filename or stdin"),
Long: applyLong,
@ -170,7 +173,6 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmd.MarkFlagRequired("filename")
cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
cmd.Flags().BoolVar(&o.Prune, "prune", o.Prune, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")
cmdutil.AddValidateFlags(cmd)
@ -237,6 +239,10 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err
}
o.DeleteOptions = o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)
err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize()
if err != nil {
return err
}
o.OpenAPISchema, _ = f.OpenAPISchema()
o.Validator, err = f.Validator(cmdutil.GetFlagBool(cmd, "validate"))

View File

@ -107,13 +107,16 @@ func NewCmdReconcile(f cmdutil.Factory, streams genericclioptions.IOStreams) *co
cmd.Flags().BoolVar(&o.DryRun, "dry-run", o.DryRun, "If true, display results but do not submit changes")
cmd.Flags().BoolVar(&o.RemoveExtraPermissions, "remove-extra-permissions", o.RemoveExtraPermissions, "If true, removes extra permissions added to roles")
cmd.Flags().BoolVar(&o.RemoveExtraSubjects, "remove-extra-subjects", o.RemoveExtraSubjects, "If true, removes extra subjects added to rolebindings")
cmd.MarkFlagRequired("filename")
return cmd
}
// Complete completes all the required options
func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string) error {
if err := o.FilenameOptions.RequireFilenameOrKustomize(); err != nil {
return err
}
if len(args) > 0 {
return errors.New("no arguments are allowed")
}

View File

@ -10,7 +10,6 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/generate:go_default_library",
"//pkg/kubectl/generate/versioned:go_default_library",
"//pkg/kubectl/polymorphichelpers:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/kubectl/util/templates:go_default_library",
@ -20,6 +19,7 @@ go_library(
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v1:go_default_library",
"//staging/src/k8s.io/client-go/scale:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],

View File

@ -28,11 +28,11 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
"k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/generate"
generateversioned "k8s.io/kubernetes/pkg/kubectl/generate/versioned"
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/kubectl/util/templates"
@ -74,10 +74,10 @@ type AutoscaleOptions struct {
namespace string
dryRun bool
builder *resource.Builder
canBeAutoscaled polymorphichelpers.CanBeAutoscaledFunc
generatorFunc func(string, *meta.RESTMapping) (generate.StructuredGenerator, error)
HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
scaleKindResolver scale.ScaleKindResolver
genericclioptions.IOStreams
}
@ -133,7 +133,11 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.builder = f.NewBuilder()
o.canBeAutoscaled = polymorphichelpers.CanBeAutoscaledFn
discoveryClient, err := f.ToDiscoveryClient()
if err != nil {
return err
}
o.scaleKindResolver = scale.NewDiscoveryScaleKindResolver(discoveryClient)
o.args = args
o.RecordFlags.Complete(cmd)
@ -196,7 +200,7 @@ func (o *AutoscaleOptions) Validate() error {
func (o *AutoscaleOptions) Run() error {
r := o.builder.
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
Unstructured().
ContinueOnError().
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, o.FilenameOptions).
@ -214,8 +218,9 @@ func (o *AutoscaleOptions) Run() error {
}
mapping := info.ResourceMapping()
if err := o.canBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
return err
gvr := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource.Resource)
if _, err := o.scaleKindResolver.ScaleForResource(gvr); err != nil {
return fmt.Errorf("cannot autoscale a %v: %v", mapping.GroupVersionKind.Kind, err)
}
generator, err := o.generatorFunc(info.Name, mapping)

View File

@ -104,7 +104,7 @@ func (o *CertificateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
}
func (o *CertificateOptions) Validate() error {
if len(o.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
if len(o.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
return fmt.Errorf("one or more CSRs must be specified as <name> or -f <filename>")
}
return nil

View File

@ -107,12 +107,15 @@ func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *co
cmdutil.AddValidateFlags(cmd)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "to need to get converted.")
cmd.MarkFlagRequired("filename")
return cmd
}
// Complete collects information required to run Convert command from command line.
func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) {
err = o.FilenameOptions.RequireFilenameOrKustomize()
if err != nil {
return err
}
o.builder = f.NewBuilder
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()

View File

@ -499,7 +499,7 @@ func (o *CopyOptions) execute(options *exec.ExecOptions) error {
}
options.Config = o.ClientConfig
options.PodClient = o.Clientset.Core()
options.PodClient = o.Clientset.CoreV1()
if err := options.Validate(); err != nil {
return err

View File

@ -103,7 +103,8 @@ func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cob
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) {
if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -k\n\n"))
defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
defaultRunFunc(cmd, args)
return
@ -119,7 +120,6 @@ func NewCmdCreate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cob
usage := "to use to create the resource"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.MarkFlagRequired("filename")
cmdutil.AddValidateFlags(cmd)
cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
@ -184,7 +184,6 @@ func (o *CreateOptions) ValidateArgs(cmd *cobra.Command, args []string) error {
// Complete completes all the required options
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.RecordFlags.Complete(cmd)
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {

View File

@ -71,6 +71,9 @@ var (
# Delete a pod using the type and name specified in pod.json.
kubectl delete -f ./pod.json
# Delete resources from a directory containing kustomization.yaml - e.g. dir/kustomization.yaml.
kubectl delete -k dir
# Delete a pod based on the type and name in the JSON passed into stdin.
cat pod.json | kubectl delete -f -
@ -102,6 +105,7 @@ type DeleteOptions struct {
DeleteNow bool
ForceDeletion bool
WaitForDeletion bool
Quiet bool
GracePeriod int
Timeout time.Duration
@ -119,7 +123,7 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra
deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")
cmd := &cobra.Command{
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
Use: "delete ([-f FILENAME] | [-k DIRECTORY] | TYPE [(NAME | -l label | --all)])",
DisableFlagsInUseLine: true,
Short: i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
Long: deleteLong,
@ -313,7 +317,9 @@ func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav
return nil, cmdutil.AddSourceToErr("deleting", info.Source, err)
}
o.PrintObj(info)
if !o.Quiet {
o.PrintObj(info)
}
return deleteResponse, nil
}

View File

@ -156,9 +156,11 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags {
filenames := []string{}
recursive := false
kustomize := ""
return &DeleteFlags{
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
// Not using helpers.go since it provides function to add '-k' for FileNameOptions, but not FileNameFlags
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},
LabelSelector: &labelSelector,
FieldSelector: &fieldSelector,
@ -186,10 +188,11 @@ func NewDeleteFlags(usage string) *DeleteFlags {
wait := false
filenames := []string{}
kustomize := ""
recursive := false
return &DeleteFlags{
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},
Cascade: &cascade,
GracePeriod: &gracePeriod,

View File

@ -131,7 +131,7 @@ func (o *DescribeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
o.EnforceNamespace = false
}
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) {
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
return fmt.Errorf("You must specify the type of resource to describe. %s\n", cmdutil.SuggestAPIResources(o.CmdParent))
}

View File

@ -118,7 +118,6 @@ func NewCmdDiff(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C
usage := "contains the configuration to diff"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddServerSideApplyFlags(cmd)
cmd.MarkFlagRequired("filename")
return cmd
}
@ -395,6 +394,11 @@ func isConflict(err error) bool {
func (o *DiffOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
err = o.FilenameOptions.RequireFilenameOrKustomize()
if err != nil {
return err
}
o.ServerSideApply = cmdutil.GetServerSideApplyFlag(cmd)
o.ForceConflicts = cmdutil.GetForceConflictsFlag(cmd)
if o.ForceConflicts && !o.ServerSideApply {

View File

@ -115,6 +115,9 @@ var (
# List a pod identified by type and name specified in "pod.yaml" in JSON output format.
kubectl get -f pod.yaml -o json
# List resources from a directory with kustomization.yaml - e.g. dir/kustomization.yaml.
kubectl get -k dir/
# Return only the phase value of the specified pod.
kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}
@ -257,7 +260,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
switch {
case o.Watch || o.WatchOnly:
default:
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
fmt.Fprintf(o.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.SuggestAPIResources(o.CmdParent))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."

View File

@ -205,7 +205,7 @@ func (o *LabelOptions) Validate() error {
if o.all && len(o.fieldSelector) > 0 {
return fmt.Errorf("cannot set --all and --field-selector at the same time")
}
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) {
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
if len(o.newLabels) < 1 && len(o.removeLabels) < 1 && !o.list {

Some files were not shown because too many files have changed in this diff Show More