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 @@ ...@@ -978,8 +978,8 @@
}, },
{ {
"ImportPath": "github.com/container-storage-interface/spec/lib/go/csi", "ImportPath": "github.com/container-storage-interface/spec/lib/go/csi",
"Comment": "v1.1.0-rc1", "Comment": "v1.1.0",
"Rev": "c751be1edc7952e7aac35720d5b34b669e48f113" "Rev": "f750e6765f5f6b4ac0e13e95214d58901290fb4b"
}, },
{ {
"ImportPath": "github.com/containerd/console", "ImportPath": "github.com/containerd/console",
......
...@@ -17,6 +17,50 @@ ...@@ -17,6 +17,50 @@
FROM golang:1.12.0 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 GOARM 7
ENV KUBE_DYNAMIC_CROSSPLATFORMS \ ENV KUBE_DYNAMIC_CROSSPLATFORMS \
armhf \ armhf \
......
...@@ -21,8 +21,8 @@ http_archive( ...@@ -21,8 +21,8 @@ http_archive(
http_archive( http_archive(
name = "io_bazel_rules_go", name = "io_bazel_rules_go",
sha256 = "6776d68ebb897625dead17ae510eac3d5f6342367327875210df44dbe2aeeb19", sha256 = "6433336b4c5feb54e2f45df4c1c84ea4385b2dc0b6f274ec2cd5d745045eae1f",
urls = mirror("https://github.com/bazelbuild/rules_go/releases/download/0.17.1/rules_go-0.17.1.tar.gz"), 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") 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 ...@@ -30,7 +30,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
go_rules_dependencies() go_rules_dependencies()
go_register_toolchains( go_register_toolchains(
go_version = "1.12", go_version = "1.12.1",
) )
http_archive( http_archive(
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
"containers": [ "containers": [
{ {
"name": "cluster-autoscaler", "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": { "livenessProbe": {
"httpGet": { "httpGet": {
"path": "/health-check", "path": "/health-check",
......
...@@ -332,9 +332,17 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io ...@@ -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. // Checks if an external CA is provided by the user (when the CA Cert is present but the CA Key is not)
externalCA, _ := certsphase.UsingExternalCA(&cfg.ClusterConfiguration) externalCA, err := certsphase.UsingExternalCA(&cfg.ClusterConfiguration)
if externalCA { 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 kubeconfigDir := options.kubeconfigDir
if options.dryRun { if options.dryRun {
kubeconfigDir = dryRunDir kubeconfigDir = dryRunDir
...@@ -342,11 +350,22 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io ...@@ -342,11 +350,22 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
if err := kubeconfigphase.ValidateKubeconfigsForExternalCA(kubeconfigDir, cfg); err != nil { if err := kubeconfigphase.ValidateKubeconfigsForExternalCA(kubeconfigDir, cfg); err != nil {
return nil, err 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{ return &initData{
cfg: cfg, cfg: cfg,
certificatesDir: cfg.CertificatesDir, certificatesDir: cfg.CertificatesDir,
......
...@@ -190,7 +190,7 @@ func runCertsSa(c workflow.RunData) error { ...@@ -190,7 +190,7 @@ func runCertsSa(c workflow.RunData) error {
// if external CA mode, skip service account key generation // if external CA mode, skip service account key generation
if data.ExternalCA() { if data.ExternalCA() {
fmt.Printf("[certs] External CA mode: Using existing sa keys\n") fmt.Printf("[certs] Using existing sa keys\n")
return nil return nil
} }
...@@ -220,7 +220,7 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error { ...@@ -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) fmt.Printf("[certs] Using existing %s certificate authority\n", ca.BaseName)
return nil 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 return nil
} }
......
...@@ -354,8 +354,9 @@ func SharedCertificateExists(cfg *kubeadmapi.ClusterConfiguration) (bool, error) ...@@ -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 // 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. // 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) { func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil { if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName, "", "CA"}); err != nil {
...@@ -364,20 +365,24 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) { ...@@ -364,20 +365,24 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
caKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName) caKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)
if _, err := os.Stat(caKeyPath); !os.IsNotExist(err) { 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 { 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 { 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 true, nil
return false, err }
}
// 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 { if err := validateCACert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, "", "front-proxy CA"}); err != nil {
return false, err return false, err
...@@ -385,11 +390,11 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) { ...@@ -385,11 +390,11 @@ func UsingExternalCA(cfg *kubeadmapi.ClusterConfiguration) (bool, error) {
frontProxyCAKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName) frontProxyCAKeyPath := filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCAKeyName)
if _, err := os.Stat(frontProxyCAKeyPath); !os.IsNotExist(err) { 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 { if err := validateSignedCert(certKeyLocation{cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, kubeadmconstants.FrontProxyClientCertAndKeyBaseName, "front-proxy client"}); err != nil {
return false, err return true, err
} }
return true, nil return true, nil
......
...@@ -514,47 +514,108 @@ func TestCreatePKIAssetsWithSparseCerts(t *testing.T) { ...@@ -514,47 +514,108 @@ func TestCreatePKIAssetsWithSparseCerts(t *testing.T) {
func TestUsingExternalCA(t *testing.T) { func TestUsingExternalCA(t *testing.T) {
tests := []struct { tests := []struct {
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error name string
expected bool 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{ setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets, CreatePKIAssets,
}, },
expected: false, externalCAFunc: UsingExternalCA,
expected: false,
}, },
{ {
name: "Test External CA, when ca.key missing",
setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{ setupFuncs: []func(cfg *kubeadmapi.InitConfiguration) error{
CreatePKIAssets, CreatePKIAssets,
deleteCAKey, deleteCertOrKey(kubeadmconstants.CAKeyName),
deleteFrontProxyCAKey,
}, },
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 { for _, test := range tests {
dir := testutil.SetupTempDir(t) t.Run(test.name, func(t *testing.T) {
defer os.RemoveAll(dir) 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{ for _, f := range test.setupFuncs {
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"}, if err := f(cfg); err != nil {
ClusterConfiguration: kubeadmapi.ClusterConfiguration{ t.Errorf("error executing setup function: %v", err)
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 { val, err := test.externalCAFunc(&cfg.ClusterConfiguration)
if err := f(cfg); err != nil { if val != test.expected {
t.Errorf("error executing setup function: %v", err) t.Errorf("UsingExternalCA did not match expected: %v", test.expected)
} }
}
if val, _ := UsingExternalCA(&cfg.ClusterConfiguration); val != test.expected { if (err != nil) != test.expectedErr {
t.Errorf("UsingExternalCA did not match expected: %v", test.expected) t.Errorf("UsingExternalCA returned un expected err: %v", err)
} }
})
} }
} }
...@@ -742,18 +803,13 @@ func TestCreateCertificateFilesMethods(t *testing.T) { ...@@ -742,18 +803,13 @@ func TestCreateCertificateFilesMethods(t *testing.T) {
} }
} }
func deleteCAKey(cfg *kubeadmapi.InitConfiguration) error { func deleteCertOrKey(name string) func(*kubeadmapi.InitConfiguration) error {
if err := os.Remove(filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)); err != nil { return func(cfg *kubeadmapi.InitConfiguration) error {
return errors.Wrapf(err, "failed removing %s", kubeadmconstants.CAKeyName) if err := os.Remove(filepath.Join(cfg.CertificatesDir, name)); err != nil {
} return errors.Wrapf(err, "failed removing %s", name)
return nil }
} 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)
} }
return nil
} }
func assertCertsExist(t *testing.T, dir string) { func assertCertsExist(t *testing.T, dir string) {
......
...@@ -137,6 +137,7 @@ func CreateStaticPodFiles(manifestDir string, cfg *kubeadmapi.ClusterConfigurati ...@@ -137,6 +137,7 @@ func CreateStaticPodFiles(manifestDir string, cfg *kubeadmapi.ClusterConfigurati
func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) []string { func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) []string {
defaultArguments := map[string]string{ defaultArguments := map[string]string{
"advertise-address": localAPIEndpoint.AdvertiseAddress, "advertise-address": localAPIEndpoint.AdvertiseAddress,
"insecure-port": "0",
"enable-admission-plugins": "NodeRestriction", "enable-admission-plugins": "NodeRestriction",
"service-cluster-ip-range": cfg.Networking.ServiceSubnet, "service-cluster-ip-range": cfg.Networking.ServiceSubnet,
"service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName), "service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName),
......
...@@ -148,6 +148,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -148,6 +148,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -184,6 +185,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -184,6 +185,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -228,6 +230,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -228,6 +230,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -269,6 +272,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -269,6 +272,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -312,6 +316,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -312,6 +316,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=baz", "--service-cluster-ip-range=baz",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -357,6 +362,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -357,6 +362,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -385,14 +391,14 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -385,14 +391,14 @@ func TestGetAPIServerCommand(t *testing.T) {
}, },
}, },
{ {
name: "secure-port extra-args", name: "insecure-port extra-args",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{ APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{ ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{ ExtraArgs: map[string]string{
"secure-port": "123", "insecure-port": "1234",
}, },
}, },
}, },
...@@ -400,6 +406,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -400,6 +406,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=1234",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
...@@ -443,6 +450,7 @@ func TestGetAPIServerCommand(t *testing.T) { ...@@ -443,6 +450,7 @@ func TestGetAPIServerCommand(t *testing.T) {
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
expected: []string{ expected: []string{
"kube-apiserver", "kube-apiserver",
"--insecure-port=0",
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
......
...@@ -22,6 +22,7 @@ go_library( ...@@ -22,6 +22,7 @@ go_library(
"//staging/src/k8s.io/client-go/util/keyutil: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", "//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
], ],
) )
......
...@@ -35,6 +35,7 @@ import ( ...@@ -35,6 +35,7 @@ import (
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
keyutil "k8s.io/client-go/util/keyutil" keyutil "k8s.io/client-go/util/keyutil"
bootstraputil "k8s.io/cluster-bootstrap/token/util" bootstraputil "k8s.io/cluster-bootstrap/token/util"
"k8s.io/klog"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" nodebootstraptokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
...@@ -191,6 +192,7 @@ func certsToTransfer(cfg *kubeadmapi.InitConfiguration) map[string]string { ...@@ -191,6 +192,7 @@ func certsToTransfer(cfg *kubeadmapi.InitConfiguration) map[string]string {
certs[externalEtcdCert] = cfg.Etcd.External.CertFile certs[externalEtcdCert] = cfg.Etcd.External.CertFile
certs[externalEtcdKey] = cfg.Etcd.External.KeyFile certs[externalEtcdKey] = cfg.Etcd.External.KeyFile
} }
return certs return certs
} }
...@@ -209,7 +211,7 @@ func getDataFromDisk(cfg *kubeadmapi.InitConfiguration, key []byte) (map[string] ...@@ -209,7 +211,7 @@ func getDataFromDisk(cfg *kubeadmapi.InitConfiguration, key []byte) (map[string]
// DownloadCerts downloads the certificates needed to join a new control plane. // DownloadCerts downloads the certificates needed to join a new control plane.
func DownloadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration, key string) error { 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) decodedKey, err := hex.DecodeString(key)
if err != nil { if err != nil {
...@@ -231,6 +233,10 @@ func DownloadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration ...@@ -231,6 +233,10 @@ func DownloadCerts(client clientset.Interface, cfg *kubeadmapi.InitConfiguration
if !found { if !found {
return errors.New("couldn't find required certificate or key in Secret") 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 { if err := writeCertOrKey(certOrKeyPath, certOrKeyData); err != nil {
return err return err
} }
...@@ -261,14 +267,20 @@ func getSecret(client clientset.Interface) (*v1.Secret, error) { ...@@ -261,14 +267,20 @@ func getSecret(client clientset.Interface) (*v1.Secret, error) {
func getDataFromSecret(secret *v1.Secret, key []byte) (map[string][]byte, error) { func getDataFromSecret(secret *v1.Secret, key []byte) (map[string][]byte, error) {
secretData := map[string][]byte{} secretData := map[string][]byte{}
for certName, encryptedCert := range secret.Data { for secretName, encryptedSecret := range secret.Data {
cert, err := cryptoutil.DecryptBytes(encryptedCert, key) // In some cases the secret might have empty data if the secrets were not present on disk
if err != nil { // when uploading. This can specially happen with external insecure etcd (no certs)
// If any of the decrypt operations fail do not return a partial result, if len(encryptedSecret) > 0 {
// return an empty result immediately cert, err := cryptoutil.DecryptBytes(encryptedSecret, key)
return map[string][]byte{}, err 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 return secretData, nil
} }
......
...@@ -362,24 +362,24 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu ...@@ -362,24 +362,24 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
kubeadmconstants.SchedulerKubeConfigFileName, 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 { if err != nil {
return err return errors.Wrapf(err, "the CA file couldn't be loaded")
} }
for _, kubeConfigFileName := range kubeConfigFileNames { controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
spec, exists := specs[kubeConfigFileName] if err != nil {
if !exists { return err
return errors.Errorf("couldn't retrive KubeConfigSpec for %s", kubeConfigFileName) }
}
kubeconfig, err := buildKubeConfigFromSpec(spec, cfg.ClusterName) validationConfig := kubeconfigutil.CreateBasic(controlPlaneEndpoint, "dummy", "dummy", pkiutil.EncodeCertPEM(caCert))
if err != nil {
return err
}
if err = validateKubeConfig(outDir, kubeConfigFileName, kubeconfig); err != nil { // validate user provided kubeconfig files
return err 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 return nil
......
...@@ -512,14 +512,23 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) { ...@@ -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) caCert, caKey := certstestutil.SetupCertificateAuthorithy(t)
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthorithy(t)
if err := pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil { if err := pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil {
t.Fatalf("failure while saving CA certificate and key: %v", err) 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") 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") 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") configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
tests := map[string]struct { tests := map[string]struct {
...@@ -539,11 +548,21 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) { ...@@ -539,11 +548,21 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
initConfig: initConfig, initConfig: initConfig,
expectedError: true, expectedError: true,
}, },
"some files are invalid": { "some files have invalid CA": {
filesToWrite: map[string]*clientcmdapi.Config{ filesToWrite: map[string]*clientcmdapi.Config{
kubeadmconstants.AdminKubeConfigFileName: config, kubeadmconstants.AdminKubeConfigFileName: config,
kubeadmconstants.KubeletKubeConfigFileName: config, kubeadmconstants.KubeletKubeConfigFileName: config,
kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa, 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, kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL,
}, },
initConfig: initConfig, initConfig: initConfig,
...@@ -567,7 +586,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) { ...@@ -567,7 +586,7 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
for name, config := range test.filesToWrite { for name, config := range test.filesToWrite {
if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil { if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil {
t.Errorf("createKubeConfigFileIfNotExists failed") t.Errorf("createKubeConfigFileIfNotExists failed: %v", err)
} }
} }
......
...@@ -52,6 +52,7 @@ spec: ...@@ -52,6 +52,7 @@ spec:
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --advertise-address=192.168.1.115 - --advertise-address=192.168.1.115
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --insecure-port=0
- --experimental-bootstrap-token-auth=true - --experimental-bootstrap-token-auth=true
- --requestheader-username-headers=X-Remote-User - --requestheader-username-headers=X-Remote-User
- --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-extra-headers-prefix=X-Remote-Extra-
...@@ -134,6 +135,7 @@ spec: ...@@ -134,6 +135,7 @@ spec:
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --advertise-address=$(HOST_IP) - --advertise-address=$(HOST_IP)
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --insecure-port=0
- --experimental-bootstrap-token-auth=true - --experimental-bootstrap-token-auth=true
- --requestheader-username-headers=X-Remote-User - --requestheader-username-headers=X-Remote-User
- --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-extra-headers-prefix=X-Remote-Extra-
......
...@@ -101,7 +101,7 @@ cgroupsPerQOS: true ...@@ -101,7 +101,7 @@ cgroupsPerQOS: true
clusterDNS: clusterDNS:
- 10.192.0.10 - 10.192.0.10
clusterDomain: cluster.global clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Cache configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5 containerLogMaxFiles: 5
containerLogMaxSize: 10Mi containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf contentType: application/vnd.kubernetes.protobuf
......
...@@ -101,7 +101,7 @@ cgroupsPerQOS: true ...@@ -101,7 +101,7 @@ cgroupsPerQOS: true
clusterDNS: clusterDNS:
- 10.192.0.10 - 10.192.0.10
clusterDomain: cluster.global clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Cache configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5 containerLogMaxFiles: 5
containerLogMaxSize: 10Mi containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf contentType: application/vnd.kubernetes.protobuf
......
...@@ -215,7 +215,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura ...@@ -215,7 +215,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura
obj.ContainerLogMaxFiles = utilpointer.Int32Ptr(5) obj.ContainerLogMaxFiles = utilpointer.Int32Ptr(5)
} }
if obj.ConfigMapAndSecretChangeDetectionStrategy == "" { if obj.ConfigMapAndSecretChangeDetectionStrategy == "" {
obj.ConfigMapAndSecretChangeDetectionStrategy = kubeletconfigv1beta1.TTLCacheChangeDetectionStrategy obj.ConfigMapAndSecretChangeDetectionStrategy = kubeletconfigv1beta1.WatchChangeDetectionStrategy
} }
if obj.EnforceNodeAllocatable == nil { if obj.EnforceNodeAllocatable == nil {
obj.EnforceNodeAllocatable = DefaultNodeAllocatableEnforcement obj.EnforceNodeAllocatable = DefaultNodeAllocatableEnforcement
......
...@@ -127,6 +127,11 @@ func (ds *dockerService) RemoveImage(_ context.Context, r *runtimeapi.RemoveImag ...@@ -127,6 +127,11 @@ func (ds *dockerService) RemoveImage(_ context.Context, r *runtimeapi.RemoveImag
return nil, err 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. // 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. // 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. // It also prevents images from being deleted, which actually are deletable using this approach.
......
...@@ -82,7 +82,8 @@ const ( ...@@ -82,7 +82,8 @@ const (
FreeDiskSpaceFailed = "FreeDiskSpaceFailed" FreeDiskSpaceFailed = "FreeDiskSpaceFailed"
// Probe event reason list // Probe event reason list
ContainerUnhealthy = "Unhealthy" ContainerUnhealthy = "Unhealthy"
ContainerProbeWarning = "ProbeWarning"
// Pod worker event reason list // Pod worker event reason list
FailedSync = "FailedSync" FailedSync = "FailedSync"
......
...@@ -66,10 +66,11 @@ func newProber( ...@@ -66,10 +66,11 @@ func newProber(
refManager *kubecontainer.RefManager, refManager *kubecontainer.RefManager,
recorder record.EventRecorder) *prober { recorder record.EventRecorder) *prober {
const followNonLocalRedirects = false
return &prober{ return &prober{
exec: execprobe.New(), exec: execprobe.New(),
readinessHttp: httprobe.New(), readinessHttp: httprobe.New(followNonLocalRedirects),
livenessHttp: httprobe.New(), livenessHttp: httprobe.New(followNonLocalRedirects),
tcp: tcprobe.New(), tcp: tcprobe.New(),
runner: runner, runner: runner,
refManager: refManager, refManager: refManager,
...@@ -96,7 +97,7 @@ func (pb *prober) probe(probeType probeType, pod *v1.Pod, status v1.PodStatus, c ...@@ -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) 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. // Probe failed in one way or another.
ref, hasRef := pb.refManager.GetRef(containerID) ref, hasRef := pb.refManager.GetRef(containerID)
if !hasRef { if !hasRef {
...@@ -115,7 +116,14 @@ func (pb *prober) probe(probeType probeType, pod *v1.Pod, status v1.PodStatus, c ...@@ -115,7 +116,14 @@ func (pb *prober) probe(probeType probeType, pod *v1.Pod, status v1.PodStatus, c
} }
return results.Failure, err 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 return results.Success, nil
} }
......
...@@ -233,6 +233,11 @@ func TestProbe(t *testing.T) { ...@@ -233,6 +233,11 @@ func TestProbe(t *testing.T) {
execResult: probe.Success, execResult: probe.Success,
expectedResult: results.Success, expectedResult: results.Success,
}, },
{ // Probe result is warning
probe: execProbe,
execResult: probe.Warning,
expectedResult: results.Success,
},
{ // Probe result is unknown { // Probe result is unknown
probe: execProbe, probe: execProbe,
execResult: probe.Unknown, execResult: probe.Unknown,
......
...@@ -22,7 +22,12 @@ go_test( ...@@ -22,7 +22,12 @@ go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["http_test.go"], srcs = ["http_test.go"],
embed = [":go_default_library"], 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( filegroup(
......
...@@ -18,6 +18,7 @@ package http ...@@ -18,6 +18,7 @@ package http
import ( import (
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
...@@ -32,13 +33,17 @@ import ( ...@@ -32,13 +33,17 @@ import (
) )
// New creates Prober that will skip TLS verification while probing. // 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} tlsConfig := &tls.Config{InsecureSkipVerify: true}
return NewWithTLSConfig(tlsConfig) return NewWithTLSConfig(tlsConfig, followNonLocalRedirects)
} }
// NewWithTLSConfig takes tls config as parameter. // 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. // We do not want the probe use node's local proxy set.
transport := utilnet.SetTransportDefaults( transport := utilnet.SetTransportDefaults(
&http.Transport{ &http.Transport{
...@@ -46,7 +51,7 @@ func NewWithTLSConfig(config *tls.Config) Prober { ...@@ -46,7 +51,7 @@ func NewWithTLSConfig(config *tls.Config) Prober {
DisableKeepAlives: true, DisableKeepAlives: true,
Proxy: http.ProxyURL(nil), 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. // Prober is an interface that defines the Probe function for doing HTTP readiness/liveness checks.
...@@ -55,12 +60,18 @@ type Prober interface { ...@@ -55,12 +60,18 @@ type Prober interface {
} }
type httpProber struct { type httpProber struct {
transport *http.Transport transport *http.Transport
followNonLocalRedirects bool
} }
// Probe returns a ProbeRunner capable of running an HTTP check. // 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) { 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. // 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 ...@@ -102,9 +113,30 @@ func DoHTTPProbe(url *url.URL, headers http.Header, client GetHTTPInterface) (pr
} }
body := string(b) body := string(b)
if res.StatusCode >= http.StatusOK && res.StatusCode < http.StatusBadRequest { 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) klog.V(4).Infof("Probe succeeded for %s, Response: %v", url.String(), *res)
return probe.Success, body, nil return probe.Success, body, nil
} }
klog.V(4).Infof("Probe failed for %s with request headers %v, response body: %v", url.String(), headers, body) 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 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 ( ...@@ -28,6 +28,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/probe" "k8s.io/kubernetes/pkg/probe"
) )
...@@ -64,7 +67,7 @@ func TestHTTPProbeProxy(t *testing.T) { ...@@ -64,7 +67,7 @@ func TestHTTPProbeProxy(t *testing.T) {
defer unsetEnv("no_proxy")() defer unsetEnv("no_proxy")()
defer unsetEnv("NO_PROXY")() defer unsetEnv("NO_PROXY")()
prober := New() prober := New(true)
go func() { go func() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
...@@ -119,7 +122,7 @@ func TestHTTPProbeChecker(t *testing.T) { ...@@ -119,7 +122,7 @@ func TestHTTPProbeChecker(t *testing.T) {
} }
} }
prober := New() prober := New(true)
testCases := []struct { testCases := []struct {
handler func(w http.ResponseWriter, r *http.Request) handler func(w http.ResponseWriter, r *http.Request)
reqHeaders http.Header reqHeaders http.Header
...@@ -223,7 +226,7 @@ func TestHTTPProbeChecker(t *testing.T) { ...@@ -223,7 +226,7 @@ func TestHTTPProbeChecker(t *testing.T) {
}, },
} }
for i, test := range testCases { 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) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
test.handler(w, r) test.handler(w, r)
})) }))
...@@ -258,6 +261,56 @@ func TestHTTPProbeChecker(t *testing.T) { ...@@ -258,6 +261,56 @@ func TestHTTPProbeChecker(t *testing.T) {
t.Errorf("Expected response not to contain %v, got %v", test.notBody, output) 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 ...@@ -22,6 +22,8 @@ type Result string
const ( const (
// Success Result // Success Result
Success Result = "success" Success Result = "success"
// Warning Result. Logically success, but with additional debugging information attached.
Warning Result = "warning"
// Failure Result // Failure Result
Failure Result = "failure" Failure Result = "failure"
// Unknown Result // Unknown Result
......
...@@ -68,7 +68,8 @@ func (server *Server) DoServerCheck() (probe.Result, string, error) { ...@@ -68,7 +68,8 @@ func (server *Server) DoServerCheck() (probe.Result, string, error) {
if server.Prober != nil { if server.Prober != nil {
return return
} }
server.Prober = httpprober.NewWithTLSConfig(server.TLSConfig) const followNonLocalRedirects = true
server.Prober = httpprober.NewWithTLSConfig(server.TLSConfig, followNonLocalRedirects)
}) })
scheme := "http" scheme := "http"
......
...@@ -48,6 +48,7 @@ go_library( ...@@ -48,6 +48,7 @@ go_library(
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/conditions:go_default_library", "//pkg/client/conditions:go_default_library",
"//pkg/kubelet:go_default_library", "//pkg/kubelet:go_default_library",
"//pkg/kubelet/events:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/sysctl:go_default_library", "//pkg/kubelet/sysctl:go_default_library",
"//pkg/security/apparmor:go_default_library", "//pkg/security/apparmor:go_default_library",
......
...@@ -18,13 +18,16 @@ package common ...@@ -18,13 +18,16 @@ package common
import ( import (
"fmt" "fmt"
"net/url"
"time" "time"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/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/intstr"
"k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/uuid"
podutil "k8s.io/kubernetes/pkg/api/v1/pod" podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/kubelet/events"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
testutils "k8s.io/kubernetes/test/utils" testutils "k8s.io/kubernetes/test/utils"
...@@ -305,6 +308,82 @@ var _ = framework.KubeDescribe("Probing container", func() { ...@@ -305,6 +308,82 @@ var _ = framework.KubeDescribe("Probing container", func() {
}, },
}, 1, defaultObservationTimeout) }, 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) { func getContainerStartedTime(p *v1.Pod, containerName string) (time.Time, error) {
......
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"sync/atomic" "sync"
"time" "time"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
...@@ -434,19 +434,11 @@ type chaosMonkeyAdapter struct { ...@@ -434,19 +434,11 @@ type chaosMonkeyAdapter struct {
func (cma *chaosMonkeyAdapter) Test(sem *chaosmonkey.Semaphore) { func (cma *chaosMonkeyAdapter) Test(sem *chaosmonkey.Semaphore) {
start := time.Now() start := time.Now()
var once sync.Once
// 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
ready := func() { ready := func() {
if atomic.CompareAndSwapUint32(&onceWithoutMutex, 0, 1) { once.Do(func() {
sem.Ready() sem.Ready()
} })
} }
defer finalizeUpgradeTest(start, cma.testReport) defer finalizeUpgradeTest(start, cma.testReport)
defer ready() defer ready()
......
...@@ -427,7 +427,8 @@ var _ = SIGDescribe("Loadbalancing: L7", func() { ...@@ -427,7 +427,8 @@ var _ = SIGDescribe("Loadbalancing: L7", func() {
// TODO(nikhiljindal): Check the instance group annotation value and verify with a multizone cluster. // 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" httpsScheme := "request_scheme=https"
By("Create a basic HTTP2 ingress") By("Create a basic HTTP2 ingress")
......
...@@ -53,8 +53,8 @@ var NodeImageWhiteList = sets.NewString( ...@@ -53,8 +53,8 @@ var NodeImageWhiteList = sets.NewString(
imageutils.GetE2EImage(imageutils.Nonewprivs), imageutils.GetE2EImage(imageutils.Nonewprivs),
imageutils.GetPauseImageName(), imageutils.GetPauseImageName(),
framework.GetGPUDevicePluginImage(), framework.GetGPUDevicePluginImage(),
"gcr.io/kubernetes-e2e-test-images/node-perf/npb-is-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-amd64: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", "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 ...@@ -56,53 +56,74 @@ func setKubeletConfig(f *framework.Framework, cfg *kubeletconfig.KubeletConfigur
// Slow by design. // Slow by design.
var _ = SIGDescribe("Node Performance Testing [Serial] [Slow]", func() { var _ = SIGDescribe("Node Performance Testing [Serial] [Slow]", func() {
f := framework.NewDefaultFramework("node-performance-testing") 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() { cleanup := func() {
It("run each pre-defined workload", func() { gp := int64(0)
By("running the workloads") delOpts := metav1.DeleteOptions{
for _, workload := range workloads.NodePerfWorkloads { GracePeriodSeconds: &gp,
By("running the pre test exec from the workload") }
err := workload.PreTestExec() f.PodClient().DeleteSync(pod.Name, &delOpts, framework.DefaultPodDeletionTimeout)
framework.ExpectNoError(err) By("running the post test exec from the workload")
err := wl.PostTestExec()
By("restarting kubelet with required configuration") framework.ExpectNoError(err)
// Get the Kubelet config required for this workload. setKubeletConfig(f, oldCfg)
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)
By("running the post test exec from the workload") runWorkload := func() {
err = workload.PostTestExec() By("running the workload and waiting for success")
framework.ExpectNoError(err) // 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. Context("Run node performance testing with pre-defined workloads", func() {
setKubeletConfig(f, oldCfg) 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 { ...@@ -43,7 +43,7 @@ func (w npbEPWorkload) PodSpec() corev1.PodSpec {
var containers []corev1.Container var containers []corev1.Container
ctn := corev1.Container{ ctn := corev1.Container{
Name: fmt.Sprintf("%s-ctn", w.Name()), 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{ Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{ Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("15000m"), corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("15000m"),
......
...@@ -41,7 +41,7 @@ func (w npbISWorkload) PodSpec() corev1.PodSpec { ...@@ -41,7 +41,7 @@ func (w npbISWorkload) PodSpec() corev1.PodSpec {
var containers []corev1.Container var containers []corev1.Container
ctn := corev1.Container{ ctn := corev1.Container{
Name: fmt.Sprintf("%s-ctn", w.Name()), 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{ Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{ Requests: corev1.ResourceList{
corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("16000m"), corev1.ResourceName(corev1.ResourceCPU): resource.MustParse("16000m"),
......
...@@ -17,7 +17,7 @@ include ../../hack/make-rules/Makefile.manifest ...@@ -17,7 +17,7 @@ include ../../hack/make-rules/Makefile.manifest
REGISTRY ?= gcr.io/kubernetes-e2e-test-images REGISTRY ?= gcr.io/kubernetes-e2e-test-images
GOARM=7 GOARM=7
QEMUVERSION=v2.9.1 QEMUVERSION=v2.9.1
GOLANG_VERSION=1.12.0 GOLANG_VERSION=1.12.1
export export
ifndef WHAT ifndef WHAT
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"net/url"
"time" "time"
) )
...@@ -42,5 +43,13 @@ func main() { ...@@ -42,5 +43,13 @@ func main() {
w.Write([]byte("ok")) 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)) log.Fatal(http.ListenAndServe(":8080", nil))
} }
...@@ -52,6 +52,7 @@ filegroup( ...@@ -52,6 +52,7 @@ filegroup(
"//test/integration/framework:all-srcs", "//test/integration/framework:all-srcs",
"//test/integration/garbagecollector:all-srcs", "//test/integration/garbagecollector:all-srcs",
"//test/integration/ipamperf:all-srcs", "//test/integration/ipamperf:all-srcs",
"//test/integration/kubelet:all-srcs",
"//test/integration/master:all-srcs", "//test/integration/master:all-srcs",
"//test/integration/metrics:all-srcs", "//test/integration/metrics:all-srcs",
"//test/integration/objectmeta: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 { ...@@ -215,7 +215,7 @@ func initImageConfigs() map[int]Config {
configs[Iperf] = Config{e2eRegistry, "iperf", "1.0"} configs[Iperf] = Config{e2eRegistry, "iperf", "1.0"}
configs[JessieDnsutils] = Config{e2eRegistry, "jessie-dnsutils", "1.0"} configs[JessieDnsutils] = Config{e2eRegistry, "jessie-dnsutils", "1.0"}
configs[Kitten] = Config{e2eRegistry, "kitten", "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[LogsGenerator] = Config{e2eRegistry, "logs-generator", "1.0"}
configs[Mounttest] = Config{e2eRegistry, "mounttest", "1.0"} configs[Mounttest] = Config{e2eRegistry, "mounttest", "1.0"}
configs[MounttestUser] = Config{e2eRegistry, "mounttest-user", "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