Update dependencies
This commit is contained in:
parent
bf5616c65b
commit
d6d374b28d
13962 changed files with 48226 additions and 3618880 deletions
22
vendor/k8s.io/apiserver/pkg/util/feature/testing/BUILD
generated
vendored
22
vendor/k8s.io/apiserver/pkg/util/feature/testing/BUILD
generated
vendored
|
|
@ -1,22 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["feature_gate_testing.go"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/k8s.io/apiserver/pkg/util/feature: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"],
|
||||
)
|
||||
40
vendor/k8s.io/apiserver/pkg/util/feature/testing/feature_gate_testing.go
generated
vendored
40
vendor/k8s.io/apiserver/pkg/util/feature/testing/feature_gate_testing.go
generated
vendored
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/feature"
|
||||
)
|
||||
|
||||
// SetFeatureGateDuringTest sets the specified gate to the specified value, and returns a function that restores the original value.
|
||||
// Failures to set or restore cause the test to fail.
|
||||
func SetFeatureGateDuringTest(t *testing.T, gate feature.FeatureGate, feature feature.Feature, value bool) func() {
|
||||
originalValue := gate.Enabled(feature)
|
||||
|
||||
if err := gate.Set(fmt.Sprintf("%s=%v", feature, value)); err != nil {
|
||||
t.Errorf("error setting %s=%v: %v", feature, value, err)
|
||||
}
|
||||
|
||||
return func() {
|
||||
if err := gate.Set(fmt.Sprintf("%s=%v", feature, originalValue)); err != nil {
|
||||
t.Errorf("error restoring %s=%v: %v", feature, originalValue, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
42
vendor/k8s.io/apiserver/pkg/util/flag/BUILD
generated
vendored
42
vendor/k8s.io/apiserver/pkg/util/flag/BUILD
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["namedcertkey_flag_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"configuration_map.go",
|
||||
"flags.go",
|
||||
"namedcertkey_flag.go",
|
||||
"string_flag.go",
|
||||
"tristate.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
53
vendor/k8s.io/apiserver/pkg/util/flag/configuration_map.go
generated
vendored
53
vendor/k8s.io/apiserver/pkg/util/flag/configuration_map.go
generated
vendored
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ConfigurationMap map[string]string
|
||||
|
||||
func (m *ConfigurationMap) String() string {
|
||||
pairs := []string{}
|
||||
for k, v := range *m {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return strings.Join(pairs, ",")
|
||||
}
|
||||
|
||||
func (m *ConfigurationMap) Set(value string) error {
|
||||
for _, s := range strings.Split(value, ",") {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
arr := strings.SplitN(s, "=", 2)
|
||||
if len(arr) == 2 {
|
||||
(*m)[strings.TrimSpace(arr[0])] = strings.TrimSpace(arr[1])
|
||||
} else {
|
||||
(*m)[strings.TrimSpace(arr[0])] = ""
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*ConfigurationMap) Type() string {
|
||||
return "mapStringString"
|
||||
}
|
||||
54
vendor/k8s.io/apiserver/pkg/util/flag/flags.go
generated
vendored
54
vendor/k8s.io/apiserver/pkg/util/flag/flags.go
generated
vendored
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flag
|
||||
|
||||
import (
|
||||
goflag "flag"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// WordSepNormalizeFunc changes all flags that contain "_" separators
|
||||
func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
if strings.Contains(name, "_") {
|
||||
return pflag.NormalizedName(strings.Replace(name, "_", "-", -1))
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
// WarnWordSepNormalizeFunc changes and warns for flags that contain "_" separators
|
||||
func WarnWordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
if strings.Contains(name, "_") {
|
||||
nname := strings.Replace(name, "_", "-", -1)
|
||||
glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", name, nname)
|
||||
|
||||
return pflag.NormalizedName(nname)
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
// InitFlags normalizes, parses, then logs the command line flags
|
||||
func InitFlags() {
|
||||
pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc)
|
||||
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
pflag.Parse()
|
||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
||||
glog.V(4).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
|
||||
})
|
||||
}
|
||||
113
vendor/k8s.io/apiserver/pkg/util/flag/namedcertkey_flag.go
generated
vendored
113
vendor/k8s.io/apiserver/pkg/util/flag/namedcertkey_flag.go
generated
vendored
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 flag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NamedCertKey is a flag value parsing "certfile,keyfile" and "certfile,keyfile:name,name,name".
|
||||
type NamedCertKey struct {
|
||||
Names []string
|
||||
CertFile, KeyFile string
|
||||
}
|
||||
|
||||
var _ flag.Value = &NamedCertKey{}
|
||||
|
||||
func (nkc *NamedCertKey) String() string {
|
||||
s := nkc.CertFile + "," + nkc.KeyFile
|
||||
if len(nkc.Names) > 0 {
|
||||
s = s + ":" + strings.Join(nkc.Names, ",")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (nkc *NamedCertKey) Set(value string) error {
|
||||
cs := strings.SplitN(value, ":", 2)
|
||||
var keycert string
|
||||
if len(cs) == 2 {
|
||||
var names string
|
||||
keycert, names = strings.TrimSpace(cs[0]), strings.TrimSpace(cs[1])
|
||||
if names == "" {
|
||||
return errors.New("empty names list is not allowed")
|
||||
}
|
||||
nkc.Names = nil
|
||||
for _, name := range strings.Split(names, ",") {
|
||||
nkc.Names = append(nkc.Names, strings.TrimSpace(name))
|
||||
}
|
||||
} else {
|
||||
nkc.Names = nil
|
||||
keycert = strings.TrimSpace(cs[0])
|
||||
}
|
||||
cs = strings.Split(keycert, ",")
|
||||
if len(cs) != 2 {
|
||||
return errors.New("expected comma separated certificate and key file paths")
|
||||
}
|
||||
nkc.CertFile = strings.TrimSpace(cs[0])
|
||||
nkc.KeyFile = strings.TrimSpace(cs[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NamedCertKey) Type() string {
|
||||
return "namedCertKey"
|
||||
}
|
||||
|
||||
// NamedCertKeyArray is a flag value parsing NamedCertKeys, each passed with its own
|
||||
// flag instance (in contrast to comma separated slices).
|
||||
type NamedCertKeyArray struct {
|
||||
value *[]NamedCertKey
|
||||
changed bool
|
||||
}
|
||||
|
||||
var _ flag.Value = &NamedCertKey{}
|
||||
|
||||
// NewNamedKeyCertArray creates a new NamedCertKeyArray with the internal value
|
||||
// pointing to p.
|
||||
func NewNamedCertKeyArray(p *[]NamedCertKey) *NamedCertKeyArray {
|
||||
return &NamedCertKeyArray{
|
||||
value: p,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *NamedCertKeyArray) Set(val string) error {
|
||||
nkc := NamedCertKey{}
|
||||
err := nkc.Set(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !a.changed {
|
||||
*a.value = []NamedCertKey{nkc}
|
||||
a.changed = true
|
||||
} else {
|
||||
*a.value = append(*a.value, nkc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *NamedCertKeyArray) Type() string {
|
||||
return "namedCertKey"
|
||||
}
|
||||
|
||||
func (a *NamedCertKeyArray) String() string {
|
||||
nkcs := make([]string, 0, len(*a.value))
|
||||
for i := range *a.value {
|
||||
nkcs = append(nkcs, (*a.value)[i].String())
|
||||
}
|
||||
return "[" + strings.Join(nkcs, ";") + "]"
|
||||
}
|
||||
139
vendor/k8s.io/apiserver/pkg/util/flag/namedcertkey_flag_test.go
generated
vendored
139
vendor/k8s.io/apiserver/pkg/util/flag/namedcertkey_flag_test.go
generated
vendored
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestNamedCertKeyArrayFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
def []NamedCertKey
|
||||
expected []NamedCertKey
|
||||
parseError string
|
||||
}{
|
||||
{
|
||||
args: []string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key"},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{" foo.crt , foo.key "},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key:abc"},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
Names: []string{"abc"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key: abc "},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
Names: []string{"abc"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key:"},
|
||||
parseError: "empty names list is not allowed",
|
||||
},
|
||||
{
|
||||
args: []string{""},
|
||||
parseError: "expected comma separated certificate and key file paths",
|
||||
},
|
||||
{
|
||||
args: []string{" "},
|
||||
parseError: "expected comma separated certificate and key file paths",
|
||||
},
|
||||
{
|
||||
args: []string{"a,b,c"},
|
||||
parseError: "expected comma separated certificate and key file paths",
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key:abc,def,ghi"},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
Names: []string{"abc", "def", "ghi"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key:*.*.*"},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
Names: []string{"*.*.*"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
args: []string{"foo.crt,foo.key", "bar.crt,bar.key"},
|
||||
expected: []NamedCertKey{{
|
||||
KeyFile: "foo.key",
|
||||
CertFile: "foo.crt",
|
||||
}, {
|
||||
KeyFile: "bar.key",
|
||||
CertFile: "bar.crt",
|
||||
}},
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
fs := pflag.NewFlagSet("testNamedCertKeyArray", pflag.ContinueOnError)
|
||||
var nkcs []NamedCertKey
|
||||
for _, d := range test.def {
|
||||
nkcs = append(nkcs, d)
|
||||
}
|
||||
fs.Var(NewNamedCertKeyArray(&nkcs), "tls-sni-cert-key", "usage")
|
||||
|
||||
args := []string{}
|
||||
for _, a := range test.args {
|
||||
args = append(args, fmt.Sprintf("--tls-sni-cert-key=%s", a))
|
||||
}
|
||||
|
||||
err := fs.Parse(args)
|
||||
if test.parseError != "" {
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error %q, got nil", i, test.parseError)
|
||||
} else if !strings.Contains(err.Error(), test.parseError) {
|
||||
t.Errorf("%d: expected error %q, got %q", i, test.parseError, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("%d: expected nil error, got %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(nkcs, test.expected) {
|
||||
t.Errorf("%d: expected %+v, got %+v", i, test.expected, nkcs)
|
||||
}
|
||||
}
|
||||
}
|
||||
56
vendor/k8s.io/apiserver/pkg/util/flag/string_flag.go
generated
vendored
56
vendor/k8s.io/apiserver/pkg/util/flag/string_flag.go
generated
vendored
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flag
|
||||
|
||||
// StringFlag is a string flag compatible with flags and pflags that keeps track of whether it had a value supplied or not.
|
||||
type StringFlag struct {
|
||||
// If Set has been invoked this value is true
|
||||
provided bool
|
||||
// The exact value provided on the flag
|
||||
value string
|
||||
}
|
||||
|
||||
func NewStringFlag(defaultVal string) StringFlag {
|
||||
return StringFlag{value: defaultVal}
|
||||
}
|
||||
|
||||
func (f *StringFlag) Default(value string) {
|
||||
f.value = value
|
||||
}
|
||||
|
||||
func (f StringFlag) String() string {
|
||||
return f.value
|
||||
}
|
||||
|
||||
func (f StringFlag) Value() string {
|
||||
return f.value
|
||||
}
|
||||
|
||||
func (f *StringFlag) Set(value string) error {
|
||||
f.value = value
|
||||
f.provided = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f StringFlag) Provided() bool {
|
||||
return f.provided
|
||||
}
|
||||
|
||||
func (f *StringFlag) Type() string {
|
||||
return "string"
|
||||
}
|
||||
83
vendor/k8s.io/apiserver/pkg/util/flag/tristate.go
generated
vendored
83
vendor/k8s.io/apiserver/pkg/util/flag/tristate.go
generated
vendored
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Tristate is a flag compatible with flags and pflags that
|
||||
// keeps track of whether it had a value supplied or not.
|
||||
type Tristate int
|
||||
|
||||
const (
|
||||
Unset Tristate = iota // 0
|
||||
True
|
||||
False
|
||||
)
|
||||
|
||||
func (f *Tristate) Default(value bool) {
|
||||
*f = triFromBool(value)
|
||||
}
|
||||
|
||||
func (f Tristate) String() string {
|
||||
b := boolFromTri(f)
|
||||
return fmt.Sprintf("%t", b)
|
||||
}
|
||||
|
||||
func (f Tristate) Value() bool {
|
||||
b := boolFromTri(f)
|
||||
return b
|
||||
}
|
||||
|
||||
func (f *Tristate) Set(value string) error {
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = triFromBool(boolVal)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f Tristate) Provided() bool {
|
||||
if f != Unset {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *Tristate) Type() string {
|
||||
return "tristate"
|
||||
}
|
||||
|
||||
func boolFromTri(t Tristate) bool {
|
||||
if t == True {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func triFromBool(b bool) Tristate {
|
||||
if b {
|
||||
return True
|
||||
} else {
|
||||
return False
|
||||
}
|
||||
}
|
||||
34
vendor/k8s.io/apiserver/pkg/util/flushwriter/BUILD
generated
vendored
34
vendor/k8s.io/apiserver/pkg/util/flushwriter/BUILD
generated
vendored
|
|
@ -1,34 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["writer_test.go"],
|
||||
library = ":go_default_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"writer.go",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
19
vendor/k8s.io/apiserver/pkg/util/flushwriter/doc.go
generated
vendored
19
vendor/k8s.io/apiserver/pkg/util/flushwriter/doc.go
generated
vendored
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flushwriter implements a wrapper for a writer that flushes on every
|
||||
// write if that writer implements the io.Flusher interface
|
||||
package flushwriter // import "k8s.io/apiserver/pkg/util/flushwriter"
|
||||
53
vendor/k8s.io/apiserver/pkg/util/flushwriter/writer.go
generated
vendored
53
vendor/k8s.io/apiserver/pkg/util/flushwriter/writer.go
generated
vendored
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flushwriter
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Wrap wraps an io.Writer into a writer that flushes after every write if
|
||||
// the writer implements the Flusher interface.
|
||||
func Wrap(w io.Writer) io.Writer {
|
||||
fw := &flushWriter{
|
||||
writer: w,
|
||||
}
|
||||
if flusher, ok := w.(http.Flusher); ok {
|
||||
fw.flusher = flusher
|
||||
}
|
||||
return fw
|
||||
}
|
||||
|
||||
// flushWriter provides wrapper for responseWriter with HTTP streaming capabilities
|
||||
type flushWriter struct {
|
||||
flusher http.Flusher
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// Write is a FlushWriter implementation of the io.Writer that sends any buffered
|
||||
// data to the client.
|
||||
func (fw *flushWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = fw.writer.Write(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if fw.flusher != nil {
|
||||
fw.flusher.Flush()
|
||||
}
|
||||
return
|
||||
}
|
||||
86
vendor/k8s.io/apiserver/pkg/util/flushwriter/writer_test.go
generated
vendored
86
vendor/k8s.io/apiserver/pkg/util/flushwriter/writer_test.go
generated
vendored
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flushwriter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type writerWithFlush struct {
|
||||
writeCount, flushCount int
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *writerWithFlush) Flush() {
|
||||
w.flushCount++
|
||||
}
|
||||
|
||||
func (w *writerWithFlush) Write(p []byte) (n int, err error) {
|
||||
w.writeCount++
|
||||
return len(p), w.err
|
||||
}
|
||||
|
||||
type writerWithNoFlush struct {
|
||||
writeCount int
|
||||
}
|
||||
|
||||
func (w *writerWithNoFlush) Write(p []byte) (n int, err error) {
|
||||
w.writeCount++
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestWriteWithFlush(t *testing.T) {
|
||||
w := &writerWithFlush{}
|
||||
fw := Wrap(w)
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := fw.Write([]byte("Test write"))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error while writing with flush writer: %v", err)
|
||||
}
|
||||
}
|
||||
if w.flushCount != 10 {
|
||||
t.Errorf("Flush not called the expected number of times. Actual: %d", w.flushCount)
|
||||
}
|
||||
if w.writeCount != 10 {
|
||||
t.Errorf("Write not called the expected number of times. Actual: %d", w.writeCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteWithoutFlush(t *testing.T) {
|
||||
w := &writerWithNoFlush{}
|
||||
fw := Wrap(w)
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err := fw.Write([]byte("Test write"))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error while writing with flush writer: %v", err)
|
||||
}
|
||||
}
|
||||
if w.writeCount != 10 {
|
||||
t.Errorf("Write not called the expected number of times. Actual: %d", w.writeCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteError(t *testing.T) {
|
||||
e := fmt.Errorf("Error")
|
||||
w := &writerWithFlush{err: e}
|
||||
fw := Wrap(w)
|
||||
_, err := fw.Write([]byte("Test write"))
|
||||
if err != e {
|
||||
t.Errorf("Did not get expected error. Got: %#v", err)
|
||||
}
|
||||
}
|
||||
29
vendor/k8s.io/apiserver/pkg/util/logs/BUILD
generated
vendored
29
vendor/k8s.io/apiserver/pkg/util/logs/BUILD
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["logs.go"],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
61
vendor/k8s.io/apiserver/pkg/util/logs/logs.go
generated
vendored
61
vendor/k8s.io/apiserver/pkg/util/logs/logs.go
generated
vendored
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 logs
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
var logFlushFreq = pflag.Duration("log-flush-frequency", 5*time.Second, "Maximum number of seconds between log flushes")
|
||||
|
||||
// TODO(thockin): This is temporary until we agree on log dirs and put those into each cmd.
|
||||
func init() {
|
||||
flag.Set("logtostderr", "true")
|
||||
}
|
||||
|
||||
// GlogWriter serves as a bridge between the standard log package and the glog package.
|
||||
type GlogWriter struct{}
|
||||
|
||||
// Write implements the io.Writer interface.
|
||||
func (writer GlogWriter) Write(data []byte) (n int, err error) {
|
||||
glog.Info(string(data))
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// InitLogs initializes logs the way we want for kubernetes.
|
||||
func InitLogs() {
|
||||
log.SetOutput(GlogWriter{})
|
||||
log.SetFlags(0)
|
||||
// The default glog flush interval is 30 seconds, which is frighteningly long.
|
||||
go wait.Until(glog.Flush, *logFlushFreq, wait.NeverStop)
|
||||
}
|
||||
|
||||
// FlushLogs flushes logs immediately.
|
||||
func FlushLogs() {
|
||||
glog.Flush()
|
||||
}
|
||||
|
||||
// NewLogger creates a new log.Logger which sends logs to glog.Info.
|
||||
func NewLogger(prefix string) *log.Logger {
|
||||
return log.New(GlogWriter{}, prefix, 0)
|
||||
}
|
||||
44
vendor/k8s.io/apiserver/pkg/util/proxy/BUILD
generated
vendored
44
vendor/k8s.io/apiserver/pkg/util/proxy/BUILD
generated
vendored
|
|
@ -1,44 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["proxy_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["proxy.go"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
127
vendor/k8s.io/apiserver/pkg/util/proxy/proxy.go
generated
vendored
127
vendor/k8s.io/apiserver/pkg/util/proxy/proxy.go
generated
vendored
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
listersv1 "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// findServicePort finds the service port by name or numerically.
|
||||
func findServicePort(svc *v1.Service, port intstr.IntOrString) (*v1.ServicePort, error) {
|
||||
for _, svcPort := range svc.Spec.Ports {
|
||||
if (port.Type == intstr.Int && int32(svcPort.Port) == port.IntVal) || (port.Type == intstr.String && svcPort.Name == port.StrVal) {
|
||||
return &svcPort, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.NewServiceUnavailable(fmt.Sprintf("no service port %q found for service %q", port.String(), svc.Name))
|
||||
}
|
||||
|
||||
// ResourceLocation returns a URL to which one can send traffic for the specified service.
|
||||
func ResolveEndpoint(services listersv1.ServiceLister, endpoints listersv1.EndpointsLister, namespace, id string) (*url.URL, error) {
|
||||
svc, err := services.Services(namespace).Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := intstr.FromInt(443)
|
||||
svcPort, err := findServicePort(svc, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case svc.Spec.Type == v1.ServiceTypeClusterIP, svc.Spec.Type == v1.ServiceTypeLoadBalancer, svc.Spec.Type == v1.ServiceTypeNodePort:
|
||||
// these are fine
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported service type %q", svc.Spec.Type)
|
||||
}
|
||||
|
||||
eps, err := endpoints.Endpoints(namespace).Get(svc.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(eps.Subsets) == 0 {
|
||||
return nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", svc.Name))
|
||||
}
|
||||
|
||||
// Pick a random Subset to start searching from.
|
||||
ssSeed := rand.Intn(len(eps.Subsets))
|
||||
|
||||
// Find a Subset that has the port.
|
||||
for ssi := 0; ssi < len(eps.Subsets); ssi++ {
|
||||
ss := &eps.Subsets[(ssSeed+ssi)%len(eps.Subsets)]
|
||||
if len(ss.Addresses) == 0 {
|
||||
continue
|
||||
}
|
||||
for i := range ss.Ports {
|
||||
if ss.Ports[i].Name == svcPort.Name {
|
||||
// Pick a random address.
|
||||
ip := ss.Addresses[rand.Intn(len(ss.Addresses))].IP
|
||||
port := int(ss.Ports[i].Port)
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(ip, strconv.Itoa(port)),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", id))
|
||||
}
|
||||
|
||||
func ResolveCluster(services listersv1.ServiceLister, namespace, id string) (*url.URL, error) {
|
||||
svc, err := services.Services(namespace).Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := intstr.FromInt(443)
|
||||
|
||||
switch {
|
||||
case svc.Spec.Type == v1.ServiceTypeClusterIP && svc.Spec.ClusterIP == v1.ClusterIPNone:
|
||||
return nil, fmt.Errorf(`cannot route to service with ClusterIP "None"`)
|
||||
// use IP from a clusterIP for these service types
|
||||
case svc.Spec.Type == v1.ServiceTypeClusterIP, svc.Spec.Type == v1.ServiceTypeLoadBalancer, svc.Spec.Type == v1.ServiceTypeNodePort:
|
||||
svcPort, err := findServicePort(svc, port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(svc.Spec.ClusterIP, fmt.Sprintf("%d", svcPort.Port)),
|
||||
}, nil
|
||||
case svc.Spec.Type == v1.ServiceTypeExternalName:
|
||||
if port.Type != intstr.Int {
|
||||
return nil, fmt.Errorf("named ports not supported")
|
||||
}
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(svc.Spec.ExternalName, port.String()),
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported service type %q", svc.Spec.Type)
|
||||
}
|
||||
}
|
||||
243
vendor/k8s.io/apiserver/pkg/util/proxy/proxy_test.go
generated
vendored
243
vendor/k8s.io/apiserver/pkg/util/proxy/proxy_test.go
generated
vendored
|
|
@ -1,243 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 proxy
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
v1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
matchingEndpoints := func(svc *v1.Service) []*v1.Endpoints {
|
||||
ports := []v1.EndpointPort{}
|
||||
for _, p := range svc.Spec.Ports {
|
||||
if p.TargetPort.Type != intstr.Int {
|
||||
continue
|
||||
}
|
||||
ports = append(ports, v1.EndpointPort{Name: p.Name, Port: p.TargetPort.IntVal})
|
||||
}
|
||||
|
||||
return []*v1.Endpoints{{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: svc.Namespace, Name: svc.Name},
|
||||
Subsets: []v1.EndpointSubset{{
|
||||
Addresses: []v1.EndpointAddress{{Hostname: "dummy-host", IP: "127.0.0.1"}},
|
||||
Ports: ports,
|
||||
}},
|
||||
}}
|
||||
}
|
||||
|
||||
type expectation struct {
|
||||
url string
|
||||
error bool
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
services []*v1.Service
|
||||
endpoints func(svc *v1.Service) []*v1.Endpoints
|
||||
|
||||
clusterMode expectation
|
||||
endpointMode expectation
|
||||
}{
|
||||
{
|
||||
name: "cluster ip without 443 port",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
ClusterIP: "hit",
|
||||
Ports: []v1.ServicePort{
|
||||
{Port: 1234, TargetPort: intstr.FromInt(1234)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: matchingEndpoints,
|
||||
|
||||
clusterMode: expectation{error: true},
|
||||
endpointMode: expectation{error: true},
|
||||
},
|
||||
{
|
||||
name: "cluster ip",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
ClusterIP: "hit",
|
||||
Ports: []v1.ServicePort{
|
||||
{Name: "https", Port: 443, TargetPort: intstr.FromInt(1443)},
|
||||
{Port: 1234, TargetPort: intstr.FromInt(1234)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: matchingEndpoints,
|
||||
|
||||
clusterMode: expectation{url: "https://hit:443"},
|
||||
endpointMode: expectation{url: "https://127.0.0.1:1443"},
|
||||
},
|
||||
{
|
||||
name: "cluster ip without endpoints",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
ClusterIP: "hit",
|
||||
Ports: []v1.ServicePort{
|
||||
{Name: "https", Port: 443, TargetPort: intstr.FromInt(1443)},
|
||||
{Port: 1234, TargetPort: intstr.FromInt(1234)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: nil,
|
||||
|
||||
clusterMode: expectation{url: "https://hit:443"},
|
||||
endpointMode: expectation{error: true},
|
||||
},
|
||||
{
|
||||
name: "none cluster ip",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: nil,
|
||||
|
||||
clusterMode: expectation{error: true},
|
||||
endpointMode: expectation{error: true},
|
||||
},
|
||||
{
|
||||
name: "loadbalancer",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeLoadBalancer,
|
||||
ClusterIP: "lb",
|
||||
Ports: []v1.ServicePort{
|
||||
{Name: "https", Port: 443, TargetPort: intstr.FromInt(1443)},
|
||||
{Port: 1234, TargetPort: intstr.FromInt(1234)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: matchingEndpoints,
|
||||
|
||||
clusterMode: expectation{url: "https://lb:443"},
|
||||
endpointMode: expectation{url: "https://127.0.0.1:1443"},
|
||||
},
|
||||
{
|
||||
name: "node port",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeNodePort,
|
||||
ClusterIP: "np",
|
||||
Ports: []v1.ServicePort{
|
||||
{Name: "https", Port: 443, TargetPort: intstr.FromInt(1443)},
|
||||
{Port: 1234, TargetPort: intstr.FromInt(1234)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: matchingEndpoints,
|
||||
|
||||
clusterMode: expectation{url: "https://np:443"},
|
||||
endpointMode: expectation{url: "https://127.0.0.1:1443"},
|
||||
},
|
||||
{
|
||||
name: "external name",
|
||||
services: []*v1.Service{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "one", Name: "alfa"},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeExternalName,
|
||||
ExternalName: "foo.bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
endpoints: nil,
|
||||
|
||||
clusterMode: expectation{url: "https://foo.bar.com:443"},
|
||||
endpointMode: expectation{error: true},
|
||||
},
|
||||
{
|
||||
name: "missing service",
|
||||
services: nil,
|
||||
endpoints: nil,
|
||||
|
||||
clusterMode: expectation{error: true},
|
||||
endpointMode: expectation{error: true},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
serviceCache := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
serviceLister := v1listers.NewServiceLister(serviceCache)
|
||||
for i := range test.services {
|
||||
if err := serviceCache.Add(test.services[i]); err != nil {
|
||||
t.Fatalf("%s unexpected service add error: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
endpointCache := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
endpointLister := v1listers.NewEndpointsLister(endpointCache)
|
||||
if test.endpoints != nil {
|
||||
for _, svc := range test.services {
|
||||
for _, ep := range test.endpoints(svc) {
|
||||
if err := endpointCache.Add(ep); err != nil {
|
||||
t.Fatalf("%s unexpected endpoint add error: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check := func(mode string, expected expectation, url *url.URL, err error) {
|
||||
switch {
|
||||
case err == nil && expected.error:
|
||||
t.Errorf("%s in %s mode expected error, got none", test.name, mode)
|
||||
case err != nil && expected.error:
|
||||
// ignore
|
||||
case err != nil:
|
||||
t.Errorf("%s in %s mode unexpected error: %v", test.name, mode, err)
|
||||
case url.String() != expected.url:
|
||||
t.Errorf("%s in %s mode expected url %q, got %q", test.name, mode, expected.url, url.String())
|
||||
}
|
||||
}
|
||||
|
||||
clusterURL, err := ResolveCluster(serviceLister, "one", "alfa")
|
||||
check("cluster", test.clusterMode, clusterURL, err)
|
||||
|
||||
endpointURL, err := ResolveEndpoint(serviceLister, endpointLister, "one", "alfa")
|
||||
check("endpoint", test.endpointMode, endpointURL, err)
|
||||
}
|
||||
}
|
||||
25
vendor/k8s.io/apiserver/pkg/util/trace/BUILD
generated
vendored
25
vendor/k8s.io/apiserver/pkg/util/trace/BUILD
generated
vendored
|
|
@ -1,25 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["trace.go"],
|
||||
deps = ["//vendor/github.com/golang/glog:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
89
vendor/k8s.io/apiserver/pkg/util/trace/trace.go
generated
vendored
89
vendor/k8s.io/apiserver/pkg/util/trace/trace.go
generated
vendored
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type traceStep struct {
|
||||
stepTime time.Time
|
||||
msg string
|
||||
}
|
||||
|
||||
type Trace struct {
|
||||
name string
|
||||
startTime time.Time
|
||||
steps []traceStep
|
||||
}
|
||||
|
||||
func New(name string) *Trace {
|
||||
return &Trace{name, time.Now(), nil}
|
||||
}
|
||||
|
||||
func (t *Trace) Step(msg string) {
|
||||
if t.steps == nil {
|
||||
// traces almost always have less than 6 steps, do this to avoid more than a single allocation
|
||||
t.steps = make([]traceStep, 0, 6)
|
||||
}
|
||||
t.steps = append(t.steps, traceStep{time.Now(), msg})
|
||||
}
|
||||
|
||||
func (t *Trace) Log() {
|
||||
// an explicit logging request should dump all the steps out at the higher level
|
||||
t.logWithStepThreshold(0)
|
||||
}
|
||||
|
||||
func (t *Trace) logWithStepThreshold(stepThreshold time.Duration) {
|
||||
var buffer bytes.Buffer
|
||||
tracenum := rand.Int31()
|
||||
endTime := time.Now()
|
||||
|
||||
totalTime := endTime.Sub(t.startTime)
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: %q (started: %v) (total time: %v):\n", tracenum, t.name, t.startTime, totalTime))
|
||||
lastStepTime := t.startTime
|
||||
for _, step := range t.steps {
|
||||
stepDuration := step.stepTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || glog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] %v\n", tracenum, step.stepTime.Sub(t.startTime), stepDuration, step.msg))
|
||||
}
|
||||
lastStepTime = step.stepTime
|
||||
}
|
||||
stepDuration := endTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || glog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] END\n", tracenum, endTime.Sub(t.startTime), stepDuration))
|
||||
}
|
||||
|
||||
glog.Info(buffer.String())
|
||||
}
|
||||
|
||||
func (t *Trace) LogIfLong(threshold time.Duration) {
|
||||
if time.Since(t.startTime) >= threshold {
|
||||
// if any step took more than it's share of the total allowed time, it deserves a higher log level
|
||||
stepThreshold := threshold / time.Duration(len(t.steps)+1)
|
||||
t.logWithStepThreshold(stepThreshold)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Trace) TotalTime() time.Duration {
|
||||
return time.Since(t.startTime)
|
||||
}
|
||||
52
vendor/k8s.io/apiserver/pkg/util/webhook/BUILD
generated
vendored
52
vendor/k8s.io/apiserver/pkg/util/webhook/BUILD
generated
vendored
|
|
@ -1,52 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["webhook.go"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"certs_test.go",
|
||||
"webhook_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
214
vendor/k8s.io/apiserver/pkg/util/webhook/certs_test.go
generated
vendored
214
vendor/k8s.io/apiserver/pkg/util/webhook/certs_test.go
generated
vendored
|
|
@ -1,214 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
// This file was generated using openssl by the gencerts.sh script
|
||||
// and holds raw certificates for the webhook tests.
|
||||
|
||||
package webhook
|
||||
|
||||
var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwmdGuDsPPyirvNqlqoDmwf/bmF3zTJBFYQRsJK0vKBAdrfMY
|
||||
MVwoPEpM5ZZ+VCHEB6vSuGYbC0PyO97H/kIV22FsMwN7zifhJP2T2Hb+B7Nc6s8W
|
||||
tAQn1J4xUY31xOXOEcXe2nuGezrlKfX3DpA1FVXp6/Cf8vhyUQ0fJXwuZE/Pbhvp
|
||||
bBUciQLqfPSH6EnShZvzjJBP5Bs13UqzaRobhUf9A3pyk2Mb3PXkTetET7hLc4J2
|
||||
uIp5BxOoNZSvgQCvCjRyV5s1his7BNRALKG3qz/48i6JRyO7FvNoxDkWC09zIqD8
|
||||
1bw9I1d/+EwWdqAPZLa8iLpSsd0gD03gDSFbOwIDAQABAoIBAQC0JPO5oLDePBf4
|
||||
pzxBJbWwLCIXrWfZmQ9RecGksv8xxs1Z9hyDEP0P8WIUlkJ2P9vhp+1ahvOkmtAL
|
||||
fsQg7qhGZJ7ZHu9I+Fd/6aNpQcrg4+rEhCZrpjYqpnTZOA146eLtQUjjePgDlW3q
|
||||
Vk0cJ7GpFbXwt0fg5S05wkkMeWib9mKvme3vooNbog52164U1wo/p4WBTpbAMoYA
|
||||
XlJSqXeoxBpsLWBZHlRG+AYfYpk7BXk8MkIslcKh97RmLsZt52Fh3SbsFJeIEmD5
|
||||
2hQDvn/PJojAnM6SMkUqfvv87SdkryvqQYJ80b2D6qd+y8o7gUFr8WkEqVRCqVLh
|
||||
GaD2C06hAoGBAO9JOe+typoQUPi24aj5BoqWcpyrHQkCdjLlxS805oLsPfmb+EqF
|
||||
1HwnA8UHNMlrdiczJ8f2M7Y4cSUIEXv6LSE5r4teSiYWASidDLREi0q8scw21CGH
|
||||
BnCc7PUhUnBngXJ3B1MtCj+r3TFfpOEEi1J1HtMK1AxAaq7zEFzdOrtjAoGBAM/7
|
||||
fC89Awvd7yJsgTVumKVx/bA+Q54YJOFMkdba3JbcLsQyn4TBaFv0pwqqXqmkTLZz
|
||||
WHjkNscomRf9VY34D4q07nO4YGXCBNqm3MaV3mE0xhIyBsATRZnf03O2a/pnRPu/
|
||||
yTE1EyuIqK/l4+5iv2O5mWzxorC4qdV34Wf5WCRJAoGBANfmfjvf1zoDFswSVrGb
|
||||
X2eUL31kdyI18mgiITRiysm+VnztWa4D6qDKowAXbG2AZG8iHPazEh2L96quCPiP
|
||||
1kBwSA+717Ndj1YRvfC5F+UrNFFJ90T5C7p4HOVgV33MJmQdOaK2tNSWQVHXNnFB
|
||||
JGQWAOXykzkqthd8gHsJsYB5AoGAd7BfKAQxg5vFqYbN2MT7vYJbHxjF6u40Ex/w
|
||||
cbfj6EFv/GKxoEF5YCnsE1w2O+QcbYb1rCSRTY2UhNS6bogJ0aYL77Z0azr7diU+
|
||||
ul226zPmpMP7VIACtumzE00w2JqjfUlCbDoB/TSY9xkSUbasM6S0oZhxKsgqnHlv
|
||||
01kQG1kCgYBPZfZiqKwnnsOSRy8UBN4Oo5zg9QrbMki472/s4FhHaunXF0pFmIUG
|
||||
QU/9kYteJ8DlCppvvtU5C3qmEkW6c2u8KAfJXRmA51uS6v36kEx/8313ZJ5afwLU
|
||||
i2ZMmS8OabHjIhdnCSA2N7geqaAZa7BCLqt8735Doys1p0KB0y+ZNw==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var caCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAM1Z9H27fWCyMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDCZ0a4Ow8/KKu82qWqgObB/9uYXfNMkEVhBGwk
|
||||
rS8oEB2t8xgxXCg8Skzlln5UIcQHq9K4ZhsLQ/I73sf+QhXbYWwzA3vOJ+Ek/ZPY
|
||||
dv4Hs1zqzxa0BCfUnjFRjfXE5c4Rxd7ae4Z7OuUp9fcOkDUVVenr8J/y+HJRDR8l
|
||||
fC5kT89uG+lsFRyJAup89IfoSdKFm/OMkE/kGzXdSrNpGhuFR/0DenKTYxvc9eRN
|
||||
60RPuEtzgna4inkHE6g1lK+BAK8KNHJXmzWGKzsE1EAsoberP/jyLolHI7sW82jE
|
||||
ORYLT3MioPzVvD0jV3/4TBZ2oA9ktryIulKx3SAPTeANIVs7AgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBS0/gwwXmdxIe8o+a7WKbdiTYPy+TBLBgNVHSMERDBCgBS0/gwwXmdx
|
||||
Ie8o+a7WKbdiTYPy+aEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
AM1Z9H27fWCyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHGUdTCN
|
||||
rm0Mx6V5SmSIGbOB/+3QMqE1ZBIWwPsVFKHhROLaouELZFO+QysfLufB8SS54aM6
|
||||
ewufjfSz4KL26DnoSwOFirPxpG0+Sdry55lCjmZ50KtENZDu6g288Qx9GBzqgVHz
|
||||
kGi/eciV4fZ4HYIhZY+oR29n3YYQOID4UqbQ86lSoN781dmsEQLL+TEK4mJJFcNg
|
||||
SKHM526WdwJ15zqpKNlcqXtTyx3UfBFlNwvrxHNFbth1vOfdTW8zAs9Xzcr5vSm2
|
||||
G8nJ3FF/UF4dYpjDzggO3ALZZqUJHnl/XusETo5kYY3Ozp0xQYg2beR8irElqP8f
|
||||
oNcE4Ycfe10Hmec=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var badCAKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAxrCZsZHIeKuYSUYw4qKB8P+HeU81VP7F0tVbLOffj587sfDu
|
||||
N2QsOsnmiWKS9HiNVUs5ap/Zg3rekbnZV5eBeS5qOPC95c8Oyac7A2HNYSRp08kQ
|
||||
dUUo3ueDqwmZ5dNiDp0SriQCWfPYxiWHdWSacqDxxzXPFWCs6z3vwFYV35VxDFhr
|
||||
M8gPWgSqjWdqj+p5cHrL88me7vWWsj6ceL/i2KWo8lmuHuhUzggn0ekU/aKsavHi
|
||||
X/MNVd5tPzGIR+B49aywQZ9KyrJ7V8SdFqKYEGfH6RaiDhaNNUOv9E9668PdupnG
|
||||
qei0260zB8SDY4j6VKPGOS90YBA66qOP6J11rwIDAQABAoIBAQCsgs0PNep/i01v
|
||||
4Xe0bzCvVM4Fb9Z4c7TDN+gv9ytOggzMlMngYiNc78wwYNwDU2AzPFsfzqaG1/nD
|
||||
QUAKI0uRMdGcmrnmfH70azR73UD7JSiVb6/QgjnYP988c9uhhoVO9uYvOKip/WSr
|
||||
tg4EyVKoUEFcm8WvY/7/SQmPT68yLf5VpbtuCysAkSLPUjcBer4A6eWwlZ9PWrtj
|
||||
rLjUCGXXDKRgMmQRAwL+tpBgMwb1+euriv4+M6ddZCkcyaohW076qA3aaq3+XtAB
|
||||
RTGQubWshuri3N1WRQcn1ZvGURLCAhI8q+9i/wXADKrAlL6imDuYzTW+LMQdZLuH
|
||||
bwHvq/yRAoGBAP3beuc2R/jjkDttFsSce2dMx6F8AKPpmdbzVw7/DhflzNRDM/Yo
|
||||
dfVOabRLqcAyfhNm2L6CdUaIJuHRKyRJT3X5wgxUapAjXFUE0kH+qnaq3BxZCCjU
|
||||
fwDUZ4SUVDAuyaMo5OfVbqkI/L3rvSSgklNOnSkXMPtftDkz8pVljLo9AoGBAMhd
|
||||
6uiddCt3Dpt75C1BDRX0xGKc4KwtPK0CnQeQmQNXUx192m6IhfPW7YUoKvIZibWB
|
||||
f9NNJ/KCxDGG+QP7X+0sWQZMfdp5f1l6EsM6HFPLAOgjQ4PyBVWqxknJyxy6GCnt
|
||||
vI3s6cwMxN7B7QJ/87ffO23elEu7bCdg0lrOAmpbAoGBALN6fI+B6irGwU+ylflV
|
||||
5U2olC/Q2ycIXtMBYpjgrRcqSsH77X3pJ1TTJprpL9AKIucWvMEcvUursUnQt97E
|
||||
0iBH//D1sg3MYlhdu0Ybhmu16z9Dlyg+7LgqdDHhKRCT082+ePCMDtwF1aN1S1nd
|
||||
CPdLSoQluGTRSjtzRdxoWrHFAoGAJqNlz2G9qzwURwuHHuryeQ9wZ4vVD57RmpNs
|
||||
cK8Dss8+KevBGZueKT2DJDBwx6sBEU1dtwOj9nIdH2fl0UzCXNw2dq59fon7cufF
|
||||
gnxMRiRZkmpqdKFRQgninwwY7Ps9+afsunm7RCwaMtK2v8qo1wZnUXKgqlIEMzvK
|
||||
lNQxRw0CgYEA0uT5TkrZ3S+GAbyQtwb6FtXiLoocL+8QyBw6d1+5Fy7D0pYYQDLw
|
||||
TMeR2NOEqQGLgsqCnbuKzKBIuIY8wm0VIzOqRGmk4fWiOGxrtEBrfPl+A21bexyC
|
||||
qv5UBMLcEinZEM3x1e/rloDwKi0IGfyKiRfVpxdVKebs7dJfFYkhmw0=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var badCACert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDNzCCAh+gAwIBAgIJAPbb5w6p8Cw8MA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjUzWhgPMjI5MDExMjcw
|
||||
MDIyNTNaMBsxGTAXBgNVBAMUEHdlYmhvb2tfdGVzdHNfY2EwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDGsJmxkch4q5hJRjDiooHw/4d5TzVU/sXS1Vss
|
||||
59+Pnzux8O43ZCw6yeaJYpL0eI1VSzlqn9mDet6RudlXl4F5Lmo48L3lzw7JpzsD
|
||||
Yc1hJGnTyRB1RSje54OrCZnl02IOnRKuJAJZ89jGJYd1ZJpyoPHHNc8VYKzrPe/A
|
||||
VhXflXEMWGszyA9aBKqNZ2qP6nlwesvzyZ7u9ZayPpx4v+LYpajyWa4e6FTOCCfR
|
||||
6RT9oqxq8eJf8w1V3m0/MYhH4Hj1rLBBn0rKsntXxJ0WopgQZ8fpFqIOFo01Q6/0
|
||||
T3rrw926mcap6LTbrTMHxINjiPpUo8Y5L3RgEDrqo4/onXWvAgMBAAGjfDB6MB0G
|
||||
A1UdDgQWBBTTHlbuK0loVSNNa+TCM0Bt7dLEcTBLBgNVHSMERDBCgBTTHlbuK0lo
|
||||
VSNNa+TCM0Bt7dLEcaEfpB0wGzEZMBcGA1UEAxQQd2ViaG9va190ZXN0c19jYYIJ
|
||||
APbb5w6p8Cw8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBACrsdqfQ
|
||||
7be0HI4+/cz/VwzvbQwjr9O6Ybxqs/eKOprh8RWRtjYeuagvTc6f5jH39W9kZdjs
|
||||
E3ktCG3RJJ/SyooeJlzNhgaAaATnMqEf7GyiQv3ch0B/Mc4TOPJeQ2E/pzFj3snq
|
||||
Edm8Xu9+WLwTTF4j/WlSY1sgVSnFk3Yzl5cn0ip00DVCOsL2sP3JlX9HRG4IrdiX
|
||||
jYXb+nGUPYFultSGSUw+5SiL2yM1ZyHfOBaO1RH8QIRA1/aFTfE+1QRuja5YtCwl
|
||||
ahpWVRhii7GVR3zKEgKFTxjELHm8x3vBC/HAhj5J3433nlRrgvwZXsZYplqp8422
|
||||
IpexMtsutA+y9aE=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var serverKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQSQ0euiUYgcNTw2Y87
|
||||
2fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMMCljs1sgAVLXnAmWR
|
||||
sE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0GnyAMx/UyMrq40lht6
|
||||
gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc35Y3bSY86CYsAZic
|
||||
PZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqrocmwajSfyiJe/1qJ
|
||||
dMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABAoIBAQCDhfNbEpa16qn9
|
||||
TUZr9CxQKLNpD3Q6/8oo0jRp6t98WizHRK0v/pM9gdPhETsyDVfbjpEUDG8+dw1q
|
||||
s+NSsOgZ3SIxBuRz5oVobm4wbskUP4nuPbZpW44jaXBMkyNDxcW2ztb8RgM+svTa
|
||||
gNea5Qa80sU+1zo47OLhcltZWBag3KCU/JQT+3LThVZDHt3GRx4QCASTJx3v/vBB
|
||||
o9M5wCYZp6sP7wmFUZfwEpkTfJ5M7sG1h7ibD/8kjIvpnQj+OFpcoylDxTINvqsN
|
||||
ADAe1NPK00Rx6vE9GNQ8ZA/lg0pih+EpK4PpE5cDDkYs3VchUlYHBSrsc7+K6kUk
|
||||
mMTdmVvpAoGBAP7sHhKMEpmPIUqxb5M95l+PX5uOS0x08HM40Vr8mxgx4z849CpW
|
||||
1vcQlZwcXwkxUfJyXZCPx9CK0Sw877Afpac1OL4RiEKZ3qmwLeI9RnRTeKC5qXJ9
|
||||
u31l+dgoSbRZDUdcM1ZwFs9+V8+zId58SifDaBjm3466VCMnD7KQUz4jAoGBANs9
|
||||
udy4Os+SvCYVUaiVvtoYMdcyym+VQzf3ycVAk91dO8Qha/5uFYD/f7ykgEgg7QCd
|
||||
jQp+ZVYPD7Hbh8XNwAt/6T+bF1qe8TSM3K8uk2Wt/tlk1ZqRnNNYsIZ8BO8c4T+f
|
||||
pbu/mCDdmTKWQWVEwCj2kKNBHptmlLO5Ie2nebujAoGBAIqoZccS138dAi+9iYHe
|
||||
VnM96fQTltN0e+FAU2eZJMcpQ4D8+poY9/4U0DvEltDKOdeU612ZR0cgapwUXQ9A
|
||||
d3sWkNGZebM4PIux35NCXxMg3+kUc51p1FRl5lrztvtYwMdC2E241D9yalL4DYEV
|
||||
u8QbHoEE+y6IHQGt2nT22cBfAoGAWmZuT+uLHHIFsLJTtG7ifi1Bx9lCjZX/XIGI
|
||||
qhQBpGJANZQOYp/jsAgqFI/D8XnaH8nXET+i60RUlWLO7inziQpaFAcQLyagkKmQ
|
||||
iY9r6Z5AGkWwqgZmouLMDvfuVOYUntZmUS8kPFEDTU+VcXtSvNFGPHqqcytuH1kz
|
||||
+zl2QX8CgYB9nFMpqARHl0kJp1UXtq9prUei+Lyu2gvrl3a74w96gqg3yx9+fU/n
|
||||
FzGF2VXnC5n1KvCvQi3xjLNzCOXKM3igu7u50CiaA/HEYmyWyOJw2Nt2+ICvxcCH
|
||||
rnsA8P8I/R5Esl0rvv2BbA1Q1O6SLC+Dfnhf7KulWmNgqVXKllj+Ng==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var serverCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/vMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfc2VydmVyMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2lF1EXRYzsUIwM1vrMM/OIinPvOzqSQS
|
||||
Q0euiUYgcNTw2Y872fNWnQXOP3JDHgzZw231kJbdRxeWgRdeTnvCFUaBzA63GXMM
|
||||
Cljs1sgAVLXnAmWRsE/TzG/OwsZUnyYzQMdG49PAEpw8GlX9t0eojmXPy7C/M0Gn
|
||||
yAMx/UyMrq40lht6gdUI2gYRUofRRtHFs+a0EX8/q+49ciwgQx5inj2BX3Jc2cvc
|
||||
35Y3bSY86CYsAZicPZP84wP5iWkYmvFhJUoS/JY2FMC6CvRFPcTi8Dnp28kHEaqr
|
||||
ocmwajSfyiJe/1qJdMlHAInvTxp9E53cOzfCP6nmHbSKQPxm5u8hSQIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAX/CG
|
||||
3C1nwCWabpBw6h0k7UlDI55nnTH6xHdSX9EFmHz49NmAym9gUwXK5xDPVDNYURjb
|
||||
TD3R2e76Cov7wXRzw99BMzKOhNrMgjiOrc0WT4Ck5MOaKgjzZEIXRSSBllsrF9ut
|
||||
hnnuSaaKwUVn4D/9vPMp/TuZoK7yZaW3Pyv0ScQfpkECDLKYIkXOlyhC/I5Tfbof
|
||||
+zReStbTsc0EWMVLLIAbP7uPf1VcH5HnElh1ignxRAPBsXwF8jQzjUBTWcZ5dEi9
|
||||
ofIrWo+AVKvcoRlyZZyLjOKPzhA5+pwG4yBkWJB5Cshq2trOYVf3+uUN8lz6i57M
|
||||
wqxS1Q1MmtLhyhy79Q==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var clientKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWMRv2eGeuSBT1YMjDP
|
||||
X5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IMExNDpYGti3ncLf1O
|
||||
HP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5guwQQy7beQhWV38YuR
|
||||
vf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP7LLf/tLOMNq/PeMz
|
||||
I6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuvBwan/Guk9vW970ON
|
||||
sNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABAoIBAE2gCVQCWsrJ9dRa
|
||||
NWEoLLpfpeXRc4vG8R0MlCgWl0jZrg7A7NZXCyb/KwqitWY/G/fB2/hGwu3QLfwi
|
||||
TBI+cF+N0fA1Xx/zbFEfwmcRkf4zSuqxd7PwJDv6icD8kCtnWFqWiZokmhYBhCvX
|
||||
kquuq8zNU4QJ9uiPvateD/zEqzSGgMeL+j7RGJsRmh2TnSBgKXLwadRhYLeHiFu/
|
||||
AwoWljlhLNXrCzCLx2kJPIA9CNYYtShhQncfZfkfC0I02vPWX9hu8lMpKQp2MmD9
|
||||
b3DvVW3H6cjAtm/nsjGghYNCngep8uPX2twcrLOZfzJgsZJf+yn/KLWb/yhGBXjd
|
||||
TERHRCECgYEA2i5OfkIrBIPQcjhQCtkjYBgKUKVS54KTTPQQ0zoiGRPkXb6kpqrt
|
||||
kaCKGYXT4oqvQQapNZQykrLUQ/xzbdAAzdIwZ8hTWS5K5cxHOnkmOcPiKu2+jM4I
|
||||
zT7sdAYn0aSbrh1pNRQDV0tQZcI1Urp/OcEuniaEblWhq5/VRCmpCBECgYEA13wg
|
||||
jKRobq4QBoQM8pu1Ha7waeJZ26NcZwCxno0TwH2JZ6X9e4iXfhywUOgVW7hXzcs5
|
||||
2nBciVX5h31u1EDPJz6aPyzzQHi0YspDy/zuO0GWEJxLKm5QMyjh5vakhF5bVP6f
|
||||
Dh3rXts/ZYKk3+p4ezXs2b+uTelowuq8Kk55qnsCgYAy/tvN2v1fAsg3yj27K2F/
|
||||
Vl8i1mF4RybSt8Eu/cl2fxXDa4nkgtMgVJuyt3r82ll4I2xtX4Qqka3Xbiw0oIdv
|
||||
lA9IUqRYld9fss17N1Hd8pDsY8FD++xGvMxbmgy4jXbtzWYHx/O39ZyHDEuWWIzg
|
||||
HO0effY6K72r9aHNWsdtYQKBgQDNXUw8Hbg1u4gkXZdlZEYxevc/Qmz3KXK36+5b
|
||||
uAJaEopwkL7LC/utQjQ7d2RbnI154TRK3Ykjjh+ZJE8K1JVYxo4EpZdTG3Z3LGN+
|
||||
tphpOvGE9R+h2a5vg4gAMZHLYY3TrDL0JkmahoOd/+uYR4L5kgQf5lF9iXTBRyt7
|
||||
enzznwKBgBr9rHUsdd8whEo/UntKaGQ6sqevd/ESxDErUqkTkiTtDAT2vuLQwgA0
|
||||
JnzYUv1/wmoLCz6Nbe6Fg9shAgRY/tz7GI75NBi4HBjp286df6Uu6qEJ4LAELc0+
|
||||
Yh/uRF/H4WkpmBckEXobnRxoX/0HWFY5SETLoG/nwaxJG9YL/Acr
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
var clientCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIJAPJbY53f15/wMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
||||
BAMUEHdlYmhvb2tfdGVzdHNfY2EwIBcNMTcwMjExMDAyMjU0WhgPMjI5MDExMjcw
|
||||
MDIyNTRaMB8xHTAbBgNVBAMMFHdlYmhvb2tfdGVzdHNfY2xpZW50MIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt6auF7X7jl94ilPpJid6k15/Tnt/hFWM
|
||||
Rv2eGeuSBT1YMjDPX5pIaseeIVdXl0ePKzPeyYJma4t7EpGhUO7TX12Cr1e5S8IM
|
||||
ExNDpYGti3ncLf1OHP+faJNAlbKMTgf2xr0HPg8Q2lsDKfam/OEn+7jqv3FpZ5gu
|
||||
wQQy7beQhWV38YuRvf2ChNJVcalN0h+MRNkT+hsGKGM9XKgKFGknDuCP8N0H7HrP
|
||||
7LLf/tLOMNq/PeMzI6MXMlXmB4VRPMlf1zJGfvE6i0sSbNM0p2ZjArpjhjdveLuv
|
||||
Bwan/Guk9vW970ONsNn9gdLiwCSLqzhRy0cTlIJsSnlkhbuOQZsqKwIDAQABo0ow
|
||||
SDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
|
||||
KwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAD8aZ
|
||||
E0nof9Rh7s+uwtF70KPgcz71ft0c1+vSmeLm4IkN0f+amcvgaT8xZLwNv1b77NZo
|
||||
uMWXvit24eIuiqzq7umKiHP/UrFv+Rl+9ue+lA3N0e3WikRoJsh3aoIn8BQUBbnX
|
||||
Nr9R69SeRYYRpMrs19N5Wn4gN7Nfie+1FKWsL3myJYDFsg+8GMEcOJ0YdOMALMy0
|
||||
tIJdYji28mTQ++lpGbekjhf7p9wazQ/6CVd8WNpIbGO84QbGCcpCaVM2XxOSiV/F
|
||||
hIGO1Z30SBq8rQw51XbhdRX+uvRM1ya4RuBMCSX/hpsMu9lVRqCzbkU4PvuUTqLA
|
||||
CebKCgjYbM0CWrP9kw==
|
||||
-----END CERTIFICATE-----`)
|
||||
107
vendor/k8s.io/apiserver/pkg/util/webhook/gencerts.sh
generated
vendored
107
vendor/k8s.io/apiserver/pkg/util/webhook/gencerts.sh
generated
vendored
|
|
@ -1,107 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright 2017 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.
|
||||
|
||||
set -e
|
||||
|
||||
# gencerts.sh generates the certificates for the webhook tests.
|
||||
#
|
||||
# It is not expected to be run often (there is no go generate rule), and mainly
|
||||
# exists for documentation purposes.
|
||||
|
||||
CN_BASE="webhook_tests"
|
||||
|
||||
cat > server.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
cat > client.conf << EOF
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
[req_distinguished_name]
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth, serverAuth
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
# Create a certificate authority
|
||||
openssl genrsa -out caKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key caKey.pem -days 100000 -out caCert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a second certificate authority
|
||||
openssl genrsa -out badCAKey.pem 2048
|
||||
openssl req -x509 -new -nodes -key badCAKey.pem -days 100000 -out badCACert.pem -subj "/CN=${CN_BASE}_ca"
|
||||
|
||||
# Create a server certiticate
|
||||
openssl genrsa -out serverKey.pem 2048
|
||||
openssl req -new -key serverKey.pem -out server.csr -subj "/CN=${CN_BASE}_server" -config server.conf
|
||||
openssl x509 -req -in server.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out serverCert.pem -days 100000 -extensions v3_req -extfile server.conf
|
||||
|
||||
# Create a client certiticate
|
||||
openssl genrsa -out clientKey.pem 2048
|
||||
openssl req -new -key clientKey.pem -out client.csr -subj "/CN=${CN_BASE}_client" -config client.conf
|
||||
openssl x509 -req -in client.csr -CA caCert.pem -CAkey caKey.pem -CAcreateserial -out clientCert.pem -days 100000 -extensions v3_req -extfile client.conf
|
||||
|
||||
outfile=certs_test.go
|
||||
|
||||
cat > $outfile << EOF
|
||||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
EOF
|
||||
|
||||
echo "// This file was generated using openssl by the gencerts.sh script" >> $outfile
|
||||
echo "// and holds raw certificates for the webhook tests." >> $outfile
|
||||
echo "" >> $outfile
|
||||
echo "package webhook" >> $outfile
|
||||
for file in caKey caCert badCAKey badCACert serverKey serverCert clientKey clientCert; do
|
||||
data=$(cat ${file}.pem)
|
||||
echo "" >> $outfile
|
||||
echo "var $file = []byte(\`$data\`)" >> $outfile
|
||||
done
|
||||
|
||||
# Clean up after we're done.
|
||||
rm *.pem
|
||||
rm *.csr
|
||||
rm *.srl
|
||||
rm *.conf
|
||||
110
vendor/k8s.io/apiserver/pkg/util/webhook/webhook.go
generated
vendored
110
vendor/k8s.io/apiserver/pkg/util/webhook/webhook.go
generated
vendored
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 webhook implements a generic HTTP webhook plugin.
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type GenericWebhook struct {
|
||||
RestClient *rest.RESTClient
|
||||
initialBackoff time.Duration
|
||||
}
|
||||
|
||||
// NewGenericWebhook creates a new GenericWebhook from the provided kubeconfig file.
|
||||
func NewGenericWebhook(registry *registered.APIRegistrationManager, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, initialBackoff time.Duration) (*GenericWebhook, error) {
|
||||
for _, groupVersion := range groupVersions {
|
||||
if !registry.IsEnabledVersion(groupVersion) {
|
||||
return nil, fmt.Errorf("webhook plugin requires enabling extension resource: %s", groupVersion)
|
||||
}
|
||||
}
|
||||
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
loadingRules.ExplicitPath = kubeConfigFile
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
|
||||
clientConfig, err := loader.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
codec := codecFactory.LegacyCodec(groupVersions...)
|
||||
clientConfig.ContentConfig.NegotiatedSerializer = runtimeserializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||
|
||||
restClient, err := rest.UnversionedRESTClientFor(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(ericchiang): Can we ensure remote service is reachable?
|
||||
|
||||
return &GenericWebhook{restClient, initialBackoff}, nil
|
||||
}
|
||||
|
||||
// WithExponentialBackoff will retry webhookFn() up to 5 times with exponentially increasing backoff when
|
||||
// it returns an error for which apierrors.SuggestsClientDelay() or apierrors.IsInternalError() returns true.
|
||||
func (g *GenericWebhook) WithExponentialBackoff(webhookFn func() rest.Result) rest.Result {
|
||||
var result rest.Result
|
||||
WithExponentialBackoff(g.initialBackoff, func() error {
|
||||
result = webhookFn()
|
||||
return result.Error()
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// WithExponentialBackoff will retry webhookFn() up to 5 times with exponentially increasing backoff when
|
||||
// it returns an error for which apierrors.SuggestsClientDelay() or apierrors.IsInternalError() returns true.
|
||||
func WithExponentialBackoff(initialBackoff time.Duration, webhookFn func() error) error {
|
||||
backoff := wait.Backoff{
|
||||
Duration: initialBackoff,
|
||||
Factor: 1.5,
|
||||
Jitter: 0.2,
|
||||
Steps: 5,
|
||||
}
|
||||
|
||||
var err error
|
||||
wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
err = webhookFn()
|
||||
// these errors indicate a need to retry an authentication check
|
||||
if apierrors.IsServerTimeout(err) || apierrors.IsTimeout(err) || apierrors.IsTooManyRequests(err) {
|
||||
return false, nil
|
||||
}
|
||||
// if the error sends the Retry-After header, we respect it as an explicit confirmation we should retry.
|
||||
if _, shouldRetry := apierrors.SuggestsClientDelay(err); shouldRetry {
|
||||
return false, nil
|
||||
}
|
||||
if apierrors.IsInternalError(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
609
vendor/k8s.io/apiserver/pkg/util/webhook/webhook_test.go
generated
vendored
609
vendor/k8s.io/apiserver/pkg/util/webhook/webhook_test.go
generated
vendored
|
|
@ -1,609 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 webhook
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
errBadCertificate = "Get .*: remote error: tls: bad certificate"
|
||||
errNoConfiguration = "invalid configuration: no configuration has been provided"
|
||||
errMissingCertPath = "invalid configuration: unable to read %s %s for %s due to open %s: .*"
|
||||
errSignedByUnknownCA = "Get .*: x509: certificate signed by unknown authority"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
}
|
||||
defaultUser = v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
}
|
||||
namedCluster = v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
Name: "test-cluster",
|
||||
}
|
||||
groupVersions = []schema.GroupVersion{}
|
||||
retryBackoff = time.Duration(500) * time.Millisecond
|
||||
)
|
||||
|
||||
// TestDisabledGroupVersion ensures that requiring a group version works as expected
|
||||
func TestDisabledGroupVersion(t *testing.T) {
|
||||
gv := schema.GroupVersion{Group: "webhook.util.k8s.io", Version: "v1"}
|
||||
gvs := []schema.GroupVersion{gv}
|
||||
registry := registered.NewOrDie(gv.String())
|
||||
_, err := NewGenericWebhook(registry, scheme.Codecs, "/some/path", gvs, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
} else {
|
||||
aErrMsg := err.Error()
|
||||
eErrMsg := fmt.Sprintf("webhook plugin requires enabling extension resource: %s", gv)
|
||||
|
||||
if aErrMsg != eErrMsg {
|
||||
t.Errorf("unexpected error message mismatch:\n Expected: %s\n Actual: %s", eErrMsg, aErrMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestKubeConfigFile ensures that a kube config file, regardless of validity, is handled properly
|
||||
func TestKubeConfigFile(t *testing.T) {
|
||||
badCAPath := "/tmp/missing/ca.pem"
|
||||
badClientCertPath := "/tmp/missing/client.pem"
|
||||
badClientKeyPath := "/tmp/missing/client-key.pem"
|
||||
dir := bootstrapTestDir(t)
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// These tests check for all of the ways in which a Kubernetes config file could be malformed within the context of
|
||||
// configuring a webhook. Configuration issues that arise while using the webhook are tested elsewhere.
|
||||
tests := []struct {
|
||||
test string
|
||||
cluster *v1.NamedCluster
|
||||
context *v1.NamedContext
|
||||
currentContext string
|
||||
user *v1.NamedAuthInfo
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "missing context (no default, none specified)",
|
||||
cluster: &namedCluster,
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "missing context (specified context is missing)",
|
||||
cluster: &namedCluster,
|
||||
currentContext: "missing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without cluster",
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context without user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "context with missing cluster",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: "missing-cluster",
|
||||
},
|
||||
},
|
||||
errRegex: errNoConfiguration,
|
||||
},
|
||||
{
|
||||
test: "context with missing user",
|
||||
cluster: &namedCluster,
|
||||
context: &v1.NamedContext{
|
||||
Context: v1.Context{
|
||||
Cluster: namedCluster.Name,
|
||||
AuthInfo: "missing-user",
|
||||
},
|
||||
},
|
||||
currentContext: "testing-context",
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate path",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthority: badCAPath,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "certificate-authority", badCAPath, "", badCAPath),
|
||||
},
|
||||
{
|
||||
test: "cluster with invalid CA certificate ",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: namedCluster.Cluster.Server,
|
||||
CertificateAuthorityData: caKey,
|
||||
},
|
||||
},
|
||||
user: &defaultUser,
|
||||
errRegex: "", // Not an error at parse time, only when using the webhook
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: badClientCertPath,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-cert", badClientCertPath, "", badClientCertPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientKey,
|
||||
ClientKeyData: defaultUser.AuthInfo.ClientKeyData,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched",
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate path",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKey: badClientKeyPath,
|
||||
},
|
||||
},
|
||||
errRegex: fmt.Sprintf(errMissingCertPath, "client-key", badClientKeyPath, "", badClientKeyPath),
|
||||
},
|
||||
{
|
||||
test: "user with invalid client certificate",
|
||||
cluster: &defaultCluster,
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: defaultUser.AuthInfo.ClientCertificateData,
|
||||
ClientKeyData: clientCert,
|
||||
},
|
||||
},
|
||||
errRegex: "tls: found a certificate rather than a key in the PEM for the private key",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate data embeded in config)",
|
||||
cluster: &defaultCluster,
|
||||
user: &defaultUser,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "valid configuration (certificate files referenced in config)",
|
||||
cluster: &v1.NamedCluster{
|
||||
Cluster: v1.Cluster{
|
||||
Server: "https://webhook.example.com",
|
||||
CertificateAuthority: filepath.Join(dir, "ca.pem"),
|
||||
},
|
||||
},
|
||||
user: &v1.NamedAuthInfo{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificate: filepath.Join(dir, "client.pem"),
|
||||
ClientKey: filepath.Join(dir, "client-key.pem"),
|
||||
},
|
||||
},
|
||||
errRegex: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
err := func() error {
|
||||
kubeConfig := v1.Config{}
|
||||
|
||||
if tt.cluster != nil {
|
||||
kubeConfig.Clusters = []v1.NamedCluster{*tt.cluster}
|
||||
}
|
||||
|
||||
if tt.context != nil {
|
||||
kubeConfig.Contexts = []v1.NamedContext{*tt.context}
|
||||
}
|
||||
|
||||
if tt.user != nil {
|
||||
kubeConfig.AuthInfos = []v1.NamedAuthInfo{*tt.user}
|
||||
}
|
||||
|
||||
kubeConfigFile, err := newKubeConfigFile(kubeConfig)
|
||||
|
||||
if err == nil {
|
||||
defer os.Remove(kubeConfigFile)
|
||||
|
||||
_, err = NewGenericWebhook(registered.NewOrDie(""), scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff)
|
||||
}
|
||||
|
||||
return err
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message to match:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMissingKubeConfigFile ensures that a kube config path to a missing file is handled properly
|
||||
func TestMissingKubeConfigFile(t *testing.T) {
|
||||
kubeConfigPath := "/some/missing/path"
|
||||
_, err := NewGenericWebhook(registered.NewOrDie(""), scheme.Codecs, kubeConfigPath, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("creating the webhook should had failed")
|
||||
} else if strings.Index(err.Error(), fmt.Sprintf("stat %s", kubeConfigPath)) != 0 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTLSConfig ensures that the TLS-based communication between client and server works as expected
|
||||
func TestTLSConfig(t *testing.T) {
|
||||
invalidCert := []byte("invalid")
|
||||
tests := []struct {
|
||||
test string
|
||||
clientCert, clientKey, clientCA []byte
|
||||
serverCert, serverKey, serverCA []byte
|
||||
errRegex string
|
||||
}{
|
||||
{
|
||||
test: "invalid server CA",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: invalidCert,
|
||||
errRegex: errBadCertificate,
|
||||
},
|
||||
{
|
||||
test: "invalid client certificate",
|
||||
clientCert: invalidCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in certificate input",
|
||||
},
|
||||
{
|
||||
test: "invalid client key",
|
||||
clientCert: clientCert, clientKey: invalidCert, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "tls: failed to find any PEM data in key input",
|
||||
},
|
||||
{
|
||||
test: "client does not trust server",
|
||||
clientCert: clientCert, clientKey: clientKey,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
{
|
||||
test: "server does not trust client",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: badCACert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: errSignedByUnknownCA + " .*",
|
||||
},
|
||||
{
|
||||
test: "server requires auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey, serverCA: caCert,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth",
|
||||
clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "server does not require client auth, client provides it",
|
||||
clientCert: clientCert, clientKey: clientKey, clientCA: caCert,
|
||||
serverCert: serverCert, serverKey: serverKey,
|
||||
errRegex: "",
|
||||
},
|
||||
{
|
||||
test: "webhook does not support insecure servers",
|
||||
errRegex: errSignedByUnknownCA,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// Use a closure so defer statements trigger between loop iterations.
|
||||
func() {
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(tt.serverCert, tt.serverKey, tt.serverCA, nil)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to create server: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: tt.clientCA,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: tt.clientCert,
|
||||
ClientKeyData: tt.clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.test, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(registered.NewOrDie(""), scheme.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err == nil {
|
||||
err = wh.RestClient.Get().Do().Error()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if tt.errRegex != "" {
|
||||
t.Errorf("%s: expected an error", tt.test)
|
||||
}
|
||||
} else {
|
||||
if tt.errRegex == "" {
|
||||
t.Errorf("%s: unexpected error: %v", tt.test, err)
|
||||
} else if !regexp.MustCompile(tt.errRegex).MatchString(err.Error()) {
|
||||
t.Errorf("%s: unexpected error message mismatch:\n Expected: %s\n Actual: %s", tt.test, tt.errRegex, err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithExponentialBackoff ensures that the webhook's exponential backoff support works as expected
|
||||
func TestWithExponentialBackoff(t *testing.T) {
|
||||
count := 0 // To keep track of the requests
|
||||
gr := schema.GroupResource{
|
||||
Group: "webhook.util.k8s.io",
|
||||
Resource: "test",
|
||||
}
|
||||
|
||||
// Handler that will handle all backoff CONDITIONS
|
||||
ebHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
switch count++; count {
|
||||
case 1:
|
||||
// Timeout error with retry supplied
|
||||
w.WriteHeader(http.StatusGatewayTimeout)
|
||||
json.NewEncoder(w).Encode(apierrors.NewServerTimeout(gr, "get", 2))
|
||||
case 2:
|
||||
// Internal server error
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(apierrors.NewInternalError(fmt.Errorf("nope")))
|
||||
case 3:
|
||||
// HTTP error that is not retryable
|
||||
w.WriteHeader(http.StatusNotAcceptable)
|
||||
json.NewEncoder(w).Encode(apierrors.NewGenericServerResponse(http.StatusNotAcceptable, "get", gr, "testing", "nope", 0, false))
|
||||
case 4:
|
||||
// Successful request
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"status": "OK",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Create and start a simple HTTPS server
|
||||
server, err := newTestServer(clientCert, clientKey, caCert, ebHandler)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create server: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer server.Close()
|
||||
|
||||
// Create a Kubernetes client configuration file
|
||||
configFile, err := newKubeConfigFile(v1.Config{
|
||||
Clusters: []v1.NamedCluster{
|
||||
{
|
||||
Cluster: v1.Cluster{
|
||||
Server: server.URL,
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
},
|
||||
},
|
||||
AuthInfos: []v1.NamedAuthInfo{
|
||||
{
|
||||
AuthInfo: v1.AuthInfo{
|
||||
ClientCertificateData: clientCert,
|
||||
ClientKeyData: clientKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to create the client config file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer os.Remove(configFile)
|
||||
|
||||
wh, err := NewGenericWebhook(registered.NewOrDie(""), scheme.Codecs, configFile, groupVersions, retryBackoff)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create the webhook: %v", err)
|
||||
}
|
||||
|
||||
result := wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
var statusCode int
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusNotAcceptable {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
|
||||
result = wh.WithExponentialBackoff(func() rest.Result {
|
||||
return wh.RestClient.Get().Do()
|
||||
})
|
||||
|
||||
result.StatusCode(&statusCode)
|
||||
|
||||
if statusCode != http.StatusOK {
|
||||
t.Errorf("unexpected status code: %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapTestDir(t *testing.T) string {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The certificates needed on disk for the tests
|
||||
files := map[string][]byte{
|
||||
"ca.pem": caCert,
|
||||
"client.pem": clientCert,
|
||||
"client-key.pem": clientKey,
|
||||
}
|
||||
|
||||
// Write the certificate files to disk or fail
|
||||
for fileName, fileData := range files {
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, fileName), fileData, 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func newKubeConfigFile(config v1.Config) (string, error) {
|
||||
configFile, err := ioutil.TempFile("", "")
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to create the Kubernetes client config file: %v", err)
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(configFile).Encode(config); err != nil {
|
||||
return "", fmt.Errorf("unable to write the Kubernetes client configuration to disk: %v", err)
|
||||
}
|
||||
|
||||
return configFile.Name(), nil
|
||||
}
|
||||
|
||||
func newTestServer(clientCert, clientKey, caCert []byte, handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, error) {
|
||||
var tlsConfig *tls.Config
|
||||
|
||||
if clientCert != nil {
|
||||
cert, err := tls.X509KeyPair(clientCert, clientKey)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
}
|
||||
|
||||
if caCert != nil {
|
||||
rootCAs := x509.NewCertPool()
|
||||
|
||||
rootCAs.AppendCertsFromPEM(caCert)
|
||||
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{}
|
||||
}
|
||||
|
||||
tlsConfig.ClientCAs = rootCAs
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
handler = func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
}
|
||||
}
|
||||
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(handler))
|
||||
|
||||
server.TLS = tlsConfig
|
||||
server.StartTLS()
|
||||
|
||||
return server, nil
|
||||
}
|
||||
44
vendor/k8s.io/apiserver/pkg/util/wsstream/BUILD
generated
vendored
44
vendor/k8s.io/apiserver/pkg/util/wsstream/BUILD
generated
vendored
|
|
@ -1,44 +0,0 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"conn_test.go",
|
||||
"stream_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/golang.org/x/net/websocket:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"conn.go",
|
||||
"doc.go",
|
||||
"stream.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/net/websocket:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
352
vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go
generated
vendored
352
vendor/k8s.io/apiserver/pkg/util/wsstream/conn.go
generated
vendored
|
|
@ -1,352 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 wsstream
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
// The Websocket subprotocol "channel.k8s.io" prepends each binary message with a byte indicating
|
||||
// the channel number (zero indexed) the message was sent on. Messages in both directions should
|
||||
// prefix their messages with this channel byte. When used for remote execution, the channel numbers
|
||||
// are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT, and STDERR
|
||||
// (0, 1, and 2). No other conversion is performed on the raw subprotocol - writes are sent as they
|
||||
// are received by the server.
|
||||
//
|
||||
// Example client session:
|
||||
//
|
||||
// CONNECT http://server.com with subprotocol "channel.k8s.io"
|
||||
// WRITE []byte{0, 102, 111, 111, 10} # send "foo\n" on channel 0 (STDIN)
|
||||
// READ []byte{1, 10} # receive "\n" on channel 1 (STDOUT)
|
||||
// CLOSE
|
||||
//
|
||||
const ChannelWebSocketProtocol = "channel.k8s.io"
|
||||
|
||||
// The Websocket subprotocol "base64.channel.k8s.io" base64 encodes each message with a character
|
||||
// indicating the channel number (zero indexed) the message was sent on. Messages in both directions
|
||||
// should prefix their messages with this channel char. When used for remote execution, the channel
|
||||
// numbers are by convention defined to match the POSIX file-descriptors assigned to STDIN, STDOUT,
|
||||
// and STDERR ('0', '1', and '2'). The data received on the server is base64 decoded (and must be
|
||||
// be valid) and data written by the server to the client is base64 encoded.
|
||||
//
|
||||
// Example client session:
|
||||
//
|
||||
// CONNECT http://server.com with subprotocol "base64.channel.k8s.io"
|
||||
// WRITE []byte{48, 90, 109, 57, 118, 67, 103, 111, 61} # send "foo\n" (base64: "Zm9vCgo=") on channel '0' (STDIN)
|
||||
// READ []byte{49, 67, 103, 61, 61} # receive "\n" (base64: "Cg==") on channel '1' (STDOUT)
|
||||
// CLOSE
|
||||
//
|
||||
const Base64ChannelWebSocketProtocol = "base64.channel.k8s.io"
|
||||
|
||||
type codecType int
|
||||
|
||||
const (
|
||||
rawCodec codecType = iota
|
||||
base64Codec
|
||||
)
|
||||
|
||||
type ChannelType int
|
||||
|
||||
const (
|
||||
IgnoreChannel ChannelType = iota
|
||||
ReadChannel
|
||||
WriteChannel
|
||||
ReadWriteChannel
|
||||
)
|
||||
|
||||
var (
|
||||
// connectionUpgradeRegex matches any Connection header value that includes upgrade
|
||||
connectionUpgradeRegex = regexp.MustCompile("(^|.*,\\s*)upgrade($|\\s*,)")
|
||||
)
|
||||
|
||||
// IsWebSocketRequest returns true if the incoming request contains connection upgrade headers
|
||||
// for WebSockets.
|
||||
func IsWebSocketRequest(req *http.Request) bool {
|
||||
if !strings.EqualFold(req.Header.Get("Upgrade"), "websocket") {
|
||||
return false
|
||||
}
|
||||
return connectionUpgradeRegex.MatchString(strings.ToLower(req.Header.Get("Connection")))
|
||||
}
|
||||
|
||||
// IgnoreReceives reads from a WebSocket until it is closed, then returns. If timeout is set, the
|
||||
// read and write deadlines are pushed every time a new message is received.
|
||||
func IgnoreReceives(ws *websocket.Conn, timeout time.Duration) {
|
||||
defer runtime.HandleCrash()
|
||||
var data []byte
|
||||
for {
|
||||
resetTimeout(ws, timeout)
|
||||
if err := websocket.Message.Receive(ws, &data); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handshake ensures the provided user protocol matches one of the allowed protocols. It returns
|
||||
// no error if no protocol is specified.
|
||||
func handshake(config *websocket.Config, req *http.Request, allowed []string) error {
|
||||
protocols := config.Protocol
|
||||
if len(protocols) == 0 {
|
||||
protocols = []string{""}
|
||||
}
|
||||
|
||||
for _, protocol := range protocols {
|
||||
for _, allow := range allowed {
|
||||
if allow == protocol {
|
||||
config.Protocol = []string{protocol}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("requested protocol(s) are not supported: %v; supports %v", config.Protocol, allowed)
|
||||
}
|
||||
|
||||
// ChannelProtocolConfig describes a websocket subprotocol with channels.
|
||||
type ChannelProtocolConfig struct {
|
||||
Binary bool
|
||||
Channels []ChannelType
|
||||
}
|
||||
|
||||
// NewDefaultChannelProtocols returns a channel protocol map with the
|
||||
// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io" and the given
|
||||
// channels.
|
||||
func NewDefaultChannelProtocols(channels []ChannelType) map[string]ChannelProtocolConfig {
|
||||
return map[string]ChannelProtocolConfig{
|
||||
"": {Binary: true, Channels: channels},
|
||||
ChannelWebSocketProtocol: {Binary: true, Channels: channels},
|
||||
Base64ChannelWebSocketProtocol: {Binary: false, Channels: channels},
|
||||
}
|
||||
}
|
||||
|
||||
// Conn supports sending multiple binary channels over a websocket connection.
|
||||
type Conn struct {
|
||||
protocols map[string]ChannelProtocolConfig
|
||||
selectedProtocol string
|
||||
channels []*websocketChannel
|
||||
codec codecType
|
||||
ready chan struct{}
|
||||
ws *websocket.Conn
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewConn creates a WebSocket connection that supports a set of channels. Channels begin each
|
||||
// web socket message with a single byte indicating the channel number (0-N). 255 is reserved for
|
||||
// future use. The channel types for each channel are passed as an array, supporting the different
|
||||
// duplex modes. Read and Write refer to whether the channel can be used as a Reader or Writer.
|
||||
//
|
||||
// The protocols parameter maps subprotocol names to ChannelProtocols. The empty string subprotocol
|
||||
// name is used if websocket.Config.Protocol is empty.
|
||||
func NewConn(protocols map[string]ChannelProtocolConfig) *Conn {
|
||||
return &Conn{
|
||||
ready: make(chan struct{}),
|
||||
protocols: protocols,
|
||||
}
|
||||
}
|
||||
|
||||
// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified,
|
||||
// there is no timeout on the connection.
|
||||
func (conn *Conn) SetIdleTimeout(duration time.Duration) {
|
||||
conn.timeout = duration
|
||||
}
|
||||
|
||||
// Open the connection and create channels for reading and writing. It returns
|
||||
// the selected subprotocol, a slice of channels and an error.
|
||||
func (conn *Conn) Open(w http.ResponseWriter, req *http.Request) (string, []io.ReadWriteCloser, error) {
|
||||
go func() {
|
||||
defer runtime.HandleCrash()
|
||||
defer conn.Close()
|
||||
websocket.Server{Handshake: conn.handshake, Handler: conn.handle}.ServeHTTP(w, req)
|
||||
}()
|
||||
<-conn.ready
|
||||
rwc := make([]io.ReadWriteCloser, len(conn.channels))
|
||||
for i := range conn.channels {
|
||||
rwc[i] = conn.channels[i]
|
||||
}
|
||||
return conn.selectedProtocol, rwc, nil
|
||||
}
|
||||
|
||||
func (conn *Conn) initialize(ws *websocket.Conn) {
|
||||
negotiated := ws.Config().Protocol
|
||||
conn.selectedProtocol = negotiated[0]
|
||||
p := conn.protocols[conn.selectedProtocol]
|
||||
if p.Binary {
|
||||
conn.codec = rawCodec
|
||||
} else {
|
||||
conn.codec = base64Codec
|
||||
}
|
||||
conn.ws = ws
|
||||
conn.channels = make([]*websocketChannel, len(p.Channels))
|
||||
for i, t := range p.Channels {
|
||||
switch t {
|
||||
case ReadChannel:
|
||||
conn.channels[i] = newWebsocketChannel(conn, byte(i), true, false)
|
||||
case WriteChannel:
|
||||
conn.channels[i] = newWebsocketChannel(conn, byte(i), false, true)
|
||||
case ReadWriteChannel:
|
||||
conn.channels[i] = newWebsocketChannel(conn, byte(i), true, true)
|
||||
case IgnoreChannel:
|
||||
conn.channels[i] = newWebsocketChannel(conn, byte(i), false, false)
|
||||
}
|
||||
}
|
||||
|
||||
close(conn.ready)
|
||||
}
|
||||
|
||||
func (conn *Conn) handshake(config *websocket.Config, req *http.Request) error {
|
||||
supportedProtocols := make([]string, 0, len(conn.protocols))
|
||||
for p := range conn.protocols {
|
||||
supportedProtocols = append(supportedProtocols, p)
|
||||
}
|
||||
return handshake(config, req, supportedProtocols)
|
||||
}
|
||||
|
||||
func (conn *Conn) resetTimeout() {
|
||||
if conn.timeout > 0 {
|
||||
conn.ws.SetDeadline(time.Now().Add(conn.timeout))
|
||||
}
|
||||
}
|
||||
|
||||
// Close is only valid after Open has been called
|
||||
func (conn *Conn) Close() error {
|
||||
<-conn.ready
|
||||
for _, s := range conn.channels {
|
||||
s.Close()
|
||||
}
|
||||
conn.ws.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// handle implements a websocket handler.
|
||||
func (conn *Conn) handle(ws *websocket.Conn) {
|
||||
defer conn.Close()
|
||||
conn.initialize(ws)
|
||||
|
||||
for {
|
||||
conn.resetTimeout()
|
||||
var data []byte
|
||||
if err := websocket.Message.Receive(ws, &data); err != nil {
|
||||
if err != io.EOF {
|
||||
glog.Errorf("Error on socket receive: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
channel := data[0]
|
||||
if conn.codec == base64Codec {
|
||||
channel = channel - '0'
|
||||
}
|
||||
data = data[1:]
|
||||
if int(channel) >= len(conn.channels) {
|
||||
glog.V(6).Infof("Frame is targeted for a reader %d that is not valid, possible protocol error", channel)
|
||||
continue
|
||||
}
|
||||
if _, err := conn.channels[channel].DataFromSocket(data); err != nil {
|
||||
glog.Errorf("Unable to write frame to %d: %v\n%s", channel, err, string(data))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write multiplexes the specified channel onto the websocket
|
||||
func (conn *Conn) write(num byte, data []byte) (int, error) {
|
||||
conn.resetTimeout()
|
||||
switch conn.codec {
|
||||
case rawCodec:
|
||||
frame := make([]byte, len(data)+1)
|
||||
frame[0] = num
|
||||
copy(frame[1:], data)
|
||||
if err := websocket.Message.Send(conn.ws, frame); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case base64Codec:
|
||||
frame := string('0'+num) + base64.StdEncoding.EncodeToString(data)
|
||||
if err := websocket.Message.Send(conn.ws, frame); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// websocketChannel represents a channel in a connection
|
||||
type websocketChannel struct {
|
||||
conn *Conn
|
||||
num byte
|
||||
r io.Reader
|
||||
w io.WriteCloser
|
||||
|
||||
read, write bool
|
||||
}
|
||||
|
||||
// newWebsocketChannel creates a pipe for writing to a websocket. Do not write to this pipe
|
||||
// prior to the connection being opened. It may be no, half, or full duplex depending on
|
||||
// read and write.
|
||||
func newWebsocketChannel(conn *Conn, num byte, read, write bool) *websocketChannel {
|
||||
r, w := io.Pipe()
|
||||
return &websocketChannel{conn, num, r, w, read, write}
|
||||
}
|
||||
|
||||
func (p *websocketChannel) Write(data []byte) (int, error) {
|
||||
if !p.write {
|
||||
return len(data), nil
|
||||
}
|
||||
return p.conn.write(p.num, data)
|
||||
}
|
||||
|
||||
// DataFromSocket is invoked by the connection receiver to move data from the connection
|
||||
// into a specific channel.
|
||||
func (p *websocketChannel) DataFromSocket(data []byte) (int, error) {
|
||||
if !p.read {
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
switch p.conn.codec {
|
||||
case rawCodec:
|
||||
return p.w.Write(data)
|
||||
case base64Codec:
|
||||
dst := make([]byte, len(data))
|
||||
n, err := base64.StdEncoding.Decode(dst, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return p.w.Write(dst[:n])
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (p *websocketChannel) Read(data []byte) (int, error) {
|
||||
if !p.read {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return p.r.Read(data)
|
||||
}
|
||||
|
||||
func (p *websocketChannel) Close() error {
|
||||
return p.w.Close()
|
||||
}
|
||||
272
vendor/k8s.io/apiserver/pkg/util/wsstream/conn_test.go
generated
vendored
272
vendor/k8s.io/apiserver/pkg/util/wsstream/conn_test.go
generated
vendored
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 wsstream
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func newServer(handler http.Handler) (*httptest.Server, string) {
|
||||
server := httptest.NewServer(handler)
|
||||
serverAddr := server.Listener.Addr().String()
|
||||
return server, serverAddr
|
||||
}
|
||||
|
||||
func TestRawConn(t *testing.T) {
|
||||
channels := []ChannelType{ReadWriteChannel, ReadWriteChannel, IgnoreChannel, ReadChannel, WriteChannel}
|
||||
conn := NewConn(NewDefaultChannelProtocols(channels))
|
||||
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
conn.Open(w, req)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
client, err := websocket.Dial("ws://"+addr, "", "http://localhost/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
<-conn.ready
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// verify we can read a client write
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
data, err := ioutil.ReadAll(conn.channels[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(data, []byte("client")) {
|
||||
t.Errorf("unexpected server read: %v", data)
|
||||
}
|
||||
}()
|
||||
|
||||
if n, err := client.Write(append([]byte{0}, []byte("client")...)); err != nil || n != 7 {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
|
||||
// verify we can read a server write
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if n, err := conn.channels[1].Write([]byte("server")); err != nil && n != 6 {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
}()
|
||||
|
||||
data := make([]byte, 1024)
|
||||
if n, err := io.ReadAtLeast(client, data, 6); n != 7 || err != nil {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
if !reflect.DeepEqual(data[:7], append([]byte{1}, []byte("server")...)) {
|
||||
t.Errorf("unexpected client read: %v", data[:7])
|
||||
}
|
||||
|
||||
// verify that an ignore channel is empty in both directions.
|
||||
if n, err := conn.channels[2].Write([]byte("test")); n != 4 || err != nil {
|
||||
t.Errorf("writes should be ignored")
|
||||
}
|
||||
data = make([]byte, 1024)
|
||||
if n, err := conn.channels[2].Read(data); n != 0 || err != io.EOF {
|
||||
t.Errorf("reads should be ignored")
|
||||
}
|
||||
|
||||
// verify that a write to a Read channel doesn't block
|
||||
if n, err := conn.channels[3].Write([]byte("test")); n != 4 || err != nil {
|
||||
t.Errorf("writes should be ignored")
|
||||
}
|
||||
|
||||
// verify that a read from a Write channel doesn't block
|
||||
data = make([]byte, 1024)
|
||||
if n, err := conn.channels[4].Read(data); n != 0 || err != io.EOF {
|
||||
t.Errorf("reads should be ignored")
|
||||
}
|
||||
|
||||
// verify that a client write to a Write channel doesn't block (is dropped)
|
||||
if n, err := client.Write(append([]byte{4}, []byte("ignored")...)); err != nil || n != 8 {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
|
||||
client.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBase64Conn(t *testing.T) {
|
||||
conn := NewConn(NewDefaultChannelProtocols([]ChannelType{ReadWriteChannel, ReadWriteChannel}))
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
conn.Open(w, req)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
config, err := websocket.NewConfig("ws://"+addr, "http://localhost/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config.Protocol = []string{"base64.channel.k8s.io"}
|
||||
client, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
<-conn.ready
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
data, err := ioutil.ReadAll(conn.channels[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(data, []byte("client")) {
|
||||
t.Errorf("unexpected server read: %s", string(data))
|
||||
}
|
||||
}()
|
||||
|
||||
clientData := base64.StdEncoding.EncodeToString([]byte("client"))
|
||||
if n, err := client.Write(append([]byte{'0'}, clientData...)); err != nil || n != len(clientData)+1 {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if n, err := conn.channels[1].Write([]byte("server")); err != nil && n != 6 {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
}()
|
||||
|
||||
data := make([]byte, 1024)
|
||||
if n, err := io.ReadAtLeast(client, data, 9); n != 9 || err != nil {
|
||||
t.Fatalf("%d: %v", n, err)
|
||||
}
|
||||
expect := []byte(base64.StdEncoding.EncodeToString([]byte("server")))
|
||||
|
||||
if !reflect.DeepEqual(data[:9], append([]byte{'1'}, expect...)) {
|
||||
t.Errorf("unexpected client read: %v", data[:9])
|
||||
}
|
||||
|
||||
client.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
type versionTest struct {
|
||||
supported map[string]bool // protocol -> binary
|
||||
requested []string
|
||||
error bool
|
||||
expected string
|
||||
}
|
||||
|
||||
func versionTests() []versionTest {
|
||||
const (
|
||||
binary = true
|
||||
base64 = false
|
||||
)
|
||||
return []versionTest{
|
||||
{
|
||||
supported: nil,
|
||||
requested: []string{"raw"},
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
supported: map[string]bool{"": binary, "raw": binary, "base64": base64},
|
||||
requested: nil,
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
supported: map[string]bool{"": binary, "raw": binary, "base64": base64},
|
||||
requested: []string{"v1.raw"},
|
||||
error: true,
|
||||
},
|
||||
{
|
||||
supported: map[string]bool{"": binary, "raw": binary, "base64": base64},
|
||||
requested: []string{"v1.raw", "v1.base64"},
|
||||
error: true,
|
||||
}, {
|
||||
supported: map[string]bool{"": binary, "raw": binary, "base64": base64},
|
||||
requested: []string{"v1.raw", "raw"},
|
||||
expected: "raw",
|
||||
},
|
||||
{
|
||||
supported: map[string]bool{"": binary, "v1.raw": binary, "v1.base64": base64, "v2.raw": binary, "v2.base64": base64},
|
||||
requested: []string{"v1.raw"},
|
||||
expected: "v1.raw",
|
||||
},
|
||||
{
|
||||
supported: map[string]bool{"": binary, "v1.raw": binary, "v1.base64": base64, "v2.raw": binary, "v2.base64": base64},
|
||||
requested: []string{"v2.base64"},
|
||||
expected: "v2.base64",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionedConn(t *testing.T) {
|
||||
for i, test := range versionTests() {
|
||||
func() {
|
||||
supportedProtocols := map[string]ChannelProtocolConfig{}
|
||||
for p, binary := range test.supported {
|
||||
supportedProtocols[p] = ChannelProtocolConfig{
|
||||
Binary: binary,
|
||||
Channels: []ChannelType{ReadWriteChannel},
|
||||
}
|
||||
}
|
||||
conn := NewConn(supportedProtocols)
|
||||
// note that it's not enough to wait for conn.ready to avoid a race here. Hence,
|
||||
// we use a channel.
|
||||
selectedProtocol := make(chan string, 0)
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
p, _, _ := conn.Open(w, req)
|
||||
selectedProtocol <- p
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
config, err := websocket.NewConfig("ws://"+addr, "http://localhost/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config.Protocol = test.requested
|
||||
client, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
if !test.error {
|
||||
t.Fatalf("test %d: didn't expect error: %v", i, err)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer client.Close()
|
||||
if test.error && err == nil {
|
||||
t.Fatalf("test %d: expected an error", i)
|
||||
}
|
||||
|
||||
<-conn.ready
|
||||
if got, expected := <-selectedProtocol, test.expected; got != expected {
|
||||
t.Fatalf("test %d: unexpected protocol version: got=%s expected=%s", i, got, expected)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
21
vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go
generated
vendored
21
vendor/k8s.io/apiserver/pkg/util/wsstream/doc.go
generated
vendored
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 wsstream contains utilities for streaming content over WebSockets.
|
||||
// The Conn type allows callers to multiplex multiple read/write channels over
|
||||
// a single websocket. The Reader type allows an io.Reader to be copied over
|
||||
// a websocket channel as binary content.
|
||||
package wsstream // import "k8s.io/apiserver/pkg/util/wsstream"
|
||||
177
vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go
generated
vendored
177
vendor/k8s.io/apiserver/pkg/util/wsstream/stream.go
generated
vendored
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 wsstream
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
// The WebSocket subprotocol "binary.k8s.io" will only send messages to the
|
||||
// client and ignore messages sent to the server. The received messages are
|
||||
// the exact bytes written to the stream. Zero byte messages are possible.
|
||||
const binaryWebSocketProtocol = "binary.k8s.io"
|
||||
|
||||
// The WebSocket subprotocol "base64.binary.k8s.io" will only send messages to the
|
||||
// client and ignore messages sent to the server. The received messages are
|
||||
// a base64 version of the bytes written to the stream. Zero byte messages are
|
||||
// possible.
|
||||
const base64BinaryWebSocketProtocol = "base64.binary.k8s.io"
|
||||
|
||||
// ReaderProtocolConfig describes a websocket subprotocol with one stream.
|
||||
type ReaderProtocolConfig struct {
|
||||
Binary bool
|
||||
}
|
||||
|
||||
// NewDefaultReaderProtocols returns a stream protocol map with the
|
||||
// subprotocols "", "channel.k8s.io", "base64.channel.k8s.io".
|
||||
func NewDefaultReaderProtocols() map[string]ReaderProtocolConfig {
|
||||
return map[string]ReaderProtocolConfig{
|
||||
"": {Binary: true},
|
||||
binaryWebSocketProtocol: {Binary: true},
|
||||
base64BinaryWebSocketProtocol: {Binary: false},
|
||||
}
|
||||
}
|
||||
|
||||
// Reader supports returning an arbitrary byte stream over a websocket channel.
|
||||
type Reader struct {
|
||||
err chan error
|
||||
r io.Reader
|
||||
ping bool
|
||||
timeout time.Duration
|
||||
protocols map[string]ReaderProtocolConfig
|
||||
selectedProtocol string
|
||||
|
||||
handleCrash func() // overridable for testing
|
||||
}
|
||||
|
||||
// NewReader creates a WebSocket pipe that will copy the contents of r to a provided
|
||||
// WebSocket connection. If ping is true, a zero length message will be sent to the client
|
||||
// before the stream begins reading.
|
||||
//
|
||||
// The protocols parameter maps subprotocol names to StreamProtocols. The empty string
|
||||
// subprotocol name is used if websocket.Config.Protocol is empty.
|
||||
func NewReader(r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
err: make(chan error),
|
||||
ping: ping,
|
||||
protocols: protocols,
|
||||
handleCrash: func() { runtime.HandleCrash() },
|
||||
}
|
||||
}
|
||||
|
||||
// SetIdleTimeout sets the interval for both reads and writes before timeout. If not specified,
|
||||
// there is no timeout on the reader.
|
||||
func (r *Reader) SetIdleTimeout(duration time.Duration) {
|
||||
r.timeout = duration
|
||||
}
|
||||
|
||||
func (r *Reader) handshake(config *websocket.Config, req *http.Request) error {
|
||||
supportedProtocols := make([]string, 0, len(r.protocols))
|
||||
for p := range r.protocols {
|
||||
supportedProtocols = append(supportedProtocols, p)
|
||||
}
|
||||
return handshake(config, req, supportedProtocols)
|
||||
}
|
||||
|
||||
// Copy the reader to the response. The created WebSocket is closed after this
|
||||
// method completes.
|
||||
func (r *Reader) Copy(w http.ResponseWriter, req *http.Request) error {
|
||||
go func() {
|
||||
defer r.handleCrash()
|
||||
websocket.Server{Handshake: r.handshake, Handler: r.handle}.ServeHTTP(w, req)
|
||||
}()
|
||||
return <-r.err
|
||||
}
|
||||
|
||||
// handle implements a WebSocket handler.
|
||||
func (r *Reader) handle(ws *websocket.Conn) {
|
||||
// Close the connection when the client requests it, or when we finish streaming, whichever happens first
|
||||
closeConnOnce := &sync.Once{}
|
||||
closeConn := func() {
|
||||
closeConnOnce.Do(func() {
|
||||
ws.Close()
|
||||
})
|
||||
}
|
||||
|
||||
negotiated := ws.Config().Protocol
|
||||
r.selectedProtocol = negotiated[0]
|
||||
defer close(r.err)
|
||||
defer closeConn()
|
||||
|
||||
go func() {
|
||||
defer runtime.HandleCrash()
|
||||
// This blocks until the connection is closed.
|
||||
// Client should not send anything.
|
||||
IgnoreReceives(ws, r.timeout)
|
||||
// Once the client closes, we should also close
|
||||
closeConn()
|
||||
}()
|
||||
|
||||
r.err <- messageCopy(ws, r.r, !r.protocols[r.selectedProtocol].Binary, r.ping, r.timeout)
|
||||
}
|
||||
|
||||
func resetTimeout(ws *websocket.Conn, timeout time.Duration) {
|
||||
if timeout > 0 {
|
||||
ws.SetDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
}
|
||||
|
||||
func messageCopy(ws *websocket.Conn, r io.Reader, base64Encode, ping bool, timeout time.Duration) error {
|
||||
buf := make([]byte, 2048)
|
||||
if ping {
|
||||
resetTimeout(ws, timeout)
|
||||
if base64Encode {
|
||||
if err := websocket.Message.Send(ws, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := websocket.Message.Send(ws, []byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for {
|
||||
resetTimeout(ws, timeout)
|
||||
n, err := r.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if n > 0 {
|
||||
if base64Encode {
|
||||
if err := websocket.Message.Send(ws, base64.StdEncoding.EncodeToString(buf[:n])); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := websocket.Message.Send(ws, buf[:n]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
294
vendor/k8s.io/apiserver/pkg/util/wsstream/stream_test.go
generated
vendored
294
vendor/k8s.io/apiserver/pkg/util/wsstream/stream_test.go
generated
vendored
|
|
@ -1,294 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 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 wsstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func TestStream(t *testing.T) {
|
||||
input := "some random text"
|
||||
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
|
||||
r.SetIdleTimeout(time.Second)
|
||||
data, err := readWebSocket(r, t, nil)
|
||||
if !reflect.DeepEqual(data, []byte(input)) {
|
||||
t.Errorf("unexpected server read: %v", data)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamPing(t *testing.T) {
|
||||
input := "some random text"
|
||||
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
|
||||
r.SetIdleTimeout(time.Second)
|
||||
err := expectWebSocketFrames(r, t, nil, [][]byte{
|
||||
{},
|
||||
[]byte(input),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamBase64(t *testing.T) {
|
||||
input := "some random text"
|
||||
encoded := base64.StdEncoding.EncodeToString([]byte(input))
|
||||
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
|
||||
data, err := readWebSocket(r, t, nil, "base64.binary.k8s.io")
|
||||
if !reflect.DeepEqual(data, []byte(encoded)) {
|
||||
t.Errorf("unexpected server read: %v\n%v", data, []byte(encoded))
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamVersionedBase64(t *testing.T) {
|
||||
input := "some random text"
|
||||
encoded := base64.StdEncoding.EncodeToString([]byte(input))
|
||||
r := NewReader(bytes.NewBuffer([]byte(input)), true, map[string]ReaderProtocolConfig{
|
||||
"": {Binary: true},
|
||||
"binary.k8s.io": {Binary: true},
|
||||
"base64.binary.k8s.io": {Binary: false},
|
||||
"v1.binary.k8s.io": {Binary: true},
|
||||
"v1.base64.binary.k8s.io": {Binary: false},
|
||||
"v2.binary.k8s.io": {Binary: true},
|
||||
"v2.base64.binary.k8s.io": {Binary: false},
|
||||
})
|
||||
data, err := readWebSocket(r, t, nil, "v2.base64.binary.k8s.io")
|
||||
if !reflect.DeepEqual(data, []byte(encoded)) {
|
||||
t.Errorf("unexpected server read: %v\n%v", data, []byte(encoded))
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamVersionedCopy(t *testing.T) {
|
||||
for i, test := range versionTests() {
|
||||
func() {
|
||||
supportedProtocols := map[string]ReaderProtocolConfig{}
|
||||
for p, binary := range test.supported {
|
||||
supportedProtocols[p] = ReaderProtocolConfig{
|
||||
Binary: binary,
|
||||
}
|
||||
}
|
||||
input := "some random text"
|
||||
r := NewReader(bytes.NewBuffer([]byte(input)), true, supportedProtocols)
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
err := r.Copy(w, req)
|
||||
if err != nil {
|
||||
w.WriteHeader(503)
|
||||
}
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
config, err := websocket.NewConfig("ws://"+addr, "http://localhost/")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
config.Protocol = test.requested
|
||||
client, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
if !test.error {
|
||||
t.Errorf("test %d: didn't expect error: %v", i, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
if test.error && err == nil {
|
||||
t.Errorf("test %d: expected an error", i)
|
||||
return
|
||||
}
|
||||
|
||||
<-r.err
|
||||
if got, expected := r.selectedProtocol, test.expected; got != expected {
|
||||
t.Errorf("test %d: unexpected protocol version: got=%s expected=%s", i, got, expected)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamError(t *testing.T) {
|
||||
input := "some random text"
|
||||
errs := &errorReader{
|
||||
reads: [][]byte{
|
||||
[]byte("some random"),
|
||||
[]byte(" text"),
|
||||
},
|
||||
err: fmt.Errorf("bad read"),
|
||||
}
|
||||
r := NewReader(errs, false, NewDefaultReaderProtocols())
|
||||
|
||||
data, err := readWebSocket(r, t, nil)
|
||||
if !reflect.DeepEqual(data, []byte(input)) {
|
||||
t.Errorf("unexpected server read: %v", data)
|
||||
}
|
||||
if err == nil || err.Error() != "bad read" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamSurvivesPanic(t *testing.T) {
|
||||
input := "some random text"
|
||||
errs := &errorReader{
|
||||
reads: [][]byte{
|
||||
[]byte("some random"),
|
||||
[]byte(" text"),
|
||||
},
|
||||
panicMessage: "bad read",
|
||||
}
|
||||
r := NewReader(errs, false, NewDefaultReaderProtocols())
|
||||
|
||||
// do not call runtime.HandleCrash() in handler. Otherwise, the tests are interrupted.
|
||||
r.handleCrash = func() { recover() }
|
||||
|
||||
data, err := readWebSocket(r, t, nil)
|
||||
if !reflect.DeepEqual(data, []byte(input)) {
|
||||
t.Errorf("unexpected server read: %v", data)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamClosedDuringRead(t *testing.T) {
|
||||
for i := 0; i < 25; i++ {
|
||||
ch := make(chan struct{})
|
||||
input := "some random text"
|
||||
errs := &errorReader{
|
||||
reads: [][]byte{
|
||||
[]byte("some random"),
|
||||
[]byte(" text"),
|
||||
},
|
||||
err: fmt.Errorf("stuff"),
|
||||
pause: ch,
|
||||
}
|
||||
r := NewReader(errs, false, NewDefaultReaderProtocols())
|
||||
|
||||
data, err := readWebSocket(r, t, func(c *websocket.Conn) {
|
||||
c.Close()
|
||||
close(ch)
|
||||
})
|
||||
// verify that the data returned by the server on an early close always has a specific error
|
||||
if err == nil || !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// verify that the data returned is a strict subset of the input
|
||||
if !bytes.HasPrefix([]byte(input), data) && len(data) != 0 {
|
||||
t.Fatalf("unexpected server read: %q", string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type errorReader struct {
|
||||
reads [][]byte
|
||||
err error
|
||||
panicMessage string
|
||||
pause chan struct{}
|
||||
}
|
||||
|
||||
func (r *errorReader) Read(p []byte) (int, error) {
|
||||
if len(r.reads) == 0 {
|
||||
if r.pause != nil {
|
||||
<-r.pause
|
||||
}
|
||||
if len(r.panicMessage) != 0 {
|
||||
panic(r.panicMessage)
|
||||
}
|
||||
return 0, r.err
|
||||
}
|
||||
next := r.reads[0]
|
||||
r.reads = r.reads[1:]
|
||||
copy(p, next)
|
||||
return len(next), nil
|
||||
}
|
||||
|
||||
func readWebSocket(r *Reader, t *testing.T, fn func(*websocket.Conn), protocols ...string) ([]byte, error) {
|
||||
errCh := make(chan error, 1)
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
errCh <- r.Copy(w, req)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
config, _ := websocket.NewConfig("ws://"+addr, "http://"+addr)
|
||||
config.Protocol = protocols
|
||||
client, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if fn != nil {
|
||||
fn(client)
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(client)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
return data, <-errCh
|
||||
}
|
||||
|
||||
func expectWebSocketFrames(r *Reader, t *testing.T, fn func(*websocket.Conn), frames [][]byte, protocols ...string) error {
|
||||
errCh := make(chan error, 1)
|
||||
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
errCh <- r.Copy(w, req)
|
||||
}))
|
||||
defer s.Close()
|
||||
|
||||
config, _ := websocket.NewConfig("ws://"+addr, "http://"+addr)
|
||||
config.Protocol = protocols
|
||||
ws, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
if fn != nil {
|
||||
fn(ws)
|
||||
}
|
||||
|
||||
for i := range frames {
|
||||
var data []byte
|
||||
if err := websocket.Message.Receive(ws, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
if !reflect.DeepEqual(frames[i], data) {
|
||||
return fmt.Errorf("frame %d did not match expected: %v", data, err)
|
||||
}
|
||||
}
|
||||
var data []byte
|
||||
if err := websocket.Message.Receive(ws, &data); err != io.EOF {
|
||||
return fmt.Errorf("expected no more frames: %v (%v)", err, data)
|
||||
}
|
||||
return <-errCh
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue