Commit 06860bb0 authored by Hannes Hoerl's avatar Hannes Hoerl

Merge remote-tracking branch 'origin/master' into release-1.14

parents 846a82fe ebea0377
......@@ -978,8 +978,8 @@
},
{
"ImportPath": "github.com/container-storage-interface/spec/lib/go/csi",
"Comment": "v1.1.0-rc1",
"Rev": "c751be1edc7952e7aac35720d5b34b669e48f113"
"Comment": "v1.1.0",
"Rev": "f750e6765f5f6b4ac0e13e95214d58901290fb4b"
},
{
"ImportPath": "github.com/containerd/console",
......
......@@ -17,6 +17,50 @@
FROM golang:1.12.0
################################################################################
# this is from the upstream golang image source so we can get go1.2.1
# https://github.com/docker-library/golang/blob/fd272b2b72db82a0bd516ce3d09bba624651516c/1.12/stretch/Dockerfile#L12-L44
# TODO(bentheelder): remove this block
################################################################################
ENV GOLANG_VERSION 1.12.1
RUN set -eux; \
\
# this "case" statement is generated via "update.sh"
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64) goRelArch='linux-amd64'; goRelSha256='2a3fdabf665496a0db5f41ec6af7a9b15a49fbe71a85a50ca38b1f13a103aeec' ;; \
armhf) goRelArch='linux-armv6l'; goRelSha256='ceac33f07f8fdbccd6c6f7339db33479e1be8c206e67458ba259470fe796dbf2' ;; \
arm64) goRelArch='linux-arm64'; goRelSha256='10dba44cf95c7aa7abc3c72610c12ebcaf7cad6eed761d5ad92736ca3bc0d547' ;; \
i386) goRelArch='linux-386'; goRelSha256='af74b6572dd0c133e5de121928616eab60a6252c66f6d9b15007c82207416a2c' ;; \
ppc64el) goRelArch='linux-ppc64le'; goRelSha256='e1258c81f420c88339abf40888423904c0023497b4e9bbffac9ee484597a57d3' ;; \
s390x) goRelArch='linux-s390x'; goRelSha256='a9b8f49be6b2083e2586c2ce8a2a86d5dbf47cca64ac6195546a81c9927f9513' ;; \
*) goRelArch='src'; goRelSha256='0be127684df4b842a64e58093154f9d15422f1405f1fcff4b2c36ffc6a15818a'; \
echo >&2; echo >&2 "warning: current architecture ($dpkgArch) does not have a corresponding Go binary release; will be building from source"; echo >&2 ;; \
esac; \
\
url="https://golang.org/dl/go${GOLANG_VERSION}.${goRelArch}.tar.gz"; \
wget -O go.tgz "$url"; \
echo "${goRelSha256} *go.tgz" | sha256sum -c -; \
tar -C /usr/local -xzf go.tgz; \
rm go.tgz; \
\
if [ "$goRelArch" = 'src' ]; then \
echo >&2; \
echo >&2 'error: UNIMPLEMENTED'; \
echo >&2 'TODO install golang-any from jessie-backports for GOROOT_BOOTSTRAP (and uninstall after build)'; \
echo >&2; \
exit 1; \
fi; \
\
export PATH="/usr/local/go/bin:$PATH"; \
go version
################################################################################
# below is our usual sources
################################################################################
ENV GOARM 7
ENV KUBE_DYNAMIC_CROSSPLATFORMS \
armhf \
......
......@@ -21,8 +21,8 @@ http_archive(
http_archive(
name = "io_bazel_rules_go",
sha256 = "6776d68ebb897625dead17ae510eac3d5f6342367327875210df44dbe2aeeb19",
urls = mirror("https://github.com/bazelbuild/rules_go/releases/download/0.17.1/rules_go-0.17.1.tar.gz"),
sha256 = "6433336b4c5feb54e2f45df4c1c84ea4385b2dc0b6f274ec2cd5d745045eae1f",
urls = mirror("https://github.com/bazelbuild/rules_go/releases/download/0.17.2/rules_go-0.17.2.tar.gz"),
)
load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
......@@ -30,7 +30,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
go_rules_dependencies()
go_register_toolchains(
go_version = "1.12",
go_version = "1.12.1",
)
http_archive(
......
......@@ -17,7 +17,7 @@
"containers": [
{
"name": "cluster-autoscaler",
"image": "k8s.gcr.io/cluster-autoscaler:v1.14.0-beta.1",
"image": "k8s.gcr.io/cluster-autoscaler:v1.14.0-beta.2",
"livenessProbe": {
"httpGet": {
"path": "/health-check",
......
......@@ -332,9 +332,17 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
}
}
// Checks if an external CA is provided by the user.
externalCA, _ := certsphase.UsingExternalCA(&cfg.ClusterConfiguration)
// Checks if an external CA is provided by the user (when the CA Cert is present but the CA Key is not)
externalCA, err := certsphase.UsingExternalCA(&cfg.ClusterConfiguration)
if externalCA {
// In case the certificates signed by CA (that should be provided by the user) are missing or invalid,
// returns, because kubeadm can't regenerate them without the CA Key
if err != nil {
return nil, errors.Wrapf(err, "invalid or incomplete external CA")
}
// Validate that also the required kubeconfig files exists and are invalid, because
// kubeadm can't regenerate them without the CA Key
kubeconfigDir := options.kubeconfigDir
if options.dryRun {
kubeconfigDir = dryRunDir
......@@ -342,11 +350,22 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
if err := kubeconfigphase.ValidateKubeconfigsForExternalCA(kubeconfigDir, cfg); err != nil {
return nil, err
}
if options.uploadCerts {
return nil, errors.New("can't use externalCA mode and upload-certs")
}
// Checks if an external Front-Proxy CA is provided by the user (when the Front-Proxy CA Cert is present but the Front-Proxy CA Key is not)
externalFrontProxyCA, err := certsphase.UsingExternalFrontProxyCA(&cfg.ClusterConfiguration)
if externalFrontProxyCA {
// In case the certificates signed by Front-Proxy CA (that should be provided by the user) are missing or invalid,
// returns, because kubeadm can't regenerate them without the Front-Proxy CA Key
if err != nil {
return nil, errors.Wrapf(err, "invalid or incomplete external front-proxy CA")
}
}
if options.uploadCerts && (externalCA || externalFrontProxyCA) {
return nil, errors.New("can't use upload-certs with an external CA or an external front-proxy CA")
}
return &initData{
cfg: cfg,
certificatesDir: cfg.CertificatesDir,
......
......@@ -190,7 +190,7 @@ func runCertsSa(c workflow.RunData) error {
// if external CA mode, skip service account key generation
if data.ExternalCA() {
fmt.Printf("[certs] External CA mode: Using existing sa keys\n")
fmt.Printf("[certs] Using existing sa keys\n")
return nil
}
......@@ -220,7 +220,7 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
fmt.Printf("[certs] Using existing %s certificate authority\n", ca.BaseName)
return nil
}
fmt.Printf("[certs] Using existing %s keyless certificate authority", ca.BaseName)
fmt.Printf("[certs] Using existing %s keyless certificate authority\n", ca.BaseName)
return nil
}
......
......@@ -354,8 +354,9 @@ func SharedCertificateExists(cfg *kubeadmapi.ClusterConfiguration) (bool, error)
}
// UsingExternalCA determines whether the user is relying on an external CA. We currently implicitly determine this is the case
// when both the CA Cert and the front proxy CA Cert are present but the CA Key and front proxy CA Key are not.
// when the CA Cert is present but the CA Key is not.
// This allows us to, e.g., skip generating certs or not start the csr signing controller.
// In case we are using an external front-proxy CA, the function validates the certificates signed by front-proxy CA that should be provided by the user.
func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil {
......@@ -364,20 +365,24 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
caKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)
if _, err := os.Stat(caKeyPath); !os.IsNotExist(err) {
return false, errors.Errorf("%s exists", kubeadmconstants.CAKeyName)
return false, nil
}
if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerCertAndKeyBaseName, "API server"}); err != nil {
return false, err
return true, err
}
if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName, "API server kubelet client"}); err != nil {
return false, err
return true, err
}
if err := validatePrivatePublicKey(certKeyLocation{cfg.CertificatesDir, "", kubeadmconstants.ServiceAccountKeyBaseName, "service account"}); err != nil {
return false, err
}
return true, nil
}
// UsingExternalFrontProxyCA determines whether the user is relying on an external front-proxy CA. We currently implicitly determine this is the case
// when the front proxy CA Cert is present but the front proxy CA Key is not.
// In case we are using an external front-proxy CA, the function validates the certificates signed by front-proxy CA that should be provided by the user.
func UsingExternalFrontProxyCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, "", "front-proxy CA"}); err != nil {
return false, err
......@@ -385,11 +390,11 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
frontProxyCAKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName)
if _, err := os.Stat(frontProxyCAKeyPath); !os.IsNotExist(err) {
return false, errors.Errorf("%s exists", kubeadmconstants.FrontProxyCAKeyName)
return false, nil
}
if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, kubeadmconstants.FrontProxyClientCertAndKeyBaseName, "front-proxy client"}); err != nil {
return false, err
return true, err
}
return true, nil
......
......@@ -514,47 +514,108 @@ func TestCreatePKIAssetsWithSparseCerts(t *testing.T) {
func TestUsingExternalCA(t *testing.T) {
tests := []struct {
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error
expected bool
name string
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error
externalCAFunc func(*kubeadmapi.ClusterConfiguration) (bool, error)
expected bool
expectedErr bool
}{
{
name: "Test External CA, when complete PKI exists",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
},
expected: false,
externalCAFunc: UsingExternalCA,
expected: false,
},
{
name: "Test External CA, when ca.key missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
deleteCAKey,
deleteFrontProxyCAKey,
deleteCertOrKey(kubeadmconstants.CAKeyName),
},
expected: true,
externalCAFunc: UsingExternalCA,
expected: true,
},
{
name: "Test External CA, when ca.key missing and signed certs are missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
deleteCertOrKey(kubeadmconstants.CAKeyName),
deleteCertOrKey(kubeadmconstants.APIServerCertName),
},
externalCAFunc: UsingExternalCA,
expected: true,
expectedErr: true,
},
{
name: "Test External CA, when ca.key missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
deleteCertOrKey(kubeadmconstants.CAKeyName),
},
externalCAFunc: UsingExternalCA,
expected: true,
},
{
name: "Test External Front Proxy CA, when complete PKI exists",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
},
externalCAFunc: UsingExternalFrontProxyCA,
expected: false,
},
{
name: "Test External Front Proxy CA, when front-proxy-ca.key missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
deleteCertOrKey(kubeadmconstants.FrontProxyCAKeyName),
},
externalCAFunc: UsingExternalFrontProxyCA,
expected: true,
},
{
name: "Test External Front Proxy CA, when front-proxy-.key missing and signed certs are missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets,
deleteCertOrKey(kubeadmconstants.FrontProxyCAKeyName),
deleteCertOrKey(kubeadmconstants.FrontProxyClientCertName),
},
externalCAFunc: UsingExternalFrontProxyCA,
expected: true,
expectedErr: true,
},
}
for _, test := range tests {
dir := testutil.SetupTempDir(t)
defer os.RemoveAll(dir)
t.Run(test.name, func(t *testing.T) {
dir := testutil.SetupTempDir(t)
defer os.RemoveAll(dir)
cfg := &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
CertificatesDir: dir,
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"},
}
cfg := &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
CertificatesDir: dir,
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"},
}
for _, f := range test.setupFuncs {
if err := f(cfg); err != nil {
t.Errorf("error executing setup function: %v", err)
}
}
for _, f := range test.setupFuncs {
if err := f(cfg); err != nil {
t.Errorf("error executing setup function: %v", err)
val, err := test.externalCAFunc(&cfg.ClusterConfiguration)
if val != test.expected {
t.Errorf("UsingExternalCA did not match expected: %v", test.expected)
}
}
if val, _ := UsingExternalCA(&cfg.ClusterConfiguration); val != test.expected {
t.Errorf("UsingExternalCA did not match expected: %v", test.expected)
}
if (err != nil) != test.expectedErr {
t.Errorf("UsingExternalCA returned un expected err: %v", err)
}
})
}
}
......@@ -742,18 +803,13 @@ func TestCreateCertificateFilesMethods(t *testing.T) {
}
}
func deleteCAKey(cfg *kubeadmapi.InitConfiguration) error {
if err := os.Remove(filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)); err != nil {
return errors.Wrapf(err, "failed removing %s", kubeadmconstants.CAKeyName)
}
return nil
}
func deleteFrontProxyCAKey(cfg *kubeadmapi.InitConfiguration) error {
if err := os.Remove(filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName)); err != nil {
return errors.Wrapf(err, "failed removing %s", kubeadmconstants.FrontProxyCAKeyName)
func deleteCertOrKey(name string) func(*kubeadmapi.InitConfiguration) error {
return func(cfg *kubeadmapi.InitConfiguration) error {
if err := os.Remove(filepath.Join(cfg.CertificatesDir, name)); err != nil {
return errors.Wrapf(err, "failed removing %s", name)
}
return nil
}
return nil
}
func assertCertsExist(t *testing.T, dir string) {
......
......@@ -137,6 +137,7 @@ func CreateStaticPodFiles(manifestDir string, cfg *kubeadmapi.ClusterConfigurati
func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) []string {
defaultArguments := map[string]string{
"advertise-address": localAPIEndpoint.AdvertiseAddress,
"insecure-port": "0",
"enable-admission-plugins": "NodeRestriction",
"service-cluster-ip-range": cfg.Networking.ServiceSubnet,
"service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName),
......
......@@ -148,6 +148,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -184,6 +185,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -228,6 +230,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -269,6 +272,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -312,6 +316,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=baz",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -357,6 +362,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -385,14 +391,14 @@ func TestGetAPIServerCommand(t *testing.T) {
},
},
{
name: "secure-port extra-args",
name: "insecure-port extra-args",
cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
"secure-port": "123",
"insecure-port": "1234",
},
},
},
......@@ -400,6 +406,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{
"kube-apiserver",
"--insecure-port=1234",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......@@ -443,6 +450,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{
"kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub",
......
......@@ -22,6 +22,7 @@ go_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",
"//vendor/k8s.io/klog:go_default_library",
],
)
......
......@@ -35,6 +35,7 @@ import (
certutil "k8s.io/client-go/util/cert"
keyutil "k8s.io/client-go/util/keyutil"
bootstraputil "k8s.io/cluster-bootstrap/token/util"
"k8s.io/klog"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
......@@ -191,6 +192,7 @@ func certsToTransfer(cfg *kubeadmapi.InitConfiguration) map[string]string {
certs[externalEtcdCert] = cfg.Etcd.External.CertFile
certs[externalEtcdKey] = cfg.Etcd.External.KeyFile
}
return certs
}
......@@ -209,7 +211,7 @@ func getDataFromDisk(cfg *kubeadmapi.InitConfiguration, key []byte) (map[string]
// 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)
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 {
......@@ -231,6 +233,10 @@ func DownloadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration
if !found {
return errors.New("couldn't find required certificate or key in Secret")
}
if len(certOrKeyData) == 0 {
klog.V(1).Infof("[download-certs] Not saving %q to disk, since it is empty in the %q Secret\n", certOrKeyName, kubeadmconstants.KubeadmCertsSecret)
continue
}
if err := writeCertOrKey(certOrKeyPath, certOrKeyData); err != nil {
return err
}
......@@ -261,14 +267,20 @@ func getSecret(client clientset.Interface) (*v1.Secret, error) {
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
for secretName, encryptedSecret := range secret.Data {
// In some cases the secret might have empty data if the secrets were not present on disk
// when uploading. This can specially happen with external insecure etcd (no certs)
if len(encryptedSecret) > 0 {
cert, err := cryptoutil.DecryptBytes(encryptedSecret, 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[secretName] = cert
} else {
secretData[secretName] = []byte{}
}
secretData[certName] = cert
}
return secretData, nil
}
......
......@@ -362,24 +362,24 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
kubeadmconstants.SchedulerKubeConfigFileName,
}
specs, err := getKubeConfigSpecs(cfg)
// Creates a kubeconfig file with the target CA and server URL
// to be used as a input for validating user provided kubeconfig files
caCert, err := pkiutil.TryLoadCertFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
return err
return errors.Wrapf(err, "the CA file couldn't be loaded")
}
for _, kubeConfigFileName := range kubeConfigFileNames {
spec, exists := specs[kubeConfigFileName]
if !exists {
return errors.Errorf("couldn't retrive KubeConfigSpec for %s", kubeConfigFileName)
}
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
if err != nil {
return err
}
kubeconfig, err := buildKubeConfigFromSpec(spec, cfg.ClusterName)
if err != nil {
return err
}
validationConfig := kubeconfigutil.CreateBasic(controlPlaneEndpoint, "dummy", "dummy", pkiutil.EncodeCertPEM(caCert))
if err = validateKubeConfig(outDir, kubeConfigFileName, kubeconfig); err != nil {
return err
// validate user provided kubeconfig files
for _, kubeConfigFileName := range kubeConfigFileNames {
if err = validateKubeConfig(outDir, kubeConfigFileName, validationConfig); err != nil {
return errors.Wrapf(err, "the %s file does not exists or it is not valid", kubeConfigFileName)
}
}
return nil
......
......@@ -512,14 +512,23 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
},
}
// creates CA, write to pkiDir and remove ca.key to get into external CA condition
caCert, caKey := certstestutil.SetupCertificateAuthorithy(t)
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthorithy(t)
if err := pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil {
t.Fatalf("failure while saving CA certificate and key: %v", err)
}
if err := os.Remove(filepath.Join(pkiDir, kubeadmconstants.CAKeyName)); err != nil {
t.Fatalf("failure while deleting ca.key: %v", err)
}
// create a valid config
config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
// create a config with another CA
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthorithy(t)
configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
// create a config with another server URL
configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
tests := map[string]struct {
......@@ -539,11 +548,21 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
initConfig: initConfig,
expectedError: true,
},
"some files are invalid": {
"some files have invalid CA": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa,
kubeadmconstants.SchedulerKubeConfigFileName: config,
},
initConfig: initConfig,
expectedError: true,
},
"some files have invalid Server Url": {
filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: config,
kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL,
},
initConfig: initConfig,
......@@ -567,7 +586,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
for name, config := range test.filesToWrite {
if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil {
t.Errorf("createKubeConfigFileIfNotExists failed")
t.Errorf("createKubeConfigFileIfNotExists failed: %v", err)
}
}
......
......@@ -52,6 +52,7 @@ spec:
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --advertise-address=192.168.1.115
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --insecure-port=0
- --experimental-bootstrap-token-auth=true
- --requestheader-username-headers=X-Remote-User
- --requestheader-extra-headers-prefix=X-Remote-Extra-
......@@ -134,6 +135,7 @@ spec:
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --advertise-address=$(HOST_IP)
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --insecure-port=0
- --experimental-bootstrap-token-auth=true
- --requestheader-username-headers=X-Remote-User
- --requestheader-extra-headers-prefix=X-Remote-Extra-
......
......@@ -101,7 +101,7 @@ cgroupsPerQOS: true
clusterDNS:
- 10.192.0.10
clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Cache
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
......
......@@ -101,7 +101,7 @@ cgroupsPerQOS: true
clusterDNS:
- 10.192.0.10
clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Cache
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
......
......@@ -215,7 +215,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura
obj.ContainerLogMaxFiles = utilpointer.Int32Ptr(5)
}
if obj.ConfigMapAndSecretChangeDetectionStrategy == "" {
obj.ConfigMapAndSecretChangeDetectionStrategy = kubeletconfigv1beta1.TTLCacheChangeDetectionStrategy
obj.ConfigMapAndSecretChangeDetectionStrategy = kubeletconfigv1beta1.WatchChangeDetectionStrategy
}
if obj.EnforceNodeAllocatable == nil {
obj.EnforceNodeAllocatable = DefaultNodeAllocatableEnforcement
......
......@@ -127,6 +127,11 @@ func (ds *dockerService) RemoveImage(_ context.Context, r *runtimeapi.RemoveImag
return nil, err
}
if imageInspect == nil {
// image is nil, assuming it doesn't exist.
return &runtimeapi.RemoveImageResponse{}, nil
}
// An image can have different numbers of RepoTags and RepoDigests.
// Iterating over both of them plus the image ID ensures the image really got removed.
// It also prevents images from being deleted, which actually are deletable using this approach.
......
......@@ -82,7 +82,8 @@ const (
FreeDiskSpaceFailed = "FreeDiskSpaceFailed"
// Probe event reason list
ContainerUnhealthy = "Unhealthy"
ContainerUnhealthy = "Unhealthy"
ContainerProbeWarning = "ProbeWarning"
// Pod worker event reason list
FailedSync = "FailedSync"
......
......@@ -66,10 +66,11 @@ func newProber(
refManager *kubecontainer.RefManager,
recorder record.EventRecorder) *prober {
const followNonLocalRedirects = false
return &prober{
exec: execprobe.New(),
readinessHttp: httprobe.New(),
livenessHttp: httprobe.New(),
readinessHttp: httprobe.New(followNonLocalRedirects),
livenessHttp: httprobe.New(followNonLocalRedirects),
tcp: tcprobe.New(),
runner: runner,
refManager: refManager,
......@@ -96,7 +97,7 @@ func (pb *prober) probe(probeType probeType, pod *v1.Pod, status v1.PodStatus, c
}
result, output, err := pb.runProbeWithRetries(probeType, probeSpec, pod, status, container, containerID, maxProbeRetries)
if err != nil || result != probe.Success {
if err != nil || (result != probe.Success && result != probe.Warning) {
// Probe failed in one way or another.
ref, hasRef := pb.refManager.GetRef(containerID)
if !hasRef {
......@@ -115,7 +116,14 @@ func (pb *prober) probe(probeType probeType, pod *v1.Pod, status v1.PodStatus, c
}
return results.Failure, err
}
klog.V(3).Infof("%s probe for %q succeeded", probeType, ctrName)
if result == probe.Warning {
if ref, hasRef := pb.refManager.GetRef(containerID); hasRef {
pb.recorder.Eventf(ref, v1.EventTypeWarning, events.ContainerProbeWarning, "%s probe warning: %s", probeType, output)
}
klog.V(3).Infof("%s probe for %q succeeded with a warning: %s", probeType, ctrName, output)
} else {
klog.V(3).Infof("%s probe for %q succeeded", probeType, ctrName)
}
return results.Success, nil
}
......
......@@ -233,6 +233,11 @@ func TestProbe(t *testing.T) {
execResult: probe.Success,
expectedResult: results.Success,
},
{ // Probe result is warning
probe: execProbe,
execResult: probe.Warning,
expectedResult: results.Success,
},
{ // Probe result is unknown
probe: execProbe,
execResult: probe.Unknown,
......
......@@ -22,7 +22,12 @@ go_test(
name = "go_default_test",
srcs = ["http_test.go"],
embed = [":go_default_library"],
deps = ["//pkg/probe:go_default_library"],
deps = [
"//pkg/probe:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
],
)
filegroup(
......
......@@ -18,6 +18,7 @@ package http
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net/http"
......@@ -32,13 +33,17 @@ import (
)
// New creates Prober that will skip TLS verification while probing.
func New() Prober {
// followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
// If disabled, redirects to other hosts will trigger a warning result.
func New(followNonLocalRedirects bool) Prober {
tlsConfig := &tls.Config{InsecureSkipVerify: true}
return NewWithTLSConfig(tlsConfig)
return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
}
// NewWithTLSConfig takes tls config as parameter.
func NewWithTLSConfig(config *tls.Config) Prober {
// followNonLocalRedirects configures whether the prober should follow redirects to a different hostname.
// If disabled, redirects to other hosts will trigger a warning result.
func NewWithTLSConfig(config *tls.Config, followNonLocalRedirects bool) Prober {
// We do not want the probe use node's local proxy set.
transport := utilnet.SetTransportDefaults(
&http.Transport{
......@@ -46,7 +51,7 @@ func NewWithTLSConfig(config *tls.Config) Prober {
DisableKeepAlives: true,
Proxy: http.ProxyURL(nil),
})
return httpProber{transport}
return httpProber{transport, followNonLocalRedirects}
}
// Prober is an interface that defines the Probe function for doing HTTP readiness/liveness checks.
......@@ -55,12 +60,18 @@ type Prober interface {
}
type httpProber struct {
transport *http.Transport
transport *http.Transport
followNonLocalRedirects bool
}
// Probe returns a ProbeRunner capable of running an HTTP check.
func (pr httpProber) Probe(url *url.URL, headers http.Header, timeout time.Duration) (probe.Result, string, error) {
return DoHTTPProbe(url, headers, &http.Client{Timeout: timeout, Transport: pr.transport})
client := &http.Client{
Timeout: timeout,
Transport: pr.transport,
CheckRedirect: redirectChecker(pr.followNonLocalRedirects),
}
return DoHTTPProbe(url, headers, client)
}
// GetHTTPInterface is an interface for making HTTP requests, that returns a response and error.
......@@ -102,9 +113,30 @@ func DoHTTPProbe(url *url.URL, headers http.Header, client GetHTTPInterface) (pr
}
body := string(b)
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest {
if res.StatusCode >= http.StatusMultipleChoices { // Redirect
klog.V(4).Infof("Probe terminated redirects for %s, Response: %v", url.String(), *res)
return probe.Warning, body, nil
}
klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
return probe.Success, body, nil
}
klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body)
return probe.Failure, fmt.Sprintf("HTTP probe failed with statuscode: %d", res.StatusCode), nil
}
func redirectChecker(followNonLocalRedirects bool) func(*http.Request, []*http.Request) error {
if followNonLocalRedirects {
return nil // Use the default http client checker.
}
return func(req *http.Request, via []*http.Request) error {
if req.URL.Hostname() != via[0].URL.Hostname() {
return http.ErrUseLastResponse
}
// Default behavior: stop after 10 redirects.
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
}
......@@ -28,6 +28,9 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/probe"
)
......@@ -64,7 +67,7 @@ func TestHTTPProbeProxy(t *testing.T) {
defer unsetEnv("no_proxy")()
defer unsetEnv("NO_PROXY")()
prober := New()
prober := New(true)
go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
......@@ -119,7 +122,7 @@ func TestHTTPProbeChecker(t *testing.T) {
}
}
prober := New()
prober := New(true)
testCases := []struct {
handler func(w http.ResponseWriter, r *http.Request)
reqHeaders http.Header
......@@ -223,7 +226,7 @@ func TestHTTPProbeChecker(t *testing.T) {
},
}
for i, test := range testCases {
func() {
t.Run(fmt.Sprintf("case-%2d", i), func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
test.handler(w, r)
}))
......@@ -258,6 +261,56 @@ func TestHTTPProbeChecker(t *testing.T) {
t.Errorf("Expected response not to contain %v, got %v", test.notBody, output)
}
}
}()
})
}
}
func TestHTTPProbeChecker_NonLocalRedirects(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/redirect":
loc, _ := url.QueryUnescape(r.URL.Query().Get("loc"))
http.Redirect(w, r, loc, http.StatusFound)
case "/loop":
http.Redirect(w, r, "/loop", http.StatusFound)
case "/success":
w.WriteHeader(http.StatusOK)
default:
http.Error(w, "", http.StatusInternalServerError)
}
})
server := httptest.NewServer(handler)
defer server.Close()
newportServer := httptest.NewServer(handler)
defer newportServer.Close()
testCases := map[string]struct {
redirect string
expectLocalResult probe.Result
expectNonLocalResult probe.Result
}{
"local success": {"/success", probe.Success, probe.Success},
"local fail": {"/fail", probe.Failure, probe.Failure},
"newport success": {newportServer.URL + "/success", probe.Success, probe.Success},
"newport fail": {newportServer.URL + "/fail", probe.Failure, probe.Failure},
"bogus nonlocal": {"http://0.0.0.0/fail", probe.Warning, probe.Failure},
"redirect loop": {"/loop", probe.Failure, probe.Failure},
}
for desc, test := range testCases {
t.Run(desc+"-local", func(t *testing.T) {
prober := New(false)
target, err := url.Parse(server.URL + "/redirect?loc=" + url.QueryEscape(test.redirect))
require.NoError(t, err)
result, _, _ := prober.Probe(target, nil, wait.ForeverTestTimeout)
assert.Equal(t, test.expectLocalResult, result)
})
t.Run(desc+"-nonlocal", func(t *testing.T) {
prober := New(true)
target, err := url.Parse(server.URL + "/redirect?loc=" + url.QueryEscape(test.redirect))
require.NoError(t, err)
result, _, _ := prober.Probe(target, nil, wait.ForeverTestTimeout)
assert.Equal(t, test.expectNonLocalResult, result)
})
}
}
......@@ -22,6 +22,8 @@ type Result string
const (
// Success Result
Success Result = "success"
// Warning Result. Logically success, but with additional debugging information attached.
Warning Result = "warning"
// Failure Result
Failure Result = "failure"
// Unknown Result
......
......@@ -68,7 +68,8 @@ func (server *Server) DoServerCheck() (probe.Result, string, error) {
if server.Prober != nil {
return
}
server.Prober = httpprober.NewWithTLSConfig(server.TLSConfig)
const followNonLocalRedirects = true
server.Prober = httpprober.NewWithTLSConfig(server.TLSConfig, followNonLocalRedirects)
})
scheme := "http"
......
......@@ -48,6 +48,7 @@ go_library(
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/conditions:go_default_library",
"//pkg/kubelet:go_default_library",
"//pkg/kubelet/events:go_default_library",
"//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/sysctl:go_default_library",
"//pkg/security/apparmor:go_default_library",
......
......@@ -18,13 +18,16 @@ package common
import (
"fmt"
"net/url"
"time"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/uuid"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/test/e2e/framework"
testutils "k8s.io/kubernetes/test/utils"
......@@ -305,6 +308,82 @@ var _ = framework.KubeDescribe("Probing container", func() {
},
}, 1, defaultObservationTimeout)
})
/*
Release : v1.14
Testname: Pod http liveness probe, redirected to a local address
Description: A Pod is created with liveness probe on http endpoint /redirect?loc=healthz. The http handler on the /redirect will redirect to the /healthz endpoint, which will return a http error after 10 seconds since the Pod is started. This MUST result in liveness check failure. The Pod MUST now be killed and restarted incrementing restart count to 1.
*/
It("should be restarted with a local redirect http liveness probe", func() {
runLivenessTest(f, &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "liveness-http-redirect",
Labels: map[string]string{"test": "liveness"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "liveness",
Image: imageutils.GetE2EImage(imageutils.Liveness),
Command: []string{"/server"},
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/redirect?loc=" + url.QueryEscape("/healthz"),
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},
},
}, 1, defaultObservationTimeout)
})
/*
Release : v1.14
Testname: Pod http liveness probe, redirected to a non-local address
Description: A Pod is created with liveness probe on http endpoint /redirect with a redirect to http://0.0.0.0/. The http handler on the /redirect should not follow the redirect, but instead treat it as a success and generate an event.
*/
It("should *not* be restarted with a non-local redirect http liveness probe", func() {
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "liveness-http-redirect",
Labels: map[string]string{"test": "liveness"},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "liveness",
Image: imageutils.GetE2EImage(imageutils.Liveness),
Command: []string{"/server"},
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/redirect?loc=" + url.QueryEscape("http://0.0.0.0/"),
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},
},
}
runLivenessTest(f, pod, 0, defaultObservationTimeout)
// Expect an event of type "ProbeWarning".
expectedEvent := fields.Set{
"involvedObject.kind": "Pod",
"involvedObject.name": pod.Name,
"involvedObject.namespace": f.Namespace.Name,
"reason": events.ContainerProbeWarning,
}.AsSelector().String()
framework.ExpectNoError(framework.WaitTimeoutForPodEvent(
f.ClientSet, pod.Name, f.Namespace.Name, expectedEvent, "0.0.0.0", framework.PodEventTimeout))
})
})
func getContainerStartedTime(p *v1.Pod, containerName string) (time.Time, error) {
......
......@@ -24,7 +24,7 @@ import (
"path/filepath"
"regexp"
"strings"
"sync/atomic"
"sync"
"time"
"k8s.io/apimachinery/pkg/util/version"
......@@ -434,19 +434,11 @@ type chaosMonkeyAdapter struct {
func (cma *chaosMonkeyAdapter) Test(sem *chaosmonkey.Semaphore) {
start := time.Now()
// Using an atomic with a CAS is a potential workaround for #74890.
//
// This is a speculative workaround - we are really seeing if
// this is better; if not we should revert.
//
// If it is better we should file a bug against go 1.12, and
// then revert!
var onceWithoutMutex uint32
var once sync.Once
ready := func() {
if atomic.CompareAndSwapUint32(&onceWithoutMutex, 0, 1) {
once.Do(func() {
sem.Ready()
}
})
}
defer finalizeUpgradeTest(start, cma.testReport)
defer ready()
......
......@@ -427,7 +427,8 @@ var _ = SIGDescribe("Loadbalancing: L7", func() {
// TODO(nikhiljindal): Check the instance group annotation value and verify with a multizone cluster.
})
It("should be able to switch between HTTPS and HTTP2 modes", func() {
// TODO: remove [Unreleased] tag to once the new GCE API GO client gets revendored in ingress-gce repo
It("should be able to switch between HTTPS and HTTP2 modes [Unreleased]", func() {
httpsScheme := "request_scheme=https"
By("Create a basic HTTP2 ingress")
......
......@@ -53,8 +53,8 @@ var NodeImageWhiteList = sets.NewString(
imageutils.GetE2EImage(imageutils.Nonewprivs),
imageutils.GetPauseImageName(),
framework.GetGPUDevicePluginImage(),
"gcr.io/kubernetes-e2e-test-images/node-perf/npb-is-amd64:1.0",
"gcr.io/kubernetes-e2e-test-images/node-perf/npb-ep-amd64:1.0",
"gcr.io/kubernetes-e2e-test-images/node-perf/npb-is:1.0",
"gcr.io/kubernetes-e2e-test-images/node-perf/npb-ep:1.0",
"gcr.io/kubernetes-e2e-test-images/node-perf/tf-wide-deep-amd64:1.0",
)
......
......@@ -56,53 +56,74 @@ func setKubeletConfig(f *framework.Framework, cfg *kubeletconfig.KubeletConfigur
// Slow by design.
var _ = SIGDescribe("Node Performance Testing [Serial] [Slow]", func() {
f := framework.NewDefaultFramework("node-performance-testing")
var (
wl workloads.NodePerfWorkload
oldCfg *kubeletconfig.KubeletConfiguration
newCfg *kubeletconfig.KubeletConfiguration
pod *corev1.Pod
)
JustBeforeEach(func() {
err := wl.PreTestExec()
framework.ExpectNoError(err)
oldCfg, err = getCurrentKubeletConfig()
framework.ExpectNoError(err)
newCfg, err = wl.KubeletConfig(oldCfg)
framework.ExpectNoError(err)
setKubeletConfig(f, newCfg)
})
Context("Run node performance testing with pre-defined workloads", func() {
It("run each pre-defined workload", func() {
By("running the workloads")
for _, workload := range workloads.NodePerfWorkloads {
By("running the pre test exec from the workload")
err := workload.PreTestExec()
framework.ExpectNoError(err)
By("restarting kubelet with required configuration")
// Get the Kubelet config required for this workload.
oldCfg, err := getCurrentKubeletConfig()
framework.ExpectNoError(err)
newCfg, err := workload.KubeletConfig(oldCfg)
framework.ExpectNoError(err)
// Set the Kubelet config required for this workload.
setKubeletConfig(f, newCfg)
By("running the workload and waiting for success")
// Make the pod for the workload.
pod := makeNodePerfPod(workload)
// Create the pod.
pod = f.PodClient().CreateSync(pod)
// Wait for pod success.
f.PodClient().WaitForSuccess(pod.Name, workload.Timeout())
podLogs, err := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
framework.ExpectNoError(err)
perf, err := workload.ExtractPerformanceFromLogs(podLogs)
framework.ExpectNoError(err)
framework.Logf("Time to complete workload %s: %v", workload.Name(), perf)
// Delete the pod.
gp := int64(0)
delOpts := metav1.DeleteOptions{
GracePeriodSeconds: &gp,
}
f.PodClient().DeleteSync(pod.Name, &delOpts, framework.DefaultPodDeletionTimeout)
cleanup := func() {
gp := int64(0)
delOpts := metav1.DeleteOptions{
GracePeriodSeconds: &gp,
}
f.PodClient().DeleteSync(pod.Name, &delOpts, framework.DefaultPodDeletionTimeout)
By("running the post test exec from the workload")
err := wl.PostTestExec()
framework.ExpectNoError(err)
setKubeletConfig(f, oldCfg)
}
By("running the post test exec from the workload")
err = workload.PostTestExec()
framework.ExpectNoError(err)
runWorkload := func() {
By("running the workload and waiting for success")
// Make the pod for the workload.
pod = makeNodePerfPod(wl)
// Create the pod.
pod = f.PodClient().CreateSync(pod)
// Wait for pod success.
f.PodClient().WaitForSuccess(pod.Name, wl.Timeout())
podLogs, err := framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, pod.Spec.Containers[0].Name)
framework.ExpectNoError(err)
perf, err := wl.ExtractPerformanceFromLogs(podLogs)
framework.ExpectNoError(err)
framework.Logf("Time to complete workload %s: %v", wl.Name(), perf)
}
// Set the Kubelet config back to the old one.
setKubeletConfig(f, oldCfg)
}
Context("Run node performance testing with pre-defined workloads", func() {
BeforeEach(func() {
wl = workloads.NodePerfWorkloads[0]
})
It("NAS parallel benchmark (NPB) suite - Integer Sort (IS) workload", func() {
defer cleanup()
runWorkload()
})
})
Context("Run node performance testing with pre-defined workloads", func() {
BeforeEach(func() {
wl = workloads.NodePerfWorkloads[1]
})
It("NAS parallel benchmark (NPB) suite - Embarrassingly Parallel (EP) workload", func() {
defer cleanup()
runWorkload()
})
})
Context("Run node performance testing with pre-defined workloads", func() {
BeforeEach(func() {
wl = workloads.NodePerfWorkloads[2]
})
It("TensorFlow workload", func() {
defer cleanup()
runWorkload()
})
})
})
......@@ -43,7 +43,7 @@ func (w npbEPWorkload) PodSpec() corev1.PodSpec {
var containers []corev1.Container
ctn := corev1.Container{
Name: fmt.Sprintf("%s-ctn", w.Name()),
Image: "gcr.io/kubernetes-e2e-test-images/node-perf/npb-ep-amd64:1.0",
Image: "gcr.io/kubernetes-e2e-test-images/node-perf/npb-ep:1.0",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("15000m"),
......
......@@ -41,7 +41,7 @@ func (w npbISWorkload) PodSpec() corev1.PodSpec {
var containers []corev1.Container
ctn := corev1.Container{
Name: fmt.Sprintf("%s-ctn", w.Name()),
Image: "gcr.io/kubernetes-e2e-test-images/node-perf/npb-is-amd64:1.0",
Image: "gcr.io/kubernetes-e2e-test-images/node-perf/npb-is:1.0",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("16000m"),
......
......@@ -17,7 +17,7 @@ include ../../hack/make-rules/Makefile.manifest
REGISTRY ?= gcr.io/kubernetes-e2e-test-images
GOARM=7
QEMUVERSION=v2.9.1
GOLANG_VERSION=1.12.0
GOLANG_VERSION=1.12.1
export
ifndef WHAT
......
......@@ -22,6 +22,7 @@ import (
"fmt"
"log"
"net/http"
"net/url"
"time"
)
......@@ -42,5 +43,13 @@ func main() {
w.Write([]byte("ok"))
}
})
http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
loc, err := url.QueryUnescape(r.URL.Query().Get("loc"))
if err != nil {
http.Error(w, fmt.Sprintf("invalid redirect: %q", r.URL.Query().Get("loc")), http.StatusBadRequest)
return
}
http.Redirect(w, r, loc, http.StatusFound)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
......@@ -52,6 +52,7 @@ filegroup(
"//test/integration/framework:all-srcs",
"//test/integration/garbagecollector:all-srcs",
"//test/integration/ipamperf:all-srcs",
"//test/integration/kubelet:all-srcs",
"//test/integration/master:all-srcs",
"//test/integration/metrics:all-srcs",
"//test/integration/objectmeta:all-srcs",
......
load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"main_test.go",
"watch_manager_test.go",
],
tags = ["integration"],
deps = [
"//cmd/kube-apiserver/app/testing:go_default_library",
"//pkg/kubelet/util/manager: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/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//test/integration/framework:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubelet
import (
"testing"
"k8s.io/kubernetes/test/integration/framework"
)
func TestMain(m *testing.M) {
framework.EtcdMain(m.Run)
}
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubelet
import (
"fmt"
"sync"
"testing"
"time"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/pkg/kubelet/util/manager"
"k8s.io/kubernetes/test/integration/framework"
)
func TestWatchBasedManager(t *testing.T) {
server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
defer server.TearDownFn()
server.ClientConfig.QPS = 10000
client, err := kubernetes.NewForConfig(server.ClientConfig)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
listObj := func(namespace string, options metav1.ListOptions) (runtime.Object, error) {
return client.CoreV1().Secrets(namespace).List(options)
}
watchObj := func(namespace string, options metav1.ListOptions) (watch.Interface, error) {
return client.CoreV1().Secrets(namespace).Watch(options)
}
newObj := func() runtime.Object { return &v1.Secret{} }
store := manager.NewObjectCache(listObj, watchObj, newObj, schema.GroupResource{Group: "v1", Resource: "secrets"})
// create 1000 secrets in parallel
t.Log(time.Now(), "creating 1000 secrets")
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for j := 0; j < 100; j++ {
name := fmt.Sprintf("s%d", i*100+j)
if _, err := client.CoreV1().Secrets("default").Create(&v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name}}); err != nil {
t.Fatal(err)
}
}
fmt.Print(".")
}(i)
}
wg.Wait()
t.Log(time.Now(), "finished creating 1000 secrets")
// fetch all secrets
wg = sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for j := 0; j < 100; j++ {
name := fmt.Sprintf("s%d", i*100+j)
start := time.Now()
store.AddReference("default", name)
err := wait.PollImmediate(10*time.Millisecond, 10*time.Second, func() (bool, error) {
obj, err := store.Get("default", name)
if err != nil {
t.Logf("failed on %s, retrying: %v", name, err)
return false, nil
}
if obj.(*v1.Secret).Name != name {
return false, fmt.Errorf("wrong object: %v", obj.(*v1.Secret).Name)
}
return true, nil
})
if err != nil {
t.Fatalf("failed on %s: %v", name, err)
}
if d := time.Now().Sub(start); d > time.Second {
t.Logf("%s took %v", name, d)
}
}
}(i)
}
wg.Wait()
}
......@@ -215,7 +215,7 @@ func initImageConfigs() map[int]Config {
configs[Iperf] = Config{e2eRegistry, "iperf", "1.0"}
configs[JessieDnsutils] = Config{e2eRegistry, "jessie-dnsutils", "1.0"}
configs[Kitten] = Config{e2eRegistry, "kitten", "1.0"}
configs[Liveness] = Config{e2eRegistry, "liveness", "1.0"}
configs[Liveness] = Config{e2eRegistry, "liveness", "1.1"}
configs[LogsGenerator] = Config{e2eRegistry, "logs-generator", "1.0"}
configs[Mounttest] = Config{e2eRegistry, "mounttest", "1.0"}
configs[MounttestUser] = Config{e2eRegistry, "mounttest-user", "1.0"}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment