Bumping k8s dependencies to 1.13

This commit is contained in:
Cheng Xing
2018-11-16 14:08:25 -08:00
parent 305407125c
commit b4c0b68ec7
8002 changed files with 884099 additions and 276228 deletions

7
vendor/k8s.io/client-go/tools/auth/OWNERS generated vendored Normal file
View File

@@ -0,0 +1,7 @@
approvers:
- sig-auth-authenticators-approvers
reviewers:
- sig-auth-authenticators-reviewers
labels:
- sig/auth

View File

@@ -1,5 +0,0 @@
approvers:
- jbeda
- luxas
reviewers:
- mattmoyer

View File

@@ -1,20 +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 api (k8s.io/client-go/tools/bootstrap/token/api) contains constants and types needed for
// bootstrap tokens as maintained by the BootstrapSigner and TokenCleaner
// controllers (in k8s.io/kubernetes/pkg/controller/bootstrap)
package api // import "k8s.io/client-go/tools/bootstrap/token/api"

View File

@@ -1,112 +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 api
import (
"k8s.io/api/core/v1"
)
const (
// BootstrapTokenSecretPrefix is the prefix for bootstrap token names.
// Bootstrap tokens secrets must be named in the form
// `bootstrap-token-<token-id>`. This is the prefix to be used before the
// token ID.
BootstrapTokenSecretPrefix = "bootstrap-token-"
// SecretTypeBootstrapToken is used during the automated bootstrap process (first
// implemented by kubeadm). It stores tokens that are used to sign well known
// ConfigMaps. They may also eventually be used for authentication.
SecretTypeBootstrapToken v1.SecretType = "bootstrap.kubernetes.io/token"
// BootstrapTokenIDKey is the id of this token. This can be transmitted in the
// clear and encoded in the name of the secret. It must be a random 6 character
// string that matches the regexp `^([a-z0-9]{6})$`. Required.
BootstrapTokenIDKey = "token-id"
// BootstrapTokenSecretKey is the actual secret. It must be a random 16 character
// string that matches the regexp `^([a-z0-9]{16})$`. Required.
BootstrapTokenSecretKey = "token-secret"
// BootstrapTokenExpirationKey is when this token should be expired and no
// longer used. A controller will delete this resource after this time. This
// is an absolute UTC time using RFC3339. If this cannot be parsed, the token
// should be considered invalid. Optional.
BootstrapTokenExpirationKey = "expiration"
// BootstrapTokenDescriptionKey is a description in human-readable format that
// describes what the bootstrap token is used for. Optional.
BootstrapTokenDescriptionKey = "description"
// BootstrapTokenExtraGroupsKey is a comma-separated list of group names.
// The bootstrap token will authenticate as these groups in addition to the
// "system:bootstrappers" group.
BootstrapTokenExtraGroupsKey = "auth-extra-groups"
// BootstrapTokenUsagePrefix is the prefix for the other usage constants that specifies different
// functions of a bootstrap token
BootstrapTokenUsagePrefix = "usage-bootstrap-"
// BootstrapTokenUsageSigningKey signals that this token should be used to
// sign configs as part of the bootstrap process. Value must be "true". Any
// other value is assumed to be false. Optional.
BootstrapTokenUsageSigningKey = "usage-bootstrap-signing"
// BootstrapTokenUsageAuthentication signals that this token should be used
// as a bearer token to authenticate against the Kubernetes API. The bearer
// token takes the form "<token-id>.<token-secret>" and authenticates as the
// user "system:bootstrap:<token-id>" in the "system:bootstrappers" group
// as well as any groups specified using BootstrapTokenExtraGroupsKey.
// Value must be "true". Any other value is assumed to be false. Optional.
BootstrapTokenUsageAuthentication = "usage-bootstrap-authentication"
// ConfigMapClusterInfo defines the name for the ConfigMap where the information how to connect and trust the cluster exist
ConfigMapClusterInfo = "cluster-info"
// KubeConfigKey defines at which key in the Data object of the ConfigMap the KubeConfig object is stored
KubeConfigKey = "kubeconfig"
// JWSSignatureKeyPrefix defines what key prefix the JWS-signed tokens have
JWSSignatureKeyPrefix = "jws-kubeconfig-"
// BootstrapUserPrefix is the username prefix bootstrapping bearer tokens
// authenticate as. The full username given is "system:bootstrap:<token-id>".
BootstrapUserPrefix = "system:bootstrap:"
// BootstrapDefaultGroup is the default group for bootstrapping bearer
// tokens (in addition to any groups from BootstrapTokenExtraGroupsKey).
BootstrapDefaultGroup = "system:bootstrappers"
// BootstrapGroupPattern is the valid regex pattern that all groups
// assigned to a bootstrap token by BootstrapTokenExtraGroupsKey must match.
// See also util.ValidateBootstrapGroupName()
BootstrapGroupPattern = `\Asystem:bootstrappers:[a-z0-9:-]{0,255}[a-z0-9]\z`
// BootstrapTokenPattern defines the {id}.{secret} regular expression pattern
BootstrapTokenPattern = `\A([a-z0-9]{6})\.([a-z0-9]{16})\z`
// BootstrapTokenIDPattern defines token's id regular expression pattern
BootstrapTokenIDPattern = `\A([a-z0-9]{6})\z`
// BootstrapTokenIDBytes defines the number of bytes used for the Bootstrap Token's ID field
BootstrapTokenIDBytes = 6
// BootstrapTokenSecretBytes defines the number of bytes used the Bootstrap Token's Secret field
BootstrapTokenSecretBytes = 16
)
// KnownTokenUsages specifies the known functions a token will get.
var KnownTokenUsages = []string{"signing", "authentication"}

View File

@@ -1,133 +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 util
import (
"bufio"
"crypto/rand"
"fmt"
"regexp"
"strings"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/bootstrap/token/api"
)
// validBootstrapTokenChars defines the characters a bootstrap token can consist of
const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz"
var (
// BootstrapTokenRegexp is a compiled regular expression of TokenRegexpString
BootstrapTokenRegexp = regexp.MustCompile(api.BootstrapTokenPattern)
// BootstrapTokenIDRegexp is a compiled regular expression of TokenIDRegexpString
BootstrapTokenIDRegexp = regexp.MustCompile(api.BootstrapTokenIDPattern)
// BootstrapGroupRegexp is a compiled regular expression of BootstrapGroupPattern
BootstrapGroupRegexp = regexp.MustCompile(api.BootstrapGroupPattern)
)
// GenerateBootstrapToken generates a new, random Bootstrap Token.
func GenerateBootstrapToken() (string, error) {
tokenID, err := randBytes(api.BootstrapTokenIDBytes)
if err != nil {
return "", err
}
tokenSecret, err := randBytes(api.BootstrapTokenSecretBytes)
if err != nil {
return "", err
}
return TokenFromIDAndSecret(tokenID, tokenSecret), nil
}
// randBytes returns a random string consisting of the characters in
// validBootstrapTokenChars, with the length customized by the parameter
func randBytes(length int) (string, error) {
// len("0123456789abcdefghijklmnopqrstuvwxyz") = 36 which doesn't evenly divide
// the possible values of a byte: 256 mod 36 = 4. Discard any random bytes we
// read that are >= 252 so the bytes we evenly divide the character set.
const maxByteValue = 252
var (
b byte
err error
token = make([]byte, length)
)
reader := bufio.NewReaderSize(rand.Reader, length*2)
for i := range token {
for {
if b, err = reader.ReadByte(); err != nil {
return "", err
}
if b < maxByteValue {
break
}
}
token[i] = validBootstrapTokenChars[int(b)%len(validBootstrapTokenChars)]
}
return string(token), nil
}
// TokenFromIDAndSecret returns the full token which is of the form "{id}.{secret}"
func TokenFromIDAndSecret(id, secret string) string {
return fmt.Sprintf("%s.%s", id, secret)
}
// IsValidBootstrapToken returns whether the given string is valid as a Bootstrap Token and
// in other words satisfies the BootstrapTokenRegexp
func IsValidBootstrapToken(token string) bool {
return BootstrapTokenRegexp.MatchString(token)
}
// IsValidBootstrapTokenID returns whether the given string is valid as a Bootstrap Token ID and
// in other words satisfies the BootstrapTokenIDRegexp
func IsValidBootstrapTokenID(tokenID string) bool {
return BootstrapTokenIDRegexp.MatchString(tokenID)
}
// BootstrapTokenSecretName returns the expected name for the Secret storing the
// Bootstrap Token in the Kubernetes API.
func BootstrapTokenSecretName(tokenID string) string {
return fmt.Sprintf("%s%s", api.BootstrapTokenSecretPrefix, tokenID)
}
// ValidateBootstrapGroupName checks if the provided group name is a valid
// bootstrap group name. Returns nil if valid or a validation error if invalid.
func ValidateBootstrapGroupName(name string) error {
if BootstrapGroupRegexp.Match([]byte(name)) {
return nil
}
return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, api.BootstrapGroupPattern)
}
// ValidateUsages validates that the passed in string are valid usage strings for bootstrap tokens.
func ValidateUsages(usages []string) error {
validUsages := sets.NewString(api.KnownTokenUsages...)
invalidUsages := sets.NewString()
for _, usage := range usages {
if !validUsages.Has(usage) {
invalidUsages.Insert(usage)
}
}
if len(invalidUsages) > 0 {
return fmt.Errorf("invalid bootstrap token usage string: %s, valid usage options: %s", strings.Join(invalidUsages.List(), ","), strings.Join(api.KnownTokenUsages, ","))
}
return nil
}

