diff --git a/NOTICE.txt b/NOTICE.txt index 7dd844f01fb..f49f6a92dfe 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1722,6 +1722,35 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +Module : github.com/tsenart/vegeta +Version : v12.7.0+incompatible +Time : 2019-07-21 12:37:32 +0000 UTC + +Contents of probable licence file $GOMODCACHE/github.com/tsenart/vegeta@v12.7.0+incompatible/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2013-2016 Tomás Senart + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------------- Module : go.uber.org/zap Version : v1.12.0 diff --git a/go.mod b/go.mod index ebb1e2c33d2..17dd6d556eb 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,9 @@ require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.4.2 // indirect github.com/Masterminds/sprig v2.20.0+incompatible + github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect github.com/davecgh/go-spew v1.1.1 + github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051 // indirect github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect github.com/elastic/go-ucfg v0.7.0 github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 // indirect @@ -22,6 +24,7 @@ require ( github.com/hashicorp/vault/api v1.0.4 github.com/huandu/xstrings v1.2.0 // indirect github.com/imdario/mergo v0.3.8 + github.com/influxdata/tdigest v0.0.1 // indirect github.com/magiconair/properties v1.8.1 github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect @@ -35,7 +38,9 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.4.0 + github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 // indirect github.com/stretchr/testify v1.4.0 + github.com/tsenart/vegeta v12.7.0+incompatible go.uber.org/zap v1.12.0 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 golang.org/x/net v0.0.0-20191011234655-491137f69257 // indirect diff --git a/go.sum b/go.sum index 1799e36fbf5..b361d69c6f6 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= +github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -66,6 +68,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051 h1:ByJUvQYyTtNNCVfYNM48q6uYUT4fAlN0wNmd3th4BSo= +github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -244,6 +248,8 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= +github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -270,8 +276,10 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -404,6 +412,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25 h1:7z3LSn867ex6VSaahyKadf4WtSsJIgne6A1WLOAGM8A= +github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -415,6 +425,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tsenart/vegeta v12.7.0+incompatible h1:sGlrv11EMxQoKOlDuMWR23UdL90LE5VlhKw/6PWkZmU= +github.com/tsenart/vegeta v12.7.0+incompatible/go.mod h1:Smz/ZWfhKRcyDDChZkG3CyTHdj87lHzio/HOCkbndXM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -453,8 +465,10 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -541,6 +555,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -564,8 +579,12 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= diff --git a/pkg/controller/common/defaults/pod_template.go b/pkg/controller/common/defaults/pod_template.go index 4195fffe6a9..505bc98ef47 100644 --- a/pkg/controller/common/defaults/pod_template.go +++ b/pkg/controller/common/defaults/pod_template.go @@ -315,3 +315,16 @@ func (b *PodTemplateBuilder) WithResources(resources corev1.ResourceRequirements } return b } + +func (b *PodTemplateBuilder) WithPreStopHook(handler corev1.Handler) *PodTemplateBuilder { + if b.Container.Lifecycle == nil { + b.Container.Lifecycle = &corev1.Lifecycle{} + } + + if b.Container.Lifecycle.PreStop == nil { + // no user-provided hook, we can use our own + b.Container.Lifecycle.PreStop = &handler + } + + return b +} diff --git a/pkg/controller/common/defaults/pod_template_test.go b/pkg/controller/common/defaults/pod_template_test.go index 936ddc95dbe..67352246435 100644 --- a/pkg/controller/common/defaults/pod_template_test.go +++ b/pkg/controller/common/defaults/pod_template_test.go @@ -1047,3 +1047,88 @@ func TestPodTemplateBuilder_WithDefaultResources(t *testing.T) { }) } } + +func TestPodTemplateBuilder_WithPreStopHook(t *testing.T) { + containerName := "mycontainer" + defaultHook := corev1.Handler{Exec: &corev1.ExecAction{Command: []string{"default", "command"}}} + userHook := &corev1.Handler{} + tests := []struct { + name string + podTemplate corev1.PodTemplateSpec + preStopHook corev1.Handler + wantPreStop corev1.Handler + wantPostStart *corev1.Handler + }{ + { + name: "no pre stop hook in pod template: use default one", + podTemplate: corev1.PodTemplateSpec{}, + preStopHook: defaultHook, + wantPreStop: defaultHook, + wantPostStart: nil, + }, + { + name: "user provided post start hook, but no pre stop hook in pod template: use default one", + podTemplate: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: containerName, + Lifecycle: &corev1.Lifecycle{ + PostStart: userHook, + }, + }, + }, + }, + }, + preStopHook: defaultHook, + wantPreStop: defaultHook, + wantPostStart: userHook, + }, + { + name: "pre stop hook in pod template: use provided one", + podTemplate: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: containerName, + Lifecycle: &corev1.Lifecycle{ + PreStop: userHook, + }, + }, + }, + }}, + preStopHook: *userHook, + wantPostStart: nil, + }, + { + name: "user provided post start hook and pre stop hook in pod template: use provided one", + podTemplate: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: containerName, + Lifecycle: &corev1.Lifecycle{ + PostStart: &corev1.Handler{}, + PreStop: userHook, + }, + }, + }, + }, + }, + preStopHook: *userHook, + wantPostStart: userHook, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := NewPodTemplateBuilder(tt.podTemplate, "mycontainer") + got := b.WithPreStopHook(tt.preStopHook).Container.Lifecycle + if !reflect.DeepEqual(got.PreStop, &tt.wantPreStop) { + t.Errorf("PreStop after PodTemplateBuilder.WithPreStopHook() = %v, want %v", got.PreStop, tt.wantPreStop) + } + if !reflect.DeepEqual(got.PostStart, tt.wantPostStart) { + t.Errorf("PostStart after PodTemplateBuilder.WithPreStopHook() = %v, want %v", got.PostStart, tt.wantPostStart) + } + }) + } +} diff --git a/pkg/controller/common/license/trial.go b/pkg/controller/common/license/trial.go index 90b4192b424..2b9d9778365 100644 --- a/pkg/controller/common/license/trial.go +++ b/pkg/controller/common/license/trial.go @@ -24,7 +24,7 @@ const ( TrialStatusSecretKey = "trial-status" TrialPubkeyKey = "pubkey" - TrialLicenseSecretName = "trial.k8s.elastic.co/secret-name" // nolint + TrialLicenseSecretName = "trial.k8s.elastic.co/secret-name" // nolint TrialLicenseSecretNamespace = "trial.k8s.elastic.co/secret-namespace" // nolint ) diff --git a/pkg/controller/elasticsearch/configmap/configmap.go b/pkg/controller/elasticsearch/configmap/configmap.go index 5ea296a814f..975fd39e4ee 100644 --- a/pkg/controller/elasticsearch/configmap/configmap.go +++ b/pkg/controller/elasticsearch/configmap/configmap.go @@ -41,6 +41,7 @@ func ReconcileScriptsConfigMap(c k8s.Client, scheme *runtime.Scheme, es esv1.Ela types.NamespacedName{Namespace: es.Namespace, Name: esv1.ScriptsConfigMap(es.Name)}, map[string]string{ nodespec.ReadinessProbeScriptConfigKey: nodespec.ReadinessProbeScript, + nodespec.PreStopHookScriptConfigKey: nodespec.PreStopHookScript, initcontainer.PrepareFsScriptConfigKey: fsScript, }, ) diff --git a/pkg/controller/elasticsearch/nodespec/defaults.go b/pkg/controller/elasticsearch/nodespec/defaults.go index 582edf2286a..1ebb15d20f6 100644 --- a/pkg/controller/elasticsearch/nodespec/defaults.go +++ b/pkg/controller/elasticsearch/nodespec/defaults.go @@ -51,13 +51,14 @@ var ( ) // DefaultEnvVars are environment variables injected into Elasticsearch pods. -func DefaultEnvVars(httpCfg commonv1.HTTPConfig) []corev1.EnvVar { +func DefaultEnvVars(httpCfg commonv1.HTTPConfig, headlessServiceName string) []corev1.EnvVar { return append( defaults.PodDownwardEnvVars, []corev1.EnvVar{ {Name: settings.EnvProbePasswordPath, Value: path.Join(esvolume.ProbeUserSecretMountPath, user.InternalProbeUserName)}, {Name: settings.EnvProbeUsername, Value: user.InternalProbeUserName}, {Name: settings.EnvReadinessProbeProtocol, Value: httpCfg.Protocol()}, + {Name: settings.HeadlessServiceName, Value: headlessServiceName}, // Disable curl/libnss use of sqlite caching to avoid triggering an issue in linux/kubernetes // where the kernel's dentry cache grows by 5mb every time curl is invoked. This cache usage diff --git a/pkg/controller/elasticsearch/nodespec/lifecycle_hook.go b/pkg/controller/elasticsearch/nodespec/lifecycle_hook.go new file mode 100644 index 00000000000..ab37d997a6d --- /dev/null +++ b/pkg/controller/elasticsearch/nodespec/lifecycle_hook.go @@ -0,0 +1,56 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package nodespec + +import ( + "path" + + "github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/volume" + v1 "k8s.io/api/core/v1" +) + +func NewPreStopHook() *v1.Handler { + return &v1.Handler{ + Exec: &v1.ExecAction{ + Command: []string{"bash", "-c", path.Join(volume.ScriptsVolumeMountPath, PreStopHookScriptConfigKey)}}, + } +} + +const PreStopHookScriptConfigKey = "pre-stop-hook-script.sh" +const PreStopHookScript = `#!/usr/bin/env bash + +set -eux + +# This script will wait for up to $MAX_WAIT_SECONDS for $POD_IP to disappear from DNS record, +# then it will wait additional $ADDITIONAL_WAIT_SECONDS and exit. This slows down the process shutdown +# and allows to make changes to the pool gracefully, without blackholing traffic when DNS +# contains IP that is already inactive. Assumes $HEADLESS_SERVICE_NAME and $POD_IP env variables are defined. + +# max time to wait for pods IP to disappear from DNS. As this runs in parallel to grace period +# (defaulting to 30s) after which process is SIGKILLed, it should be set to allow enough time +# for the process to gracefully terminate. +MAX_WAIT_SECONDS=${MAX_WAIT_SECONDS:=20} + +# additional wait, allows queries to successfully use IP from DNS from before pod termination +# this gives a little bit more time for clients that resolved DNS just before DNS record +# was updated. +ADDITIONAL_WAIT_SECONDS=${ADDITIONAL_WAIT_SECONDS:=1} + +START_TIME=$(date +%s) +while true; do + ELAPSED_TIME=$(($(date +%s) - $START_TIME)) + + if [ $ELAPSED_TIME -gt $MAX_WAIT_SECONDS ]; then + exit 1 + fi + + if ! getent hosts $HEADLESS_SERVICE_NAME | grep $POD_IP; then + sleep $ADDITIONAL_WAIT_SECONDS + exit 0 + fi + + sleep 1 +done +` diff --git a/pkg/controller/elasticsearch/nodespec/podspec.go b/pkg/controller/elasticsearch/nodespec/podspec.go index d8a27a45e88..78cd3a126b7 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec.go +++ b/pkg/controller/elasticsearch/nodespec/podspec.go @@ -56,11 +56,12 @@ func BuildPodTemplateSpec( WithPorts(DefaultContainerPorts). WithReadinessProbe(*NewReadinessProbe()). WithAffinity(DefaultAffinity(es.Name)). - WithEnv(DefaultEnvVars(es.Spec.HTTP)...). + WithEnv(DefaultEnvVars(es.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(es.Name, nodeSet.Name)))...). WithVolumes(volumes...). WithVolumeMounts(volumeMounts...). WithLabels(labels). WithInitContainers(initContainers...). + WithPreStopHook(*NewPreStopHook()). WithInitContainerDefaults() return builder.PodTemplate, nil diff --git a/pkg/controller/elasticsearch/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 7d113d81018..6629bccbd66 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -172,10 +172,15 @@ func TestBuildPodTemplateSpec(t *testing.T) { {Name: "http", HostPort: 0, ContainerPort: 9200, Protocol: "TCP", HostIP: ""}, {Name: "transport", HostPort: 0, ContainerPort: 9300, Protocol: "TCP", HostIP: ""}, }, - Env: append(DefaultEnvVars(sampleES.Spec.HTTP), corev1.EnvVar{Name: "my-env", Value: "my-value"}), + Env: append( + DefaultEnvVars(sampleES.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name))), + corev1.EnvVar{Name: "my-env", Value: "my-value"}), Resources: DefaultResources, VolumeMounts: volumeMounts, ReadinessProbe: NewReadinessProbe(), + Lifecycle: &corev1.Lifecycle{ + PreStop: NewPreStopHook(), + }, }, }, TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, diff --git a/pkg/controller/elasticsearch/nodespec/readiness_probe.go b/pkg/controller/elasticsearch/nodespec/readiness_probe.go index 171d3fd9852..b9c72e87c48 100644 --- a/pkg/controller/elasticsearch/nodespec/readiness_probe.go +++ b/pkg/controller/elasticsearch/nodespec/readiness_probe.go @@ -27,8 +27,7 @@ func NewReadinessProbe() *corev1.Probe { } const ReadinessProbeScriptConfigKey = "readiness-probe-script.sh" -const ReadinessProbeScript string = ` -#!/usr/bin/env bash +const ReadinessProbeScript = `#!/usr/bin/env bash # Consider a node to be healthy if it responds to a simple GET on "/_cat/nodes?local" CURL_TIMEOUT=3 diff --git a/pkg/controller/elasticsearch/settings/environment.go b/pkg/controller/elasticsearch/settings/environment.go index cfc5d799be6..eee45ff88f1 100644 --- a/pkg/controller/elasticsearch/settings/environment.go +++ b/pkg/controller/elasticsearch/settings/environment.go @@ -11,6 +11,7 @@ const ( EnvProbePasswordPath = "PROBE_PASSWORD_PATH" EnvProbeUsername = "PROBE_USERNAME" EnvReadinessProbeProtocol = "READINESS_PROBE_PROTOCOL" + HeadlessServiceName = "HEADLESS_SERVICE_NAME" // EnvPodName and EnvPodIP are injected as env var into the ES pod at runtime, // to be referenced in ES configuration file diff --git a/test/e2e/es/mutation_test.go b/test/e2e/es/mutation_test.go index c1ceca89bc3..11362965b34 100644 --- a/test/e2e/es/mutation_test.go +++ b/test/e2e/es/mutation_test.go @@ -5,10 +5,18 @@ package es import ( + "encoding/json" + "fmt" + "net/http" "testing" + "time" + esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1" + "github.com/elastic/cloud-on-k8s/pkg/dev/portforward" "github.com/elastic/cloud-on-k8s/test/e2e/test" "github.com/elastic/cloud-on-k8s/test/e2e/test/elasticsearch" + "github.com/stretchr/testify/assert" + vegeta "github.com/tsenart/vegeta/lib" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) @@ -227,6 +235,91 @@ func TestMutationWithLargerMaxUnavailable(t *testing.T) { RunESMutation(t, b, mutated) } +func TestMutationWhileLoadTesting(t *testing.T) { + pt := corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: esv1.ElasticsearchContainerName, + Env: []corev1.EnvVar{ + { + // Use much higher value to greatly reduce chance of race condition. + Name: "ADDITIONAL_WAIT_SECONDS", + Value: "10", + }, + }, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + // This has to be set for the topology check to pass, as we query ES pods for their + // memory and pod template has to match. + corev1.ResourceMemory: resource.MustParse("2Gi"), + }, + }, + }, + }, + }, + } + b := elasticsearch.NewBuilder("test-while-load-testing"). + WithESMasterDataNodes(3, elasticsearch.DefaultResources). + WithPodTemplate(pt) + + // force a rolling upgrade through label change + pt.Labels = map[string]string{"some_label_name": "some_new_value"} + mutated := b.WithNoESTopology(). + WithESMasterDataNodes(3, elasticsearch.DefaultResources). + WithPodTemplate(pt) + + var metrics vegeta.Metrics + var attacker vegeta.Attacker + // keep hitting ES endpoints at high rate during pod cycling to catch any downtime + w := test.NewOnceWatcher( + "load test", + func(k *test.K8sClient, t *testing.T) { + url := fmt.Sprintf("https://%s.%s.svc.cluster.local:9200/", esv1.HTTPService(b.Elasticsearch.Name), b.Elasticsearch.Namespace) + rate := vegeta.Rate{Freq: 10, Per: time.Second} + targeter := vegeta.NewStaticTargeter(vegeta.Target{ + Method: "GET", + URL: url, + }) + + var attackerOption func(*vegeta.Attacker) + if test.Ctx().AutoPortForwarding { + // we need to forward, use our dialer + c := &http.Client{ + Timeout: vegeta.DefaultTimeout, + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: portforward.NewForwardingDialer().DialContext, + TLSClientConfig: vegeta.DefaultTLSConfig, + MaxIdleConnsPerHost: vegeta.DefaultConnections, + DisableKeepAlives: true, + }, + } + attackerOption = vegeta.Client(c) + } else { + // no forwarding needed, just turn off keep alives + attackerOption = vegeta.KeepAlive(false) + } + + attacker = *vegeta.NewAttacker(attackerOption) + for res := range attacker.Attack(targeter, rate, 0, "ES load test while recycling pods") { + metrics.Add(res) + } + }, + func(k *test.K8sClient, t *testing.T) { + attacker.Stop() + metrics.Close() + bytes, _ := json.Marshal(metrics) + msgAndArgs := []interface{}{"metrics: ", string(bytes)} + assert.Equal(t, 1, len(metrics.StatusCodes), msgAndArgs) + if _, ok := metrics.StatusCodes["401"]; !ok { + assert.Fail(t, "all status codes should be 401", msgAndArgs) + } + }) + + test.RunMutationsWhileWatching(t, []test.Builder{b}, []test.Builder{mutated.WithMutatedFrom(&b)}, []test.Watcher{w}) +} + func RunESMutation(t *testing.T, toCreate elasticsearch.Builder, mutateTo elasticsearch.Builder) { test.RunMutation(t, toCreate, mutateTo.WithMutatedFrom(&toCreate)) } diff --git a/test/e2e/test/elasticsearch/checks_k8s.go b/test/e2e/test/elasticsearch/checks_k8s.go index 852d7c2738f..2bb329d97e2 100644 --- a/test/e2e/test/elasticsearch/checks_k8s.go +++ b/test/e2e/test/elasticsearch/checks_k8s.go @@ -159,7 +159,7 @@ func clusterHealthGreen(b Builder, k *test.K8sClient) error { return nil } -// CheckServices checks that all ES services are created +// CheckServices checks that all ES services are created and external IP is provisioned for all LB services func CheckServices(b Builder, k *test.K8sClient) test.Step { return test.Step{ Name: "ES services should be created", @@ -167,9 +167,15 @@ func CheckServices(b Builder, k *test.K8sClient) test.Step { for _, s := range []string{ esv1.HTTPService(b.Elasticsearch.Name), } { - if _, err := k.GetService(b.Elasticsearch.Namespace, s); err != nil { + svc, err := k.GetService(b.Elasticsearch.Namespace, s) + if err != nil { return err } + if svc.Spec.Type == corev1.ServiceTypeLoadBalancer { + if len(svc.Status.LoadBalancer.Ingress) == 0 { + return fmt.Errorf("load balancer for %s not ready yet", svc.Name) + } + } } return nil }), diff --git a/test/e2e/test/elasticsearch/steps_mutation.go b/test/e2e/test/elasticsearch/steps_mutation.go index 05ab3ada022..d0f4bd612f3 100644 --- a/test/e2e/test/elasticsearch/steps_mutation.go +++ b/test/e2e/test/elasticsearch/steps_mutation.go @@ -171,13 +171,14 @@ func (hc *ContinuousHealthCheck) Start() { case <-hc.stopChan: return case <-ticker.C: - ctx, cancel := context.WithTimeout(context.Background(), continuousHealthCheckTimeout) // recreate the Elasticsearch client at each iteration, since we may have switched protocol from http to https during the mutation client, err := hc.esClientFactory() if err != nil { // treat client creation failure same as unavailable cluster hc.AppendErr(err) + continue } + ctx, cancel := context.WithTimeout(context.Background(), continuousHealthCheckTimeout) defer cancel() health, err := client.GetClusterHealth(ctx) if err != nil { diff --git a/test/e2e/test/watcher.go b/test/e2e/test/watcher.go index beaea5eb8ab..26e949b00a1 100644 --- a/test/e2e/test/watcher.go +++ b/test/e2e/test/watcher.go @@ -13,20 +13,33 @@ import ( var NOOPCheck func(k *K8sClient, t *testing.T) = nil type Watcher struct { - name string - interval time.Duration - watchFn func(k *K8sClient, t *testing.T) - checkFn func(k *K8sClient, t *testing.T) - stopChan chan struct{} + name string + interval time.Duration + watchFn func(k *K8sClient, t *testing.T) + checkFn func(k *K8sClient, t *testing.T) + stopChan chan struct{} + watchOnce bool } func NewWatcher(name string, interval time.Duration, watchFn func(k *K8sClient, t *testing.T), checkFn func(k *K8sClient, t *testing.T)) Watcher { return Watcher{ - name: name, - interval: interval, - watchFn: watchFn, - checkFn: checkFn, - stopChan: make(chan struct{}), + name: name, + interval: interval, + watchFn: watchFn, + checkFn: checkFn, + stopChan: make(chan struct{}), + watchOnce: false, + } +} + +func NewOnceWatcher(name string, watchFn func(k *K8sClient, t *testing.T), checkFn func(k *K8sClient, t *testing.T)) Watcher { + return Watcher{ + name: name, + interval: 1 * time.Second, + watchFn: watchFn, + checkFn: checkFn, + stopChan: make(chan struct{}), + watchOnce: true, } } @@ -34,6 +47,11 @@ func (w *Watcher) StartStep(k *K8sClient) Step { return Step{ Name: fmt.Sprintf("Starting to %s", w.name), Test: func(t *testing.T) { + if w.watchOnce { + go w.watchFn(k, t) + return + } + go func() { ticker := time.NewTicker(w.interval) for { @@ -63,5 +81,7 @@ func (w *Watcher) StopStep(k *K8sClient) Step { } func (w *Watcher) stop() { - w.stopChan <- struct{}{} + if !w.watchOnce { + w.stopChan <- struct{}{} + } }