diff --git a/go.mod b/go.mod index 28b2f321d..ce4504c7e 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,10 @@ require ( github.com/gorilla/sessions v0.0.0-20171008214740-a3acf13e802c github.com/gorilla/websocket v1.4.1 // indirect github.com/grpc-ecosystem/grpc-gateway v1.10.0 // indirect - github.com/openshift/api v0.0.0-20200429152225-b98a784d8e6d + github.com/openshift/api v0.0.0-20200521101457-60c476765272 github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc - github.com/openshift/client-go v0.0.0-20200422192633-6f6c07fc2a70 - github.com/openshift/library-go v0.0.0-20200429122220-9e6c27e916a0 + github.com/openshift/client-go v0.0.0-20200521150516-05eb9880269c + github.com/openshift/library-go v0.0.0-20201209131625-07b0830b8740 github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect @@ -35,4 +35,6 @@ require ( replace ( github.com/RangelReale/osin => github.com/openshift/osin v1.0.1-0.20180202150137-2dc1b4316769 github.com/RangelReale/osincli => github.com/openshift/osincli v0.0.0-20160924135400-fababb0555f2 + k8s.io/apiserver => github.com/openshift/kubernetes-apiserver v0.0.0-20201209103546-c5587e940bd4 // points to openshift-apiserver-4.5-kubernetes-1.18.9 + k8s.io/client-go => github.com/openshift/kubernetes-client-go v0.0.0-20201209131240-ad062a7baf0b ) diff --git a/go.sum b/go.sum index e36f9c975..4d2ef56d7 100644 --- a/go.sum +++ b/go.sum @@ -304,19 +304,18 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20191031171055-b133feaeeb2e/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/openshift/api v0.0.0-20200326152221-912866ddb162/go.mod h1:RKMJ5CBnljLfnej+BJ/xnOWc3kZDvJUaIAEq2oKSPtE= -github.com/openshift/api v0.0.0-20200424083944-0422dc17083e/go.mod h1:VnbEzX8SAaRj7Yfl836NykdQIlbEjfL6/CD+AaJQg5Q= -github.com/openshift/api v0.0.0-20200429152225-b98a784d8e6d h1:E6wmob+4UwmxOWf/yA26q2PTBtLdAjo/aY5gAUfGS+E= -github.com/openshift/api v0.0.0-20200429152225-b98a784d8e6d/go.mod h1:VnbEzX8SAaRj7Yfl836NykdQIlbEjfL6/CD+AaJQg5Q= -github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160 h1:V4E6yt4XWiBEPKnJbs/E8pgUq9AjZqzQfsL3eeT84Qs= -github.com/openshift/build-machinery-go v0.0.0-20200211121458-5e3d6e570160/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= +github.com/openshift/api v0.0.0-20200521101457-60c476765272 h1:tYm+7yLrK+Ivi5hESBUTIt8+4ejbVux9kdscALiu57g= +github.com/openshift/api v0.0.0-20200521101457-60c476765272/go.mod h1:TkhafijfTiRi1Q3120/ZSE4oIWKQ4DGRh3byPywv4Mw= github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc h1:Bu1p7+ItPqhJhmMve7sVluKCYV+o+x1Ede0WY2/BI8Q= github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc= -github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0/go.mod h1:uUQ4LClRO+fg5MF/P6QxjMCb1C9f7Oh4RKepftDnEJE= -github.com/openshift/client-go v0.0.0-20200422192633-6f6c07fc2a70 h1:LvJxSt/lnLTBbKJC5DfWM9ZvT9Jk0nQa+xPozSJtkqc= -github.com/openshift/client-go v0.0.0-20200422192633-6f6c07fc2a70/go.mod h1:HeCrq1LSOBgHAUpINH4IgBLkt2U/NBwE5sq4JJgcl2Y= -github.com/openshift/library-go v0.0.0-20200429122220-9e6c27e916a0 h1:PWdXDFk+4DLTdoEc4G5v5YWvDaI098/Wvo6Y551A4sk= -github.com/openshift/library-go v0.0.0-20200429122220-9e6c27e916a0/go.mod h1:2kWwXTkpoQJUN3jZ3QW88EIY1hdRMqxgRs2hheEW/pg= +github.com/openshift/client-go v0.0.0-20200521150516-05eb9880269c h1:l7CmbzzkyWl4Y6qHmy6m4FvbH4iLnIXGrXqOfE5IFNA= +github.com/openshift/client-go v0.0.0-20200521150516-05eb9880269c/go.mod h1:kCMeo6IE4o4qvnepM9lgHQ4j/ZFfvY/N/2G/jpJdwm4= +github.com/openshift/kubernetes-apiserver v0.0.0-20201209103546-c5587e940bd4 h1:SEVqzz07YLCtncVyWxGptdrzG3dT3BNdMCRXhAeiwR0= +github.com/openshift/kubernetes-apiserver v0.0.0-20201209103546-c5587e940bd4/go.mod h1:vXQzMtUCLsGg1Bh+7Jo2mZKHpHZFCZn8eTNSepcIA1M= +github.com/openshift/kubernetes-client-go v0.0.0-20201209131240-ad062a7baf0b h1:YD4CN65MUXEMp/g32czgGH9w5aN0cqj+HR/bGoLWfCg= +github.com/openshift/kubernetes-client-go v0.0.0-20201209131240-ad062a7baf0b/go.mod h1:UjkEetDmr40P9NX0Ok3Idt08FCf2I4mIHgjFsot77uY= +github.com/openshift/library-go v0.0.0-20201209131625-07b0830b8740 h1:i2j/J5wuy9Rd08xzGjbbIBFYc8I81RQLotdWpr3cZ9o= +github.com/openshift/library-go v0.0.0-20201209131625-07b0830b8740/go.mod h1:lz1w1fHp/mRdMZYJt2khpWudZrb6QP7PmKcBQL6DPmg= github.com/openshift/osin v1.0.1-0.20180202150137-2dc1b4316769 h1:jTaxjdXcD89WEuNXU/R+ByAMhcI3ETa6Y/o4V9v0s9Q= github.com/openshift/osin v1.0.1-0.20180202150137-2dc1b4316769/go.mod h1:/gGuqQHvGNST0GB+Pomi3398FTdcM+9UaXafpqHvfDM= github.com/openshift/osincli v0.0.0-20160924135400-fababb0555f2 h1:9oADVMmPa4G60MQtoSjD26aD/vZreqbIAfiUiO220eY= @@ -562,30 +561,15 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= k8s.io/api v0.18.9 h1:7VDtivqwbvLOf8hmXSd/PDSSbpCBq49MELg84EYBYiQ= k8s.io/api v0.18.9/go.mod h1:9u/h6sUh6FxfErv7QqetX1EB3yBMIYOBXzdcf0Gf0rc= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apiextensions-apiserver v0.18.3/go.mod h1:TMsNGs7DYpMXd+8MOCX8KzPOCx8fnZMoIGB24m03+JE= +k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.9 h1:3ZABKQx3F3xPWlsGhCfUl8W+JXRRblV6Wo2A3zn0pvY= k8s.io/apimachinery v0.18.9/go.mod h1:PF5taHbXgTEJLU+xMypMmYTXTWPJ5LaW8bfsisxnEXk= -k8s.io/apiserver v0.18.2 h1:fwKxdTWwwYhxvtjo0UUfX+/fsitsNtfErPNegH2x9ic= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= -k8s.io/apiserver v0.18.9 h1:ziivCosB28MltECzhDBLLVKW01zCghl/lXke3B62y8g= -k8s.io/apiserver v0.18.9/go.mod h1:vXQzMtUCLsGg1Bh+7Jo2mZKHpHZFCZn8eTNSepcIA1M= -k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= -k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/client-go v0.18.9 h1:sPHX49yOtUqv1fl49TwV3f8cC0N3etSnwgFGsIsXnZc= -k8s.io/client-go v0.18.9/go.mod h1:UjkEetDmr40P9NX0Ok3Idt08FCf2I4mIHgjFsot77uY= -k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= -k8s.io/component-base v0.18.2 h1:SJweNZAGcUvsypLGNPNGeJ9UgPZQ6+bW+gEHe8uyh/Y= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= +k8s.io/component-base v0.18.3/go.mod h1:bp5GzGR0aGkYEfTj+eTY0AN/vXTgkJdQXjNTTVUaa3k= k8s.io/component-base v0.18.9 h1:7G0D/PUKrVxyUxjT5HV4aTqYqhPj60erA1ab1JUw7m8= k8s.io/component-base v0.18.9/go.mod h1:tUo4qZtV8m7t/U+0DgY+fcnn4BFZ480fZdzxOkWH4zk= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -594,9 +578,7 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-aggregator v0.18.2/go.mod h1:ijq6FnNUoKinA6kKbkN6svdTacSoQVNtKqmQ1+XJEYQ= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-aggregator v0.18.3/go.mod h1:fux0WabUOggW2yAACL4jQGVd6kv7mSgBnJ3GgCXCris= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml index ebabc9009..398292f0d 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_apiserver.crd.yaml @@ -40,6 +40,7 @@ spec: metadata: type: object spec: + description: spec holds user settable values for configuration type: object properties: additionalCORSAllowedOrigins: @@ -226,4 +227,6 @@ spec: - Modern - Custom status: + description: status holds observed values from the cluster. They may not + be overridden. type: object diff --git a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml index 7b2e45a5e..3bca0f60c 100644 --- a/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml +++ b/vendor/github.com/openshift/api/config/v1/0000_10_config-operator_01_oauth.crd.yaml @@ -40,7 +40,7 @@ spec: metadata: type: object spec: - description: OAuthSpec contains desired cluster auth configuration + description: spec holds user settable values for configuration type: object properties: identityProviders: @@ -647,6 +647,6 @@ spec: type: integer format: int32 status: - description: OAuthStatus shows current known state of OAuth server in the - cluster + description: status holds observed values from the cluster. They may not + be overridden. type: object diff --git a/vendor/github.com/openshift/api/config/v1/types_apiserver.go b/vendor/github.com/openshift/api/config/v1/types_apiserver.go index b347bd80e..2fffa794b 100644 --- a/vendor/github.com/openshift/api/config/v1/types_apiserver.go +++ b/vendor/github.com/openshift/api/config/v1/types_apiserver.go @@ -14,9 +14,11 @@ import ( type APIServer struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` + // spec holds user settable values for configuration // +kubebuilder:validation:Required // +required Spec APIServerSpec `json:"spec"` + // status holds observed values from the cluster. They may not be overridden. // +optional Status APIServerStatus `json:"status"` } diff --git a/vendor/github.com/openshift/api/config/v1/types_oauth.go b/vendor/github.com/openshift/api/config/v1/types_oauth.go index 3ca73930b..a3fb7ac87 100644 --- a/vendor/github.com/openshift/api/config/v1/types_oauth.go +++ b/vendor/github.com/openshift/api/config/v1/types_oauth.go @@ -14,10 +14,11 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" type OAuth struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` - + // spec holds user settable values for configuration // +kubebuilder:validation:Required // +required Spec OAuthSpec `json:"spec"` + // status holds observed values from the cluster. They may not be overridden. // +optional Status OAuthStatus `json:"status"` } diff --git a/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go index 21c53d589..5e8eca32c 100644 --- a/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go +++ b/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go @@ -244,7 +244,9 @@ func (StringSourceSpec) SwaggerDoc() map[string]string { } var map_APIServer = map[string]string{ - "": "APIServer holds configuration (like serving certificates, client CA and CORS domains) shared by all API servers in the system, among them especially kube-apiserver and openshift-apiserver. The canonical name of an instance is 'cluster'.", + "": "APIServer holds configuration (like serving certificates, client CA and CORS domains) shared by all API servers in the system, among them especially kube-apiserver and openshift-apiserver. The canonical name of an instance is 'cluster'.", + "spec": "spec holds user settable values for configuration", + "status": "status holds observed values from the cluster. They may not be overridden.", } func (APIServer) SwaggerDoc() map[string]string { @@ -1114,7 +1116,9 @@ func (LDAPIdentityProvider) SwaggerDoc() map[string]string { } var map_OAuth = map[string]string{ - "": "OAuth holds cluster-wide information about OAuth. The canonical name is `cluster`. It is used to configure the integrated OAuth server. This configuration is only honored when the top level Authentication config has type set to IntegratedOAuth.", + "": "OAuth holds cluster-wide information about OAuth. The canonical name is `cluster`. It is used to configure the integrated OAuth server. This configuration is only honored when the top level Authentication config has type set to IntegratedOAuth.", + "spec": "spec holds user settable values for configuration", + "status": "status holds observed values from the cluster. They may not be overridden.", } func (OAuth) SwaggerDoc() map[string]string { diff --git a/vendor/github.com/openshift/library-go/pkg/config/client/client_config.go b/vendor/github.com/openshift/library-go/pkg/config/client/client_config.go index a24731105..e2b90ca53 100644 --- a/vendor/github.com/openshift/library-go/pkg/config/client/client_config.go +++ b/vendor/github.com/openshift/library-go/pkg/config/client/client_config.go @@ -2,14 +2,12 @@ package client import ( "io/ioutil" - "net" - "net/http" - "time" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "net/http" configv1 "github.com/openshift/api/config/v1" + "github.com/openshift/library-go/pkg/network" ) // GetKubeConfigOrInClusterConfig loads in-cluster config if kubeConfigFile is empty or the file if not, @@ -101,10 +99,7 @@ func (c ClientTransportOverrides) DefaultClientTransport(rt http.RoundTripper) h return rt } - transport.DialContext = (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext + transport.DialContext = network.DefaultClientDialContext() // Hold open more internal idle connections transport.MaxIdleConnsPerHost = 100 diff --git a/vendor/github.com/openshift/library-go/pkg/network/dialer.go b/vendor/github.com/openshift/library-go/pkg/network/dialer.go new file mode 100644 index 000000000..f19be44a3 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/network/dialer.go @@ -0,0 +1,13 @@ +package network + +import ( + "context" + "net" +) + +type DialContext func(ctx context.Context, network, address string) (net.Conn, error) + +// DefaultDialContext returns a DialContext function from a network dialer with default options sets. +func DefaultClientDialContext() DialContext { + return dialerWithDefaultOptions() +} diff --git a/vendor/github.com/openshift/library-go/pkg/network/dialer_linux.go b/vendor/github.com/openshift/library-go/pkg/network/dialer_linux.go new file mode 100644 index 000000000..b8ff8db85 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/network/dialer_linux.go @@ -0,0 +1,93 @@ +// +build linux + +package network + +import ( + "net" + "os" + "syscall" + "time" + + "golang.org/x/sys/unix" + + utilerrors "k8s.io/apimachinery/pkg/util/errors" +) + +func dialerWithDefaultOptions() DialContext { + nd := &net.Dialer{ + // TCP_USER_TIMEOUT does affect the behaviour of connect() which is controlled by this field so we set it to the same value + Timeout: 25 * time.Second, + // KeepAlive must to be set to a negative value to stop std library from applying the default values + // by doing so we ensure that the options we are interested in won't be overwritten + KeepAlive: time.Duration(-1), + Control: func(network, address string, con syscall.RawConn) error { + var errs []error + err := con.Control(func(fd uintptr) { + optionsErr := setDefaultSocketOptions(int(fd)) + if optionsErr != nil { + errs = append(errs, optionsErr) + } + }) + if err != nil { + errs = append(errs, err) + } + return utilerrors.NewAggregate(errs) + }, + } + return nd.DialContext +} + +// setDefaultSocketOptions sets custom socket options so that we can detect connections to an unhealthy (dead) peer quickly. +// In particular we set TCP_USER_TIMEOUT that specifies the maximum amount of time that transmitted data may remain +// unacknowledged before TCP will forcibly close the connection. +// +// Note +// TCP_USER_TIMEOUT can't be too low because a single dropped packet might drop the entire connection. +// Ideally it should be set to: TCP_KEEPIDLE + TCP_KEEPINTVL * TCP_KEEPCNT +func setDefaultSocketOptions(fd int) error { + // specifies the maximum amount of time in milliseconds that transmitted data may remain + // unacknowledged before TCP will forcibly close the corresponding connection and return ETIMEDOUT to the application + tcpUserTimeoutInMilliSeconds := int(25 * time.Second / time.Millisecond) + + // specifies the interval at which probes are sent in seconds + tcpKeepIntvl := int(roundDuration(5*time.Second, time.Second)) + + // specifies the threshold for sending the first KEEP ALIVE probe in seconds + tcpKeepIdle := int(roundDuration(2*time.Second, time.Second)) + + // enable keep-alive probes + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil { + return wrapSyscallError("setsockopt", err) + } + + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, tcpUserTimeoutInMilliSeconds); err != nil { + return wrapSyscallError("setsockopt", err) + } + + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, tcpKeepIntvl); err != nil { + return wrapSyscallError("setsockopt", err) + } + + if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, tcpKeepIdle); err != nil { + return wrapSyscallError("setsockopt", err) + } + return nil +} + +// roundDurationUp rounds d to the next multiple of to. +// +// note that it was copied from the std library +func roundDuration(d time.Duration, to time.Duration) time.Duration { + return (d + to - 1) / to +} + +// wrapSyscallError takes an error and a syscall name. If the error is +// a syscall.Errno, it wraps it in a os.SyscallError using the syscall name. +// +// note that it was copied from the std library +func wrapSyscallError(name string, err error) error { + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError(name, err) + } + return err +} diff --git a/vendor/github.com/openshift/library-go/pkg/network/dialer_others.go b/vendor/github.com/openshift/library-go/pkg/network/dialer_others.go new file mode 100644 index 000000000..75f008454 --- /dev/null +++ b/vendor/github.com/openshift/library-go/pkg/network/dialer_others.go @@ -0,0 +1,19 @@ +// +build !linux + +package network + +import ( + "net" + "time" + + "k8s.io/klog" +) + +func dialerWithDefaultOptions() DialContext { + klog.V(2).Info("Creating the default network Dialer (unsupported platform). It may take up to 15 minutes to detect broken connections and establish a new one") + nd := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + } + return nd.DialContext +} diff --git a/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go b/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go index 779ab425d..7965836d3 100644 --- a/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go +++ b/vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go @@ -224,7 +224,13 @@ func (l *Lifecycle) ValidateInitialization() error { // accessReviewResources are resources which give a view into permissions in a namespace. Users must be allowed to create these // resources because returning "not found" errors allows someone to search for the "people I'm going to fire in 2017" namespace. var accessReviewResources = map[schema.GroupResource]bool{ - {Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: true, + {Group: "authorization.k8s.io", Resource: "localsubjectaccessreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "subjectaccessreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "localsubjectaccessreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "resourceaccessreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "localresourceaccessreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "selfsubjectrulesreviews"}: true, + schema.GroupResource{Group: "authorization.openshift.io", Resource: "subjectrulesreviews"}: true, } func isAccessReview(a admission.Attributes) bool { diff --git a/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader_controller.go b/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader_controller.go new file mode 100644 index 000000000..9e8bc8b21 --- /dev/null +++ b/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader_controller.go @@ -0,0 +1,337 @@ +/* +Copyright 2020 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 headerrequest + +import ( + "context" + "encoding/json" + "fmt" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + coreinformers "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" + corev1listers "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + "sync/atomic" +) + +const ( + authenticationRoleName = "extension-apiserver-authentication-reader" +) + +// RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct +type RequestHeaderAuthRequestProvider interface { + UsernameHeaders() []string + GroupHeaders() []string + ExtraHeaderPrefixes() []string + AllowedClientNames() []string +} + +var _ RequestHeaderAuthRequestProvider = &RequestHeaderAuthRequestController{} + +type requestHeaderBundle struct { + UsernameHeaders []string + GroupHeaders []string + ExtraHeaderPrefixes []string + AllowedClientNames []string +} + +// RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling parts of RequestHeaderConfig struct. +// The methods are sourced from the config map which is being monitored by this controller. +// The controller is primed from the server at the construction time for components that don't want to dynamically react to changes +// in the config map. +type RequestHeaderAuthRequestController struct { + name string + + configmapName string + configmapNamespace string + + client kubernetes.Interface + configmapLister corev1listers.ConfigMapNamespaceLister + configmapInformer cache.SharedIndexInformer + configmapInformerSynced cache.InformerSynced + + queue workqueue.RateLimitingInterface + + // exportedRequestHeaderBundle is a requestHeaderBundle that contains the last read, non-zero length content of the configmap + exportedRequestHeaderBundle atomic.Value + + usernameHeadersKey string + groupHeadersKey string + extraHeaderPrefixesKey string + allowedClientNamesKey string +} + +// NewRequestHeaderAuthRequestController creates a new controller that implements RequestHeaderAuthRequestController +func NewRequestHeaderAuthRequestController( + cmName string, + cmNamespace string, + client kubernetes.Interface, + usernameHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) *RequestHeaderAuthRequestController { + c := &RequestHeaderAuthRequestController{ + name: "RequestHeaderAuthRequestController", + + client: client, + + configmapName: cmName, + configmapNamespace: cmNamespace, + + usernameHeadersKey: usernameHeadersKey, + groupHeadersKey: groupHeadersKey, + extraHeaderPrefixesKey: extraHeaderPrefixesKey, + allowedClientNamesKey: allowedClientNamesKey, + + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "RequestHeaderAuthRequestController"), + } + + // we construct our own informer because we need such a small subset of the information available. Just one namespace. + c.configmapInformer = coreinformers.NewFilteredConfigMapInformer(client, c.configmapNamespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *metav1.ListOptions) { + listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", c.configmapName).String() + }) + + c.configmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: func(obj interface{}) bool { + if cast, ok := obj.(*corev1.ConfigMap); ok { + return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace + } + if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok { + if cast, ok := tombstone.Obj.(*corev1.ConfigMap); ok { + return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace + } + } + return true // always return true just in case. The checks are fairly cheap + }, + Handler: cache.ResourceEventHandlerFuncs{ + // we have a filter, so any time we're called, we may as well queue. We only ever check one configmap + // so we don't have to be choosy about our key. + AddFunc: func(obj interface{}) { + c.queue.Add(c.keyFn()) + }, + UpdateFunc: func(oldObj, newObj interface{}) { + c.queue.Add(c.keyFn()) + }, + DeleteFunc: func(obj interface{}) { + c.queue.Add(c.keyFn()) + }, + }, + }) + + c.configmapLister = corev1listers.NewConfigMapLister(c.configmapInformer.GetIndexer()).ConfigMaps(c.configmapNamespace) + c.configmapInformerSynced = c.configmapInformer.HasSynced + + return c +} + +func (c *RequestHeaderAuthRequestController) UsernameHeaders() []string { + return c.loadRequestHeaderFor(c.usernameHeadersKey) +} + +func (c *RequestHeaderAuthRequestController) GroupHeaders() []string { + return c.loadRequestHeaderFor(c.groupHeadersKey) +} + +func (c *RequestHeaderAuthRequestController) ExtraHeaderPrefixes() []string { + return c.loadRequestHeaderFor(c.extraHeaderPrefixesKey) +} + +func (c *RequestHeaderAuthRequestController) AllowedClientNames() []string { + return c.loadRequestHeaderFor(c.allowedClientNamesKey) +} + +// Run starts RequestHeaderAuthRequestController controller and blocks until stopCh is closed. +func (c *RequestHeaderAuthRequestController) Run(workers int, stopCh <-chan struct{}) { + defer utilruntime.HandleCrash() + defer c.queue.ShutDown() + + klog.Infof("Starting %s", c.name) + defer klog.Infof("Shutting down %s", c.name) + + go c.configmapInformer.Run(stopCh) + + // wait for caches to fill before starting your work + if !cache.WaitForNamedCacheSync(c.name, stopCh, c.configmapInformerSynced) { + return + } + + // doesn't matter what workers say, only start one. + go wait.Until(c.runWorker, time.Second, stopCh) + + <-stopCh +} + +// // RunOnce runs a single sync loop +func (c *RequestHeaderAuthRequestController) RunOnce() error { + configMap, err := c.client.CoreV1().ConfigMaps(c.configmapNamespace).Get(context.TODO(), c.configmapName, metav1.GetOptions{}) + switch { + case errors.IsNotFound(err): + // ignore, authConfigMap is nil now + return nil + case errors.IsForbidden(err): + klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+ + "'kubectl create rolebinding -n %s ROLEBINDING_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'", + c.configmapName, c.configmapNamespace, c.configmapNamespace, authenticationRoleName) + return err + case err != nil: + return err + } + return c.syncConfigMap(configMap) +} + +func (c *RequestHeaderAuthRequestController) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *RequestHeaderAuthRequestController) processNextWorkItem() bool { + dsKey, quit := c.queue.Get() + if quit { + return false + } + defer c.queue.Done(dsKey) + + err := c.sync() + if err == nil { + c.queue.Forget(dsKey) + return true + } + + utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err)) + c.queue.AddRateLimited(dsKey) + + return true +} + +// sync reads the config and propagates the changes to exportedRequestHeaderBundle +// which is exposed by the set of methods that are used to fill RequestHeaderConfig struct +func (c *RequestHeaderAuthRequestController) sync() error { + configMap, err := c.configmapLister.Get(c.configmapName) + if err != nil { + return err + } + return c.syncConfigMap(configMap) +} + +func (c *RequestHeaderAuthRequestController) syncConfigMap(configMap *corev1.ConfigMap) error { + hasChanged, newRequestHeaderBundle, err := c.hasRequestHeaderBundleChanged(configMap) + if err != nil { + return err + } + if hasChanged { + c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle) + klog.V(2).Infof("Loaded a new request header values for %v", c.name) + } + return nil +} + +func (c *RequestHeaderAuthRequestController) hasRequestHeaderBundleChanged(cm *corev1.ConfigMap) (bool, *requestHeaderBundle, error) { + currentHeadersBundle, err := c.getRequestHeaderBundleFromConfigMap(cm) + if err != nil { + return false, nil, err + } + + rawHeaderBundle := c.exportedRequestHeaderBundle.Load() + if rawHeaderBundle == nil { + return true, currentHeadersBundle, nil + } + + // check to see if we have a change. If the values are the same, do nothing. + loadedHeadersBundle, ok := rawHeaderBundle.(*requestHeaderBundle) + if !ok { + return true, currentHeadersBundle, nil + } + + if !equality.Semantic.DeepEqual(loadedHeadersBundle, currentHeadersBundle) { + return true, currentHeadersBundle, nil + } + return false, nil, nil +} + +func (c *RequestHeaderAuthRequestController) getRequestHeaderBundleFromConfigMap(cm *corev1.ConfigMap) (*requestHeaderBundle, error) { + usernameHeaderCurrentValue, err := deserializeStrings(cm.Data[c.usernameHeadersKey]) + if err != nil { + return nil, err + } + + groupHeadersCurrentValue, err := deserializeStrings(cm.Data[c.groupHeadersKey]) + if err != nil { + return nil, err + } + + extraHeaderPrefixesCurrentValue, err := deserializeStrings(cm.Data[c.extraHeaderPrefixesKey]) + if err != nil { + return nil, err + + } + + allowedClientNamesCurrentValue, err := deserializeStrings(cm.Data[c.allowedClientNamesKey]) + if err != nil { + return nil, err + } + + return &requestHeaderBundle{ + UsernameHeaders: usernameHeaderCurrentValue, + GroupHeaders: groupHeadersCurrentValue, + ExtraHeaderPrefixes: extraHeaderPrefixesCurrentValue, + AllowedClientNames: allowedClientNamesCurrentValue, + }, nil +} + +func (c *RequestHeaderAuthRequestController) loadRequestHeaderFor(key string) []string { + rawHeaderBundle := c.exportedRequestHeaderBundle.Load() + if rawHeaderBundle == nil { + return nil // this can happen if we've been unable load data from the apiserver for some reason + } + headerBundle := rawHeaderBundle.(*requestHeaderBundle) + + switch key { + case c.usernameHeadersKey: + return headerBundle.UsernameHeaders + case c.groupHeadersKey: + return headerBundle.GroupHeaders + case c.extraHeaderPrefixesKey: + return headerBundle.ExtraHeaderPrefixes + case c.allowedClientNamesKey: + return headerBundle.AllowedClientNames + default: + return nil + } +} + +func (c *RequestHeaderAuthRequestController) keyFn() string { + // this format matches DeletionHandlingMetaNamespaceKeyFunc for our single key + return c.configmapNamespace + "/" + c.configmapName +} + +func deserializeStrings(in string) ([]string, error) { + if len(in) == 0 { + return nil, nil + } + var ret []string + if err := json.Unmarshal([]byte(in), &ret); err != nil { + return nil, err + } + return ret, nil +} diff --git a/vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go b/vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go index 6f1a836e3..9b79b74f5 100644 --- a/vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go +++ b/vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go @@ -159,6 +159,17 @@ var ( }, []string{"verb", "group", "version", "resource", "subresource", "scope", "component", "code"}, ) + + // requestAbortsTotal is a number of aborted requests with http.ErrAbortHandler + requestAbortsTotal = compbasemetrics.NewCounterVec( + &compbasemetrics.CounterOpts{ + Name: "apiserver_request_aborts_total", + Help: "Number of requests which apiserver aborted possibly due to a timeout, for each group, version, verb, resource, subresource and scope", + StabilityLevel: compbasemetrics.ALPHA, + }, + []string{"verb", "group", "version", "resource", "subresource", "scope"}, + ) + kubectlExeRegexp = regexp.MustCompile(`^.*((?i:kubectl\.exe))`) metrics = []resettableCollector{ @@ -172,6 +183,7 @@ var ( WatchEventsSizes, currentInflightRequests, requestTerminationsTotal, + requestAbortsTotal, } // these are the known (e.g. whitelisted/known) content types which we will report for @@ -236,6 +248,22 @@ func UpdateInflightRequestMetrics(nonmutating, mutating int) { currentInflightRequests.WithLabelValues(MutatingKind).Set(float64(mutating)) } +// RecordRequestAbort records that the request was aborted possibly due to a timeout. +func RecordRequestAbort(req *http.Request, requestInfo *request.RequestInfo) { + if requestInfo == nil { + requestInfo = &request.RequestInfo{Verb: req.Method, Path: req.URL.Path} + } + + scope := CleanScope(requestInfo) + reportedVerb := cleanVerb(canonicalVerb(strings.ToUpper(req.Method), scope), req) + resource := requestInfo.Resource + subresource := requestInfo.Subresource + group := requestInfo.APIGroup + version := requestInfo.APIVersion + + requestAbortsTotal.WithLabelValues(reportedVerb, group, version, resource, subresource, scope).Inc() +} + // RecordRequestTermination records that the request was terminated early as part of a resource // preservation or apiserver self-defense mechanism (e.g. timeouts, maxinflight throttling, // proxyHandler errors). RecordRequestTermination should only be called zero or one times diff --git a/vendor/k8s.io/apiserver/pkg/server/config.go b/vendor/k8s.io/apiserver/pkg/server/config.go index 00efd0cc9..1e301301e 100644 --- a/vendor/k8s.io/apiserver/pkg/server/config.go +++ b/vendor/k8s.io/apiserver/pkg/server/config.go @@ -688,7 +688,7 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler { handler = genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance) } handler = genericapifilters.WithCacheControl(handler) - handler = genericfilters.WithPanicRecovery(handler) + handler = genericfilters.WithPanicRecovery(handler, c.RequestInfoResolver) return handler } diff --git a/vendor/k8s.io/apiserver/pkg/server/filters/timeout.go b/vendor/k8s.io/apiserver/pkg/server/filters/timeout.go index 9b8d6d4b1..2405bfd1f 100644 --- a/vendor/k8s.io/apiserver/pkg/server/filters/timeout.go +++ b/vendor/k8s.io/apiserver/pkg/server/filters/timeout.go @@ -33,8 +33,6 @@ import ( apirequest "k8s.io/apiserver/pkg/endpoints/request" ) -var errConnKilled = fmt.Errorf("killing connection/stream because serving request timed out and response had been started") - // WithTimeoutForNonLongRunningRequests times out non-long-running requests after the time given by timeout. func WithTimeoutForNonLongRunningRequests(handler http.Handler, longRunning apirequest.LongRunningRequestCheck, timeout time.Duration) http.Handler { if longRunning == nil { @@ -246,15 +244,17 @@ func (tw *baseTimeoutWriter) timeout(err *apierrors.StatusError) { // no way to timeout the HTTP request at the point. We have to shutdown // the connection for HTTP1 or reset stream for HTTP2. // - // Note from: Brad Fitzpatrick - // if the ServeHTTP goroutine panics, that will do the best possible thing for both - // HTTP/1 and HTTP/2. In HTTP/1, assuming you're replying with at least HTTP/1.1 and - // you've already flushed the headers so it's using HTTP chunking, it'll kill the TCP - // connection immediately without a proper 0-byte EOF chunk, so the peer will recognize - // the response as bogus. In HTTP/2 the server will just RST_STREAM the stream, leaving - // the TCP connection open, but resetting the stream to the peer so it'll have an error, - // like the HTTP/1 case. - panic(errConnKilled) + // Note from the golang's docs: + // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes + // that the effect of the panic was isolated to the active request. + // It recovers the panic, logs a stack trace to the server error log, + // and either closes the network connection or sends an HTTP/2 + // RST_STREAM, depending on the HTTP protocol. To abort a handler so + // the client sees an interrupted response but the server doesn't log + // an error, panic with the value ErrAbortHandler. + // + // We are throwing http.ErrAbortHandler deliberately so that a client is notified and to suppress a not helpful stacktrace in the logs + panic(http.ErrAbortHandler) } } diff --git a/vendor/k8s.io/apiserver/pkg/server/filters/wrap.go b/vendor/k8s.io/apiserver/pkg/server/filters/wrap.go index 96bebdbd6..ca71a40e0 100644 --- a/vendor/k8s.io/apiserver/pkg/server/filters/wrap.go +++ b/vendor/k8s.io/apiserver/pkg/server/filters/wrap.go @@ -17,22 +17,41 @@ limitations under the License. package filters import ( + "fmt" "net/http" - "k8s.io/klog" - "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apiserver/pkg/endpoints/metrics" + "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/server/httplog" + "k8s.io/klog" ) // WithPanicRecovery wraps an http Handler to recover and log panics (except in the special case of http.ErrAbortHandler panics, which suppress logging). -func WithPanicRecovery(handler http.Handler) http.Handler { +func WithPanicRecovery(handler http.Handler, resolver request.RequestInfoResolver) http.Handler { return withPanicRecovery(handler, func(w http.ResponseWriter, req *http.Request, err interface{}) { if err == http.ErrAbortHandler { - // honor the http.ErrAbortHandler sentinel panic value: - // ErrAbortHandler is a sentinel panic value to abort a handler. - // While any panic from ServeHTTP aborts the response to the client, - // panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log. + // Honor the http.ErrAbortHandler sentinel panic value + // + // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes + // that the effect of the panic was isolated to the active request. + // It recovers the panic, logs a stack trace to the server error log, + // and either closes the network connection or sends an HTTP/2 + // RST_STREAM, depending on the HTTP protocol. To abort a handler so + // the client sees an interrupted response but the server doesn't log + // an error, panic with the value ErrAbortHandler. + // + // Note that the ReallyCrash variable controls the behaviour of the HandleCrash function + // So it might actually crash, after calling the handlers + if info, err := resolver.NewRequestInfo(req); err != nil { + metrics.RecordRequestAbort(req, nil) + } else { + metrics.RecordRequestAbort(req, info) + } + // This call can have different handlers, but the default chain rate limits. Call it after the metrics are updated + // in case the rate limit delays it. If you outrun the rate for this one timed out requests, something has gone + // seriously wrong with your server, but generally having a logging signal for timeouts is useful. + runtime.HandleError(fmt.Errorf("timeout or abort while handling: %v %q", req.Method, req.URL.Path)) return } http.Error(w, "This request caused apiserver to panic. Look in the logs for details.", http.StatusInternalServerError) diff --git a/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go b/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go index 9c623f648..ca3fbd9d5 100644 --- a/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go +++ b/vendor/k8s.io/apiserver/pkg/server/genericapiserver.go @@ -330,7 +330,7 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error { }() // close socket after delayed stopCh - err := s.NonBlockingRun(delayedStopCh) + stoppedCh, err := s.NonBlockingRun(delayedStopCh) if err != nil { return err } @@ -345,6 +345,8 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error { // wait for the delayed stopCh before closing the handler chain (it rejects everything after Wait has been called). <-delayedStopCh + // wait for stoppedCh that is closed when the graceful termination (server.Shutdown) is finished. + <-stoppedCh // Wait for all requests to finish, which are bounded by the RequestTimeout variable. s.HandlerChainWaitGroup.Wait() @@ -354,7 +356,8 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error { // NonBlockingRun spawns the secure http server. An error is // returned if the secure port cannot be listened on. -func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error { +// The returned channel is closed when the (asynchronous) termination is finished. +func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) (<-chan struct{}, error) { // Use an stop channel to allow graceful shutdown without dropping audit events // after http server shutdown. auditStopCh := make(chan struct{}) @@ -363,7 +366,7 @@ func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error { // before http server start serving. Otherwise the Backend.ProcessEvents call might block. if s.AuditBackend != nil { if err := s.AuditBackend.Run(auditStopCh); err != nil { - return fmt.Errorf("failed to run the audit backend: %v", err) + return nil, fmt.Errorf("failed to run the audit backend: %v", err) } } @@ -376,7 +379,7 @@ func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error { if err != nil { close(internalStopCh) close(auditStopCh) - return err + return nil, err } } @@ -399,7 +402,7 @@ func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error { klog.Errorf("Unable to send systemd daemon successful start message: %v\n", err) } - return nil + return stoppedCh, nil } // installAPIResources is a private method for installing the REST storage backing each api groupversionresource diff --git a/vendor/k8s.io/apiserver/pkg/server/options/authentication.go b/vendor/k8s.io/apiserver/pkg/server/options/authentication.go index 9a395c94a..66f7d306c 100644 --- a/vendor/k8s.io/apiserver/pkg/server/options/authentication.go +++ b/vendor/k8s.io/apiserver/pkg/server/options/authentication.go @@ -17,8 +17,6 @@ limitations under the License. package options import ( - "context" - "encoding/json" "fmt" "strings" "time" @@ -27,7 +25,6 @@ import ( "github.com/spf13/pflag" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/authentication/authenticatorfactory" "k8s.io/apiserver/pkg/authentication/request/headerrequest" @@ -180,6 +177,10 @@ type DelegatingAuthenticationOptions struct { // TolerateInClusterLookupFailure indicates failures to look up authentication configuration from the cluster configmap should not be fatal. // Setting this can result in an authenticator that will reject all requests. TolerateInClusterLookupFailure bool + + // ClientTimeout specifies a time limit for requests made by the authorization webhook client. + // The default value is set to 10 seconds. + ClientTimeout time.Duration } func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { @@ -192,9 +193,15 @@ func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { GroupHeaders: []string{"x-remote-group"}, ExtraHeaderPrefixes: []string{"x-remote-extra-"}, }, + ClientTimeout: 10 * time.Second, } } +// WithClientTimeout sets the given timeout for the authentication webhook client. +func (s *DelegatingAuthenticationOptions) WithClientTimeout(timeout time.Duration) { + s.ClientTimeout = timeout +} + func (s *DelegatingAuthenticationOptions) Validate() []error { allErrors := []error{} allErrors = append(allErrors, s.RequestHeader.Validate()...) @@ -331,66 +338,28 @@ const ( // by --requestheader-username-headers. This is created in the cluster by the kube-apiserver. // "WARNING: generally do not depend on authorization being already done for incoming requests.") authenticationConfigMapName = "extension-apiserver-authentication" - authenticationRoleName = "extension-apiserver-authentication-reader" ) func (s *DelegatingAuthenticationOptions) createRequestHeaderConfig(client kubernetes.Interface) (*authenticatorfactory.RequestHeaderConfig, error) { - requestHeaderCAProvider, err := dynamiccertificates.NewDynamicCAFromConfigMapController("client-ca", authenticationConfigMapNamespace, authenticationConfigMapName, "requestheader-client-ca-file", client) + dynamicRequestHeaderProvider, err := newDynamicRequestHeaderController(client) if err != nil { return nil, fmt.Errorf("unable to create request header authentication config: %v", err) } - authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(context.TODO(), authenticationConfigMapName, metav1.GetOptions{}) - switch { - case errors.IsNotFound(err): - // ignore, authConfigMap is nil now - return nil, nil - case errors.IsForbidden(err): - klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+ - "'kubectl create rolebinding -n %s ROLEBINDING_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'", - authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName) - return nil, err - case err != nil: - return nil, err - } - - usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"]) - if err != nil { - return nil, err - } - groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"]) - if err != nil { - return nil, err - } - extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"]) - if err != nil { - return nil, err - } - allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"]) - if err != nil { + // look up authentication configuration in the cluster and in case of an err defer to authentication-tolerate-lookup-failure flag + if err := dynamicRequestHeaderProvider.RunOnce(); err != nil { return nil, err } return &authenticatorfactory.RequestHeaderConfig{ - CAContentProvider: requestHeaderCAProvider, - UsernameHeaders: headerrequest.StaticStringSlice(usernameHeaders), - GroupHeaders: headerrequest.StaticStringSlice(groupHeaders), - ExtraHeaderPrefixes: headerrequest.StaticStringSlice(extraHeaderPrefixes), - AllowedClientNames: headerrequest.StaticStringSlice(allowedNames), + CAContentProvider: dynamicRequestHeaderProvider, + UsernameHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.UsernameHeaders)), + GroupHeaders: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.GroupHeaders)), + ExtraHeaderPrefixes: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.ExtraHeaderPrefixes)), + AllowedClientNames: headerrequest.StringSliceProvider(headerrequest.StringSliceProviderFunc(dynamicRequestHeaderProvider.AllowedClientNames)), }, nil } -func deserializeStrings(in string) ([]string, error) { - if len(in) == 0 { - return nil, nil - } - var ret []string - if err := json.Unmarshal([]byte(in), &ret); err != nil { - return nil, err - } - return ret, nil -} - // getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned // if no kubeconfig is specified by the user and the in-cluster config is not found. func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) { @@ -419,6 +388,7 @@ func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, err // set high qps/burst limits since this will effectively limit API server responsiveness clientConfig.QPS = 200 clientConfig.Burst = 400 + clientConfig.Timeout = s.ClientTimeout return kubernetes.NewForConfig(clientConfig) } diff --git a/vendor/k8s.io/apiserver/pkg/server/options/authentication_dynamic_request_header.go b/vendor/k8s.io/apiserver/pkg/server/options/authentication_dynamic_request_header.go new file mode 100644 index 000000000..5c558b06d --- /dev/null +++ b/vendor/k8s.io/apiserver/pkg/server/options/authentication_dynamic_request_header.go @@ -0,0 +1,79 @@ +/* +Copyright 2020 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 options + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/request/headerrequest" + "k8s.io/apiserver/pkg/server/dynamiccertificates" + "k8s.io/client-go/kubernetes" +) + +var _ dynamiccertificates.ControllerRunner = &DynamicRequestHeaderController{} +var _ dynamiccertificates.Notifier = &DynamicRequestHeaderController{} +var _ dynamiccertificates.CAContentProvider = &DynamicRequestHeaderController{} + +var _ headerrequest.RequestHeaderAuthRequestProvider = &DynamicRequestHeaderController{} + +// DynamicRequestHeaderController combines DynamicCAFromConfigMapController and RequestHeaderAuthRequestController +// into one controller for dynamically filling RequestHeaderConfig struct +type DynamicRequestHeaderController struct { + *dynamiccertificates.ConfigMapCAController + *headerrequest.RequestHeaderAuthRequestController +} + +// newDynamicRequestHeaderController creates a new controller that implements DynamicRequestHeaderController +func newDynamicRequestHeaderController(client kubernetes.Interface) (*DynamicRequestHeaderController, error) { + requestHeaderCAController, err := dynamiccertificates.NewDynamicCAFromConfigMapController( + "client-ca", + authenticationConfigMapNamespace, + authenticationConfigMapName, + "requestheader-client-ca-file", + client) + if err != nil { + return nil, fmt.Errorf("unable to create DynamicCAFromConfigMap controller: %v", err) + } + + requestHeaderAuthRequestController := headerrequest.NewRequestHeaderAuthRequestController( + authenticationConfigMapName, + authenticationConfigMapNamespace, + client, + "requestheader-username-headers", + "requestheader-group-headers", + "requestheader-extra-headers-prefix", + "requestheader-allowed-names", + ) + return &DynamicRequestHeaderController{ + ConfigMapCAController: requestHeaderCAController, + RequestHeaderAuthRequestController: requestHeaderAuthRequestController, + }, nil +} + +func (c *DynamicRequestHeaderController) RunOnce() error { + errs := []error{} + errs = append(errs, c.ConfigMapCAController.RunOnce()) + errs = append(errs, c.RequestHeaderAuthRequestController.RunOnce()) + return errors.NewAggregate(errs) +} + +func (c *DynamicRequestHeaderController) Run(workers int, stopCh <-chan struct{}) { + go c.ConfigMapCAController.Run(workers, stopCh) + go c.RequestHeaderAuthRequestController.Run(workers, stopCh) + <-stopCh +} diff --git a/vendor/k8s.io/apiserver/pkg/server/options/authorization.go b/vendor/k8s.io/apiserver/pkg/server/options/authorization.go index 7284c261f..263bc1412 100644 --- a/vendor/k8s.io/apiserver/pkg/server/options/authorization.go +++ b/vendor/k8s.io/apiserver/pkg/server/options/authorization.go @@ -59,6 +59,10 @@ type DelegatingAuthorizationOptions struct { // AlwaysAllowGroups are groups which are allowed to take any actions. In kube, this is system:masters. AlwaysAllowGroups []string + + // ClientTimeout specifies a time limit for requests made by SubjectAccessReviews client. + // The default value is set to 10 seconds. + ClientTimeout time.Duration } func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions { @@ -66,6 +70,7 @@ func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions { // very low for responsiveness, but high enough to handle storms AllowCacheTTL: 10 * time.Second, DenyCacheTTL: 10 * time.Second, + ClientTimeout: 10 * time.Second, } } @@ -81,6 +86,11 @@ func (s *DelegatingAuthorizationOptions) WithAlwaysAllowPaths(paths ...string) * return s } +// WithClientTimeout sets the given timeout for SAR client used by this authorizer +func (s *DelegatingAuthorizationOptions) WithClientTimeout(timeout time.Duration) { + s.ClientTimeout = timeout +} + func (s *DelegatingAuthorizationOptions) Validate() []error { allErrors := []error{} return allErrors @@ -186,6 +196,12 @@ func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, erro // set high qps/burst limits since this will effectively limit API server responsiveness clientConfig.QPS = 200 clientConfig.Burst = 400 + clientConfig.Timeout = s.ClientTimeout + + // make the client use protobuf + protoConfig := rest.CopyConfig(clientConfig) + protoConfig.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json" + protoConfig.ContentType = "application/vnd.kubernetes.protobuf" - return kubernetes.NewForConfig(clientConfig) + return kubernetes.NewForConfig(protoConfig) } diff --git a/vendor/k8s.io/apiserver/pkg/server/patch.go b/vendor/k8s.io/apiserver/pkg/server/patch.go new file mode 100644 index 000000000..54ffbe35c --- /dev/null +++ b/vendor/k8s.io/apiserver/pkg/server/patch.go @@ -0,0 +1,8 @@ +package server + +func (s *GenericAPIServer) RemoveOpenAPIData() { + if s.Handler != nil && s.Handler.NonGoRestfulMux != nil { + s.Handler.NonGoRestfulMux.Unregister("/openapi/v2") + } + s.openAPIConfig = nil +} diff --git a/vendor/k8s.io/apiserver/pkg/server/routes/openapi.go b/vendor/k8s.io/apiserver/pkg/server/routes/openapi.go index 23bbee99f..2abd0f387 100644 --- a/vendor/k8s.io/apiserver/pkg/server/routes/openapi.go +++ b/vendor/k8s.io/apiserver/pkg/server/routes/openapi.go @@ -17,6 +17,8 @@ limitations under the License. package routes import ( + "strings" + restful "github.com/emicklei/go-restful" "github.com/go-openapi/spec" "k8s.io/klog" @@ -38,6 +40,19 @@ func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) (*hand if err != nil { klog.Fatalf("Failed to build open api spec for root: %v", err) } + + // we shadow ClustResourceQuotas, RoleBindingRestrictions, and SecurityContextContstraints + // with a CRD. This loop removes all CRQ,RBR, SCC paths + // from the OpenAPI spec such that they don't conflict with the CRD + // apiextensions-apiserver spec during merging. + for pth := range spec.Paths.Paths { + if strings.HasPrefix(pth, "/apis/quota.openshift.io/v1/clusterresourcequotas") || + strings.Contains(pth, "rolebindingrestrictions") || + strings.HasPrefix(pth, "/apis/security.openshift.io/v1/securitycontextconstraints") { + delete(spec.Paths.Paths, pth) + } + } + openAPIVersionedService, err := handler.RegisterOpenAPIVersionedService(spec, "/openapi/v2", mux) if err != nil { klog.Fatalf("Failed to register versioned open api spec for root: %v", err) diff --git a/vendor/k8s.io/client-go/transport/cache.go b/vendor/k8s.io/client-go/transport/cache.go index 36d6500f5..af66b5823 100644 --- a/vendor/k8s.io/client-go/transport/cache.go +++ b/vendor/k8s.io/client-go/transport/cache.go @@ -18,12 +18,13 @@ package transport import ( "fmt" - "net" "net/http" "strings" "sync" "time" + libgonetwork "github.com/openshift/library-go/pkg/network" + utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/wait" ) @@ -89,10 +90,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) { dial := config.Dial if dial == nil { - dial = (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext + dial = libgonetwork.DefaultClientDialContext() } // If we use are reloading files, we need to handle certificate rotation properly diff --git a/vendor/modules.txt b/vendor/modules.txt index 9730f5b95..debab1a46 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -103,7 +103,7 @@ github.com/modern-go/concurrent github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/munnerz/goautoneg -# github.com/openshift/api v0.0.0-20200429152225-b98a784d8e6d +# github.com/openshift/api v0.0.0-20200521101457-60c476765272 github.com/openshift/api/config/v1 github.com/openshift/api/oauth/v1 github.com/openshift/api/operator/v1alpha1 @@ -119,7 +119,7 @@ github.com/openshift/build-machinery-go/make/targets/golang github.com/openshift/build-machinery-go/make/targets/openshift github.com/openshift/build-machinery-go/make/targets/openshift/operator github.com/openshift/build-machinery-go/scripts -# github.com/openshift/client-go v0.0.0-20200422192633-6f6c07fc2a70 +# github.com/openshift/client-go v0.0.0-20200521150516-05eb9880269c github.com/openshift/client-go/oauth/clientset/versioned github.com/openshift/client-go/oauth/clientset/versioned/fake github.com/openshift/client-go/oauth/clientset/versioned/scheme @@ -132,7 +132,7 @@ github.com/openshift/client-go/user/clientset/versioned/fake github.com/openshift/client-go/user/clientset/versioned/scheme github.com/openshift/client-go/user/clientset/versioned/typed/user/v1 github.com/openshift/client-go/user/clientset/versioned/typed/user/v1/fake -# github.com/openshift/library-go v0.0.0-20200429122220-9e6c27e916a0 +# github.com/openshift/library-go v0.0.0-20201209131625-07b0830b8740 github.com/openshift/library-go/pkg/apiserver/httprequest github.com/openshift/library-go/pkg/authentication/bootstrapauthenticator github.com/openshift/library-go/pkg/authorization/scopemetadata @@ -140,6 +140,7 @@ github.com/openshift/library-go/pkg/config/client github.com/openshift/library-go/pkg/config/helpers github.com/openshift/library-go/pkg/config/serving github.com/openshift/library-go/pkg/controller/metrics +github.com/openshift/library-go/pkg/network github.com/openshift/library-go/pkg/oauth/oauthdiscovery github.com/openshift/library-go/pkg/oauth/oauthserviceaccountclient github.com/openshift/library-go/pkg/security/ldapclient @@ -394,7 +395,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.18.9 +# k8s.io/apiserver v0.18.9 => github.com/openshift/kubernetes-apiserver v0.0.0-20201209103546-c5587e940bd4 k8s.io/apiserver/pkg/admission k8s.io/apiserver/pkg/admission/configuration k8s.io/apiserver/pkg/admission/initializer @@ -515,7 +516,7 @@ k8s.io/apiserver/plugin/pkg/audit/truncate k8s.io/apiserver/plugin/pkg/audit/webhook k8s.io/apiserver/plugin/pkg/authenticator/token/webhook k8s.io/apiserver/plugin/pkg/authorizer/webhook -# k8s.io/client-go v0.18.9 +# k8s.io/client-go v0.18.9 => github.com/openshift/kubernetes-client-go v0.0.0-20201209131240-ad062a7baf0b k8s.io/client-go/discovery k8s.io/client-go/discovery/fake k8s.io/client-go/informers