View File

@@ -1,213 +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 util
import (
"strings"
"testing"
)
func TestGenerateBootstrapToken(t *testing.T) {
token, err := GenerateBootstrapToken()
if err != nil {
t.Fatalf("GenerateBootstrapToken returned an unexpected error: %+v", err)
}
if !IsValidBootstrapToken(token) {
t.Errorf("GenerateBootstrapToken didn't generate a valid token: %q", token)
}
}
func TestRandBytes(t *testing.T) {
var randTest = []int{
0,
1,
2,
3,
100,
}
for _, rt := range randTest {
actual, err := randBytes(rt)
if err != nil {
t.Errorf("failed randBytes: %v", err)
}
if len(actual) != rt {
t.Errorf("failed randBytes:\n\texpected: %d\n\t actual: %d\n", rt, len(actual))
}
}
}
func TestTokenFromIDAndSecret(t *testing.T) {
var tests = []struct {
id string
secret string
expected string
}{
{"foo", "bar", "foo.bar"}, // should use default
{"abcdef", "abcdef0123456789", "abcdef.abcdef0123456789"},
{"h", "b", "h.b"},
}
for _, rt := range tests {
actual := TokenFromIDAndSecret(rt.id, rt.secret)
if actual != rt.expected {
t.Errorf(
"failed TokenFromIDAndSecret:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}
func TestIsValidBootstrapToken(t *testing.T) {
var tests = []struct {
token string
expected bool
}{
{token: "", expected: false},
{token: ".", expected: false},
{token: "1234567890123456789012", expected: false}, // invalid parcel size
{token: "12345.1234567890123456", expected: false}, // invalid parcel size
{token: ".1234567890123456", expected: false}, // invalid parcel size
{token: "123456.", expected: false}, // invalid parcel size
{token: "123456:1234567890.123456", expected: false}, // invalid separation
{token: "abcdef:1234567890123456", expected: false}, // invalid separation
{token: "Abcdef.1234567890123456", expected: false}, // invalid token id
{token: "123456.AABBCCDDEEFFGGHH", expected: false}, // invalid token secret
{token: "123456.AABBCCD-EEFFGGHH", expected: false}, // invalid character
{token: "abc*ef.1234567890123456", expected: false}, // invalid character
{token: "abcdef.1234567890123456", expected: true},
{token: "123456.aabbccddeeffgghh", expected: true},
{token: "ABCDEF.abcdef0123456789", expected: false},
{token: "abcdef.abcdef0123456789", expected: true},
{token: "123456.1234560123456789", expected: true},
}
for _, rt := range tests {
actual := IsValidBootstrapToken(rt.token)
if actual != rt.expected {
t.Errorf(
"failed IsValidBootstrapToken for the token %q\n\texpected: %t\n\t actual: %t",
rt.token,
rt.expected,
actual,
)
}
}
}
func TestIsValidBootstrapTokenID(t *testing.T) {
var tests = []struct {
tokenID string
expected bool
}{
{tokenID: "", expected: false},
{tokenID: "1234567890123456789012", expected: false},
{tokenID: "12345", expected: false},
{tokenID: "Abcdef", expected: false},
{tokenID: "ABCDEF", expected: false},
{tokenID: "abcdef.", expected: false},
{tokenID: "abcdef", expected: true},
{tokenID: "123456", expected: true},
}
for _, rt := range tests {
actual := IsValidBootstrapTokenID(rt.tokenID)
if actual != rt.expected {
t.Errorf(
"failed IsValidBootstrapTokenID for the token %q\n\texpected: %t\n\t actual: %t",
rt.tokenID,
rt.expected,
actual,
)
}
}
}
func TestBootstrapTokenSecretName(t *testing.T) {
var tests = []struct {
tokenID string
expected string
}{
{"foo", "bootstrap-token-foo"},
{"bar", "bootstrap-token-bar"},
{"", "bootstrap-token-"},
{"abcdef", "bootstrap-token-abcdef"},
}
for _, rt := range tests {
actual := BootstrapTokenSecretName(rt.tokenID)
if actual != rt.expected {
t.Errorf(
"failed BootstrapTokenSecretName:\n\texpected: %s\n\t actual: %s",
rt.expected,
actual,
)
}
}
}
func TestValidateBootstrapGroupName(t *testing.T) {
tests := []struct {
name string
input string
valid bool
}{
{"valid", "system:bootstrappers:foo", true},
{"valid nested", "system:bootstrappers:foo:bar:baz", true},
{"valid with dashes and number", "system:bootstrappers:foo-bar-42", true},
{"invalid uppercase", "system:bootstrappers:Foo", false},
{"missing prefix", "foo", false},
{"prefix with no body", "system:bootstrappers:", false},
{"invalid spaces", "system:bootstrappers: ", false},
{"invalid asterisk", "system:bootstrappers:*", false},
{"trailing colon", "system:bootstrappers:foo:", false},
{"trailing dash", "system:bootstrappers:foo-", false},
{"script tags", "system:bootstrappers:<script> alert(\"scary?!\") </script>", false},
{"too long", "system:bootstrappers:" + strings.Repeat("x", 300), false},
}
for _, test := range tests {
err := ValidateBootstrapGroupName(test.input)
if err != nil && test.valid {
t.Errorf("test %q: ValidateBootstrapGroupName(%q) returned unexpected error: %v", test.name, test.input, err)
}
if err == nil && !test.valid {
t.Errorf("test %q: ValidateBootstrapGroupName(%q) was supposed to return an error but didn't", test.name, test.input)
}
}
}
func TestValidateUsages(t *testing.T) {
tests := []struct {
name string
input []string
valid bool
}{
{"valid of signing", []string{"signing"}, true},
{"valid of authentication", []string{"authentication"}, true},
{"all valid", []string{"authentication", "signing"}, true},
{"single invalid", []string{"authentication", "foo"}, false},
{"all invalid", []string{"foo", "bar"}, false},
}
for _, test := range tests {
err := ValidateUsages(test.input)
if err != nil && test.valid {
t.Errorf("test %q: ValidateUsages(%v) returned unexpected error: %v", test.name, test.input, err)
}
if err == nil && !test.valid {
t.Errorf("test %q: ValidateUsages(%v) was supposed to return an error but didn't", test.name, test.input)
}
}
}

View File

@@ -179,7 +179,7 @@ func (c *ExpirationCache) Delete(obj interface{}) error {
func (c *ExpirationCache) Replace(list []interface{}, resourceVersion string) error {
c.expirationLock.Lock()
defer c.expirationLock.Unlock()
items := map[string]interface{}{}
items := make(map[string]interface{}, len(list))
ts := c.clock.Now()
for _, item := range list {
key, err := c.keyFunc(item)

View File

@@ -297,7 +297,7 @@ func (f *FIFO) Pop(process PopProcessFunc) (interface{}, error) {
// after calling this function. f's queue is reset, too; upon return, it
// will contain the items in the map, in no particular order.
func (f *FIFO) Replace(list []interface{}, resourceVersion string) error {
items := map[string]interface{}{}
items := make(map[string]interface{}, len(list))
for _, item := range list {
key, err := f.keyFunc(item)
if err != nil {

View File

@@ -204,7 +204,7 @@ func (h *Heap) AddIfNotPresent(obj interface{}) error {
return nil
}
// addIfNotPresentLocked assumes the lock is already held and adds the the provided
// addIfNotPresentLocked assumes the lock is already held and adds the provided
// item to the queue if it does not already exist.
func (h *Heap) addIfNotPresentLocked(key string, obj interface{}) {
if _, exists := h.data.items[key]; exists {

View File

@@ -17,6 +17,7 @@ limitations under the License.
package cache
import (
"k8s.io/apimachinery/pkg/util/sets"
"strings"
"testing"
@@ -70,60 +71,31 @@ func TestMultiIndexKeys(t *testing.T) {
index.Add(pod2)
index.Add(pod3)
erniePods, err := index.ByIndex("byUser", "ernie")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(erniePods) != 2 {
t.Errorf("Expected 2 pods but got %v", len(erniePods))
}
for _, erniePod := range erniePods {
if erniePod.(*v1.Pod).Name != "one" && erniePod.(*v1.Pod).Name != "tre" {
t.Errorf("Expected only 'one' or 'tre' but got %s", erniePod.(*v1.Pod).Name)
}
}
bertPods, err := index.ByIndex("byUser", "bert")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(bertPods) != 2 {
t.Errorf("Expected 2 pods but got %v", len(bertPods))
}
for _, bertPod := range bertPods {
if bertPod.(*v1.Pod).Name != "one" && bertPod.(*v1.Pod).Name != "two" {
t.Errorf("Expected only 'one' or 'two' but got %s", bertPod.(*v1.Pod).Name)
}
}
oscarPods, err := index.ByIndex("byUser", "oscar")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(oscarPods) != 1 {
t.Errorf("Expected 1 pods but got %v", len(erniePods))
}
for _, oscarPod := range oscarPods {
if oscarPod.(*v1.Pod).Name != "two" {
t.Errorf("Expected only 'two' but got %s", oscarPod.(*v1.Pod).Name)
}
}
ernieAndBertKeys, err := index.Index("byUser", pod1)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(ernieAndBertKeys) != 3 {
t.Errorf("Expected 3 pods but got %v", len(ernieAndBertKeys))
}
for _, ernieAndBertKey := range ernieAndBertKeys {
if ernieAndBertKey.(*v1.Pod).Name != "one" && ernieAndBertKey.(*v1.Pod).Name != "two" && ernieAndBertKey.(*v1.Pod).Name != "tre" {
t.Errorf("Expected only 'one', 'two' or 'tre' but got %s", ernieAndBertKey.(*v1.Pod).Name)
expected := map[string]sets.String{}
expected["ernie"] = sets.NewString("one", "tre")
expected["bert"] = sets.NewString("one", "two")
expected["elmo"] = sets.NewString("tre")
expected["oscar"] = sets.NewString("two")
expected["elmo"] = sets.NewString() // let's just make sure we don't get anything back in this case
{
for k, v := range expected {
found := sets.String{}
indexResults, err := index.ByIndex("byUser", k)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
for _, item := range indexResults {
found.Insert(item.(*v1.Pod).Name)
}
items := v.List()
if !found.HasAll(items...) {
t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List())
}
}
}
index.Delete(pod3)
erniePods, err = index.ByIndex("byUser", "ernie")
erniePods, err := index.ByIndex("byUser", "ernie")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -147,7 +119,7 @@ func TestMultiIndexKeys(t *testing.T) {
copyOfPod2 := pod2.DeepCopy()
copyOfPod2.Annotations["users"] = "oscar"
index.Update(copyOfPod2)
bertPods, err = index.ByIndex("byUser", "bert")
bertPods, err := index.ByIndex("byUser", "bert")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -159,5 +131,4 @@ func TestMultiIndexKeys(t *testing.T) {
t.Errorf("Expected only 'one' but got %s", bertPod.(*v1.Pod).Name)
}
}
}

View File

@@ -86,7 +86,7 @@ func NewSharedIndexInformer(lw ListerWatcher, objType runtime.Object, defaultEve
resyncCheckPeriod: defaultEventHandlerResyncPeriod,
defaultEventHandlerResyncPeriod: defaultEventHandlerResyncPeriod,
cacheMutationDetector: NewCacheMutationDetector(fmt.Sprintf("%T", objType)),
clock: realClock,
clock: realClock,
}
return sharedIndexInformer
}

View File

@@ -210,7 +210,7 @@ func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error)
// 'c' takes ownership of the list, you should not reference the list again
// after calling this function.
func (c *cache) Replace(list []interface{}, resourceVersion string) error {
items := map[string]interface{}{}
items := make(map[string]interface{}, len(list))
for _, item := range list {
key, err := c.keyFunc(item)
if err != nil {

View File

@@ -15,4 +15,5 @@ limitations under the License.
*/
// +k8s:deepcopy-gen=package
package api

View File

@@ -15,4 +15,5 @@ limitations under the License.
*/
// +k8s:deepcopy-gen=package
package v1

View File

@@ -229,11 +229,11 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
if len(configAuthInfo.Token) > 0 {
mergedConfig.BearerToken = configAuthInfo.Token
} else if len(configAuthInfo.TokenFile) > 0 {
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
if err != nil {
ts := restclient.NewCachedFileTokenSource(configAuthInfo.TokenFile)
if _, err := ts.Token(); err != nil {
return nil, err
}
mergedConfig.BearerToken = string(tokenBytes)
mergedConfig.WrapTransport = restclient.TokenSourceWrapTransport(ts)
}
if len(configAuthInfo.Impersonate) > 0 {
mergedConfig.Impersonate = restclient.ImpersonationConfig{

View File

@@ -18,12 +18,14 @@ package clientcmd
import (
"io/ioutil"
"net/http"
"os"
"reflect"
"strings"
"testing"
"github.com/imdario/mergo"
restclient "k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
@@ -243,7 +245,7 @@ func TestCertificateData(t *testing.T) {
config := clientcmdapi.NewConfig()
config.Clusters["clean"] = &clientcmdapi.Cluster{
Server: "https://localhost:8443",
Server: "https://localhost:8443",
CertificateAuthorityData: caData,
}
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
@@ -332,7 +334,19 @@ func TestBasicTokenFile(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
matchStringArg(token, clientConfig.BearerToken, t)
var out *http.Request
clientConfig.WrapTransport(fakeTransport(func(req *http.Request) (*http.Response, error) {
out = req
return &http.Response{}, nil
})).RoundTrip(&http.Request{})
matchStringArg(token, strings.TrimPrefix(out.Header.Get("Authorization"), "Bearer "), t)
}
type fakeTransport func(*http.Request) (*http.Response, error)
func (ft fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return ft(req)
}
func TestPrecedenceTokenFile(t *testing.T) {

View File

@@ -224,7 +224,7 @@ func (le *LeaderElector) renew(ctx context.Context) {
le.maybeReportTransition()
desc := le.config.Lock.Describe()
if err == nil {
glog.V(4).Infof("successfully renewed lease %v", desc)
glog.V(5).Infof("successfully renewed lease %v", desc)
return
}
le.config.Lock.RecordEvent("stopped leading")

View File

@@ -80,7 +80,7 @@ func (cml *ConfigMapLock) Create(ler LeaderElectionRecord) error {
// Update will update an existing annotation on a given resource.
func (cml *ConfigMapLock) Update(ler LeaderElectionRecord) error {
if cml.cm == nil {
return errors.New("endpoint not initialized, call get or create first")
return errors.New("configmap not initialized, call get or create first")
}
recordBytes, err := json.Marshal(ler)
if err != nil {

View File

@@ -39,8 +39,9 @@ const PortForwardProtocolV1Name = "portforward.k8s.io"
// PortForwarder knows how to listen for local connections and forward them to
// a remote pod via an upgraded HTTP request.
type PortForwarder struct {
ports []ForwardedPort
stopChan <-chan struct{}
addresses []listenAddress
ports []ForwardedPort
stopChan <-chan struct{}
dialer httpstream.Dialer
streamConn httpstream.Connection
@@ -110,8 +111,52 @@ func parsePorts(ports []string) ([]ForwardedPort, error) {
return forwards, nil
}
// New creates a new PortForwarder.
type listenAddress struct {
address string
protocol string
failureMode string
}
func parseAddresses(addressesToParse []string) ([]listenAddress, error) {
var addresses []listenAddress
parsed := make(map[string]listenAddress)
for _, address := range addressesToParse {
if address == "localhost" {
ip := listenAddress{address: "127.0.0.1", protocol: "tcp4", failureMode: "all"}
parsed[ip.address] = ip
ip = listenAddress{address: "::1", protocol: "tcp6", failureMode: "all"}
parsed[ip.address] = ip
} else if net.ParseIP(address).To4() != nil {
parsed[address] = listenAddress{address: address, protocol: "tcp4", failureMode: "any"}
} else if net.ParseIP(address) != nil {
parsed[address] = listenAddress{address: address, protocol: "tcp6", failureMode: "any"}
} else {
return nil, fmt.Errorf("%s is not a valid IP", address)
}
}
addresses = make([]listenAddress, len(parsed))
id := 0
for _, v := range parsed {
addresses[id] = v
id++
}
return addresses, nil
}
// New creates a new PortForwarder with localhost listen addresses.
func New(dialer httpstream.Dialer, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) {
return NewOnAddresses(dialer, []string{"localhost"}, ports, stopChan, readyChan, out, errOut)
}
// NewOnAddresses creates a new PortForwarder with custom listen addresses.
func NewOnAddresses(dialer httpstream.Dialer, addresses []string, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) {
if len(addresses) == 0 {
return nil, errors.New("You must specify at least 1 address")
}
parsedAddresses, err := parseAddresses(addresses)
if err != nil {
return nil, err
}
if len(ports) == 0 {
return nil, errors.New("You must specify at least 1 port")
}
@@ -120,12 +165,13 @@ func New(dialer httpstream.Dialer, ports []string, stopChan <-chan struct{}, rea
return nil, err
}
return &PortForwarder{
dialer: dialer,
ports: parsedPorts,
stopChan: stopChan,
Ready: readyChan,
out: out,
errOut: errOut,
dialer: dialer,
addresses: parsedAddresses,
ports: parsedPorts,
stopChan: stopChan,
Ready: readyChan,
out: out,
errOut: errOut,
}, nil
}
@@ -181,13 +227,26 @@ func (pf *PortForwarder) forward() error {
return nil
}
// listenOnPort delegates tcp4 and tcp6 listener creation and waits for connections on both of these addresses.
// If both listener creation fail, an error is raised.
// listenOnPort delegates listener creation and waits for connections on requested bind addresses.
// An error is raised based on address groups (default and localhost) and their failure modes
func (pf *PortForwarder) listenOnPort(port *ForwardedPort) error {
errTcp4 := pf.listenOnPortAndAddress(port, "tcp4", "127.0.0.1")
errTcp6 := pf.listenOnPortAndAddress(port, "tcp6", "::1")
if errTcp4 != nil && errTcp6 != nil {
return fmt.Errorf("All listeners failed to create with the following errors: %s, %s", errTcp4, errTcp6)
var errors []error
failCounters := make(map[string]int, 2)
successCounters := make(map[string]int, 2)
for _, addr := range pf.addresses {
err := pf.listenOnPortAndAddress(port, addr.protocol, addr.address)
if err != nil {
errors = append(errors, err)
failCounters[addr.failureMode]++
} else {
successCounters[addr.failureMode]++
}
}
if successCounters["all"] == 0 && failCounters["all"] > 0 {
return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errors)
}
if failCounters["any"] > 0 {
return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errors)
}
return nil
}
@@ -216,6 +275,7 @@ func (pf *PortForwarder) getListener(protocol string, hostname string, port *For
localPortUInt, err := strconv.ParseUint(localPort, 10, 16)
if err != nil {
fmt.Fprintf(pf.out, "Failed to forward from %s:%d -> %d\n", hostname, localPortUInt, port.Remote)
return nil, fmt.Errorf("Error parsing local port: %s from %s (%s)", err, listenerAddress, host)
}
port.Local = uint16(localPortUInt)
@@ -340,3 +400,20 @@ func (pf *PortForwarder) Close() {
}
}
}
// GetPorts will return the ports that were forwarded; this can be used to
// retrieve the locally-bound port in cases where the input was port 0. This
// function will signal an error if the Ready channel is nil or if the
// listeners are not ready yet; this function will succeed after the Ready
// channel has been closed.
func (pf *PortForwarder) GetPorts() ([]ForwardedPort, error) {
if pf.Ready == nil {
return nil, fmt.Errorf("no Ready channel provided")
}
select {
case <-pf.Ready:
return pf.ports, nil
default:
return nil, fmt.Errorf("listeners not ready")
}
}

View File

@@ -20,6 +20,7 @@ import (
"net"
"os"
"reflect"
"sort"
"strings"
"testing"
@@ -40,23 +41,52 @@ func (d *fakeDialer) Dial(protocols ...string) (httpstream.Connection, string, e
func TestParsePortsAndNew(t *testing.T) {
tests := []struct {
input []string
expected []ForwardedPort
expectParseError bool
expectNewError bool
input []string
addresses []string
expectedPorts []ForwardedPort
expectedAddresses []listenAddress
expectPortParseError bool
expectAddressParseError bool
expectNewError bool
}{
{input: []string{}, expectNewError: true},
{input: []string{"a"}, expectParseError: true, expectNewError: true},
{input: []string{":a"}, expectParseError: true, expectNewError: true},
{input: []string{"-1"}, expectParseError: true, expectNewError: true},
{input: []string{"65536"}, expectParseError: true, expectNewError: true},
{input: []string{"0"}, expectParseError: true, expectNewError: true},
{input: []string{"0:0"}, expectParseError: true, expectNewError: true},
{input: []string{"a:5000"}, expectParseError: true, expectNewError: true},
{input: []string{"5000:a"}, expectParseError: true, expectNewError: true},
{input: []string{"a"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{":a"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"-1"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"65536"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"0"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"0:0"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"a:5000"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"5000:a"}, expectPortParseError: true, expectAddressParseError: false, expectNewError: true},
{input: []string{"5000:5000"}, addresses: []string{"127.0.0.257"}, expectPortParseError: false, expectAddressParseError: true, expectNewError: true},
{input: []string{"5000:5000"}, addresses: []string{"::g"}, expectPortParseError: false, expectAddressParseError: true, expectNewError: true},
{input: []string{"5000:5000"}, addresses: []string{"domain.invalid"}, expectPortParseError: false, expectAddressParseError: true, expectNewError: true},
{
input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"},
expected: []ForwardedPort{
input: []string{"5000:5000"},
addresses: []string{"localhost"},
expectedPorts: []ForwardedPort{
{5000, 5000},
},
expectedAddresses: []listenAddress{
{protocol: "tcp4", address: "127.0.0.1", failureMode: "all"},
{protocol: "tcp6", address: "::1", failureMode: "all"},
},
},
{
input: []string{"5000:5000"},
addresses: []string{"localhost", "127.0.0.1"},
expectedPorts: []ForwardedPort{
{5000, 5000},
},
expectedAddresses: []listenAddress{
{protocol: "tcp4", address: "127.0.0.1", failureMode: "any"},
{protocol: "tcp6", address: "::1", failureMode: "all"},
},
},
{
input: []string{"5000", "5000:5000", "8888:5000", "5000:8888", ":5000", "0:5000"},
addresses: []string{"127.0.0.1", "::1"},
expectedPorts: []ForwardedPort{
{5000, 5000},
{5000, 5000},
{8888, 5000},
@@ -64,34 +94,63 @@ func TestParsePortsAndNew(t *testing.T) {
{0, 5000},
{0, 5000},
},
expectedAddresses: []listenAddress{
{protocol: "tcp4", address: "127.0.0.1", failureMode: "any"},
{protocol: "tcp6", address: "::1", failureMode: "any"},
},
},
}
for i, test := range tests {
parsed, err := parsePorts(test.input)
parsedPorts, err := parsePorts(test.input)
haveError := err != nil
if e, a := test.expectParseError, haveError; e != a {
if e, a := test.expectPortParseError, haveError; e != a {
t.Fatalf("%d: parsePorts: error expected=%t, got %t: %s", i, e, a, err)
}
// default to localhost
if len(test.addresses) == 0 && len(test.expectedAddresses) == 0 {
test.addresses = []string{"localhost"}
test.expectedAddresses = []listenAddress{{protocol: "tcp4", address: "127.0.0.1"}, {protocol: "tcp6", address: "::1"}}
}
// assert address parser
parsedAddresses, err := parseAddresses(test.addresses)
haveError = err != nil
if e, a := test.expectAddressParseError, haveError; e != a {
t.Fatalf("%d: parseAddresses: error expected=%t, got %t: %s", i, e, a, err)
}
dialer := &fakeDialer{}
expectedStopChan := make(chan struct{})
readyChan := make(chan struct{})
pf, err := New(dialer, test.input, expectedStopChan, readyChan, os.Stdout, os.Stderr)
var pf *PortForwarder
if len(test.addresses) > 0 {
pf, err = NewOnAddresses(dialer, test.addresses, test.input, expectedStopChan, readyChan, os.Stdout, os.Stderr)
} else {
pf, err = New(dialer, test.input, expectedStopChan, readyChan, os.Stdout, os.Stderr)
}
haveError = err != nil
if e, a := test.expectNewError, haveError; e != a {
t.Fatalf("%d: New: error expected=%t, got %t: %s", i, e, a, err)
}
if test.expectParseError || test.expectNewError {
if test.expectPortParseError || test.expectAddressParseError || test.expectNewError {
continue
}
for pi, expectedPort := range test.expected {
if e, a := expectedPort.Local, parsed[pi].Local; e != a {
sort.Slice(test.expectedAddresses, func(i, j int) bool { return test.expectedAddresses[i].address < test.expectedAddresses[j].address })
sort.Slice(parsedAddresses, func(i, j int) bool { return parsedAddresses[i].address < parsedAddresses[j].address })
if !reflect.DeepEqual(test.expectedAddresses, parsedAddresses) {
t.Fatalf("%d: expectedAddresses: %v, got: %v", i, test.expectedAddresses, parsedAddresses)
}
for pi, expectedPort := range test.expectedPorts {
if e, a := expectedPort.Local, parsedPorts[pi].Local; e != a {
t.Fatalf("%d: local expected: %d, got: %d", i, e, a)
}
if e, a := expectedPort.Remote, parsed[pi].Remote; e != a {
if e, a := expectedPort.Remote, parsedPorts[pi].Remote; e != a {
t.Fatalf("%d: remote expected: %d, got: %d", i, e, a)
}
}
@@ -99,8 +158,17 @@ func TestParsePortsAndNew(t *testing.T) {
if dialer.dialed {
t.Fatalf("%d: expected not dialed", i)
}
if e, a := test.expected, pf.ports; !reflect.DeepEqual(e, a) {
t.Fatalf("%d: ports: expected %#v, got %#v", i, e, a)
if _, portErr := pf.GetPorts(); portErr == nil {
t.Fatalf("%d: GetPorts: error expected but got nil", i)
}
// mock-signal the Ready channel
close(readyChan)
if ports, portErr := pf.GetPorts(); portErr != nil {
t.Fatalf("%d: GetPorts: unable to retrieve ports: %s", i, portErr)
} else if !reflect.DeepEqual(test.expectedPorts, ports) {
t.Fatalf("%d: ports: expected %#v, got %#v", i, test.expectedPorts, ports)
}
if e, a := expectedStopChan, pf.stopChan; e != a {
t.Fatalf("%d: stopChan: expected %#v, got %#v", i, e, a)