Switch to github.com/golang/dep for vendoring
Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
parent
d6ab91be27
commit
8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions
119
vendor/k8s.io/kubernetes/pkg/util/BUILD
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/util/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"template.go",
|
||||
"trace.go",
|
||||
"trie.go",
|
||||
"umask.go",
|
||||
"util.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/golang/glog"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"template_test.go",
|
||||
"util_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/stretchr/testify/assert",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/diff",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/async:all-srcs",
|
||||
"//pkg/util/bandwidth:all-srcs",
|
||||
"//pkg/util/cert:all-srcs",
|
||||
"//pkg/util/chmod:all-srcs",
|
||||
"//pkg/util/chown:all-srcs",
|
||||
"//pkg/util/clock:all-srcs",
|
||||
"//pkg/util/config:all-srcs",
|
||||
"//pkg/util/configz:all-srcs",
|
||||
"//pkg/util/crlf:all-srcs",
|
||||
"//pkg/util/dbus:all-srcs",
|
||||
"//pkg/util/diff:all-srcs",
|
||||
"//pkg/util/ebtables:all-srcs",
|
||||
"//pkg/util/env:all-srcs",
|
||||
"//pkg/util/errors:all-srcs",
|
||||
"//pkg/util/exec:all-srcs",
|
||||
"//pkg/util/flag:all-srcs",
|
||||
"//pkg/util/flock:all-srcs",
|
||||
"//pkg/util/flowcontrol:all-srcs",
|
||||
"//pkg/util/framer:all-srcs",
|
||||
"//pkg/util/goroutinemap:all-srcs",
|
||||
"//pkg/util/hash:all-srcs",
|
||||
"//pkg/util/homedir:all-srcs",
|
||||
"//pkg/util/httpstream:all-srcs",
|
||||
"//pkg/util/i18n:all-srcs",
|
||||
"//pkg/util/initsystem:all-srcs",
|
||||
"//pkg/util/integer:all-srcs",
|
||||
"//pkg/util/interrupt:all-srcs",
|
||||
"//pkg/util/intstr:all-srcs",
|
||||
"//pkg/util/io:all-srcs",
|
||||
"//pkg/util/iptables:all-srcs",
|
||||
"//pkg/util/json:all-srcs",
|
||||
"//pkg/util/jsonpath:all-srcs",
|
||||
"//pkg/util/keymutex:all-srcs",
|
||||
"//pkg/util/labels:all-srcs",
|
||||
"//pkg/util/limitwriter:all-srcs",
|
||||
"//pkg/util/logs:all-srcs",
|
||||
"//pkg/util/maps:all-srcs",
|
||||
"//pkg/util/metrics:all-srcs",
|
||||
"//pkg/util/mount:all-srcs",
|
||||
"//pkg/util/net:all-srcs",
|
||||
"//pkg/util/netsh:all-srcs",
|
||||
"//pkg/util/node:all-srcs",
|
||||
"//pkg/util/oom:all-srcs",
|
||||
"//pkg/util/parsers:all-srcs",
|
||||
"//pkg/util/procfs:all-srcs",
|
||||
"//pkg/util/proxy:all-srcs",
|
||||
"//pkg/util/rand:all-srcs",
|
||||
"//pkg/util/resourcecontainer:all-srcs",
|
||||
"//pkg/util/rlimit:all-srcs",
|
||||
"//pkg/util/runtime:all-srcs",
|
||||
"//pkg/util/selinux:all-srcs",
|
||||
"//pkg/util/sets:all-srcs",
|
||||
"//pkg/util/slice:all-srcs",
|
||||
"//pkg/util/strategicpatch:all-srcs",
|
||||
"//pkg/util/strings:all-srcs",
|
||||
"//pkg/util/sysctl:all-srcs",
|
||||
"//pkg/util/system:all-srcs",
|
||||
"//pkg/util/taints:all-srcs",
|
||||
"//pkg/util/term:all-srcs",
|
||||
"//pkg/util/testing:all-srcs",
|
||||
"//pkg/util/threading:all-srcs",
|
||||
"//pkg/util/uuid:all-srcs",
|
||||
"//pkg/util/validation:all-srcs",
|
||||
"//pkg/util/version:all-srcs",
|
||||
"//pkg/util/wait:all-srcs",
|
||||
"//pkg/util/workqueue:all-srcs",
|
||||
"//pkg/util/yaml:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
35
vendor/k8s.io/kubernetes/pkg/util/async/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/async/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["runner.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["runner_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
58
vendor/k8s.io/kubernetes/pkg/util/async/runner.go
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/pkg/util/async/runner.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
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 async
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Runner is an abstraction to make it easy to start and stop groups of things that can be
|
||||
// described by a single function which waits on a channel close to exit.
|
||||
type Runner struct {
|
||||
lock sync.Mutex
|
||||
loopFuncs []func(stop chan struct{})
|
||||
stop *chan struct{}
|
||||
}
|
||||
|
||||
// NewRunner makes a runner for the given function(s). The function(s) should loop until
|
||||
// the channel is closed.
|
||||
func NewRunner(f ...func(stop chan struct{})) *Runner {
|
||||
return &Runner{loopFuncs: f}
|
||||
}
|
||||
|
||||
// Start begins running.
|
||||
func (r *Runner) Start() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
if r.stop == nil {
|
||||
c := make(chan struct{})
|
||||
r.stop = &c
|
||||
for i := range r.loopFuncs {
|
||||
go r.loopFuncs[i](*r.stop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops running.
|
||||
func (r *Runner) Stop() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
if r.stop != nil {
|
||||
close(*r.stop)
|
||||
r.stop = nil
|
||||
}
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/util/async/runner_test.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/util/async/runner_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
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 async
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunner(t *testing.T) {
|
||||
var (
|
||||
lock sync.Mutex
|
||||
events []string
|
||||
funcs []func(chan struct{})
|
||||
)
|
||||
done := make(chan struct{}, 20)
|
||||
for i := 0; i < 10; i++ {
|
||||
iCopy := i
|
||||
funcs = append(funcs, func(c chan struct{}) {
|
||||
lock.Lock()
|
||||
events = append(events, fmt.Sprintf("%v starting\n", iCopy))
|
||||
lock.Unlock()
|
||||
<-c
|
||||
lock.Lock()
|
||||
events = append(events, fmt.Sprintf("%v stopping\n", iCopy))
|
||||
lock.Unlock()
|
||||
done <- struct{}{}
|
||||
})
|
||||
}
|
||||
|
||||
r := NewRunner(funcs...)
|
||||
r.Start()
|
||||
r.Stop()
|
||||
for i := 0; i < 10; i++ {
|
||||
<-done
|
||||
}
|
||||
if len(events) != 20 {
|
||||
t.Errorf("expected 20 events, but got:\n%v\n", events)
|
||||
}
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/util/bandwidth/BUILD
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/util/bandwidth/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"fake_shaper.go",
|
||||
"interfaces.go",
|
||||
"linux.go",
|
||||
"utils.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/sets",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"linux_test.go",
|
||||
"utils_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
"//pkg/util/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
18
vendor/k8s.io/kubernetes/pkg/util/bandwidth/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/util/bandwidth/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 bandwidth provides utilities for bandwidth shaping
|
||||
package bandwidth // import "k8s.io/kubernetes/pkg/util/bandwidth"
|
49
vendor/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
)
|
||||
|
||||
type FakeShaper struct {
|
||||
CIDRs []string
|
||||
ResetCIDRs []string
|
||||
}
|
||||
|
||||
func (f *FakeShaper) Limit(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *FakeShaper) Reset(cidr string) error {
|
||||
f.ResetCIDRs = append(f.ResetCIDRs, cidr)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeShaper) ReconcileInterface() error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *FakeShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *FakeShaper) GetCIDRs() ([]string, error) {
|
||||
return f.CIDRs, nil
|
||||
}
|
38
vendor/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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 bandwidth
|
||||
|
||||
import "k8s.io/kubernetes/pkg/api/resource"
|
||||
|
||||
type BandwidthShaper interface {
|
||||
// Limit the bandwidth for a particular CIDR on a particular interface
|
||||
// * ingress and egress are in bits/second
|
||||
// * cidr is expected to be a valid network CIDR (e.g. '1.2.3.4/32' or '10.20.0.1/16')
|
||||
// 'egress' bandwidth limit applies to all packets on the interface whose source matches 'cidr'
|
||||
// 'ingress' bandwidth limit applies to all packets on the interface whose destination matches 'cidr'
|
||||
// Limits are aggregate limits for the CIDR, not per IP address. CIDRs must be unique, but can be overlapping, traffic
|
||||
// that matches multiple CIDRs counts against all limits.
|
||||
Limit(cidr string, egress, ingress *resource.Quantity) error
|
||||
// Remove a bandwidth limit for a particular CIDR on a particular network interface
|
||||
Reset(cidr string) error
|
||||
// Reconcile the interface managed by this shaper with the state on the ground.
|
||||
ReconcileInterface() error
|
||||
// Reconcile a CIDR managed by this shaper with the state on the ground
|
||||
ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error
|
||||
// GetCIDRs returns the set of CIDRs that are being managed by this shaper
|
||||
GetCIDRs() ([]string, error)
|
||||
}
|
322
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux.go
generated
vendored
Normal file
322
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux.go
generated
vendored
Normal file
|
@ -0,0 +1,322 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// tcShaper provides an implementation of the BandwidthShaper interface on Linux using the 'tc' tool.
|
||||
// In general, using this requires that the caller posses the NET_CAP_ADMIN capability, though if you
|
||||
// do this within an container, it only requires the NS_CAPABLE capability for manipulations to that
|
||||
// container's network namespace.
|
||||
// Uses the hierarchical token bucket queuing discipline (htb), this requires Linux 2.4.20 or newer
|
||||
// or a custom kernel with that queuing discipline backported.
|
||||
type tcShaper struct {
|
||||
e exec.Interface
|
||||
iface string
|
||||
}
|
||||
|
||||
func NewTCShaper(iface string) BandwidthShaper {
|
||||
shaper := &tcShaper{
|
||||
e: exec.New(),
|
||||
iface: iface,
|
||||
}
|
||||
return shaper
|
||||
}
|
||||
|
||||
func (t *tcShaper) execAndLog(cmdStr string, args ...string) error {
|
||||
glog.V(6).Infof("Running: %s %s", cmdStr, strings.Join(args, " "))
|
||||
cmd := t.e.Command(cmdStr, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
glog.V(6).Infof("Output from tc: %s", string(out))
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *tcShaper) nextClassID() (int, error) {
|
||||
data, err := t.e.Command("tc", "class", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
classes := sets.String{}
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
// skip empty lines
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(line, " ")
|
||||
// expected tc line:
|
||||
// class htb 1:1 root prio 0 rate 1000Kbit ceil 1000Kbit burst 1600b cburst 1600b
|
||||
if len(parts) != 14 {
|
||||
return -1, fmt.Errorf("unexpected output from tc: %s (%v)", scanner.Text(), parts)
|
||||
}
|
||||
classes.Insert(parts[2])
|
||||
}
|
||||
|
||||
// Make sure it doesn't go forever
|
||||
for nextClass := 1; nextClass < 10000; nextClass++ {
|
||||
if !classes.Has(fmt.Sprintf("1:%d", nextClass)) {
|
||||
return nextClass, nil
|
||||
}
|
||||
}
|
||||
// This should really never happen
|
||||
return -1, fmt.Errorf("exhausted class space, please try again")
|
||||
}
|
||||
|
||||
// Convert a CIDR from text to a hex representation
|
||||
// Strips any masked parts of the IP, so 1.2.3.4/16 becomes hex(1.2.0.0)/ffffffff
|
||||
func hexCIDR(cidr string) (string, error) {
|
||||
ip, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip = ip.Mask(ipnet.Mask)
|
||||
hexIP := hex.EncodeToString([]byte(ip.To4()))
|
||||
hexMask := ipnet.Mask.String()
|
||||
return hexIP + "/" + hexMask, nil
|
||||
}
|
||||
|
||||
// Convert a CIDR from hex representation to text, opposite of the above.
|
||||
func asciiCIDR(cidr string) (string, error) {
|
||||
parts := strings.Split(cidr, "/")
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("unexpected CIDR format: %s", cidr)
|
||||
}
|
||||
ipData, err := hex.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip := net.IP(ipData)
|
||||
|
||||
maskData, err := hex.DecodeString(parts[1])
|
||||
mask := net.IPMask(maskData)
|
||||
size, _ := mask.Size()
|
||||
|
||||
return fmt.Sprintf("%s/%d", ip.String(), size), nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) findCIDRClass(cidr string) (class, handle string, found bool, err error) {
|
||||
data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", "", false, err
|
||||
}
|
||||
|
||||
hex, err := hexCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", "", false, err
|
||||
}
|
||||
spec := fmt.Sprintf("match %s", hex)
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
filter := ""
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "filter") {
|
||||
filter = line
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, spec) {
|
||||
parts := strings.Split(filter, " ")
|
||||
// expected tc line:
|
||||
// filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
|
||||
if len(parts) != 19 {
|
||||
return "", "", false, fmt.Errorf("unexpected output from tc: %s %d (%v)", filter, len(parts), parts)
|
||||
}
|
||||
return parts[18], parts[9], true, nil
|
||||
}
|
||||
}
|
||||
return "", "", false, nil
|
||||
}
|
||||
|
||||
func makeKBitString(rsrc *resource.Quantity) string {
|
||||
return fmt.Sprintf("%dkbit", (rsrc.Value() / 1000))
|
||||
}
|
||||
|
||||
func (t *tcShaper) makeNewClass(rate string) (int, error) {
|
||||
class, err := t.nextClassID()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if err := t.execAndLog("tc", "class", "add",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"classid", fmt.Sprintf("1:%d", class),
|
||||
"htb", "rate", rate); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return class, nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) Limit(cidr string, upload, download *resource.Quantity) (err error) {
|
||||
var downloadClass, uploadClass int
|
||||
if download != nil {
|
||||
if downloadClass, err = t.makeNewClass(makeKBitString(download)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "filter", "add",
|
||||
"dev", t.iface,
|
||||
"protocol", "ip",
|
||||
"parent", "1:0",
|
||||
"prio", "1", "u32",
|
||||
"match", "ip", "dst", cidr,
|
||||
"flowid", fmt.Sprintf("1:%d", downloadClass)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if upload != nil {
|
||||
if uploadClass, err = t.makeNewClass(makeKBitString(upload)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "filter", "add",
|
||||
"dev", t.iface,
|
||||
"protocol", "ip",
|
||||
"parent", "1:0",
|
||||
"prio", "1", "u32",
|
||||
"match", "ip", "src", cidr,
|
||||
"flowid", fmt.Sprintf("1:%d", uploadClass)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tests to see if an interface exists, if it does, return true and the status line for the interface
|
||||
// returns false, "", <err> if an error occurs.
|
||||
func (t *tcShaper) interfaceExists() (bool, string, error) {
|
||||
data, err := t.e.Command("tc", "qdisc", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
value := strings.TrimSpace(string(data))
|
||||
if len(value) == 0 {
|
||||
return false, "", nil
|
||||
}
|
||||
// Newer versions of tc and/or the kernel return the following instead of nothing:
|
||||
// qdisc noqueue 0: root refcnt 2
|
||||
fields := strings.Fields(value)
|
||||
if len(fields) > 1 && fields[1] == "noqueue" {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, value, nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) ReconcileCIDR(cidr string, upload, download *resource.Quantity) error {
|
||||
_, _, found, err := t.findCIDRClass(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return t.Limit(cidr, upload, download)
|
||||
}
|
||||
// TODO: actually check bandwidth limits here
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) ReconcileInterface() error {
|
||||
exists, output, err := t.interfaceExists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
glog.V(4).Info("Didn't find bandwidth interface, creating")
|
||||
return t.initializeInterface()
|
||||
}
|
||||
fields := strings.Split(output, " ")
|
||||
if len(fields) < 12 || fields[1] != "htb" || fields[2] != "1:" {
|
||||
if err := t.deleteInterface(fields[2]); err != nil {
|
||||
return err
|
||||
}
|
||||
return t.initializeInterface()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcShaper) initializeInterface() error {
|
||||
return t.execAndLog("tc", "qdisc", "add", "dev", t.iface, "root", "handle", "1:", "htb", "default", "30")
|
||||
}
|
||||
|
||||
func (t *tcShaper) Reset(cidr string) error {
|
||||
class, handle, found, err := t.findCIDRClass(cidr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("Failed to find cidr: %s on interface: %s", cidr, t.iface)
|
||||
}
|
||||
if err := t.execAndLog("tc", "filter", "del",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"proto", "ip",
|
||||
"prio", "1",
|
||||
"handle", handle, "u32"); err != nil {
|
||||
return err
|
||||
}
|
||||
return t.execAndLog("tc", "class", "del", "dev", t.iface, "parent", "1:", "classid", class)
|
||||
}
|
||||
|
||||
func (t *tcShaper) deleteInterface(class string) error {
|
||||
return t.execAndLog("tc", "qdisc", "delete", "dev", t.iface, "root", "handle", class)
|
||||
}
|
||||
|
||||
func (t *tcShaper) GetCIDRs() ([]string, error) {
|
||||
data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := []string{}
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(data))
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, "match") {
|
||||
parts := strings.Split(line, " ")
|
||||
// expected tc line:
|
||||
// match <cidr> at <number>
|
||||
if len(parts) != 4 {
|
||||
return nil, fmt.Errorf("unexpected output: %v", parts)
|
||||
}
|
||||
cidr, err := asciiCIDR(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, cidr)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
634
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go
generated
vendored
Normal file
634
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,634 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
var tcClassOutput = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
`
|
||||
|
||||
var tcClassOutput2 = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
class htb 1:5 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b
|
||||
`
|
||||
|
||||
func TestNextClassID(t *testing.T) {
|
||||
tests := []struct {
|
||||
output string
|
||||
expectErr bool
|
||||
expected int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
output: tcClassOutput,
|
||||
expected: 5,
|
||||
},
|
||||
{
|
||||
output: "\n",
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
expected: -1,
|
||||
expectErr: true,
|
||||
err: errors.New("test error"),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(test.output), test.err },
|
||||
},
|
||||
}
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd {
|
||||
return exec.InitFakeCmd(&fcmd, cmd, args...)
|
||||
},
|
||||
},
|
||||
}
|
||||
shaper := &tcShaper{e: &fexec}
|
||||
class, err := shaper.nextClassID()
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if class != test.expected {
|
||||
t.Errorf("expected: %d, found %d", test.expected, class)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHexCIDR(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
output string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
input: "1.2.0.0/16",
|
||||
output: "01020000/ffff0000",
|
||||
},
|
||||
{
|
||||
input: "172.17.0.2/32",
|
||||
output: "ac110002/ffffffff",
|
||||
},
|
||||
{
|
||||
input: "foo",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
output, err := hexCIDR(test.input)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if output != test.output {
|
||||
t.Errorf("expected: %s, saw: %s", test.output, output)
|
||||
}
|
||||
input, err := asciiCIDR(output)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if input != test.input {
|
||||
t.Errorf("expected: %s, saw: %s", test.input, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tcFilterOutput = `filter parent 1: protocol ip pref 1 u32
|
||||
filter parent 1: protocol ip pref 1 u32 fh 800: ht divisor 1
|
||||
filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
|
||||
match ac110002/ffffffff at 16
|
||||
filter parent 1: protocol ip pref 1 u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:2
|
||||
match 01020000/ffff0000 at 16
|
||||
`
|
||||
|
||||
func TestFindCIDRClass(t *testing.T) {
|
||||
tests := []struct {
|
||||
cidr string
|
||||
output string
|
||||
expectErr bool
|
||||
expectNotFound bool
|
||||
expectedClass string
|
||||
expectedHandle string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
cidr: "172.17.0.2/32",
|
||||
output: tcFilterOutput,
|
||||
expectedClass: "1:1",
|
||||
expectedHandle: "800::800",
|
||||
},
|
||||
{
|
||||
cidr: "1.2.3.4/16",
|
||||
output: tcFilterOutput,
|
||||
expectedClass: "1:2",
|
||||
expectedHandle: "800::801",
|
||||
},
|
||||
{
|
||||
cidr: "2.2.3.4/16",
|
||||
output: tcFilterOutput,
|
||||
expectNotFound: true,
|
||||
},
|
||||
{
|
||||
err: errors.New("test error"),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(test.output), test.err },
|
||||
},
|
||||
}
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd {
|
||||
return exec.InitFakeCmd(&fcmd, cmd, args...)
|
||||
},
|
||||
},
|
||||
}
|
||||
shaper := &tcShaper{e: &fexec}
|
||||
class, handle, found, err := shaper.findCIDRClass(test.cidr)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if test.expectNotFound {
|
||||
if found {
|
||||
t.Errorf("unexpectedly found an interface: %s %s", class, handle)
|
||||
}
|
||||
} else {
|
||||
if class != test.expectedClass {
|
||||
t.Errorf("expected: %s, found %s", test.expectedClass, class)
|
||||
}
|
||||
if handle != test.expectedHandle {
|
||||
t.Errorf("expected: %s, found %s", test.expectedHandle, handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCIDRs(t *testing.T) {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(tcFilterOutput), nil },
|
||||
},
|
||||
}
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd {
|
||||
return exec.InitFakeCmd(&fcmd, cmd, args...)
|
||||
},
|
||||
},
|
||||
}
|
||||
shaper := &tcShaper{e: &fexec}
|
||||
cidrs, err := shaper.GetCIDRs()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
expectedCidrs := []string{"172.17.0.2/32", "1.2.0.0/16"}
|
||||
if !reflect.DeepEqual(cidrs, expectedCidrs) {
|
||||
t.Errorf("expected: %v, saw: %v", expectedCidrs, cidrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLimit(t *testing.T) {
|
||||
tests := []struct {
|
||||
cidr string
|
||||
ingress *resource.Quantity
|
||||
egress *resource.Quantity
|
||||
expectErr bool
|
||||
expectedCalls int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
cidr: "1.2.3.4/32",
|
||||
ingress: resource.NewQuantity(10, resource.DecimalSI),
|
||||
egress: resource.NewQuantity(20, resource.DecimalSI),
|
||||
expectedCalls: 6,
|
||||
},
|
||||
{
|
||||
cidr: "1.2.3.4/32",
|
||||
ingress: resource.NewQuantity(10, resource.DecimalSI),
|
||||
egress: nil,
|
||||
expectedCalls: 3,
|
||||
},
|
||||
{
|
||||
cidr: "1.2.3.4/32",
|
||||
ingress: nil,
|
||||
egress: resource.NewQuantity(20, resource.DecimalSI),
|
||||
expectedCalls: 3,
|
||||
},
|
||||
{
|
||||
cidr: "1.2.3.4/32",
|
||||
ingress: nil,
|
||||
egress: nil,
|
||||
expectedCalls: 0,
|
||||
},
|
||||
{
|
||||
err: errors.New("test error"),
|
||||
ingress: resource.NewQuantity(10, resource.DecimalSI),
|
||||
egress: resource.NewQuantity(20, resource.DecimalSI),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(tcClassOutput), test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
func() ([]byte, error) { return []byte(tcClassOutput2), test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
iface := "cbr0"
|
||||
shaper := &tcShaper{e: &fexec, iface: iface}
|
||||
if err := shaper.Limit(test.cidr, test.ingress, test.egress); err != nil && !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
} else if err == nil && test.expectErr {
|
||||
t.Error("unexpected non-error")
|
||||
return
|
||||
}
|
||||
// No more testing in the error case
|
||||
if test.expectErr {
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if fcmd.CombinedOutputCalls != test.expectedCalls {
|
||||
t.Errorf("unexpected number of calls: %d, expected: %d", fcmd.CombinedOutputCalls, test.expectedCalls)
|
||||
}
|
||||
|
||||
for ix := range fcmd.CombinedOutputLog {
|
||||
output := fcmd.CombinedOutputLog[ix]
|
||||
if output[0] != "tc" {
|
||||
t.Errorf("unexpected command: %s, expected tc", output[0])
|
||||
}
|
||||
if output[4] != iface {
|
||||
t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output)
|
||||
}
|
||||
if ix == 1 {
|
||||
var expectedRate string
|
||||
if test.ingress != nil {
|
||||
expectedRate = makeKBitString(test.ingress)
|
||||
} else {
|
||||
expectedRate = makeKBitString(test.egress)
|
||||
}
|
||||
if output[11] != expectedRate {
|
||||
t.Errorf("unexpected ingress: %s, expected: %s", output[11], expectedRate)
|
||||
}
|
||||
if output[8] != "1:5" {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[8], "1:5")
|
||||
}
|
||||
}
|
||||
if ix == 2 {
|
||||
if output[15] != test.cidr {
|
||||
t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr)
|
||||
}
|
||||
if output[17] != "1:5" {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5")
|
||||
}
|
||||
}
|
||||
if ix == 4 {
|
||||
if output[11] != makeKBitString(test.egress) {
|
||||
t.Errorf("unexpected egress: %s, expected: %s", output[11], makeKBitString(test.egress))
|
||||
}
|
||||
if output[8] != "1:6" {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[8], "1:6")
|
||||
}
|
||||
}
|
||||
if ix == 5 {
|
||||
if output[15] != test.cidr {
|
||||
t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr)
|
||||
}
|
||||
if output[17] != "1:6" {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
tests := []struct {
|
||||
cidr string
|
||||
err error
|
||||
expectErr bool
|
||||
expectedHandle string
|
||||
expectedClass string
|
||||
}{
|
||||
{
|
||||
cidr: "1.2.3.4/16",
|
||||
expectedHandle: "800::801",
|
||||
expectedClass: "1:2",
|
||||
},
|
||||
{
|
||||
cidr: "172.17.0.2/32",
|
||||
expectedHandle: "800::800",
|
||||
expectedClass: "1:1",
|
||||
},
|
||||
{
|
||||
err: errors.New("test error"),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(tcFilterOutput), test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
func() ([]byte, error) { return []byte{}, test.err },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
iface := "cbr0"
|
||||
shaper := &tcShaper{e: &fexec, iface: iface}
|
||||
|
||||
if err := shaper.Reset(test.cidr); err != nil && !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
} else if test.expectErr && err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
return
|
||||
}
|
||||
|
||||
// No more testing in the error case
|
||||
if test.expectErr {
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if fcmd.CombinedOutputCalls != 3 {
|
||||
t.Errorf("unexpected number of calls: %d, expected: 3", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
|
||||
for ix := range fcmd.CombinedOutputLog {
|
||||
output := fcmd.CombinedOutputLog[ix]
|
||||
if output[0] != "tc" {
|
||||
t.Errorf("unexpected command: %s, expected tc", output[0])
|
||||
}
|
||||
if output[4] != iface {
|
||||
t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output)
|
||||
}
|
||||
if ix == 1 && output[12] != test.expectedHandle {
|
||||
t.Errorf("unexpected handle: %s, expected: %s", output[12], test.expectedHandle)
|
||||
}
|
||||
if ix == 2 && output[8] != test.expectedClass {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[8], test.expectedClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tcQdisc = "qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n"
|
||||
|
||||
func TestReconcileInterfaceExists(t *testing.T) {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(tcQdisc), nil },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
iface := "cbr0"
|
||||
shaper := &tcShaper{e: &fexec, iface: iface}
|
||||
err := shaper.ReconcileInterface()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
|
||||
output := fcmd.CombinedOutputLog[0]
|
||||
if len(output) != 5 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[0] != "tc" {
|
||||
t.Errorf("unexpected command: %s", output[0])
|
||||
}
|
||||
if output[4] != iface {
|
||||
t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
|
||||
}
|
||||
if output[2] != "show" {
|
||||
t.Errorf("unexpected action: %s", output[2])
|
||||
}
|
||||
}
|
||||
|
||||
func testReconcileInterfaceHasNoData(t *testing.T, output string) {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(output), nil },
|
||||
func() ([]byte, error) { return []byte(output), nil },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
iface := "cbr0"
|
||||
shaper := &tcShaper{e: &fexec, iface: iface}
|
||||
err := shaper.ReconcileInterface()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if fcmd.CombinedOutputCalls != 2 {
|
||||
t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
|
||||
for ix, output := range fcmd.CombinedOutputLog {
|
||||
if output[0] != "tc" {
|
||||
t.Errorf("unexpected command: %s", output[0])
|
||||
}
|
||||
if output[4] != iface {
|
||||
t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
|
||||
}
|
||||
if ix == 0 {
|
||||
if len(output) != 5 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[2] != "show" {
|
||||
t.Errorf("unexpected action: %s", output[2])
|
||||
}
|
||||
}
|
||||
if ix == 1 {
|
||||
if len(output) != 11 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[2] != "add" {
|
||||
t.Errorf("unexpected action: %s", output[2])
|
||||
}
|
||||
if output[7] != "1:" {
|
||||
t.Errorf("unexpected root class: %s", output[7])
|
||||
}
|
||||
if output[8] != "htb" {
|
||||
t.Errorf("unexpected qdisc algo: %s", output[8])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcileInterfaceDoesntExist(t *testing.T) {
|
||||
testReconcileInterfaceHasNoData(t, "\n")
|
||||
}
|
||||
|
||||
var tcQdiscNoqueue = "qdisc noqueue 0: root refcnt 2 \n"
|
||||
|
||||
func TestReconcileInterfaceExistsWithNoqueue(t *testing.T) {
|
||||
testReconcileInterfaceHasNoData(t, tcQdiscNoqueue)
|
||||
}
|
||||
|
||||
var tcQdiscWrong = []string{
|
||||
"qdisc htb 2: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n",
|
||||
"qdisc foo 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n",
|
||||
}
|
||||
|
||||
func TestReconcileInterfaceIsWrong(t *testing.T) {
|
||||
for _, test := range tcQdiscWrong {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(test), nil },
|
||||
func() ([]byte, error) { return []byte("\n"), nil },
|
||||
func() ([]byte, error) { return []byte("\n"), nil },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
iface := "cbr0"
|
||||
shaper := &tcShaper{e: &fexec, iface: iface}
|
||||
err := shaper.ReconcileInterface()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if fcmd.CombinedOutputCalls != 3 {
|
||||
t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
|
||||
for ix, output := range fcmd.CombinedOutputLog {
|
||||
if output[0] != "tc" {
|
||||
t.Errorf("unexpected command: %s", output[0])
|
||||
}
|
||||
if output[4] != iface {
|
||||
t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
|
||||
}
|
||||
if ix == 0 {
|
||||
if len(output) != 5 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[2] != "show" {
|
||||
t.Errorf("unexpected action: %s", output[2])
|
||||
}
|
||||
}
|
||||
if ix == 1 {
|
||||
if len(output) != 8 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[2] != "delete" {
|
||||
t.Errorf("unexpected action: %s", output[2])
|
||||
}
|
||||
if output[7] != strings.Split(test, " ")[2] {
|
||||
t.Errorf("unexpected class: %s, expected: %s", output[7], strings.Split(test, " ")[2])
|
||||
}
|
||||
}
|
||||
if ix == 2 {
|
||||
if len(output) != 11 {
|
||||
t.Errorf("unexpected command: %v", output)
|
||||
}
|
||||
if output[7] != "1:" {
|
||||
t.Errorf("unexpected root class: %s", output[7])
|
||||
}
|
||||
if output[8] != "htb" {
|
||||
t.Errorf("unexpected qdisc algo: %s", output[8])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go
generated
vendored
Normal file
52
vendor/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
)
|
||||
|
||||
type unsupportedShaper struct {
|
||||
}
|
||||
|
||||
func NewTCShaper(iface string) BandwidthShaper {
|
||||
return &unsupportedShaper{}
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) Limit(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) Reset(cidr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) ReconcileInterface() error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) ReconcileCIDR(cidr string, egress, ingress *resource.Quantity) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
|
||||
func (f *unsupportedShaper) GetCIDRs() ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
62
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils.go
generated
vendored
Normal file
62
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
)
|
||||
|
||||
var minRsrc = resource.MustParse("1k")
|
||||
var maxRsrc = resource.MustParse("1P")
|
||||
|
||||
func validateBandwidthIsReasonable(rsrc *resource.Quantity) error {
|
||||
if rsrc.Value() < minRsrc.Value() {
|
||||
return fmt.Errorf("resource is unreasonably small (< 1kbit)")
|
||||
}
|
||||
if rsrc.Value() > maxRsrc.Value() {
|
||||
return fmt.Errorf("resoruce is unreasonably large (> 1Pbit)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExtractPodBandwidthResources(podAnnotations map[string]string) (ingress, egress *resource.Quantity, err error) {
|
||||
str, found := podAnnotations["kubernetes.io/ingress-bandwidth"]
|
||||
if found {
|
||||
ingressValue, err := resource.ParseQuantity(str)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ingress = &ingressValue
|
||||
if err := validateBandwidthIsReasonable(ingress); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
str, found = podAnnotations["kubernetes.io/egress-bandwidth"]
|
||||
if found {
|
||||
egressValue, err := resource.ParseQuantity(str)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
egress = &egressValue
|
||||
if err := validateBandwidthIsReasonable(egress); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return ingress, egress, nil
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils_test.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
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 bandwidth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
)
|
||||
|
||||
func TestExtractPodBandwidthResources(t *testing.T) {
|
||||
four, _ := resource.ParseQuantity("4M")
|
||||
ten, _ := resource.ParseQuantity("10M")
|
||||
twenty, _ := resource.ParseQuantity("20M")
|
||||
|
||||
testPod := func(ingress, egress string) *api.Pod {
|
||||
pod := &api.Pod{ObjectMeta: api.ObjectMeta{Annotations: map[string]string{}}}
|
||||
if len(ingress) != 0 {
|
||||
pod.Annotations["kubernetes.io/ingress-bandwidth"] = ingress
|
||||
}
|
||||
if len(egress) != 0 {
|
||||
pod.Annotations["kubernetes.io/egress-bandwidth"] = egress
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
pod *api.Pod
|
||||
expectedIngress *resource.Quantity
|
||||
expectedEgress *resource.Quantity
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
pod: &api.Pod{},
|
||||
},
|
||||
{
|
||||
pod: testPod("10M", ""),
|
||||
expectedIngress: &ten,
|
||||
},
|
||||
{
|
||||
pod: testPod("", "10M"),
|
||||
expectedEgress: &ten,
|
||||
},
|
||||
{
|
||||
pod: testPod("4M", "20M"),
|
||||
expectedIngress: &four,
|
||||
expectedEgress: &twenty,
|
||||
},
|
||||
{
|
||||
pod: testPod("foo", ""),
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
ingress, egress, err := ExtractPodBandwidthResources(test.pod.Annotations)
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(ingress, test.expectedIngress) {
|
||||
t.Errorf("expected: %v, saw: %v", ingress, test.expectedIngress)
|
||||
}
|
||||
if !reflect.DeepEqual(egress, test.expectedEgress) {
|
||||
t.Errorf("expected: %v, saw: %v", egress, test.expectedEgress)
|
||||
}
|
||||
}
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/util/cert/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/util/cert/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cert.go",
|
||||
"csr.go",
|
||||
"io.go",
|
||||
"pem.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["csr_test.go"],
|
||||
data = [
|
||||
"testdata/dontUseThisKey.pem",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/cert/triple:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
207
vendor/k8s.io/kubernetes/pkg/util/cert/cert.go
generated
vendored
Normal file
207
vendor/k8s.io/kubernetes/pkg/util/cert/cert.go
generated
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
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 cert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rsaKeySize = 2048
|
||||
duration365d = time.Hour * 24 * 365
|
||||
)
|
||||
|
||||
// Config containes the basic fields required for creating a certificate
|
||||
type Config struct {
|
||||
CommonName string
|
||||
Organization []string
|
||||
AltNames AltNames
|
||||
}
|
||||
|
||||
// AltNames contains the domain names and IP addresses that will be added
|
||||
// to the API Server's x509 certificate SubAltNames field. The values will
|
||||
// be passed directly to the x509.Certificate object.
|
||||
type AltNames struct {
|
||||
DNSNames []string
|
||||
IPs []net.IP
|
||||
}
|
||||
|
||||
// NewPrivateKey creates an RSA private key
|
||||
func NewPrivateKey() (*rsa.PrivateKey, error) {
|
||||
return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
|
||||
}
|
||||
|
||||
// NewSelfSignedCACert creates a CA certificate
|
||||
func NewSelfSignedCACert(cfg Config, key *rsa.PrivateKey) (*x509.Certificate, error) {
|
||||
now := time.Now()
|
||||
tmpl := x509.Certificate{
|
||||
SerialNumber: new(big.Int).SetInt64(0),
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: cfg.Organization,
|
||||
},
|
||||
NotBefore: now.UTC(),
|
||||
NotAfter: now.Add(duration365d * 10).UTC(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x509.ParseCertificate(certDERBytes)
|
||||
}
|
||||
|
||||
// NewSignedCert creates a signed certificate using the given CA certificate and key
|
||||
func NewSignedCert(cfg Config, key *rsa.PrivateKey, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, error) {
|
||||
serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certTmpl := x509.Certificate{
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: caCert.Subject.Organization,
|
||||
},
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
SerialNumber: serial,
|
||||
NotBefore: caCert.NotBefore,
|
||||
NotAfter: time.Now().Add(duration365d).UTC(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x509.ParseCertificate(certDERBytes)
|
||||
}
|
||||
|
||||
// MakeEllipticPrivateKeyPEM creates an ECDSA private key
|
||||
func MakeEllipticPrivateKeyPEM() ([]byte, error) {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derBytes, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privateKeyPemBlock := &pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: derBytes,
|
||||
}
|
||||
return pem.EncodeToMemory(privateKeyPemBlock), nil
|
||||
}
|
||||
|
||||
// GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
|
||||
// Host may be an IP or a DNS name
|
||||
// You may also specify additional subject alt names (either ip or dns names) for the certificate
|
||||
func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
|
||||
priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(time.Hour * 24 * 365),
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, host)
|
||||
}
|
||||
|
||||
template.IPAddresses = append(template.IPAddresses, alternateIPs...)
|
||||
template.DNSNames = append(template.DNSNames, alternateDNS...)
|
||||
|
||||
derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Generate cert
|
||||
certBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Generate key
|
||||
keyBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return certBuffer.Bytes(), keyBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// FormatBytesCert receives byte array certificate and formats in human-readable format
|
||||
func FormatBytesCert(cert []byte) (string, error) {
|
||||
block, _ := pem.Decode(cert)
|
||||
c, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse certificate [%v]", err)
|
||||
}
|
||||
return FormatCert(c), nil
|
||||
}
|
||||
|
||||
// FormatCert receives certificate and formats in human-readable format
|
||||
func FormatCert(c *x509.Certificate) string {
|
||||
var ips []string
|
||||
for _, ip := range c.IPAddresses {
|
||||
ips = append(ips, ip.String())
|
||||
}
|
||||
altNames := append(ips, c.DNSNames...)
|
||||
res := fmt.Sprintf(
|
||||
"Issuer: CN=%s | Subject: CN=%s | CA: %t\n",
|
||||
c.Issuer.CommonName, c.Subject.CommonName, c.IsCA,
|
||||
)
|
||||
res += fmt.Sprintf("Not before: %s Not After: %s", c.NotBefore, c.NotAfter)
|
||||
if len(altNames) > 0 {
|
||||
res += fmt.Sprintf("\nAlternate Names: %v", altNames)
|
||||
}
|
||||
return res
|
||||
}
|
63
vendor/k8s.io/kubernetes/pkg/util/cert/csr.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/pkg/util/cert/csr.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
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 cert
|
||||
|
||||
import (
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"net"
|
||||
)
|
||||
|
||||
// MakeCSR generates a PEM-encoded CSR using the supplied private key, subject, and SANs.
|
||||
// All key types that are implemented via crypto.Signer are supported (This includes *rsa.PrivateKey and *ecdsa.PrivateKey.)
|
||||
func MakeCSR(privateKey interface{}, subject *pkix.Name, dnsSANs []string, ipSANs []net.IP) (csr []byte, err error) {
|
||||
// Customize the signature for RSA keys, depending on the key size
|
||||
var sigType x509.SignatureAlgorithm
|
||||
if privateKey, ok := privateKey.(*rsa.PrivateKey); ok {
|
||||
keySize := privateKey.N.BitLen()
|
||||
switch {
|
||||
case keySize >= 4096:
|
||||
sigType = x509.SHA512WithRSA
|
||||
case keySize >= 3072:
|
||||
sigType = x509.SHA384WithRSA
|
||||
default:
|
||||
sigType = x509.SHA256WithRSA
|
||||
}
|
||||
}
|
||||
|
||||
template := &x509.CertificateRequest{
|
||||
Subject: *subject,
|
||||
SignatureAlgorithm: sigType,
|
||||
DNSNames: dnsSANs,
|
||||
IPAddresses: ipSANs,
|
||||
}
|
||||
|
||||
csr, err = x509.CreateCertificateRequest(cryptorand.Reader, template, privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
csrPemBlock := &pem.Block{
|
||||
Type: "CERTIFICATE REQUEST",
|
||||
Bytes: csr,
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(csrPemBlock), nil
|
||||
}
|
46
vendor/k8s.io/kubernetes/pkg/util/cert/csr_test.go
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/util/cert/csr_test.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 cert
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMakeCSR(t *testing.T) {
|
||||
keyFile := "testdata/dontUseThisKey.pem"
|
||||
subject := &pkix.Name{
|
||||
CommonName: "kube-worker",
|
||||
}
|
||||
dnsSANs := []string{"localhost"}
|
||||
ipSANs := []net.IP{net.ParseIP("127.0.0.1")}
|
||||
|
||||
keyData, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, err := ParsePrivateKeyPEM(keyData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = MakeCSR(key, subject, dnsSANs, ipSANs)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
129
vendor/k8s.io/kubernetes/pkg/util/cert/io.go
generated
vendored
Normal file
129
vendor/k8s.io/kubernetes/pkg/util/cert/io.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
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 cert
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CanReadCertAndKey returns true if the certificate and key files already exists,
|
||||
// otherwise returns false. If lost one of cert and key, returns error.
|
||||
func CanReadCertAndKey(certPath, keyPath string) (bool, error) {
|
||||
certReadable := canReadFile(certPath)
|
||||
keyReadable := canReadFile(keyPath)
|
||||
|
||||
if certReadable == false && keyReadable == false {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if certReadable == false {
|
||||
return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", certPath)
|
||||
}
|
||||
|
||||
if keyReadable == false {
|
||||
return false, fmt.Errorf("error reading %s, certificate and key must be supplied as a pair", keyPath)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If the file represented by path exists and
|
||||
// readable, returns true otherwise returns false.
|
||||
func canReadFile(path string) bool {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// WriteCert writes the pem-encoded certificate data to certPath.
|
||||
// The certificate file will be created with file mode 0644.
|
||||
// If the certificate file already exists, it will be overwritten.
|
||||
// The parent directory of the certPath will be created as needed with file mode 0755.
|
||||
func WriteCert(certPath string, data []byte) error {
|
||||
if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(certPath, data, os.FileMode(0644)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteKey writes the pem-encoded key data to keyPath.
|
||||
// The key file will be created with file mode 0600.
|
||||
// If the key file already exists, it will be overwritten.
|
||||
// The parent directory of the keyPath will be created as needed with file mode 0755.
|
||||
func WriteKey(keyPath string, data []byte) error {
|
||||
if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(keyPath, data, os.FileMode(0600)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewPool returns an x509.CertPool containing the certificates in the given PEM-encoded file.
|
||||
// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates
|
||||
func NewPool(filename string) (*x509.CertPool, error) {
|
||||
certs, err := CertsFromFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
for _, cert := range certs {
|
||||
pool.AddCert(cert)
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// CertsFromFile returns the x509.Certificates contained in the given PEM-encoded file.
|
||||
// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates
|
||||
func CertsFromFile(file string) ([]*x509.Certificate, error) {
|
||||
pemBlock, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs, err := ParseCertsPEM(pemBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %s", file, err)
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
// PrivateKeyFromFile returns the private key in rsa.PrivateKey or ecdsa.PrivateKey format from a given PEM-encoded file.
|
||||
// Returns an error if the file could not be read or if the private key could not be parsed.
|
||||
func PrivateKeyFromFile(file string) (interface{}, error) {
|
||||
pemBlock, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := ParsePrivateKeyPEM(pemBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading %s: %v", file, err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
107
vendor/k8s.io/kubernetes/pkg/util/cert/pem.go
generated
vendored
Normal file
107
vendor/k8s.io/kubernetes/pkg/util/cert/pem.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
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 cert
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// EncodePublicKeyPEM returns PEM-endcode public data
|
||||
func EncodePublicKeyPEM(key *rsa.PublicKey) ([]byte, error) {
|
||||
der, err := x509.MarshalPKIXPublicKey(key)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
block := pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: der,
|
||||
}
|
||||
return pem.EncodeToMemory(&block), nil
|
||||
}
|
||||
|
||||
// EncodePrivateKeyPEM returns PEM-encoded private key data
|
||||
func EncodePrivateKeyPEM(key *rsa.PrivateKey) []byte {
|
||||
block := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}
|
||||
return pem.EncodeToMemory(&block)
|
||||
}
|
||||
|
||||
// EncodeCertPEM returns PEM-endcoded certificate data
|
||||
func EncodeCertPEM(cert *x509.Certificate) []byte {
|
||||
block := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
return pem.EncodeToMemory(&block)
|
||||
}
|
||||
|
||||
// ParsePrivateKeyPEM returns a private key parsed from a PEM block in the supplied data.
|
||||
// Recognizes PEM blocks for "EC PRIVATE KEY" and "RSA PRIVATE KEY"
|
||||
func ParsePrivateKeyPEM(keyData []byte) (interface{}, error) {
|
||||
for {
|
||||
var privateKeyPemBlock *pem.Block
|
||||
privateKeyPemBlock, keyData = pem.Decode(keyData)
|
||||
if privateKeyPemBlock == nil {
|
||||
// we read all the PEM blocks and didn't recognize one
|
||||
return nil, fmt.Errorf("no private key PEM block found")
|
||||
}
|
||||
|
||||
switch privateKeyPemBlock.Type {
|
||||
case "EC PRIVATE KEY":
|
||||
return x509.ParseECPrivateKey(privateKeyPemBlock.Bytes)
|
||||
case "RSA PRIVATE KEY":
|
||||
return x509.ParsePKCS1PrivateKey(privateKeyPemBlock.Bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCertsPEM returns the x509.Certificates contained in the given PEM-encoded byte array
|
||||
// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates
|
||||
func ParseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) {
|
||||
ok := false
|
||||
certs := []*x509.Certificate{}
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
// Only use PEM "CERTIFICATE" blocks without extra headers
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return certs, err
|
||||
}
|
||||
|
||||
certs = append(certs, cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return certs, errors.New("could not read any certificates")
|
||||
}
|
||||
return certs, nil
|
||||
}
|
6
vendor/k8s.io/kubernetes/pkg/util/cert/testdata/dontUseThisKey.pem
generated
vendored
Normal file
6
vendor/k8s.io/kubernetes/pkg/util/cert/testdata/dontUseThisKey.pem
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MIGkAgEBBDAPEbSXwyDfWf0+61Oofd7aHkmdX69mrzD2Xb1CHF5syfsoRIhnG0dJ
|
||||
ozBulPZCDDWgBwYFK4EEACKhZANiAATjlMJAtKhEPqU/i7MsrgKcK/RmXHC6He7W
|
||||
0p69+9qFXg2raJ9zvvbKxkiu2ELOYRDAz0utcFTBOIgoUJEzBVmsjZQ7dvFa1BKP
|
||||
Ym7MFAKG3O2espBqXn+audgdHGh5B0I=
|
||||
-----END EC PRIVATE KEY-----
|
28
vendor/k8s.io/kubernetes/pkg/util/cert/triple/BUILD
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/util/cert/triple/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["triple.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/cert:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
113
vendor/k8s.io/kubernetes/pkg/util/cert/triple/triple.go
generated
vendored
Normal file
113
vendor/k8s.io/kubernetes/pkg/util/cert/triple/triple.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
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 triple generates key-certificate pairs for the
|
||||
// triple (CA, Server, Client).
|
||||
package triple
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
type KeyPair struct {
|
||||
Key *rsa.PrivateKey
|
||||
Cert *x509.Certificate
|
||||
}
|
||||
|
||||
func NewCA(name string) (*KeyPair, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a private key for a new CA: %v", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: name,
|
||||
}
|
||||
|
||||
cert, err := certutil.NewSelfSignedCACert(config, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a self-signed certificate for a new CA: %v", err)
|
||||
}
|
||||
|
||||
return &KeyPair{
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewServerKeyPair(ca *KeyPair, commonName, svcName, svcNamespace, dnsDomain string, ips, hostnames []string) (*KeyPair, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a server private key: %v", err)
|
||||
}
|
||||
|
||||
namespacedName := fmt.Sprintf("%s.%s", svcName, svcNamespace)
|
||||
internalAPIServerFQDN := []string{
|
||||
svcName,
|
||||
namespacedName,
|
||||
fmt.Sprintf("%s.svc", namespacedName),
|
||||
fmt.Sprintf("%s.svc.%s", namespacedName, dnsDomain),
|
||||
}
|
||||
|
||||
altNames := certutil.AltNames{}
|
||||
for _, ipStr := range ips {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip != nil {
|
||||
altNames.IPs = append(altNames.IPs, ip)
|
||||
}
|
||||
}
|
||||
altNames.DNSNames = append(altNames.DNSNames, hostnames...)
|
||||
altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: commonName,
|
||||
AltNames: altNames,
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, ca.Cert, ca.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to sign the server certificate: %v", err)
|
||||
}
|
||||
|
||||
return &KeyPair{
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewClientKeyPair(ca *KeyPair, commonName string) (*KeyPair, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create a client private key: %v", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: commonName,
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, ca.Cert, ca.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to sign the client certificate: %v", err)
|
||||
}
|
||||
|
||||
return &KeyPair{
|
||||
Key: key,
|
||||
Cert: cert,
|
||||
}, nil
|
||||
}
|
30
vendor/k8s.io/kubernetes/pkg/util/chmod/BUILD
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/util/chmod/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"chmod.go",
|
||||
"doc.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
39
vendor/k8s.io/kubernetes/pkg/util/chmod/chmod.go
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/util/chmod/chmod.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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 chmod
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Interface is something that knows how to run the chmod system call.
|
||||
// It is non-recursive.
|
||||
type Interface interface {
|
||||
// Chmod changes the mode of the given file, implementing the same
|
||||
// semantics as os.Chmod.
|
||||
Chmod(path string, filemode os.FileMode) error
|
||||
}
|
||||
|
||||
func New() Interface {
|
||||
return &chmodRunner{}
|
||||
}
|
||||
|
||||
type chmodRunner struct{}
|
||||
|
||||
func (_ *chmodRunner) Chmod(path string, mode os.FileMode) error {
|
||||
return os.Chmod(path, mode)
|
||||
}
|
19
vendor/k8s.io/kubernetes/pkg/util/chmod/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/util/chmod/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 chown provides an interface and implementations
|
||||
// for things that run run the chmod system call.
|
||||
package chmod // import "k8s.io/kubernetes/pkg/util/chmod"
|
30
vendor/k8s.io/kubernetes/pkg/util/chown/BUILD
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/util/chown/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"chown.go",
|
||||
"doc.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
39
vendor/k8s.io/kubernetes/pkg/util/chown/chown.go
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/util/chown/chown.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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 chown
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Interface is something that knows how to run the chown system call.
|
||||
// It is non-recursive.
|
||||
type Interface interface {
|
||||
// Chown changes the owning UID and GID of a file, implementing
|
||||
// the exact same semantics as os.Chown.
|
||||
Chown(path string, uid, gid int) error
|
||||
}
|
||||
|
||||
func New() Interface {
|
||||
return &chownRunner{}
|
||||
}
|
||||
|
||||
type chownRunner struct{}
|
||||
|
||||
func (_ *chownRunner) Chown(path string, uid, gid int) error {
|
||||
return os.Chown(path, uid, gid)
|
||||
}
|
18
vendor/k8s.io/kubernetes/pkg/util/chown/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/util/chown/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 chown provides utilities to chown a path
|
||||
package chown // import "k8s.io/kubernetes/pkg/util/chown"
|
35
vendor/k8s.io/kubernetes/pkg/util/clock/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/clock/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["clock.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["clock_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
218
vendor/k8s.io/kubernetes/pkg/util/clock/clock.go
generated
vendored
Normal file
218
vendor/k8s.io/kubernetes/pkg/util/clock/clock.go
generated
vendored
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
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 clock
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clock allows for injecting fake or real clocks into code that
|
||||
// needs to do arbitrary things based on time.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
Since(time.Time) time.Duration
|
||||
After(d time.Duration) <-chan time.Time
|
||||
Sleep(d time.Duration)
|
||||
Tick(d time.Duration) <-chan time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
_ = Clock(RealClock{})
|
||||
_ = Clock(&FakeClock{})
|
||||
_ = Clock(&IntervalClock{})
|
||||
)
|
||||
|
||||
// RealClock really calls time.Now()
|
||||
type RealClock struct{}
|
||||
|
||||
// Now returns the current time.
|
||||
func (RealClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// Since returns time since the specified timestamp.
|
||||
func (RealClock) Since(ts time.Time) time.Duration {
|
||||
return time.Since(ts)
|
||||
}
|
||||
|
||||
// Same as time.After(d).
|
||||
func (RealClock) After(d time.Duration) <-chan time.Time {
|
||||
return time.After(d)
|
||||
}
|
||||
|
||||
func (RealClock) Tick(d time.Duration) <-chan time.Time {
|
||||
return time.Tick(d)
|
||||
}
|
||||
|
||||
func (RealClock) Sleep(d time.Duration) {
|
||||
time.Sleep(d)
|
||||
}
|
||||
|
||||
// FakeClock implements Clock, but returns an arbitrary time.
|
||||
type FakeClock struct {
|
||||
lock sync.RWMutex
|
||||
time time.Time
|
||||
|
||||
// waiters are waiting for the fake time to pass their specified time
|
||||
waiters []fakeClockWaiter
|
||||
}
|
||||
|
||||
type fakeClockWaiter struct {
|
||||
targetTime time.Time
|
||||
stepInterval time.Duration
|
||||
skipIfBlocked bool
|
||||
destChan chan<- time.Time
|
||||
}
|
||||
|
||||
func NewFakeClock(t time.Time) *FakeClock {
|
||||
return &FakeClock{
|
||||
time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// Now returns f's time.
|
||||
func (f *FakeClock) Now() time.Time {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return f.time
|
||||
}
|
||||
|
||||
// Since returns time since the time in f.
|
||||
func (f *FakeClock) Since(ts time.Time) time.Duration {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return f.time.Sub(ts)
|
||||
}
|
||||
|
||||
// Fake version of time.After(d).
|
||||
func (f *FakeClock) After(d time.Duration) <-chan time.Time {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
stopTime := f.time.Add(d)
|
||||
ch := make(chan time.Time, 1) // Don't block!
|
||||
f.waiters = append(f.waiters, fakeClockWaiter{
|
||||
targetTime: stopTime,
|
||||
destChan: ch,
|
||||
})
|
||||
return ch
|
||||
}
|
||||
|
||||
func (f *FakeClock) Tick(d time.Duration) <-chan time.Time {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
tickTime := f.time.Add(d)
|
||||
ch := make(chan time.Time, 1) // hold one tick
|
||||
f.waiters = append(f.waiters, fakeClockWaiter{
|
||||
targetTime: tickTime,
|
||||
stepInterval: d,
|
||||
skipIfBlocked: true,
|
||||
destChan: ch,
|
||||
})
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// Move clock by Duration, notify anyone that's called After or Tick
|
||||
func (f *FakeClock) Step(d time.Duration) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
f.setTimeLocked(f.time.Add(d))
|
||||
}
|
||||
|
||||
// Sets the time.
|
||||
func (f *FakeClock) SetTime(t time.Time) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
f.setTimeLocked(t)
|
||||
}
|
||||
|
||||
// Actually changes the time and checks any waiters. f must be write-locked.
|
||||
func (f *FakeClock) setTimeLocked(t time.Time) {
|
||||
f.time = t
|
||||
newWaiters := make([]fakeClockWaiter, 0, len(f.waiters))
|
||||
for i := range f.waiters {
|
||||
w := &f.waiters[i]
|
||||
if !w.targetTime.After(t) {
|
||||
|
||||
if w.skipIfBlocked {
|
||||
select {
|
||||
case w.destChan <- t:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
w.destChan <- t
|
||||
}
|
||||
|
||||
if w.stepInterval > 0 {
|
||||
for !w.targetTime.After(t) {
|
||||
w.targetTime = w.targetTime.Add(w.stepInterval)
|
||||
}
|
||||
newWaiters = append(newWaiters, *w)
|
||||
}
|
||||
|
||||
} else {
|
||||
newWaiters = append(newWaiters, f.waiters[i])
|
||||
}
|
||||
}
|
||||
f.waiters = newWaiters
|
||||
}
|
||||
|
||||
// Returns true if After has been called on f but not yet satisfied (so you can
|
||||
// write race-free tests).
|
||||
func (f *FakeClock) HasWaiters() bool {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
return len(f.waiters) > 0
|
||||
}
|
||||
|
||||
func (f *FakeClock) Sleep(d time.Duration) {
|
||||
f.Step(d)
|
||||
}
|
||||
|
||||
// IntervalClock implements Clock, but each invocation of Now steps the clock forward the specified duration
|
||||
type IntervalClock struct {
|
||||
Time time.Time
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// Now returns i's time.
|
||||
func (i *IntervalClock) Now() time.Time {
|
||||
i.Time = i.Time.Add(i.Duration)
|
||||
return i.Time
|
||||
}
|
||||
|
||||
// Since returns time since the time in i.
|
||||
func (i *IntervalClock) Since(ts time.Time) time.Duration {
|
||||
return i.Time.Sub(ts)
|
||||
}
|
||||
|
||||
// Unimplemented, will panic.
|
||||
// TODO: make interval clock use FakeClock so this can be implemented.
|
||||
func (*IntervalClock) After(d time.Duration) <-chan time.Time {
|
||||
panic("IntervalClock doesn't implement After")
|
||||
}
|
||||
|
||||
// Unimplemented, will panic.
|
||||
// TODO: make interval clock use FakeClock so this can be implemented.
|
||||
func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
|
||||
panic("IntervalClock doesn't implement Tick")
|
||||
}
|
||||
|
||||
func (*IntervalClock) Sleep(d time.Duration) {
|
||||
panic("IntervalClock doesn't implement Sleep")
|
||||
}
|
184
vendor/k8s.io/kubernetes/pkg/util/clock/clock_test.go
generated
vendored
Normal file
184
vendor/k8s.io/kubernetes/pkg/util/clock/clock_test.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
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 clock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFakeClock(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
tc := NewFakeClock(startTime)
|
||||
tc.Step(time.Second)
|
||||
now := tc.Now()
|
||||
if now.Sub(startTime) != time.Second {
|
||||
t.Errorf("input: %s now=%s gap=%s expected=%s", startTime, now, now.Sub(startTime), time.Second)
|
||||
}
|
||||
|
||||
tt := tc.Now()
|
||||
tc.SetTime(tt.Add(time.Hour))
|
||||
if tc.Now().Sub(tt) != time.Hour {
|
||||
t.Errorf("input: %s now=%s gap=%s expected=%s", tt, tc.Now(), tc.Now().Sub(tt), time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeClockSleep(t *testing.T) {
|
||||
startTime := time.Now()
|
||||
tc := NewFakeClock(startTime)
|
||||
tc.Sleep(time.Duration(1) * time.Hour)
|
||||
now := tc.Now()
|
||||
if now.Sub(startTime) != time.Hour {
|
||||
t.Errorf("Fake sleep failed, expected time to advance by one hour, instead, its %v", now.Sub(startTime))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeAfter(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
if tc.HasWaiters() {
|
||||
t.Errorf("unexpected waiter?")
|
||||
}
|
||||
oneSec := tc.After(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("unexpected lack of waiter?")
|
||||
}
|
||||
|
||||
oneOhOneSec := tc.After(time.Second + time.Millisecond)
|
||||
twoSec := tc.After(2 * time.Second)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(999 * time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
// Expected!
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
tc.Step(time.Millisecond)
|
||||
select {
|
||||
case <-oneSec:
|
||||
// should not double-trigger!
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
// Expected!
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeTick(t *testing.T) {
|
||||
tc := NewFakeClock(time.Now())
|
||||
if tc.HasWaiters() {
|
||||
t.Errorf("unexpected waiter?")
|
||||
}
|
||||
oneSec := tc.Tick(time.Second)
|
||||
if !tc.HasWaiters() {
|
||||
t.Errorf("unexpected lack of waiter?")
|
||||
}
|
||||
|
||||
oneOhOneSec := tc.Tick(time.Second + time.Millisecond)
|
||||
twoSec := tc.Tick(2 * time.Second)
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(999 * time.Millisecond) // t=.999
|
||||
select {
|
||||
case <-oneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
}
|
||||
|
||||
tc.Step(time.Millisecond) // t=1.000
|
||||
select {
|
||||
case <-oneSec:
|
||||
// Expected!
|
||||
case <-oneOhOneSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
tc.Step(time.Millisecond) // t=1.001
|
||||
select {
|
||||
case <-oneSec:
|
||||
// should not double-trigger!
|
||||
t.Errorf("unexpected channel read")
|
||||
case <-oneOhOneSec:
|
||||
// Expected!
|
||||
case <-twoSec:
|
||||
t.Errorf("unexpected channel read")
|
||||
default:
|
||||
t.Errorf("unexpected non-channel read")
|
||||
}
|
||||
|
||||
tc.Step(time.Second) // t=2.001
|
||||
tc.Step(time.Second) // t=3.001
|
||||
tc.Step(time.Second) // t=4.001
|
||||
tc.Step(time.Second) // t=5.001
|
||||
|
||||
// The one second ticker should not accumulate ticks
|
||||
accumulatedTicks := 0
|
||||
drained := false
|
||||
for !drained {
|
||||
select {
|
||||
case <-oneSec:
|
||||
accumulatedTicks++
|
||||
default:
|
||||
drained = true
|
||||
}
|
||||
}
|
||||
if accumulatedTicks != 1 {
|
||||
t.Errorf("unexpected number of accumulated ticks: %d", accumulatedTicks)
|
||||
}
|
||||
}
|
51
vendor/k8s.io/kubernetes/pkg/util/config/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/util/config/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"configuration_map.go",
|
||||
"doc.go",
|
||||
"feature_gate.go",
|
||||
"namedcertkey_flag.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"config_test.go",
|
||||
"feature_gate_test.go",
|
||||
"namedcertkey_flag_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/spf13/pflag"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
140
vendor/k8s.io/kubernetes/pkg/util/config/config.go
generated
vendored
Normal file
140
vendor/k8s.io/kubernetes/pkg/util/config/config.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
type Merger interface {
|
||||
// Invoked when a change from a source is received. May also function as an incremental
|
||||
// merger if you wish to consume changes incrementally. Must be reentrant when more than
|
||||
// one source is defined.
|
||||
Merge(source string, update interface{}) error
|
||||
}
|
||||
|
||||
// MergeFunc implements the Merger interface
|
||||
type MergeFunc func(source string, update interface{}) error
|
||||
|
||||
func (f MergeFunc) Merge(source string, update interface{}) error {
|
||||
return f(source, update)
|
||||
}
|
||||
|
||||
// Mux is a class for merging configuration from multiple sources. Changes are
|
||||
// pushed via channels and sent to the merge function.
|
||||
type Mux struct {
|
||||
// Invoked when an update is sent to a source.
|
||||
merger Merger
|
||||
|
||||
// Sources and their lock.
|
||||
sourceLock sync.RWMutex
|
||||
// Maps source names to channels
|
||||
sources map[string]chan interface{}
|
||||
}
|
||||
|
||||
// NewMux creates a new mux that can merge changes from multiple sources.
|
||||
func NewMux(merger Merger) *Mux {
|
||||
mux := &Mux{
|
||||
sources: make(map[string]chan interface{}),
|
||||
merger: merger,
|
||||
}
|
||||
return mux
|
||||
}
|
||||
|
||||
// Channel returns a channel where a configuration source
|
||||
// can send updates of new configurations. Multiple calls with the same
|
||||
// source will return the same channel. This allows change and state based sources
|
||||
// to use the same channel. Different source names however will be treated as a
|
||||
// union.
|
||||
func (m *Mux) Channel(source string) chan interface{} {
|
||||
if len(source) == 0 {
|
||||
panic("Channel given an empty name")
|
||||
}
|
||||
m.sourceLock.Lock()
|
||||
defer m.sourceLock.Unlock()
|
||||
channel, exists := m.sources[source]
|
||||
if exists {
|
||||
return channel
|
||||
}
|
||||
newChannel := make(chan interface{})
|
||||
m.sources[source] = newChannel
|
||||
go wait.Until(func() { m.listen(source, newChannel) }, 0, wait.NeverStop)
|
||||
return newChannel
|
||||
}
|
||||
|
||||
func (m *Mux) listen(source string, listenChannel <-chan interface{}) {
|
||||
for update := range listenChannel {
|
||||
m.merger.Merge(source, update)
|
||||
}
|
||||
}
|
||||
|
||||
// Accessor is an interface for retrieving the current merge state.
|
||||
type Accessor interface {
|
||||
// MergedState returns a representation of the current merge state.
|
||||
// Must be reentrant when more than one source is defined.
|
||||
MergedState() interface{}
|
||||
}
|
||||
|
||||
// AccessorFunc implements the Accessor interface.
|
||||
type AccessorFunc func() interface{}
|
||||
|
||||
func (f AccessorFunc) MergedState() interface{} {
|
||||
return f()
|
||||
}
|
||||
|
||||
type Listener interface {
|
||||
// OnUpdate is invoked when a change is made to an object.
|
||||
OnUpdate(instance interface{})
|
||||
}
|
||||
|
||||
// ListenerFunc receives a representation of the change or object.
|
||||
type ListenerFunc func(instance interface{})
|
||||
|
||||
func (f ListenerFunc) OnUpdate(instance interface{}) {
|
||||
f(instance)
|
||||
}
|
||||
|
||||
type Broadcaster struct {
|
||||
// Listeners for changes and their lock.
|
||||
listenerLock sync.RWMutex
|
||||
listeners []Listener
|
||||
}
|
||||
|
||||
// NewBroadcaster registers a set of listeners that support the Listener interface
|
||||
// and notifies them all on changes.
|
||||
func NewBroadcaster() *Broadcaster {
|
||||
return &Broadcaster{}
|
||||
}
|
||||
|
||||
// Add registers listener to receive updates of changes.
|
||||
func (b *Broadcaster) Add(listener Listener) {
|
||||
b.listenerLock.Lock()
|
||||
defer b.listenerLock.Unlock()
|
||||
b.listeners = append(b.listeners, listener)
|
||||
}
|
||||
|
||||
// Notify notifies all listeners.
|
||||
func (b *Broadcaster) Notify(instance interface{}) {
|
||||
b.listenerLock.RLock()
|
||||
listeners := b.listeners
|
||||
b.listenerLock.RUnlock()
|
||||
for _, listener := range listeners {
|
||||
listener.OnUpdate(instance)
|
||||
}
|
||||
}
|
120
vendor/k8s.io/kubernetes/pkg/util/config/config_test.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/util/config/config_test.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigurationChannels(t *testing.T) {
|
||||
mux := NewMux(nil)
|
||||
channelOne := mux.Channel("one")
|
||||
if channelOne != mux.Channel("one") {
|
||||
t.Error("Didn't get the same muxuration channel back with the same name")
|
||||
}
|
||||
channelTwo := mux.Channel("two")
|
||||
if channelOne == channelTwo {
|
||||
t.Error("Got back the same muxuration channel for different names")
|
||||
}
|
||||
}
|
||||
|
||||
type MergeMock struct {
|
||||
source string
|
||||
update interface{}
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (m MergeMock) Merge(source string, update interface{}) error {
|
||||
if m.source != source {
|
||||
m.t.Errorf("Expected %s, Got %s", m.source, source)
|
||||
}
|
||||
if !reflect.DeepEqual(m.update, update) {
|
||||
m.t.Errorf("Expected %s, Got %s", m.update, update)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMergeInvoked(t *testing.T) {
|
||||
merger := MergeMock{"one", "test", t}
|
||||
mux := NewMux(&merger)
|
||||
mux.Channel("one") <- "test"
|
||||
}
|
||||
|
||||
func TestMergeFuncInvoked(t *testing.T) {
|
||||
ch := make(chan bool)
|
||||
mux := NewMux(MergeFunc(func(source string, update interface{}) error {
|
||||
if source != "one" {
|
||||
t.Errorf("Expected %s, Got %s", "one", source)
|
||||
}
|
||||
if update.(string) != "test" {
|
||||
t.Errorf("Expected %s, Got %s", "test", update)
|
||||
}
|
||||
ch <- true
|
||||
return nil
|
||||
}))
|
||||
mux.Channel("one") <- "test"
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestSimultaneousMerge(t *testing.T) {
|
||||
ch := make(chan bool, 2)
|
||||
mux := NewMux(MergeFunc(func(source string, update interface{}) error {
|
||||
switch source {
|
||||
case "one":
|
||||
if update.(string) != "test" {
|
||||
t.Errorf("Expected %s, Got %s", "test", update)
|
||||
}
|
||||
case "two":
|
||||
if update.(string) != "test2" {
|
||||
t.Errorf("Expected %s, Got %s", "test2", update)
|
||||
}
|
||||
default:
|
||||
t.Errorf("Unexpected source, Got %s", update)
|
||||
}
|
||||
ch <- true
|
||||
return nil
|
||||
}))
|
||||
source := mux.Channel("one")
|
||||
source2 := mux.Channel("two")
|
||||
source <- "test"
|
||||
source2 <- "test2"
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
||||
|
||||
func TestBroadcaster(t *testing.T) {
|
||||
b := NewBroadcaster()
|
||||
b.Notify(struct{}{})
|
||||
|
||||
ch := make(chan bool, 2)
|
||||
b.Add(ListenerFunc(func(object interface{}) {
|
||||
if object != "test" {
|
||||
t.Errorf("Expected %s, Got %s", "test", object)
|
||||
}
|
||||
ch <- true
|
||||
}))
|
||||
b.Add(ListenerFunc(func(object interface{}) {
|
||||
if object != "test" {
|
||||
t.Errorf("Expected %s, Got %s", "test", object)
|
||||
}
|
||||
ch <- true
|
||||
}))
|
||||
b.Notify("test")
|
||||
<-ch
|
||||
<-ch
|
||||
}
|
53
vendor/k8s.io/kubernetes/pkg/util/config/configuration_map.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/util/config/configuration_map.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
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"
|
||||
}
|
20
vendor/k8s.io/kubernetes/pkg/util/config/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/kubernetes/pkg/util/config/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
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 config provides utility objects for decoupling sources of configuration and the
|
||||
// actual configuration state. Consumers must implement the Merger interface to unify
|
||||
// the sources of change into an object.
|
||||
package config // import "k8s.io/kubernetes/pkg/util/config"
|
260
vendor/k8s.io/kubernetes/pkg/util/config/feature_gate.go
generated
vendored
Normal file
260
vendor/k8s.io/kubernetes/pkg/util/config/feature_gate.go
generated
vendored
Normal file
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
flagName = "feature-gates"
|
||||
|
||||
// All known feature keys
|
||||
// To add a new feature, define a key for it below and add
|
||||
// a featureSpec entry to knownFeatures.
|
||||
|
||||
// allAlphaGate is a global toggle for alpha features. Per-feature key
|
||||
// values override the default set by allAlphaGate. Examples:
|
||||
// AllAlpha=false,NewFeature=true will result in newFeature=true
|
||||
// AllAlpha=true,NewFeature=false will result in newFeature=false
|
||||
allAlphaGate = "AllAlpha"
|
||||
externalTrafficLocalOnly = "AllowExtTrafficLocalEndpoints"
|
||||
appArmor = "AppArmor"
|
||||
dynamicKubeletConfig = "DynamicKubeletConfig"
|
||||
dynamicVolumeProvisioning = "DynamicVolumeProvisioning"
|
||||
streamingProxyRedirects = "StreamingProxyRedirects"
|
||||
|
||||
// experimentalHostUserNamespaceDefaulting Default userns=host for containers
|
||||
// that are using other host namespaces, host mounts, the pod contains a privileged container,
|
||||
// or specific non-namespaced capabilities
|
||||
// (MKNOD, SYS_MODULE, SYS_TIME). This should only be enabled if user namespace remapping is enabled
|
||||
// in the docker daemon.
|
||||
experimentalHostUserNamespaceDefaultingGate = "ExperimentalHostUserNamespaceDefaulting"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default values for recorded features. Every new feature gate should be
|
||||
// represented here.
|
||||
knownFeatures = map[string]featureSpec{
|
||||
allAlphaGate: {false, alpha},
|
||||
externalTrafficLocalOnly: {true, beta},
|
||||
appArmor: {true, beta},
|
||||
dynamicKubeletConfig: {false, alpha},
|
||||
dynamicVolumeProvisioning: {true, alpha},
|
||||
streamingProxyRedirects: {false, alpha},
|
||||
experimentalHostUserNamespaceDefaultingGate: {false, alpha},
|
||||
}
|
||||
|
||||
// Special handling for a few gates.
|
||||
specialFeatures = map[string]func(f *featureGate, val bool){
|
||||
allAlphaGate: setUnsetAlphaGates,
|
||||
}
|
||||
|
||||
// DefaultFeatureGate is a shared global FeatureGate.
|
||||
DefaultFeatureGate = &featureGate{
|
||||
known: knownFeatures,
|
||||
special: specialFeatures,
|
||||
}
|
||||
)
|
||||
|
||||
type featureSpec struct {
|
||||
enabled bool
|
||||
prerelease prerelease
|
||||
}
|
||||
|
||||
type prerelease string
|
||||
|
||||
const (
|
||||
// Values for prerelease.
|
||||
alpha = prerelease("ALPHA")
|
||||
beta = prerelease("BETA")
|
||||
ga = prerelease("")
|
||||
)
|
||||
|
||||
// FeatureGate parses and stores flag gates for known features from
|
||||
// a string like feature1=true,feature2=false,...
|
||||
type FeatureGate interface {
|
||||
AddFlag(fs *pflag.FlagSet)
|
||||
Set(value string) error
|
||||
KnownFeatures() []string
|
||||
|
||||
// Every feature gate should add method here following this template:
|
||||
//
|
||||
// // owner: @username
|
||||
// // alpha: v1.4
|
||||
// MyFeature() bool
|
||||
|
||||
// owner: @timstclair
|
||||
// beta: v1.4
|
||||
AppArmor() bool
|
||||
|
||||
// owner: @girishkalele
|
||||
// alpha: v1.4
|
||||
ExternalTrafficLocalOnly() bool
|
||||
|
||||
// owner: @saad-ali
|
||||
// alpha: v1.3
|
||||
DynamicVolumeProvisioning() bool
|
||||
|
||||
// owner: @mtaufen
|
||||
// alpha: v1.4
|
||||
DynamicKubeletConfig() bool
|
||||
|
||||
// owner: timstclair
|
||||
// alpha: v1.5
|
||||
StreamingProxyRedirects() bool
|
||||
|
||||
// owner: @pweil-
|
||||
// alpha: v1.5
|
||||
ExperimentalHostUserNamespaceDefaulting() bool
|
||||
}
|
||||
|
||||
// featureGate implements FeatureGate as well as pflag.Value for flag parsing.
|
||||
type featureGate struct {
|
||||
known map[string]featureSpec
|
||||
special map[string]func(*featureGate, bool)
|
||||
enabled map[string]bool
|
||||
}
|
||||
|
||||
func setUnsetAlphaGates(f *featureGate, val bool) {
|
||||
for k, v := range f.known {
|
||||
if v.prerelease == alpha {
|
||||
if _, found := f.enabled[k]; !found {
|
||||
f.enabled[k] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set, String, and Type implement pflag.Value
|
||||
|
||||
// Set Parses a string of the form // "key1=value1,key2=value2,..." into a
|
||||
// map[string]bool of known keys or returns an error.
|
||||
func (f *featureGate) Set(value string) error {
|
||||
f.enabled = make(map[string]bool)
|
||||
for _, s := range strings.Split(value, ",") {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
arr := strings.SplitN(s, "=", 2)
|
||||
k := strings.TrimSpace(arr[0])
|
||||
_, ok := f.known[k]
|
||||
if !ok {
|
||||
return fmt.Errorf("unrecognized key: %s", k)
|
||||
}
|
||||
if len(arr) != 2 {
|
||||
return fmt.Errorf("missing bool value for %s", k)
|
||||
}
|
||||
v := strings.TrimSpace(arr[1])
|
||||
boolValue, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value of %s: %s, err: %v", k, v, err)
|
||||
}
|
||||
f.enabled[k] = boolValue
|
||||
|
||||
// Handle "special" features like "all alpha gates"
|
||||
if fn, found := f.special[k]; found {
|
||||
fn(f, boolValue)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("feature gates: %v", f.enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *featureGate) String() string {
|
||||
pairs := []string{}
|
||||
for k, v := range f.enabled {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%t", k, v))
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return strings.Join(pairs, ",")
|
||||
}
|
||||
|
||||
func (f *featureGate) Type() string {
|
||||
return "mapStringBool"
|
||||
}
|
||||
|
||||
// ExternalTrafficLocalOnly returns value for AllowExtTrafficLocalEndpoints
|
||||
func (f *featureGate) ExternalTrafficLocalOnly() bool {
|
||||
return f.lookup(externalTrafficLocalOnly)
|
||||
}
|
||||
|
||||
// AppArmor returns the value for the AppArmor feature gate.
|
||||
func (f *featureGate) AppArmor() bool {
|
||||
return f.lookup(appArmor)
|
||||
}
|
||||
|
||||
// DynamicKubeletConfig returns value for dynamicKubeletConfig
|
||||
func (f *featureGate) DynamicKubeletConfig() bool {
|
||||
return f.lookup(dynamicKubeletConfig)
|
||||
}
|
||||
|
||||
// DynamicVolumeProvisioning returns value for dynamicVolumeProvisioning
|
||||
func (f *featureGate) DynamicVolumeProvisioning() bool {
|
||||
return f.lookup(dynamicVolumeProvisioning)
|
||||
}
|
||||
|
||||
// StreamingProxyRedirects controls whether the apiserver should intercept (and follow)
|
||||
// redirects from the backend (Kubelet) for streaming requests (exec/attach/port-forward).
|
||||
func (f *featureGate) StreamingProxyRedirects() bool {
|
||||
return f.lookup(streamingProxyRedirects)
|
||||
}
|
||||
|
||||
// ExperimentalHostUserNamespaceDefaulting returns value for experimentalHostUserNamespaceDefaulting
|
||||
func (f *featureGate) ExperimentalHostUserNamespaceDefaulting() bool {
|
||||
return f.lookup(experimentalHostUserNamespaceDefaultingGate)
|
||||
}
|
||||
|
||||
func (f *featureGate) lookup(key string) bool {
|
||||
defaultValue := f.known[key].enabled
|
||||
if f.enabled != nil {
|
||||
if v, ok := f.enabled[key]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
|
||||
}
|
||||
|
||||
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
|
||||
func (f *featureGate) AddFlag(fs *pflag.FlagSet) {
|
||||
known := f.KnownFeatures()
|
||||
fs.Var(f, flagName, ""+
|
||||
"A set of key=value pairs that describe feature gates for alpha/experimental features. "+
|
||||
"Options are:\n"+strings.Join(known, "\n"))
|
||||
}
|
||||
|
||||
// Returns a string describing the FeatureGate's known features.
|
||||
func (f *featureGate) KnownFeatures() []string {
|
||||
var known []string
|
||||
for k, v := range f.known {
|
||||
pre := ""
|
||||
if v.prerelease != ga {
|
||||
pre = fmt.Sprintf("%s - ", v.prerelease)
|
||||
}
|
||||
known = append(known, fmt.Sprintf("%s=true|false (%sdefault=%t)", k, pre, v.enabled))
|
||||
}
|
||||
sort.Strings(known)
|
||||
return known
|
||||
}
|
159
vendor/k8s.io/kubernetes/pkg/util/config/feature_gate_test.go
generated
vendored
Normal file
159
vendor/k8s.io/kubernetes/pkg/util/config/feature_gate_test.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestFeatureGateFlag(t *testing.T) {
|
||||
// gates for testing
|
||||
const testAlphaGate = "TestAlpha"
|
||||
const testBetaGate = "TestBeta"
|
||||
|
||||
tests := []struct {
|
||||
arg string
|
||||
expect map[string]bool
|
||||
parseError string
|
||||
}{
|
||||
{
|
||||
arg: "",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "fooBarBaz=maybeidk",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
parseError: "unrecognized key: fooBarBaz",
|
||||
},
|
||||
{
|
||||
arg: "AllAlpha=false",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "AllAlpha=true",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: true,
|
||||
testAlphaGate: true,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "AllAlpha=banana",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
parseError: "invalid value of AllAlpha",
|
||||
},
|
||||
{
|
||||
arg: "AllAlpha=false,TestAlpha=true",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: true,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "TestAlpha=true,AllAlpha=false",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: true,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "AllAlpha=true,TestAlpha=false",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: true,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "TestAlpha=false,AllAlpha=true",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: true,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "TestBeta=true,AllAlpha=false",
|
||||
expect: map[string]bool{
|
||||
allAlphaGate: false,
|
||||
testAlphaGate: false,
|
||||
testBetaGate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
fs := pflag.NewFlagSet("testfeaturegateflag", pflag.ContinueOnError)
|
||||
f := DefaultFeatureGate
|
||||
f.known[testAlphaGate] = featureSpec{false, alpha}
|
||||
f.known[testBetaGate] = featureSpec{false, beta}
|
||||
f.AddFlag(fs)
|
||||
|
||||
err := fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)})
|
||||
if test.parseError != "" {
|
||||
if !strings.Contains(err.Error(), test.parseError) {
|
||||
t.Errorf("%d: Parse() Expected %v, Got %v", i, test.parseError, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("%d: Parse() Expected nil, Got %v", i, err)
|
||||
}
|
||||
for k, v := range test.expect {
|
||||
if f.enabled[k] != v {
|
||||
t.Errorf("%d: expected %s=%v, Got %v", i, k, v, f.enabled[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeatureGateFlagDefaults(t *testing.T) {
|
||||
// gates for testing
|
||||
const testAlphaGate = "TestAlpha"
|
||||
const testBetaGate = "TestBeta"
|
||||
|
||||
// Don't parse the flag, assert defaults are used.
|
||||
f := DefaultFeatureGate
|
||||
f.known[testAlphaGate] = featureSpec{false, alpha}
|
||||
f.known[testBetaGate] = featureSpec{true, beta}
|
||||
|
||||
if f.lookup(testAlphaGate) != false {
|
||||
t.Errorf("Expected false")
|
||||
}
|
||||
if f.lookup(testBetaGate) != true {
|
||||
t.Errorf("Expected true")
|
||||
}
|
||||
}
|
113
vendor/k8s.io/kubernetes/pkg/util/config/namedcertkey_flag.go
generated
vendored
Normal file
113
vendor/k8s.io/kubernetes/pkg/util/config/namedcertkey_flag.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
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/kubernetes/pkg/util/config/namedcertkey_flag_test.go
generated
vendored
Normal file
139
vendor/k8s.io/kubernetes/pkg/util/config/namedcertkey_flag_test.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
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 config
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
35
vendor/k8s.io/kubernetes/pkg/util/configz/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/configz/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["configz.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["configz_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
86
vendor/k8s.io/kubernetes/pkg/util/configz/configz.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/util/configz/configz.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
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 configz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
configsGuard sync.RWMutex
|
||||
configs = map[string]*Config{}
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func InstallHandler(m mux) {
|
||||
m.Handle("/configz", http.HandlerFunc(handle))
|
||||
}
|
||||
|
||||
type mux interface {
|
||||
Handle(string, http.Handler)
|
||||
}
|
||||
|
||||
func New(name string) (*Config, error) {
|
||||
configsGuard.Lock()
|
||||
defer configsGuard.Unlock()
|
||||
if _, found := configs[name]; found {
|
||||
return nil, fmt.Errorf("register config %q twice", name)
|
||||
}
|
||||
newConfig := Config{}
|
||||
configs[name] = &newConfig
|
||||
return &newConfig, nil
|
||||
}
|
||||
|
||||
func Delete(name string) {
|
||||
configsGuard.Lock()
|
||||
defer configsGuard.Unlock()
|
||||
delete(configs, name)
|
||||
}
|
||||
|
||||
func (v *Config) Set(val interface{}) {
|
||||
configsGuard.Lock()
|
||||
defer configsGuard.Unlock()
|
||||
v.val = val
|
||||
}
|
||||
|
||||
func (v *Config) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.val)
|
||||
}
|
||||
|
||||
func handle(w http.ResponseWriter, r *http.Request) {
|
||||
if err := write(w); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func write(w io.Writer) error {
|
||||
configsGuard.Lock()
|
||||
defer configsGuard.Unlock()
|
||||
b, err := json.Marshal(configs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling json: %v", err)
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
return err
|
||||
}
|
77
vendor/k8s.io/kubernetes/pkg/util/configz/configz_test.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/util/configz/configz_test.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
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 configz
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigz(t *testing.T) {
|
||||
v, err := New("testing")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
v.Set("blah")
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(handle))
|
||||
defer s.Close()
|
||||
|
||||
resp, err := http.Get(s.URL + "/configz")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if string(body) != `{"testing":"blah"}` {
|
||||
t.Fatalf("unexpected output: %v", err)
|
||||
}
|
||||
|
||||
v.Set("bing")
|
||||
resp, err = http.Get(s.URL + "/configz")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if string(body) != `{"testing":"bing"}` {
|
||||
t.Fatalf("unexpected output: %v", err)
|
||||
}
|
||||
|
||||
Delete("testing")
|
||||
resp, err = http.Get(s.URL + "/configz")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if string(body) != `{}` {
|
||||
t.Fatalf("unexpected output: %v", err)
|
||||
}
|
||||
}
|
27
vendor/k8s.io/kubernetes/pkg/util/crlf/BUILD
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/util/crlf/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["crlf.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
57
vendor/k8s.io/kubernetes/pkg/util/crlf/crlf.go
generated
vendored
Normal file
57
vendor/k8s.io/kubernetes/pkg/util/crlf/crlf.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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 crlf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type crlfWriter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// NewCRLFWriter implements a CR/LF line ending writer used for normalizing
|
||||
// text for Windows platforms.
|
||||
func NewCRLFWriter(w io.Writer) io.Writer {
|
||||
return crlfWriter{w}
|
||||
}
|
||||
|
||||
func (w crlfWriter) Write(b []byte) (n int, err error) {
|
||||
for i, written := 0, 0; ; {
|
||||
next := bytes.Index(b[i:], []byte("\n"))
|
||||
if next == -1 {
|
||||
n, err := w.Writer.Write(b[i:])
|
||||
return written + n, err
|
||||
}
|
||||
next = next + i
|
||||
n, err := w.Writer.Write(b[i:next])
|
||||
if err != nil {
|
||||
return written + n, err
|
||||
}
|
||||
written += n
|
||||
n, err = w.Writer.Write([]byte("\r\n"))
|
||||
if err != nil {
|
||||
if n > 1 {
|
||||
n = 1
|
||||
}
|
||||
return written + n, err
|
||||
}
|
||||
written += 1
|
||||
i = next + 1
|
||||
}
|
||||
}
|
41
vendor/k8s.io/kubernetes/pkg/util/dbus/BUILD
generated
vendored
Normal file
41
vendor/k8s.io/kubernetes/pkg/util/dbus/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"dbus.go",
|
||||
"doc.go",
|
||||
"fake_dbus.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/godbus/dbus"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["dbus_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/godbus/dbus"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
133
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go
generated
vendored
Normal file
133
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
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 dbus
|
||||
|
||||
import (
|
||||
godbus "github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
// Interface is an interface that presents a subset of the godbus/dbus API. Use this
|
||||
// when you want to inject fakeable/mockable D-Bus behavior.
|
||||
type Interface interface {
|
||||
// SystemBus returns a connection to the system bus, connecting to it
|
||||
// first if necessary
|
||||
SystemBus() (Connection, error)
|
||||
// SessionBus returns a connection to the session bus, connecting to it
|
||||
// first if necessary
|
||||
SessionBus() (Connection, error)
|
||||
}
|
||||
|
||||
// Connection represents a D-Bus connection
|
||||
type Connection interface {
|
||||
// Returns an Object representing the bus itself
|
||||
BusObject() Object
|
||||
|
||||
// Object creates a representation of a remote D-Bus object
|
||||
Object(name, path string) Object
|
||||
|
||||
// Signal registers or unregisters a channel to receive D-Bus signals
|
||||
Signal(ch chan<- *godbus.Signal)
|
||||
}
|
||||
|
||||
// Object represents a remote D-Bus object
|
||||
type Object interface {
|
||||
// Call synchronously calls a D-Bus method
|
||||
Call(method string, flags godbus.Flags, args ...interface{}) Call
|
||||
}
|
||||
|
||||
// Call represents a pending or completed D-Bus method call
|
||||
type Call interface {
|
||||
// Store returns a completed call's return values, or an error
|
||||
Store(retvalues ...interface{}) error
|
||||
}
|
||||
|
||||
// Implements Interface in terms of actually talking to D-Bus
|
||||
type dbusImpl struct {
|
||||
systemBus *connImpl
|
||||
sessionBus *connImpl
|
||||
}
|
||||
|
||||
// Implements Connection as a godbus.Conn
|
||||
type connImpl struct {
|
||||
conn *godbus.Conn
|
||||
}
|
||||
|
||||
// Implements Object as a godbus.Object
|
||||
type objectImpl struct {
|
||||
object godbus.BusObject
|
||||
}
|
||||
|
||||
// Implements Call as a godbus.Call
|
||||
type callImpl struct {
|
||||
call *godbus.Call
|
||||
}
|
||||
|
||||
// New returns a new Interface which will use godbus to talk to D-Bus
|
||||
func New() Interface {
|
||||
return &dbusImpl{}
|
||||
}
|
||||
|
||||
// SystemBus is part of Interface
|
||||
func (db *dbusImpl) SystemBus() (Connection, error) {
|
||||
if db.systemBus == nil {
|
||||
bus, err := godbus.SystemBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.systemBus = &connImpl{bus}
|
||||
}
|
||||
|
||||
return db.systemBus, nil
|
||||
}
|
||||
|
||||
// SessionBus is part of Interface
|
||||
func (db *dbusImpl) SessionBus() (Connection, error) {
|
||||
if db.sessionBus == nil {
|
||||
bus, err := godbus.SessionBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.sessionBus = &connImpl{bus}
|
||||
}
|
||||
|
||||
return db.sessionBus, nil
|
||||
}
|
||||
|
||||
// BusObject is part of the Connection interface
|
||||
func (conn *connImpl) BusObject() Object {
|
||||
return &objectImpl{conn.conn.BusObject()}
|
||||
}
|
||||
|
||||
// Object is part of the Connection interface
|
||||
func (conn *connImpl) Object(name, path string) Object {
|
||||
return &objectImpl{conn.conn.Object(name, godbus.ObjectPath(path))}
|
||||
}
|
||||
|
||||
// Signal is part of the Connection interface
|
||||
func (conn *connImpl) Signal(ch chan<- *godbus.Signal) {
|
||||
conn.conn.Signal(ch)
|
||||
}
|
||||
|
||||
// Call is part of the Object interface
|
||||
func (obj *objectImpl) Call(method string, flags godbus.Flags, args ...interface{}) Call {
|
||||
return &callImpl{obj.object.Call(method, flags, args...)}
|
||||
}
|
||||
|
||||
// Store is part of the Call interface
|
||||
func (call *callImpl) Store(retvalues ...interface{}) error {
|
||||
return call.call.Store(retvalues...)
|
||||
}
|
249
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go
generated
vendored
Normal file
249
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go
generated
vendored
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
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 dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
godbus "github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
DBusNameFlagAllowReplacement uint32 = 1 << (iota + 1)
|
||||
DBusNameFlagReplaceExisting
|
||||
DBusNameFlagDoNotQueue
|
||||
)
|
||||
|
||||
const (
|
||||
DBusRequestNameReplyPrimaryOwner uint32 = iota + 1
|
||||
DBusRequestNameReplyInQueue
|
||||
DBusRequestNameReplyExists
|
||||
DBusRequestNameReplyAlreadyOwner
|
||||
)
|
||||
|
||||
const (
|
||||
DBusReleaseNameReplyReleased uint32 = iota + 1
|
||||
DBusReleaseNameReplyNonExistent
|
||||
DBusReleaseNameReplyNotOwner
|
||||
)
|
||||
|
||||
func doDBusTest(t *testing.T, dbus Interface, real bool) {
|
||||
bus, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
if !real {
|
||||
t.Errorf("dbus.SystemBus() failed with fake Interface")
|
||||
}
|
||||
t.Skipf("D-Bus is not running: %v", err)
|
||||
}
|
||||
busObj := bus.BusObject()
|
||||
|
||||
id := ""
|
||||
err = busObj.Call("org.freedesktop.DBus.GetId", 0).Store(&id)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if len(id) == 0 {
|
||||
t.Errorf("expected non-empty Id, got \"\"")
|
||||
}
|
||||
|
||||
// Switch to the session bus for the rest, since the system bus is more
|
||||
// locked down (and thus harder to trick into emitting signals).
|
||||
|
||||
bus, err = dbus.SessionBus()
|
||||
if err != nil {
|
||||
if !real {
|
||||
t.Errorf("dbus.SystemBus() failed with fake Interface")
|
||||
}
|
||||
t.Skipf("D-Bus session bus is not available: %v", err)
|
||||
}
|
||||
busObj = bus.BusObject()
|
||||
|
||||
name := fmt.Sprintf("io.kubernetes.dbus_test_%d", os.Getpid())
|
||||
owner := ""
|
||||
err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner)
|
||||
if err == nil {
|
||||
t.Errorf("expected '%s' to be un-owned, but found owner %s", name, owner)
|
||||
}
|
||||
dbuserr, ok := err.(godbus.Error)
|
||||
if !ok {
|
||||
t.Errorf("expected godbus.Error, but got %#v", err)
|
||||
}
|
||||
if dbuserr.Name != "org.freedesktop.DBus.Error.NameHasNoOwner" {
|
||||
t.Errorf("expected NameHasNoOwner error but got %v", err)
|
||||
}
|
||||
|
||||
sigchan := make(chan *godbus.Signal, 10)
|
||||
bus.Signal(sigchan)
|
||||
|
||||
rule := fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", name)
|
||||
err = busObj.Call("org.freedesktop.DBus.AddMatch", 0, rule).Store()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
|
||||
var ret uint32
|
||||
err = busObj.Call("org.freedesktop.DBus.RequestName", 0, name, DBusNameFlagDoNotQueue).Store(&ret)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if ret != DBusRequestNameReplyPrimaryOwner {
|
||||
t.Errorf("expected %v, got %v", DBusRequestNameReplyPrimaryOwner, ret)
|
||||
}
|
||||
|
||||
err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
|
||||
var changedSignal, acquiredSignal, lostSignal *godbus.Signal
|
||||
|
||||
sig1 := <-sigchan
|
||||
sig2 := <-sigchan
|
||||
// We get two signals, but the order isn't guaranteed
|
||||
if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" {
|
||||
changedSignal = sig1
|
||||
acquiredSignal = sig2
|
||||
} else {
|
||||
acquiredSignal = sig1
|
||||
changedSignal = sig2
|
||||
}
|
||||
|
||||
if acquiredSignal.Sender != "org.freedesktop.DBus" || acquiredSignal.Name != "org.freedesktop.DBus.NameAcquired" {
|
||||
t.Errorf("expected NameAcquired signal, got %v", acquiredSignal)
|
||||
}
|
||||
acquiredName := acquiredSignal.Body[0].(string)
|
||||
if acquiredName != name {
|
||||
t.Errorf("unexpected NameAcquired arguments: %v", acquiredSignal)
|
||||
}
|
||||
|
||||
if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" {
|
||||
t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal)
|
||||
}
|
||||
|
||||
changedName := changedSignal.Body[0].(string)
|
||||
oldOwner := changedSignal.Body[1].(string)
|
||||
newOwner := changedSignal.Body[2].(string)
|
||||
if changedName != name || oldOwner != "" || newOwner != owner {
|
||||
t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal)
|
||||
}
|
||||
|
||||
err = busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&ret)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if ret != DBusReleaseNameReplyReleased {
|
||||
t.Errorf("expected %v, got %v", DBusReleaseNameReplyReleased, ret)
|
||||
}
|
||||
|
||||
sig1 = <-sigchan
|
||||
sig2 = <-sigchan
|
||||
if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" {
|
||||
changedSignal = sig1
|
||||
lostSignal = sig2
|
||||
} else {
|
||||
lostSignal = sig1
|
||||
changedSignal = sig2
|
||||
}
|
||||
|
||||
if lostSignal.Sender != "org.freedesktop.DBus" || lostSignal.Name != "org.freedesktop.DBus.NameLost" {
|
||||
t.Errorf("expected NameLost signal, got %v", lostSignal)
|
||||
}
|
||||
lostName := lostSignal.Body[0].(string)
|
||||
if lostName != name {
|
||||
t.Errorf("unexpected NameLost arguments: %v", lostSignal)
|
||||
}
|
||||
|
||||
if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" {
|
||||
t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal)
|
||||
}
|
||||
|
||||
changedName = changedSignal.Body[0].(string)
|
||||
oldOwner = changedSignal.Body[1].(string)
|
||||
newOwner = changedSignal.Body[2].(string)
|
||||
if changedName != name || oldOwner != owner || newOwner != "" {
|
||||
t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal)
|
||||
}
|
||||
|
||||
if len(sigchan) != 0 {
|
||||
t.Errorf("unexpected extra signals (%d)", len(sigchan))
|
||||
}
|
||||
|
||||
// Unregister sigchan
|
||||
bus.Signal(sigchan)
|
||||
}
|
||||
|
||||
func TestRealDBus(t *testing.T) {
|
||||
dbus := New()
|
||||
doDBusTest(t, dbus, true)
|
||||
}
|
||||
|
||||
func TestFakeDBus(t *testing.T) {
|
||||
uniqueName := ":1.1"
|
||||
ownedName := ""
|
||||
|
||||
fakeSystem := NewFakeConnection()
|
||||
fakeSystem.SetBusObject(
|
||||
func(method string, args ...interface{}) ([]interface{}, error) {
|
||||
if method == "org.freedesktop.DBus.GetId" {
|
||||
return []interface{}{"foo"}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected method call '%s'", method)
|
||||
},
|
||||
)
|
||||
|
||||
fakeSession := NewFakeConnection()
|
||||
fakeSession.SetBusObject(
|
||||
func(method string, args ...interface{}) ([]interface{}, error) {
|
||||
if method == "org.freedesktop.DBus.GetNameOwner" {
|
||||
checkName := args[0].(string)
|
||||
if checkName != ownedName {
|
||||
return nil, godbus.Error{Name: "org.freedesktop.DBus.Error.NameHasNoOwner", Body: nil}
|
||||
} else {
|
||||
return []interface{}{uniqueName}, nil
|
||||
}
|
||||
} else if method == "org.freedesktop.DBus.RequestName" {
|
||||
reqName := args[0].(string)
|
||||
_ = args[1].(uint32)
|
||||
if ownedName != "" {
|
||||
return []interface{}{DBusRequestNameReplyAlreadyOwner}, nil
|
||||
}
|
||||
ownedName = reqName
|
||||
fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", reqName)
|
||||
fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, "", uniqueName)
|
||||
return []interface{}{DBusRequestNameReplyPrimaryOwner}, nil
|
||||
} else if method == "org.freedesktop.DBus.ReleaseName" {
|
||||
reqName := args[0].(string)
|
||||
if reqName != ownedName {
|
||||
return []interface{}{DBusReleaseNameReplyNotOwner}, nil
|
||||
}
|
||||
ownedName = ""
|
||||
fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, uniqueName, "")
|
||||
fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameLost", reqName)
|
||||
return []interface{}{DBusReleaseNameReplyReleased}, nil
|
||||
} else if method == "org.freedesktop.DBus.AddMatch" {
|
||||
return nil, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("unexpected method call '%s'", method)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
dbus := NewFake(fakeSystem, fakeSession)
|
||||
doDBusTest(t, dbus, false)
|
||||
}
|
18
vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 dbus provides an injectable interface and implementations for D-Bus communication
|
||||
package dbus // import "k8s.io/kubernetes/pkg/util/dbus"
|
135
vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
generated
vendored
Normal file
135
vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
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 dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
godbus "github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
// DBusFake is a simple fake Interface type.
|
||||
type DBusFake struct {
|
||||
systemBus *DBusFakeConnection
|
||||
sessionBus *DBusFakeConnection
|
||||
}
|
||||
|
||||
// DBusFakeConnection represents a fake D-Bus connection
|
||||
type DBusFakeConnection struct {
|
||||
busObject *fakeObject
|
||||
objects map[string]*fakeObject
|
||||
signalHandlers []chan<- *godbus.Signal
|
||||
}
|
||||
|
||||
// DBusFakeHandler is used to handle fake D-Bus method calls
|
||||
type DBusFakeHandler func(method string, args ...interface{}) ([]interface{}, error)
|
||||
|
||||
type fakeObject struct {
|
||||
handler DBusFakeHandler
|
||||
}
|
||||
|
||||
type fakeCall struct {
|
||||
ret []interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// NewFake returns a new Interface which will fake talking to D-Bus
|
||||
func NewFake(systemBus *DBusFakeConnection, sessionBus *DBusFakeConnection) *DBusFake {
|
||||
return &DBusFake{systemBus, sessionBus}
|
||||
}
|
||||
|
||||
func NewFakeConnection() *DBusFakeConnection {
|
||||
return &DBusFakeConnection{
|
||||
objects: make(map[string]*fakeObject),
|
||||
}
|
||||
}
|
||||
|
||||
// SystemBus is part of Interface
|
||||
func (db *DBusFake) SystemBus() (Connection, error) {
|
||||
if db.systemBus != nil {
|
||||
return db.systemBus, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("DBus is not running")
|
||||
}
|
||||
}
|
||||
|
||||
// SessionBus is part of Interface
|
||||
func (db *DBusFake) SessionBus() (Connection, error) {
|
||||
if db.sessionBus != nil {
|
||||
return db.sessionBus, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("DBus is not running")
|
||||
}
|
||||
}
|
||||
|
||||
// BusObject is part of the Connection interface
|
||||
func (conn *DBusFakeConnection) BusObject() Object {
|
||||
return conn.busObject
|
||||
}
|
||||
|
||||
// Object is part of the Connection interface
|
||||
func (conn *DBusFakeConnection) Object(name, path string) Object {
|
||||
return conn.objects[name+path]
|
||||
}
|
||||
|
||||
// Signal is part of the Connection interface
|
||||
func (conn *DBusFakeConnection) Signal(ch chan<- *godbus.Signal) {
|
||||
for i := range conn.signalHandlers {
|
||||
if conn.signalHandlers[i] == ch {
|
||||
conn.signalHandlers = append(conn.signalHandlers[:i], conn.signalHandlers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
conn.signalHandlers = append(conn.signalHandlers, ch)
|
||||
}
|
||||
|
||||
// SetBusObject sets the handler for the BusObject of conn
|
||||
func (conn *DBusFakeConnection) SetBusObject(handler DBusFakeHandler) {
|
||||
conn.busObject = &fakeObject{handler}
|
||||
}
|
||||
|
||||
// AddObject adds a handler for the Object at name and path
|
||||
func (conn *DBusFakeConnection) AddObject(name, path string, handler DBusFakeHandler) {
|
||||
conn.objects[name+path] = &fakeObject{handler}
|
||||
}
|
||||
|
||||
// EmitSignal emits a signal on conn
|
||||
func (conn *DBusFakeConnection) EmitSignal(name, path, iface, signal string, args ...interface{}) {
|
||||
sig := &godbus.Signal{
|
||||
Sender: name,
|
||||
Path: godbus.ObjectPath(path),
|
||||
Name: iface + "." + signal,
|
||||
Body: args,
|
||||
}
|
||||
for _, ch := range conn.signalHandlers {
|
||||
ch <- sig
|
||||
}
|
||||
}
|
||||
|
||||
// Call is part of the Object interface
|
||||
func (obj *fakeObject) Call(method string, flags godbus.Flags, args ...interface{}) Call {
|
||||
ret, err := obj.handler(method, args...)
|
||||
return &fakeCall{ret, err}
|
||||
}
|
||||
|
||||
// Store is part of the Call interface
|
||||
func (call *fakeCall) Store(retvalues ...interface{}) error {
|
||||
if call.err != nil {
|
||||
return call.err
|
||||
}
|
||||
return godbus.Store(call.ret, retvalues...)
|
||||
}
|
0
vendor/k8s.io/kubernetes/pkg/util/diff/.readonly
generated
vendored
Normal file
0
vendor/k8s.io/kubernetes/pkg/util/diff/.readonly
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/util/diff/BUILD
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/util/diff/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["diff.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/validation/field:go_default_library",
|
||||
"//vendor:github.com/davecgh/go-spew/spew",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["diff_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
280
vendor/k8s.io/kubernetes/pkg/util/diff/diff.go
generated
vendored
Normal file
280
vendor/k8s.io/kubernetes/pkg/util/diff/diff.go
generated
vendored
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
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 diff
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// StringDiff diffs a and b and returns a human readable diff.
|
||||
func StringDiff(a, b string) string {
|
||||
ba := []byte(a)
|
||||
bb := []byte(b)
|
||||
out := []byte{}
|
||||
i := 0
|
||||
for ; i < len(ba) && i < len(bb); i++ {
|
||||
if ba[i] != bb[i] {
|
||||
break
|
||||
}
|
||||
out = append(out, ba[i])
|
||||
}
|
||||
out = append(out, []byte("\n\nA: ")...)
|
||||
out = append(out, ba[i:]...)
|
||||
out = append(out, []byte("\n\nB: ")...)
|
||||
out = append(out, bb[i:]...)
|
||||
out = append(out, []byte("\n\n")...)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// ObjectDiff writes the two objects out as JSON and prints out the identical part of
|
||||
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
|
||||
// For debugging tests.
|
||||
func ObjectDiff(a, b interface{}) string {
|
||||
ab, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("a: %v", err))
|
||||
}
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("b: %v", err))
|
||||
}
|
||||
return StringDiff(string(ab), string(bb))
|
||||
}
|
||||
|
||||
// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
|
||||
// which shows absolutely everything by recursing into every single pointer
|
||||
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
|
||||
// can't figure out why reflect.DeepEqual is returning false and nothing is
|
||||
// showing you differences. This will.
|
||||
func ObjectGoPrintDiff(a, b interface{}) string {
|
||||
s := spew.ConfigState{DisableMethods: true}
|
||||
return StringDiff(
|
||||
s.Sprintf("%#v", a),
|
||||
s.Sprintf("%#v", b),
|
||||
)
|
||||
}
|
||||
|
||||
func ObjectReflectDiff(a, b interface{}) string {
|
||||
vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
|
||||
if vA.Type() != vB.Type() {
|
||||
return fmt.Sprintf("type A %T and type B %T do not match", a, b)
|
||||
}
|
||||
diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
|
||||
if len(diffs) == 0 {
|
||||
return "<no diffs>"
|
||||
}
|
||||
out := []string{""}
|
||||
for _, d := range diffs {
|
||||
out = append(out,
|
||||
fmt.Sprintf("%s:", d.path),
|
||||
limit(fmt.Sprintf(" a: %#v", d.a), 80),
|
||||
limit(fmt.Sprintf(" b: %#v", d.b), 80),
|
||||
)
|
||||
}
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
|
||||
func limit(s string, max int) string {
|
||||
if len(s) > max {
|
||||
return s[:max]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func public(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return s[:1] == strings.ToUpper(s[:1])
|
||||
}
|
||||
|
||||
type diff struct {
|
||||
path *field.Path
|
||||
a, b interface{}
|
||||
}
|
||||
|
||||
type orderedDiffs []diff
|
||||
|
||||
func (d orderedDiffs) Len() int { return len(d) }
|
||||
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
func (d orderedDiffs) Less(i, j int) bool {
|
||||
a, b := d[i].path.String(), d[j].path.String()
|
||||
if a < b {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
|
||||
switch a.Type().Kind() {
|
||||
case reflect.Struct:
|
||||
var changes []diff
|
||||
for i := 0; i < a.Type().NumField(); i++ {
|
||||
if !public(a.Type().Field(i).Name) {
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
continue
|
||||
}
|
||||
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
|
||||
}
|
||||
if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
|
||||
changes = append(changes, sub...)
|
||||
} else {
|
||||
if !reflect.DeepEqual(a.Field(i).Interface(), b.Field(i).Interface()) {
|
||||
changes = append(changes, diff{path: path, a: a.Field(i).Interface(), b: b.Field(i).Interface()})
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if a.IsNil() || b.IsNil() {
|
||||
switch {
|
||||
case a.IsNil() && b.IsNil():
|
||||
return nil
|
||||
case a.IsNil():
|
||||
return []diff{{path: path, a: nil, b: b.Interface()}}
|
||||
default:
|
||||
return []diff{{path: path, a: a.Interface(), b: nil}}
|
||||
}
|
||||
}
|
||||
return objectReflectDiff(path, a.Elem(), b.Elem())
|
||||
case reflect.Chan:
|
||||
if !reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
return nil
|
||||
case reflect.Slice:
|
||||
lA, lB := a.Len(), b.Len()
|
||||
l := lA
|
||||
if lB < lA {
|
||||
l = lB
|
||||
}
|
||||
if lA == lB && lA == 0 {
|
||||
if a.IsNil() != b.IsNil() {
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
|
||||
return objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))
|
||||
}
|
||||
}
|
||||
var diffs []diff
|
||||
for i := l; i < lA; i++ {
|
||||
diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
|
||||
}
|
||||
for i := l; i < lB; i++ {
|
||||
diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
|
||||
}
|
||||
if len(diffs) == 0 {
|
||||
diffs = append(diffs, diff{path: path, a: a, b: b})
|
||||
}
|
||||
return diffs
|
||||
case reflect.Map:
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return nil
|
||||
}
|
||||
aKeys := make(map[interface{}]interface{})
|
||||
for _, key := range a.MapKeys() {
|
||||
aKeys[key.Interface()] = a.MapIndex(key).Interface()
|
||||
}
|
||||
var missing []diff
|
||||
for _, key := range b.MapKeys() {
|
||||
if _, ok := aKeys[key.Interface()]; ok {
|
||||
delete(aKeys, key.Interface())
|
||||
if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
|
||||
continue
|
||||
}
|
||||
missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
|
||||
continue
|
||||
}
|
||||
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
|
||||
}
|
||||
for key, value := range aKeys {
|
||||
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
|
||||
}
|
||||
if len(missing) == 0 {
|
||||
missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
|
||||
}
|
||||
sort.Sort(orderedDiffs(missing))
|
||||
return missing
|
||||
default:
|
||||
if reflect.DeepEqual(a.Interface(), b.Interface()) {
|
||||
return nil
|
||||
}
|
||||
if !a.CanInterface() {
|
||||
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
|
||||
}
|
||||
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
|
||||
// enabling easy visual scanning for mismatches.
|
||||
func ObjectGoPrintSideBySide(a, b interface{}) string {
|
||||
s := spew.ConfigState{
|
||||
Indent: " ",
|
||||
// Extra deep spew.
|
||||
DisableMethods: true,
|
||||
}
|
||||
sA := s.Sdump(a)
|
||||
sB := s.Sdump(b)
|
||||
|
||||
linesA := strings.Split(sA, "\n")
|
||||
linesB := strings.Split(sB, "\n")
|
||||
width := 0
|
||||
for _, s := range linesA {
|
||||
l := len(s)
|
||||
if l > width {
|
||||
width = l
|
||||
}
|
||||
}
|
||||
for _, s := range linesB {
|
||||
l := len(s)
|
||||
if l > width {
|
||||
width = l
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
|
||||
max := len(linesA)
|
||||
if len(linesB) > max {
|
||||
max = len(linesB)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
var a, b string
|
||||
if i < len(linesA) {
|
||||
a = linesA[i]
|
||||
}
|
||||
if i < len(linesB) {
|
||||
b = linesB[i]
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", a, b)
|
||||
}
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
88
vendor/k8s.io/kubernetes/pkg/util/diff/diff_test.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/util/diff/diff_test.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
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 diff
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestObjectReflectDiff(t *testing.T) {
|
||||
type struct1 struct{ A []int }
|
||||
|
||||
testCases := map[string]struct {
|
||||
a, b interface{}
|
||||
out string
|
||||
}{
|
||||
"map": {
|
||||
a: map[string]int{},
|
||||
b: map[string]int{},
|
||||
},
|
||||
"detect nil map": {
|
||||
a: map[string]int(nil),
|
||||
b: map[string]int{},
|
||||
out: `
|
||||
object:
|
||||
a: map[string]int(nil)
|
||||
b: map[string]int{}`,
|
||||
},
|
||||
"detect map changes": {
|
||||
a: map[string]int{"test": 1, "other": 2},
|
||||
b: map[string]int{"test": 2, "third": 3},
|
||||
out: `
|
||||
object[other]:
|
||||
a: 2
|
||||
b: <nil>
|
||||
object[test]:
|
||||
a: 1
|
||||
b: 2
|
||||
object[third]:
|
||||
a: <nil>
|
||||
b: 3`,
|
||||
},
|
||||
"nil slice": {a: struct1{A: nil}, b: struct1{A: nil}},
|
||||
"empty slice": {a: struct1{A: []int{}}, b: struct1{A: []int{}}},
|
||||
"detect slice changes 1": {a: struct1{A: []int{1}}, b: struct1{A: []int{2}}, out: `
|
||||
object.A[0]:
|
||||
a: 1
|
||||
b: 2`,
|
||||
},
|
||||
"detect slice changes 2": {a: struct1{A: []int{}}, b: struct1{A: []int{2}}, out: `
|
||||
object.A[0]:
|
||||
a: <nil>
|
||||
b: 2`,
|
||||
},
|
||||
"detect slice changes 3": {a: struct1{A: []int{1}}, b: struct1{A: []int{}}, out: `
|
||||
object.A[0]:
|
||||
a: 1
|
||||
b: <nil>`,
|
||||
},
|
||||
"detect nil vs empty slices": {a: struct1{A: nil}, b: struct1{A: []int{}}, out: `
|
||||
object.A:
|
||||
a: []int(nil)
|
||||
b: []int{}`,
|
||||
},
|
||||
}
|
||||
for name, test := range testCases {
|
||||
expect := test.out
|
||||
if len(expect) == 0 {
|
||||
expect = "<no diffs>"
|
||||
}
|
||||
if actual := ObjectReflectDiff(test.a, test.b); actual != expect {
|
||||
t.Errorf("%s: unexpected output: %s", name, actual)
|
||||
}
|
||||
}
|
||||
}
|
20
vendor/k8s.io/kubernetes/pkg/util/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/kubernetes/pkg/util/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
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 util implements various utility functions used in both testing and implementation
|
||||
// of Kubernetes. Package util may not depend on any other package in the Kubernetes
|
||||
// package tree.
|
||||
package util // import "k8s.io/kubernetes/pkg/util"
|
37
vendor/k8s.io/kubernetes/pkg/util/ebtables/BUILD
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/util/ebtables/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["ebtables.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/exec:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["ebtables_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/exec:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
190
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables.go
generated
vendored
Normal file
190
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables.go
generated
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
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 ebtables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
utilexec "k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdebtables = "ebtables"
|
||||
|
||||
// Flag to show full mac in output. The default representation omits leading zeroes.
|
||||
fullMac = "--Lmac2"
|
||||
)
|
||||
|
||||
type RulePosition string
|
||||
|
||||
const (
|
||||
Prepend RulePosition = "-I"
|
||||
Append RulePosition = "-A"
|
||||
)
|
||||
|
||||
type Table string
|
||||
|
||||
const (
|
||||
TableNAT Table = "nat"
|
||||
TableFilter Table = "filter"
|
||||
)
|
||||
|
||||
type Chain string
|
||||
|
||||
const (
|
||||
ChainPostrouting Chain = "POSTROUTING"
|
||||
ChainPrerouting Chain = "PREROUTING"
|
||||
ChainOutput Chain = "OUTPUT"
|
||||
ChainInput Chain = "INPUT"
|
||||
)
|
||||
|
||||
type operation string
|
||||
|
||||
const (
|
||||
opCreateChain operation = "-N"
|
||||
opFlushChain operation = "-F"
|
||||
opDeleteChain operation = "-X"
|
||||
opListChain operation = "-L"
|
||||
opAppendRule operation = "-A"
|
||||
opPrependRule operation = "-I"
|
||||
opDeleteRule operation = "-D"
|
||||
)
|
||||
|
||||
// An injectable interface for running ebtables commands. Implementations must be goroutine-safe.
|
||||
type Interface interface {
|
||||
// GetVersion returns the "X.Y.Z" semver string for ebtables.
|
||||
GetVersion() (string, error)
|
||||
// EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true.
|
||||
// WARNING: ebtables does not provide check operation like iptables do. Hence we have to do a string match of args.
|
||||
// Input args must follow the format and sequence of ebtables list output. Otherwise, EnsureRule will always create
|
||||
// new rules and causing duplicates.
|
||||
EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error)
|
||||
// EnsureChain checks if the specified chain is present and, if not, creates it. If the rule existed, return true.
|
||||
EnsureChain(table Table, chain Chain) (bool, error)
|
||||
// DeleteChain deletes the specified chain. If the chain did not exist, return error.
|
||||
DeleteChain(table Table, chain Chain) error
|
||||
// FlushChain flush the specified chain. If the chain did not exist, return error.
|
||||
FlushChain(table Table, chain Chain) error
|
||||
}
|
||||
|
||||
// runner implements Interface in terms of exec("ebtables").
|
||||
type runner struct {
|
||||
mu sync.Mutex
|
||||
exec utilexec.Interface
|
||||
}
|
||||
|
||||
// New returns a new Interface which will exec ebtables.
|
||||
func New(exec utilexec.Interface) Interface {
|
||||
runner := &runner{
|
||||
exec: exec,
|
||||
}
|
||||
return runner
|
||||
}
|
||||
|
||||
func makeFullArgs(table Table, op operation, chain Chain, args ...string) []string {
|
||||
return append([]string{"-t", string(table), string(op), string(chain)}, args...)
|
||||
}
|
||||
|
||||
// getEbtablesVersionString runs "ebtables --version" to get the version string
|
||||
// in the form "X.X.X"
|
||||
func getEbtablesVersionString(exec utilexec.Interface) (string, error) {
|
||||
// this doesn't access mutable state so we don't need to use the interface / runner
|
||||
bytes, err := exec.Command(cmdebtables, "--version").CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
versionMatcher := regexp.MustCompile("v([0-9]+\\.[0-9]+\\.[0-9]+)")
|
||||
match := versionMatcher.FindStringSubmatch(string(bytes))
|
||||
if match == nil {
|
||||
return "", fmt.Errorf("no ebtables version found in string: %s", bytes)
|
||||
}
|
||||
return match[1], nil
|
||||
}
|
||||
|
||||
func (runner *runner) GetVersion() (string, error) {
|
||||
return getEbtablesVersionString(runner.exec)
|
||||
}
|
||||
|
||||
func (runner *runner) EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) {
|
||||
exist := true
|
||||
fullArgs := makeFullArgs(table, opListChain, chain, fullMac)
|
||||
out, err := runner.exec.Command(cmdebtables, fullArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
exist = false
|
||||
} else {
|
||||
exist = checkIfRuleExists(string(out), args...)
|
||||
}
|
||||
if !exist {
|
||||
fullArgs = makeFullArgs(table, operation(position), chain, args...)
|
||||
out, err := runner.exec.Command(cmdebtables, fullArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return exist, fmt.Errorf("Failed to ensure rule: %v, output: %v", err, string(out))
|
||||
}
|
||||
}
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) {
|
||||
exist := true
|
||||
|
||||
args := makeFullArgs(table, opListChain, chain)
|
||||
_, err := runner.exec.Command(cmdebtables, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
exist = false
|
||||
}
|
||||
if !exist {
|
||||
args = makeFullArgs(table, opCreateChain, chain)
|
||||
out, err := runner.exec.Command(cmdebtables, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return exist, fmt.Errorf("Failed to ensure %v chain: %v, output: %v", chain, err, string(out))
|
||||
}
|
||||
}
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
// checkIfRuleExists takes the output of ebtables list chain and checks if the input rules exists
|
||||
// WARNING: checkIfRuleExists expects the input args matches the format and sequence of ebtables list output
|
||||
func checkIfRuleExists(listChainOutput string, args ...string) bool {
|
||||
rule := strings.Join(args, " ")
|
||||
for _, line := range strings.Split(listChainOutput, "\n") {
|
||||
if strings.TrimSpace(line) == rule {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (runner *runner) DeleteChain(table Table, chain Chain) error {
|
||||
fullArgs := makeFullArgs(table, opDeleteChain, chain)
|
||||
out, err := runner.exec.Command(cmdebtables, fullArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to delete %v chain %v: %v, output: %v", string(table), string(chain), err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (runner *runner) FlushChain(table Table, chain Chain) error {
|
||||
fullArgs := makeFullArgs(table, opFlushChain, chain)
|
||||
out, err := runner.exec.Command(cmdebtables, fullArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to flush %v chain %v: %v, output: %v", string(table), string(chain), err, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
125
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables_test.go
generated
vendored
Normal file
125
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables_test.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
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 ebtables
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/exec"
|
||||
)
|
||||
|
||||
func testEnsureChain(t *testing.T) {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
// Does not Exists
|
||||
func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} },
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Exists
|
||||
func() ([]byte, error) { return nil, nil },
|
||||
// Does not Exists
|
||||
func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 1} },
|
||||
// Fail to create chain
|
||||
func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} },
|
||||
},
|
||||
}
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
|
||||
runner := New(&fexec)
|
||||
exists, err := runner.EnsureChain(TableFilter, "TEST-CHAIN")
|
||||
if exists {
|
||||
t.Errorf("expected exists = false")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expected err = nil")
|
||||
}
|
||||
|
||||
exists, err = runner.EnsureChain(TableFilter, "TEST-CHAIN")
|
||||
if !exists {
|
||||
t.Errorf("expected exists = true")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expected err = nil")
|
||||
}
|
||||
|
||||
exists, err = runner.EnsureChain(TableFilter, "TEST-CHAIN")
|
||||
if exists {
|
||||
t.Errorf("expected exists = false")
|
||||
}
|
||||
errStr := "Failed to ensure TEST-CHAIN chain: exit 2, output:"
|
||||
if err == nil || !strings.Contains(err.Error(), errStr) {
|
||||
t.Errorf("expected error: %q", errStr)
|
||||
}
|
||||
}
|
||||
|
||||
func testEnsureRule(t *testing.T) {
|
||||
fcmd := exec.FakeCmd{
|
||||
CombinedOutputScript: []exec.FakeCombinedOutputAction{
|
||||
// Exists
|
||||
func() ([]byte, error) {
|
||||
return []byte(`Bridge table: filter
|
||||
|
||||
Bridge chain: OUTPUT, entries: 4, policy: ACCEPT
|
||||
-j TEST
|
||||
`), nil
|
||||
},
|
||||
// Does not Exists.
|
||||
func() ([]byte, error) {
|
||||
return []byte(`Bridge table: filter
|
||||
|
||||
Bridge chain: TEST, entries: 0, policy: ACCEPT`), nil
|
||||
},
|
||||
// Fail to create
|
||||
func() ([]byte, error) { return nil, &exec.FakeExitError{Status: 2} },
|
||||
},
|
||||
}
|
||||
fexec := exec.FakeExec{
|
||||
CommandScript: []exec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return exec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
|
||||
runner := New(&fexec)
|
||||
|
||||
exists, err := runner.EnsureRule(Append, TableFilter, ChainOutput, "-j", "TEST")
|
||||
if !exists {
|
||||
t.Errorf("expected exists = true")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("expected err = nil")
|
||||
}
|
||||
|
||||
exists, err = runner.EnsureRule(Append, TableFilter, ChainOutput, "-j", "NEXT-TEST")
|
||||
if exists {
|
||||
t.Errorf("expected exists = false")
|
||||
}
|
||||
errStr := "Failed to ensure rule: exist 2, output: "
|
||||
if err == nil || err.Error() != errStr {
|
||||
t.Errorf("expected error: %q", errStr)
|
||||
}
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/util/env/BUILD
generated
vendored
Normal file
36
vendor/k8s.io/kubernetes/pkg/util/env/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["env.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["env_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/stretchr/testify/assert"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
51
vendor/k8s.io/kubernetes/pkg/util/env/env.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/util/env/env.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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 env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func GetEnvAsStringOrFallback(key, defaultValue string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func GetEnvAsIntOrFallback(key string, defaultValue int) (int, error) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
value, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return defaultValue, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func GetEnvAsFloat64OrFallback(key string, defaultValue float64) (float64, error) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
value, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return defaultValue, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
80
vendor/k8s.io/kubernetes/pkg/util/env/env_test.go
generated
vendored
Normal file
80
vendor/k8s.io/kubernetes/pkg/util/env/env_test.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
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 env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetEnvAsStringOrFallback(t *testing.T) {
|
||||
const expected = "foo"
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
key := "FLOCKER_SET_VAR"
|
||||
os.Setenv(key, expected)
|
||||
assert.Equal(expected, GetEnvAsStringOrFallback(key, "~"+expected))
|
||||
|
||||
key = "FLOCKER_UNSET_VAR"
|
||||
assert.Equal(expected, GetEnvAsStringOrFallback(key, expected))
|
||||
}
|
||||
|
||||
func TestGetEnvAsIntOrFallback(t *testing.T) {
|
||||
const expected = 1
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
key := "FLOCKER_SET_VAR"
|
||||
os.Setenv(key, strconv.Itoa(expected))
|
||||
returnVal, _ := GetEnvAsIntOrFallback(key, 1)
|
||||
assert.Equal(expected, returnVal)
|
||||
|
||||
key = "FLOCKER_UNSET_VAR"
|
||||
returnVal, _ = GetEnvAsIntOrFallback(key, expected)
|
||||
assert.Equal(expected, returnVal)
|
||||
|
||||
key = "FLOCKER_SET_VAR"
|
||||
os.Setenv(key, "not-an-int")
|
||||
returnVal, err := GetEnvAsIntOrFallback(key, 1)
|
||||
assert.Equal(expected, returnVal)
|
||||
assert.EqualError(err, "strconv.ParseInt: parsing \"not-an-int\": invalid syntax")
|
||||
}
|
||||
|
||||
func TestGetEnvAsFloat64OrFallback(t *testing.T) {
|
||||
const expected = 1.0
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
key := "FLOCKER_SET_VAR"
|
||||
os.Setenv(key, "1.0")
|
||||
returnVal, _ := GetEnvAsFloat64OrFallback(key, 2.0)
|
||||
assert.Equal(expected, returnVal)
|
||||
|
||||
key = "FLOCKER_UNSET_VAR"
|
||||
returnVal, _ = GetEnvAsFloat64OrFallback(key, 1.0)
|
||||
assert.Equal(expected, returnVal)
|
||||
|
||||
key = "FLOCKER_SET_VAR"
|
||||
os.Setenv(key, "not-a-float")
|
||||
returnVal, err := GetEnvAsFloat64OrFallback(key, 1.0)
|
||||
assert.Equal(expected, returnVal)
|
||||
assert.EqualError(err, "strconv.ParseFloat: parsing \"not-a-float\": invalid syntax")
|
||||
}
|
326
vendor/k8s.io/kubernetes/pkg/util/errors/errors_test.go
generated
vendored
Normal file
326
vendor/k8s.io/kubernetes/pkg/util/errors/errors_test.go
generated
vendored
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
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 errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmptyAggregate(t *testing.T) {
|
||||
var slice []error
|
||||
var agg Aggregate
|
||||
var err error
|
||||
|
||||
agg = NewAggregate(slice)
|
||||
if agg != nil {
|
||||
t.Errorf("expected nil, got %#v", agg)
|
||||
}
|
||||
err = NewAggregate(slice)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil, got %#v", err)
|
||||
}
|
||||
|
||||
// This is not normally possible, but pedantry demands I test it.
|
||||
agg = aggregate(slice) // empty aggregate
|
||||
if s := agg.Error(); s != "" {
|
||||
t.Errorf("expected empty string, got %q", s)
|
||||
}
|
||||
if s := agg.Errors(); len(s) != 0 {
|
||||
t.Errorf("expected empty slice, got %#v", s)
|
||||
}
|
||||
err = agg.(error)
|
||||
if s := err.Error(); s != "" {
|
||||
t.Errorf("expected empty string, got %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateWithNil(t *testing.T) {
|
||||
var slice []error
|
||||
slice = []error{nil}
|
||||
var agg Aggregate
|
||||
var err error
|
||||
|
||||
agg = NewAggregate(slice)
|
||||
if agg != nil {
|
||||
t.Errorf("expected nil, got %#v", agg)
|
||||
}
|
||||
err = NewAggregate(slice)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil, got %#v", err)
|
||||
}
|
||||
|
||||
// Append a non-nil error
|
||||
slice = append(slice, fmt.Errorf("err"))
|
||||
agg = NewAggregate(slice)
|
||||
if agg == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := agg.Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
if s := agg.Errors(); len(s) != 1 {
|
||||
t.Errorf("expected one-element slice, got %#v", s)
|
||||
}
|
||||
if s := agg.Errors()[0].Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
|
||||
err = agg.(error)
|
||||
if err == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := err.Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingularAggregate(t *testing.T) {
|
||||
var slice []error = []error{fmt.Errorf("err")}
|
||||
var agg Aggregate
|
||||
var err error
|
||||
|
||||
agg = NewAggregate(slice)
|
||||
if agg == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := agg.Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
if s := agg.Errors(); len(s) != 1 {
|
||||
t.Errorf("expected one-element slice, got %#v", s)
|
||||
}
|
||||
if s := agg.Errors()[0].Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
|
||||
err = agg.(error)
|
||||
if err == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := err.Error(); s != "err" {
|
||||
t.Errorf("expected 'err', got %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluralAggregate(t *testing.T) {
|
||||
var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("123")}
|
||||
var agg Aggregate
|
||||
var err error
|
||||
|
||||
agg = NewAggregate(slice)
|
||||
if agg == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := agg.Error(); s != "[abc, 123]" {
|
||||
t.Errorf("expected '[abc, 123]', got %q", s)
|
||||
}
|
||||
if s := agg.Errors(); len(s) != 2 {
|
||||
t.Errorf("expected two-elements slice, got %#v", s)
|
||||
}
|
||||
if s := agg.Errors()[0].Error(); s != "abc" {
|
||||
t.Errorf("expected '[abc, 123]', got %q", s)
|
||||
}
|
||||
|
||||
err = agg.(error)
|
||||
if err == nil {
|
||||
t.Errorf("expected non-nil")
|
||||
}
|
||||
if s := err.Error(); s != "[abc, 123]" {
|
||||
t.Errorf("expected '[abc, 123]', got %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterOut(t *testing.T) {
|
||||
testCases := []struct {
|
||||
err error
|
||||
filter []Matcher
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
nil,
|
||||
[]Matcher{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{},
|
||||
[]Matcher{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
[]Matcher{},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
[]Matcher{func(err error) bool { return false }},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
[]Matcher{func(err error) bool { return true }},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
[]Matcher{func(err error) bool { return false }, func(err error) bool { return false }},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
[]Matcher{func(err error) bool { return false }, func(err error) bool { return true }},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
|
||||
[]Matcher{func(err error) bool { return err.Error() == "def" }},
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("ghi")},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{fmt.Errorf("abc")}},
|
||||
[]Matcher{},
|
||||
aggregate{aggregate{fmt.Errorf("abc")}},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
|
||||
[]Matcher{},
|
||||
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
|
||||
[]Matcher{func(err error) bool { return err.Error() == "def" }},
|
||||
aggregate{aggregate{fmt.Errorf("abc")}},
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
err := FilterOut(testCase.err, testCase.filter...)
|
||||
if !reflect.DeepEqual(testCase.expected, err) {
|
||||
t.Errorf("%d: expected %v, got %v", i, testCase.expected, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatten(t *testing.T) {
|
||||
testCases := []struct {
|
||||
agg Aggregate
|
||||
expected Aggregate
|
||||
}{
|
||||
{
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{fmt.Errorf("abc")}},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{aggregate{fmt.Errorf("abc")}}},
|
||||
aggregate{fmt.Errorf("abc")},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("def")},
|
||||
},
|
||||
{
|
||||
aggregate{aggregate{aggregate{fmt.Errorf("abc")}, fmt.Errorf("def"), aggregate{fmt.Errorf("ghi")}}},
|
||||
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
agg := Flatten(testCase.agg)
|
||||
if !reflect.DeepEqual(testCase.expected, agg) {
|
||||
t.Errorf("%d: expected %v, got %v", i, testCase.expected, agg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAggregateGoroutines(t *testing.T) {
|
||||
testCases := []struct {
|
||||
errs []error
|
||||
expected map[string]bool // can't compare directly to Aggregate due to non-deterministic ordering
|
||||
}{
|
||||
{
|
||||
[]error{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]error{nil},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]error{nil, nil},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]error{fmt.Errorf("1")},
|
||||
map[string]bool{"1": true},
|
||||
},
|
||||
{
|
||||
[]error{fmt.Errorf("1"), nil},
|
||||
map[string]bool{"1": true},
|
||||
},
|
||||
{
|
||||
[]error{fmt.Errorf("1"), fmt.Errorf("267")},
|
||||
map[string]bool{"1": true, "267": true},
|
||||
},
|
||||
{
|
||||
[]error{fmt.Errorf("1"), nil, fmt.Errorf("1234")},
|
||||
map[string]bool{"1": true, "1234": true},
|
||||
},
|
||||
{
|
||||
[]error{nil, fmt.Errorf("1"), nil, fmt.Errorf("1234"), fmt.Errorf("22")},
|
||||
map[string]bool{"1": true, "1234": true, "22": true},
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
funcs := make([]func() error, len(testCase.errs))
|
||||
for i := range testCase.errs {
|
||||
err := testCase.errs[i]
|
||||
funcs[i] = func() error { return err }
|
||||
}
|
||||
agg := AggregateGoroutines(funcs...)
|
||||
if agg == nil {
|
||||
if len(testCase.expected) > 0 {
|
||||
t.Errorf("%d: expected %v, got nil", i, testCase.expected)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(agg.Errors()) != len(testCase.expected) {
|
||||
t.Errorf("%d: expected %d errors in aggregate, got %v", i, len(testCase.expected), agg)
|
||||
continue
|
||||
}
|
||||
for _, err := range agg.Errors() {
|
||||
if !testCase.expected[err.Error()] {
|
||||
t.Errorf("%d: expected %v, got aggregate containing %v", i, testCase.expected, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
vendor/k8s.io/kubernetes/pkg/util/exec/BUILD
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/util/exec/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"exec.go",
|
||||
"fake_exec.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["exec_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
18
vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 exec provides an injectable interface and implementations for running commands.
|
||||
package exec // import "k8s.io/kubernetes/pkg/util/exec"
|
167
vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
generated
vendored
Normal file
167
vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
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 exec
|
||||
|
||||
import (
|
||||
"io"
|
||||
osexec "os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ErrExecutableNotFound is returned if the executable is not found.
|
||||
var ErrExecutableNotFound = osexec.ErrNotFound
|
||||
|
||||
// Interface is an interface that presents a subset of the os/exec API. Use this
|
||||
// when you want to inject fakeable/mockable exec behavior.
|
||||
type Interface interface {
|
||||
// Command returns a Cmd instance which can be used to run a single command.
|
||||
// This follows the pattern of package os/exec.
|
||||
Command(cmd string, args ...string) Cmd
|
||||
|
||||
// LookPath wraps os/exec.LookPath
|
||||
LookPath(file string) (string, error)
|
||||
}
|
||||
|
||||
// Cmd is an interface that presents an API that is very similar to Cmd from os/exec.
|
||||
// As more functionality is needed, this can grow. Since Cmd is a struct, we will have
|
||||
// to replace fields with get/set method pairs.
|
||||
type Cmd interface {
|
||||
// CombinedOutput runs the command and returns its combined standard output
|
||||
// and standard error. This follows the pattern of package os/exec.
|
||||
CombinedOutput() ([]byte, error)
|
||||
// Output runs the command and returns standard output, but not standard err
|
||||
Output() ([]byte, error)
|
||||
SetDir(dir string)
|
||||
SetStdin(in io.Reader)
|
||||
SetStdout(out io.Writer)
|
||||
}
|
||||
|
||||
// ExitError is an interface that presents an API similar to os.ProcessState, which is
|
||||
// what ExitError from os/exec is. This is designed to make testing a bit easier and
|
||||
// probably loses some of the cross-platform properties of the underlying library.
|
||||
type ExitError interface {
|
||||
String() string
|
||||
Error() string
|
||||
Exited() bool
|
||||
ExitStatus() int
|
||||
}
|
||||
|
||||
// Implements Interface in terms of really exec()ing.
|
||||
type executor struct{}
|
||||
|
||||
// New returns a new Interface which will os/exec to run commands.
|
||||
func New() Interface {
|
||||
return &executor{}
|
||||
}
|
||||
|
||||
// Command is part of the Interface interface.
|
||||
func (executor *executor) Command(cmd string, args ...string) Cmd {
|
||||
return (*cmdWrapper)(osexec.Command(cmd, args...))
|
||||
}
|
||||
|
||||
// LookPath is part of the Interface interface
|
||||
func (executor *executor) LookPath(file string) (string, error) {
|
||||
return osexec.LookPath(file)
|
||||
}
|
||||
|
||||
// Wraps exec.Cmd so we can capture errors.
|
||||
type cmdWrapper osexec.Cmd
|
||||
|
||||
func (cmd *cmdWrapper) SetDir(dir string) {
|
||||
cmd.Dir = dir
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetStdin(in io.Reader) {
|
||||
cmd.Stdin = in
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) SetStdout(out io.Writer) {
|
||||
cmd.Stdout = out
|
||||
}
|
||||
|
||||
// CombinedOutput is part of the Cmd interface.
|
||||
func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
|
||||
out, err := (*osexec.Cmd)(cmd).CombinedOutput()
|
||||
if err != nil {
|
||||
return out, handleError(err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (cmd *cmdWrapper) Output() ([]byte, error) {
|
||||
out, err := (*osexec.Cmd)(cmd).Output()
|
||||
if err != nil {
|
||||
return out, handleError(err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func handleError(err error) error {
|
||||
if ee, ok := err.(*osexec.ExitError); ok {
|
||||
// Force a compile fail if exitErrorWrapper can't convert to ExitError.
|
||||
var x ExitError = &ExitErrorWrapper{ee}
|
||||
return x
|
||||
}
|
||||
if ee, ok := err.(*osexec.Error); ok {
|
||||
if ee.Err == osexec.ErrNotFound {
|
||||
return ErrExecutableNotFound
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError.
|
||||
// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited().
|
||||
type ExitErrorWrapper struct {
|
||||
*osexec.ExitError
|
||||
}
|
||||
|
||||
var _ ExitError = ExitErrorWrapper{}
|
||||
|
||||
// ExitStatus is part of the ExitError interface.
|
||||
func (eew ExitErrorWrapper) ExitStatus() int {
|
||||
ws, ok := eew.Sys().(syscall.WaitStatus)
|
||||
if !ok {
|
||||
panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper")
|
||||
}
|
||||
return ws.ExitStatus()
|
||||
}
|
||||
|
||||
// CodeExitError is an implementation of ExitError consisting of an error object
|
||||
// and an exit code (the upper bits of os.exec.ExitStatus).
|
||||
type CodeExitError struct {
|
||||
Err error
|
||||
Code int
|
||||
}
|
||||
|
||||
var _ ExitError = CodeExitError{}
|
||||
|
||||
func (e CodeExitError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func (e CodeExitError) String() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func (e CodeExitError) Exited() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (e CodeExitError) ExitStatus() int {
|
||||
return e.Code
|
||||
}
|
103
vendor/k8s.io/kubernetes/pkg/util/exec/exec_test.go
generated
vendored
Normal file
103
vendor/k8s.io/kubernetes/pkg/util/exec/exec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
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 exec
|
||||
|
||||
import (
|
||||
osexec "os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExecutorNoArgs(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
cmd := ex.Command("true")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Errorf("expected no output, got %q", string(out))
|
||||
}
|
||||
|
||||
cmd = ex.Command("false")
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil error")
|
||||
}
|
||||
if len(out) != 0 {
|
||||
t.Errorf("expected no output, got %q", string(out))
|
||||
}
|
||||
ee, ok := err.(ExitError)
|
||||
if !ok {
|
||||
t.Errorf("expected an ExitError, got %+v", err)
|
||||
}
|
||||
if ee.Exited() {
|
||||
if code := ee.ExitStatus(); code != 1 {
|
||||
t.Errorf("expected exit status 1, got %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = ex.Command("/does/not/exist")
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil error")
|
||||
}
|
||||
if ee, ok := err.(ExitError); ok {
|
||||
t.Errorf("expected non-ExitError, got %+v", ee)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutorWithArgs(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
cmd := ex.Command("echo", "stdout")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "stdout\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
|
||||
cmd = ex.Command("/bin/sh", "-c", "echo stderr > /dev/stderr")
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %+v", err)
|
||||
}
|
||||
if string(out) != "stderr\n" {
|
||||
t.Errorf("unexpected output: %q", string(out))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookPath(t *testing.T) {
|
||||
ex := New()
|
||||
|
||||
shExpected, _ := osexec.LookPath("sh")
|
||||
sh, _ := ex.LookPath("sh")
|
||||
if sh != shExpected {
|
||||
t.Errorf("unexpected result for LookPath: got %s, expected %s", sh, shExpected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutableNotFound(t *testing.T) {
|
||||
exec := New()
|
||||
cmd := exec.Command("fake_executable_name")
|
||||
_, err := cmd.CombinedOutput()
|
||||
if err != ErrExecutableNotFound {
|
||||
t.Errorf("Expected error ErrExecutableNotFound but got %v", err)
|
||||
}
|
||||
}
|
112
vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
generated
vendored
Normal file
112
vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
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 exec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A simple scripted Interface type.
|
||||
type FakeExec struct {
|
||||
CommandScript []FakeCommandAction
|
||||
CommandCalls int
|
||||
LookPathFunc func(string) (string, error)
|
||||
}
|
||||
|
||||
type FakeCommandAction func(cmd string, args ...string) Cmd
|
||||
|
||||
func (fake *FakeExec) Command(cmd string, args ...string) Cmd {
|
||||
if fake.CommandCalls > len(fake.CommandScript)-1 {
|
||||
panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
|
||||
}
|
||||
i := fake.CommandCalls
|
||||
fake.CommandCalls++
|
||||
return fake.CommandScript[i](cmd, args...)
|
||||
}
|
||||
|
||||
func (fake *FakeExec) LookPath(file string) (string, error) {
|
||||
return fake.LookPathFunc(file)
|
||||
}
|
||||
|
||||
// A simple scripted Cmd type.
|
||||
type FakeCmd struct {
|
||||
Argv []string
|
||||
CombinedOutputScript []FakeCombinedOutputAction
|
||||
CombinedOutputCalls int
|
||||
CombinedOutputLog [][]string
|
||||
Dirs []string
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
}
|
||||
|
||||
func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd {
|
||||
fake.Argv = append([]string{cmd}, args...)
|
||||
return fake
|
||||
}
|
||||
|
||||
type FakeCombinedOutputAction func() ([]byte, error)
|
||||
|
||||
func (fake *FakeCmd) SetDir(dir string) {
|
||||
fake.Dirs = append(fake.Dirs, dir)
|
||||
}
|
||||
|
||||
func (fake *FakeCmd) SetStdin(in io.Reader) {
|
||||
fake.Stdin = in
|
||||
}
|
||||
|
||||
func (fake *FakeCmd) SetStdout(out io.Writer) {
|
||||
fake.Stdout = out
|
||||
}
|
||||
|
||||
func (fake *FakeCmd) CombinedOutput() ([]byte, error) {
|
||||
if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 {
|
||||
panic("ran out of CombinedOutput() actions")
|
||||
}
|
||||
if fake.CombinedOutputLog == nil {
|
||||
fake.CombinedOutputLog = [][]string{}
|
||||
}
|
||||
i := fake.CombinedOutputCalls
|
||||
fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...))
|
||||
fake.CombinedOutputCalls++
|
||||
return fake.CombinedOutputScript[i]()
|
||||
}
|
||||
|
||||
func (fake *FakeCmd) Output() ([]byte, error) {
|
||||
return nil, fmt.Errorf("unimplemented")
|
||||
}
|
||||
|
||||
// A simple fake ExitError type.
|
||||
type FakeExitError struct {
|
||||
Status int
|
||||
}
|
||||
|
||||
func (fake *FakeExitError) String() string {
|
||||
return fmt.Sprintf("exit %d", fake.Status)
|
||||
}
|
||||
|
||||
func (fake *FakeExitError) Error() string {
|
||||
return fake.String()
|
||||
}
|
||||
|
||||
func (fake *FakeExitError) Exited() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (fake *FakeExitError) ExitStatus() int {
|
||||
return fake.Status
|
||||
}
|
35
vendor/k8s.io/kubernetes/pkg/util/flag/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/flag/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"flags.go",
|
||||
"string_flag.go",
|
||||
"tristate.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:github.com/spf13/pflag",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
51
vendor/k8s.io/kubernetes/pkg/util/flag/flags.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/util/flag/flags.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
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 and parses the command line flags
|
||||
func InitFlags() {
|
||||
pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc)
|
||||
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
pflag.Parse()
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/util/flag/string_flag.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/util/flag/string_flag.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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/kubernetes/pkg/util/flag/tristate.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/pkg/util/flag/tristate.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
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
|
||||
}
|
||||
}
|
28
vendor/k8s.io/kubernetes/pkg/util/flock/BUILD
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/util/flock/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["flock_unix.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:golang.org/x/sys/unix"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
24
vendor/k8s.io/kubernetes/pkg/util/flock/flock_other.go
generated
vendored
Normal file
24
vendor/k8s.io/kubernetes/pkg/util/flock/flock_other.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// +build !linux,!darwin,!freebsd,!openbsd,!netbsd,!dragonfly
|
||||
|
||||
/*
|
||||
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 flock
|
||||
|
||||
// Acquire is not implemented on non-unix systems.
|
||||
func Acquire(path string) error {
|
||||
return nil
|
||||
}
|
51
vendor/k8s.io/kubernetes/pkg/util/flock/flock_unix.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/util/flock/flock_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
|
||||
/*
|
||||
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 flock
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
// lock guards lockfile. Assignment is not atomic.
|
||||
lock sync.Mutex
|
||||
// os.File has a runtime.Finalizer so the fd will be closed if the struct
|
||||
// is garbage collected. Let's hold onto a reference so that doesn't happen.
|
||||
lockfile *os.File
|
||||
)
|
||||
|
||||
// Acquire acquires a lock on a file for the duration of the process. This method
|
||||
// is reentrant.
|
||||
func Acquire(path string) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
var err error
|
||||
if lockfile, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
defer lockfile.Close()
|
||||
opts := unix.Flock_t{Type: unix.F_WRLCK}
|
||||
if err := unix.FcntlFlock(lockfile.Fd(), unix.F_SETLKW, &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
47
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/BUILD
generated
vendored
Normal file
47
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"backoff.go",
|
||||
"throttle.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/clock:go_default_library",
|
||||
"//pkg/util/integer:go_default_library",
|
||||
"//vendor:github.com/juju/ratelimit",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"backoff_test.go",
|
||||
"throttle_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/util/clock:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
149
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/backoff.go
generated
vendored
Normal file
149
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/backoff.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
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 flowcontrol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/clock"
|
||||
"k8s.io/kubernetes/pkg/util/integer"
|
||||
)
|
||||
|
||||
type backoffEntry struct {
|
||||
backoff time.Duration
|
||||
lastUpdate time.Time
|
||||
}
|
||||
|
||||
type Backoff struct {
|
||||
sync.Mutex
|
||||
Clock clock.Clock
|
||||
defaultDuration time.Duration
|
||||
maxDuration time.Duration
|
||||
perItemBackoff map[string]*backoffEntry
|
||||
}
|
||||
|
||||
func NewFakeBackOff(initial, max time.Duration, tc *clock.FakeClock) *Backoff {
|
||||
return &Backoff{
|
||||
perItemBackoff: map[string]*backoffEntry{},
|
||||
Clock: tc,
|
||||
defaultDuration: initial,
|
||||
maxDuration: max,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBackOff(initial, max time.Duration) *Backoff {
|
||||
return &Backoff{
|
||||
perItemBackoff: map[string]*backoffEntry{},
|
||||
Clock: clock.RealClock{},
|
||||
defaultDuration: initial,
|
||||
maxDuration: max,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current backoff Duration
|
||||
func (p *Backoff) Get(id string) time.Duration {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
var delay time.Duration
|
||||
entry, ok := p.perItemBackoff[id]
|
||||
if ok {
|
||||
delay = entry.backoff
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
||||
// move backoff to the next mark, capping at maxDuration
|
||||
func (p *Backoff) Next(id string, eventTime time.Time) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
entry, ok := p.perItemBackoff[id]
|
||||
if !ok || hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
|
||||
entry = p.initEntryUnsafe(id)
|
||||
} else {
|
||||
delay := entry.backoff * 2 // exponential
|
||||
entry.backoff = time.Duration(integer.Int64Min(int64(delay), int64(p.maxDuration)))
|
||||
}
|
||||
entry.lastUpdate = p.Clock.Now()
|
||||
}
|
||||
|
||||
// Reset forces clearing of all backoff data for a given key.
|
||||
func (p *Backoff) Reset(id string) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
delete(p.perItemBackoff, id)
|
||||
}
|
||||
|
||||
// Returns True if the elapsed time since eventTime is smaller than the current backoff window
|
||||
func (p *Backoff) IsInBackOffSince(id string, eventTime time.Time) bool {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
entry, ok := p.perItemBackoff[id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
|
||||
return false
|
||||
}
|
||||
return p.Clock.Now().Sub(eventTime) < entry.backoff
|
||||
}
|
||||
|
||||
// Returns True if time since lastupdate is less than the current backoff window.
|
||||
func (p *Backoff) IsInBackOffSinceUpdate(id string, eventTime time.Time) bool {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
entry, ok := p.perItemBackoff[id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
|
||||
return false
|
||||
}
|
||||
return eventTime.Sub(entry.lastUpdate) < entry.backoff
|
||||
}
|
||||
|
||||
// Garbage collect records that have aged past maxDuration. Backoff users are expected
|
||||
// to invoke this periodically.
|
||||
func (p *Backoff) GC() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
now := p.Clock.Now()
|
||||
for id, entry := range p.perItemBackoff {
|
||||
if now.Sub(entry.lastUpdate) > p.maxDuration*2 {
|
||||
// GC when entry has not been updated for 2*maxDuration
|
||||
delete(p.perItemBackoff, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Backoff) DeleteEntry(id string) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
delete(p.perItemBackoff, id)
|
||||
}
|
||||
|
||||
// Take a lock on *Backoff, before calling initEntryUnsafe
|
||||
func (p *Backoff) initEntryUnsafe(id string) *backoffEntry {
|
||||
entry := &backoffEntry{backoff: p.defaultDuration}
|
||||
p.perItemBackoff[id] = entry
|
||||
return entry
|
||||
}
|
||||
|
||||
// After 2*maxDuration we restart the backoff factor to the beginning
|
||||
func hasExpired(eventTime time.Time, lastUpdate time.Time, maxDuration time.Duration) bool {
|
||||
return eventTime.Sub(lastUpdate) > maxDuration*2 // consider stable if it's ok for twice the maxDuration
|
||||
}
|
195
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/backoff_test.go
generated
vendored
Normal file
195
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/backoff_test.go
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
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 flowcontrol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/clock"
|
||||
)
|
||||
|
||||
func TestSlowBackoff(t *testing.T) {
|
||||
id := "_idSlow"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 50 * step
|
||||
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
cases := []time.Duration{0, 1, 2, 4, 8, 16, 32, 50, 50, 50}
|
||||
for ix, c := range cases {
|
||||
tc.Step(step)
|
||||
w := b.Get(id)
|
||||
if w != c*step {
|
||||
t.Errorf("input: '%d': expected %s, got %s", ix, c*step, w)
|
||||
}
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
//Now confirm that the Reset cancels backoff.
|
||||
b.Next(id, tc.Now())
|
||||
b.Reset(id)
|
||||
if b.Get(id) != 0 {
|
||||
t.Errorf("Reset didn't clear the backoff.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBackoffReset(t *testing.T) {
|
||||
id := "_idReset"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := step * 5
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
startTime := tc.Now()
|
||||
|
||||
// get to backoff = maxDuration
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
// backoff should be capped at maxDuration
|
||||
if !b.IsInBackOffSince(id, tc.Now()) {
|
||||
t.Errorf("expected to be in Backoff got %s", b.Get(id))
|
||||
}
|
||||
|
||||
lastUpdate := tc.Now()
|
||||
tc.Step(2*maxDuration + step) // time += 11s, 11 > 2*maxDuration
|
||||
if b.IsInBackOffSince(id, lastUpdate) {
|
||||
t.Errorf("expected to not be in Backoff after reset (start=%s, now=%s, lastUpdate=%s), got %s", startTime, tc.Now(), lastUpdate, b.Get(id))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoffHightWaterMark(t *testing.T) {
|
||||
id := "_idHiWaterMark"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 5 * step
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
|
||||
// get to backoff = maxDuration
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
// backoff high watermark expires after 2*maxDuration
|
||||
tc.Step(maxDuration + step)
|
||||
b.Next(id, tc.Now())
|
||||
|
||||
if b.Get(id) != maxDuration {
|
||||
t.Errorf("expected Backoff to stay at high watermark %s got %s", maxDuration, b.Get(id))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoffGC(t *testing.T) {
|
||||
id := "_idGC"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 5 * step
|
||||
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
lastUpdate := tc.Now()
|
||||
tc.Step(maxDuration + step)
|
||||
b.GC()
|
||||
_, found := b.perItemBackoff[id]
|
||||
if !found {
|
||||
t.Errorf("expected GC to skip entry, elapsed time=%s maxDuration=%s", tc.Now().Sub(lastUpdate), maxDuration)
|
||||
}
|
||||
|
||||
tc.Step(maxDuration + step)
|
||||
b.GC()
|
||||
r, found := b.perItemBackoff[id]
|
||||
if found {
|
||||
t.Errorf("expected GC of entry after %s got entry %v", tc.Now().Sub(lastUpdate), r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInBackOffSinceUpdate(t *testing.T) {
|
||||
id := "_idIsInBackOffSinceUpdate"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 10 * step
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
startTime := tc.Now()
|
||||
|
||||
cases := []struct {
|
||||
tick time.Duration
|
||||
inBackOff bool
|
||||
value int
|
||||
}{
|
||||
{tick: 0, inBackOff: false, value: 0},
|
||||
{tick: 1, inBackOff: false, value: 1},
|
||||
{tick: 2, inBackOff: true, value: 2},
|
||||
{tick: 3, inBackOff: false, value: 2},
|
||||
{tick: 4, inBackOff: true, value: 4},
|
||||
{tick: 5, inBackOff: true, value: 4},
|
||||
{tick: 6, inBackOff: true, value: 4},
|
||||
{tick: 7, inBackOff: false, value: 4},
|
||||
{tick: 8, inBackOff: true, value: 8},
|
||||
{tick: 9, inBackOff: true, value: 8},
|
||||
{tick: 10, inBackOff: true, value: 8},
|
||||
{tick: 11, inBackOff: true, value: 8},
|
||||
{tick: 12, inBackOff: true, value: 8},
|
||||
{tick: 13, inBackOff: true, value: 8},
|
||||
{tick: 14, inBackOff: true, value: 8},
|
||||
{tick: 15, inBackOff: false, value: 8},
|
||||
{tick: 16, inBackOff: true, value: 10},
|
||||
{tick: 17, inBackOff: true, value: 10},
|
||||
{tick: 18, inBackOff: true, value: 10},
|
||||
{tick: 19, inBackOff: true, value: 10},
|
||||
{tick: 20, inBackOff: true, value: 10},
|
||||
{tick: 21, inBackOff: true, value: 10},
|
||||
{tick: 22, inBackOff: true, value: 10},
|
||||
{tick: 23, inBackOff: true, value: 10},
|
||||
{tick: 24, inBackOff: true, value: 10},
|
||||
{tick: 25, inBackOff: false, value: 10},
|
||||
{tick: 26, inBackOff: true, value: 10},
|
||||
{tick: 27, inBackOff: true, value: 10},
|
||||
{tick: 28, inBackOff: true, value: 10},
|
||||
{tick: 29, inBackOff: true, value: 10},
|
||||
{tick: 30, inBackOff: true, value: 10},
|
||||
{tick: 31, inBackOff: true, value: 10},
|
||||
{tick: 32, inBackOff: true, value: 10},
|
||||
{tick: 33, inBackOff: true, value: 10},
|
||||
{tick: 34, inBackOff: true, value: 10},
|
||||
{tick: 35, inBackOff: false, value: 10},
|
||||
{tick: 56, inBackOff: false, value: 0},
|
||||
{tick: 57, inBackOff: false, value: 1},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
tc.SetTime(startTime.Add(c.tick * step))
|
||||
if c.inBackOff != b.IsInBackOffSinceUpdate(id, tc.Now()) {
|
||||
t.Errorf("expected IsInBackOffSinceUpdate %v got %v at tick %s", c.inBackOff, b.IsInBackOffSinceUpdate(id, tc.Now()), c.tick*step)
|
||||
}
|
||||
|
||||
if c.inBackOff && (time.Duration(c.value)*step != b.Get(id)) {
|
||||
t.Errorf("expected backoff value=%s got %s at tick %s", time.Duration(c.value)*step, b.Get(id), c.tick*step)
|
||||
}
|
||||
|
||||
if !c.inBackOff {
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
}
|
||||
}
|
132
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/throttle.go
generated
vendored
Normal file
132
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/throttle.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
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 flowcontrol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
|
||||
type RateLimiter interface {
|
||||
// TryAccept returns true if a token is taken immediately. Otherwise,
|
||||
// it returns false.
|
||||
TryAccept() bool
|
||||
// Accept returns once a token becomes available.
|
||||
Accept()
|
||||
// Stop stops the rate limiter, subsequent calls to CanAccept will return false
|
||||
Stop()
|
||||
// Saturation returns a percentage number which describes how saturated
|
||||
// this rate limiter is.
|
||||
// Usually we use token bucket rate limiter. In that case,
|
||||
// 1.0 means no tokens are available; 0.0 means we have a full bucket of tokens to use.
|
||||
Saturation() float64
|
||||
// QPS returns QPS of this rate limiter
|
||||
QPS() float32
|
||||
}
|
||||
|
||||
type tokenBucketRateLimiter struct {
|
||||
limiter *ratelimit.Bucket
|
||||
qps float32
|
||||
}
|
||||
|
||||
// NewTokenBucketRateLimiter creates a rate limiter which implements a token bucket approach.
|
||||
// The rate limiter allows bursts of up to 'burst' to exceed the QPS, while still maintaining a
|
||||
// smoothed qps rate of 'qps'.
|
||||
// The bucket is initially filled with 'burst' tokens, and refills at a rate of 'qps'.
|
||||
// The maximum number of tokens in the bucket is capped at 'burst'.
|
||||
func NewTokenBucketRateLimiter(qps float32, burst int) RateLimiter {
|
||||
limiter := ratelimit.NewBucketWithRate(float64(qps), int64(burst))
|
||||
return &tokenBucketRateLimiter{
|
||||
limiter: limiter,
|
||||
qps: qps,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tokenBucketRateLimiter) TryAccept() bool {
|
||||
return t.limiter.TakeAvailable(1) == 1
|
||||
}
|
||||
|
||||
func (t *tokenBucketRateLimiter) Saturation() float64 {
|
||||
capacity := t.limiter.Capacity()
|
||||
avail := t.limiter.Available()
|
||||
return float64(capacity-avail) / float64(capacity)
|
||||
}
|
||||
|
||||
// Accept will block until a token becomes available
|
||||
func (t *tokenBucketRateLimiter) Accept() {
|
||||
t.limiter.Wait(1)
|
||||
}
|
||||
|
||||
func (t *tokenBucketRateLimiter) Stop() {
|
||||
}
|
||||
|
||||
func (t *tokenBucketRateLimiter) QPS() float32 {
|
||||
return t.qps
|
||||
}
|
||||
|
||||
type fakeAlwaysRateLimiter struct{}
|
||||
|
||||
func NewFakeAlwaysRateLimiter() RateLimiter {
|
||||
return &fakeAlwaysRateLimiter{}
|
||||
}
|
||||
|
||||
func (t *fakeAlwaysRateLimiter) TryAccept() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *fakeAlwaysRateLimiter) Saturation() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t *fakeAlwaysRateLimiter) Stop() {}
|
||||
|
||||
func (t *fakeAlwaysRateLimiter) Accept() {}
|
||||
|
||||
func (t *fakeAlwaysRateLimiter) QPS() float32 {
|
||||
return 1
|
||||
}
|
||||
|
||||
type fakeNeverRateLimiter struct {
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewFakeNeverRateLimiter() RateLimiter {
|
||||
rl := fakeNeverRateLimiter{}
|
||||
rl.wg.Add(1)
|
||||
return &rl
|
||||
}
|
||||
|
||||
func (t *fakeNeverRateLimiter) TryAccept() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *fakeNeverRateLimiter) Saturation() float64 {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (t *fakeNeverRateLimiter) Stop() {
|
||||
t.wg.Done()
|
||||
}
|
||||
|
||||
func (t *fakeNeverRateLimiter) Accept() {
|
||||
t.wg.Wait()
|
||||
}
|
||||
|
||||
func (t *fakeNeverRateLimiter) QPS() float32 {
|
||||
return 1
|
||||
}
|
177
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/throttle_test.go
generated
vendored
Normal file
177
vendor/k8s.io/kubernetes/pkg/util/flowcontrol/throttle_test.go
generated
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
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 flowcontrol
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMultithreadedThrottling(t *testing.T) {
|
||||
// Bucket with 100QPS and no burst
|
||||
r := NewTokenBucketRateLimiter(100, 1)
|
||||
|
||||
// channel to collect 100 tokens
|
||||
taken := make(chan bool, 100)
|
||||
|
||||
// Set up goroutines to hammer the throttler
|
||||
startCh := make(chan bool)
|
||||
endCh := make(chan bool)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
// wait for the starting signal
|
||||
<-startCh
|
||||
for {
|
||||
// get a token
|
||||
r.Accept()
|
||||
select {
|
||||
// try to add it to the taken channel
|
||||
case taken <- true:
|
||||
continue
|
||||
// if taken is full, notify and return
|
||||
default:
|
||||
endCh <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// record wall time
|
||||
startTime := time.Now()
|
||||
// take the initial capacity so all tokens are the result of refill
|
||||
r.Accept()
|
||||
// start the thundering herd
|
||||
close(startCh)
|
||||
// wait for the first signal that we collected 100 tokens
|
||||
<-endCh
|
||||
// record wall time
|
||||
endTime := time.Now()
|
||||
|
||||
// tolerate a 1% clock change because these things happen
|
||||
if duration := endTime.Sub(startTime); duration < (time.Second * 99 / 100) {
|
||||
// We shouldn't be able to get 100 tokens out of the bucket in less than 1 second of wall clock time, no matter what
|
||||
t.Errorf("Expected it to take at least 1 second to get 100 tokens, took %v", duration)
|
||||
} else {
|
||||
t.Logf("Took %v to get 100 tokens", duration)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(1, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
}
|
||||
if r.TryAccept() {
|
||||
t.Error("unexpected true accept")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(1, 1)
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
if r.TryAccept() {
|
||||
t.Error("unexpected true accept")
|
||||
}
|
||||
|
||||
// Allow to refill
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
}
|
||||
|
||||
func TestThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(10, 5)
|
||||
|
||||
// Should consume 5 tokens immediately, then
|
||||
// the remaining 11 should take at least 1 second (0.1s each)
|
||||
expectedFinish := time.Now().Add(time.Second * 1)
|
||||
for i := 0; i < 16; i++ {
|
||||
r.Accept()
|
||||
}
|
||||
if time.Now().Before(expectedFinish) {
|
||||
t.Error("rate limit was not respected, finished too early")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRateLimiterSaturation(t *testing.T) {
|
||||
const e = 0.000001
|
||||
tests := []struct {
|
||||
capacity int
|
||||
take int
|
||||
|
||||
expectedSaturation float64
|
||||
}{
|
||||
{1, 1, 1},
|
||||
{10, 3, 0.3},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
rl := NewTokenBucketRateLimiter(1, tt.capacity)
|
||||
for i := 0; i < tt.take; i++ {
|
||||
rl.Accept()
|
||||
}
|
||||
if math.Abs(rl.Saturation()-tt.expectedSaturation) > e {
|
||||
t.Fatalf("#%d: Saturation rate difference isn't within tolerable range\n want=%f, get=%f",
|
||||
i, tt.expectedSaturation, rl.Saturation())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlwaysFake(t *testing.T) {
|
||||
rl := NewFakeAlwaysRateLimiter()
|
||||
if !rl.TryAccept() {
|
||||
t.Error("TryAccept in AlwaysFake should return true.")
|
||||
}
|
||||
// If this will block the test will timeout
|
||||
rl.Accept()
|
||||
}
|
||||
|
||||
func TestNeverFake(t *testing.T) {
|
||||
rl := NewFakeNeverRateLimiter()
|
||||
if rl.TryAccept() {
|
||||
t.Error("TryAccept in NeverFake should return false.")
|
||||
}
|
||||
|
||||
finished := false
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
rl.Accept()
|
||||
finished = true
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Wait some time to make sure it never finished.
|
||||
time.Sleep(time.Second)
|
||||
if finished {
|
||||
t.Error("Accept should block forever in NeverFake.")
|
||||
}
|
||||
|
||||
rl.Stop()
|
||||
wg.Wait()
|
||||
if !finished {
|
||||
t.Error("Stop should make Accept unblock in NeverFake.")
|
||||
}
|
||||
}
|
0
vendor/k8s.io/kubernetes/pkg/util/framer/.readonly
generated
vendored
Normal file
0
vendor/k8s.io/kubernetes/pkg/util/framer/.readonly
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/framer/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/util/framer/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["framer.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["framer_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
167
vendor/k8s.io/kubernetes/pkg/util/framer/framer.go
generated
vendored
Normal file
167
vendor/k8s.io/kubernetes/pkg/util/framer/framer.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
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 framer implements simple frame decoding techniques for an io.ReadCloser
|
||||
package framer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
type lengthDelimitedFrameWriter struct {
|
||||
w io.Writer
|
||||
h [4]byte
|
||||
}
|
||||
|
||||
func NewLengthDelimitedFrameWriter(w io.Writer) io.Writer {
|
||||
return &lengthDelimitedFrameWriter{w: w}
|
||||
}
|
||||
|
||||
// Write writes a single frame to the nested writer, prepending it with the length in
|
||||
// in bytes of data (as a 4 byte, bigendian uint32).
|
||||
func (w *lengthDelimitedFrameWriter) Write(data []byte) (int, error) {
|
||||
binary.BigEndian.PutUint32(w.h[:], uint32(len(data)))
|
||||
n, err := w.w.Write(w.h[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != len(w.h) {
|
||||
return 0, io.ErrShortWrite
|
||||
}
|
||||
return w.w.Write(data)
|
||||
}
|
||||
|
||||
type lengthDelimitedFrameReader struct {
|
||||
r io.ReadCloser
|
||||
remaining int
|
||||
}
|
||||
|
||||
// NewLengthDelimitedFrameReader returns an io.Reader that will decode length-prefixed
|
||||
// frames off of a stream.
|
||||
//
|
||||
// The protocol is:
|
||||
//
|
||||
// stream: message ...
|
||||
// message: prefix body
|
||||
// prefix: 4 byte uint32 in BigEndian order, denotes length of body
|
||||
// body: bytes (0..prefix)
|
||||
//
|
||||
// If the buffer passed to Read is not long enough to contain an entire frame, io.ErrShortRead
|
||||
// will be returned along with the number of bytes read.
|
||||
func NewLengthDelimitedFrameReader(r io.ReadCloser) io.ReadCloser {
|
||||
return &lengthDelimitedFrameReader{r: r}
|
||||
}
|
||||
|
||||
// Read attempts to read an entire frame into data. If that is not possible, io.ErrShortBuffer
|
||||
// is returned and subsequent calls will attempt to read the last frame. A frame is complete when
|
||||
// err is nil.
|
||||
func (r *lengthDelimitedFrameReader) Read(data []byte) (int, error) {
|
||||
if r.remaining <= 0 {
|
||||
header := [4]byte{}
|
||||
n, err := io.ReadAtLeast(r.r, header[:4], 4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != 4 {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
frameLength := int(binary.BigEndian.Uint32(header[:]))
|
||||
r.remaining = frameLength
|
||||
}
|
||||
|
||||
expect := r.remaining
|
||||
max := expect
|
||||
if max > len(data) {
|
||||
max = len(data)
|
||||
}
|
||||
n, err := io.ReadAtLeast(r.r, data[:max], int(max))
|
||||
r.remaining -= n
|
||||
if err == io.ErrShortBuffer || r.remaining > 0 {
|
||||
return n, io.ErrShortBuffer
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if n != expect {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (r *lengthDelimitedFrameReader) Close() error {
|
||||
return r.r.Close()
|
||||
}
|
||||
|
||||
type jsonFrameReader struct {
|
||||
r io.ReadCloser
|
||||
decoder *json.Decoder
|
||||
remaining []byte
|
||||
}
|
||||
|
||||
// NewJSONFramedReader returns an io.Reader that will decode individual JSON objects off
|
||||
// of a wire.
|
||||
//
|
||||
// The boundaries between each frame are valid JSON objects. A JSON parsing error will terminate
|
||||
// the read.
|
||||
func NewJSONFramedReader(r io.ReadCloser) io.ReadCloser {
|
||||
return &jsonFrameReader{
|
||||
r: r,
|
||||
decoder: json.NewDecoder(r),
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFrame decodes the next JSON object in the stream, or returns an error. The returned
|
||||
// byte slice will be modified the next time ReadFrame is invoked and should not be altered.
|
||||
func (r *jsonFrameReader) Read(data []byte) (int, error) {
|
||||
// Return whatever remaining data exists from an in progress frame
|
||||
if n := len(r.remaining); n > 0 {
|
||||
if n <= len(data) {
|
||||
data = append(data[0:0], r.remaining...)
|
||||
r.remaining = nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
n = len(data)
|
||||
data = append(data[0:0], r.remaining[:n]...)
|
||||
r.remaining = r.remaining[n:]
|
||||
return n, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
// RawMessage#Unmarshal appends to data - we reset the slice down to 0 and will either see
|
||||
// data written to data, or be larger than data and a different array.
|
||||
n := len(data)
|
||||
m := json.RawMessage(data[:0])
|
||||
if err := r.decoder.Decode(&m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// If capacity of data is less than length of the message, decoder will allocate a new slice
|
||||
// and set m to it, which means we need to copy the partial result back into data and preserve
|
||||
// the remaining result for subsequent reads.
|
||||
if len(m) > n {
|
||||
data = append(data[0:0], m[:n]...)
|
||||
r.remaining = m[n:]
|
||||
return n, io.ErrShortBuffer
|
||||
}
|
||||
return len(m), nil
|
||||
}
|
||||
|
||||
func (r *jsonFrameReader) Close() error {
|
||||
return r.r.Close()
|
||||
}
|
176
vendor/k8s.io/kubernetes/pkg/util/framer/framer_test.go
generated
vendored
Normal file
176
vendor/k8s.io/kubernetes/pkg/util/framer/framer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
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 framer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRead(t *testing.T) {
|
||||
data := []byte{
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
0x00, 0x00, 0x00, 0x03,
|
||||
0x05, 0x06, 0x07,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x08,
|
||||
}
|
||||
b := bytes.NewBuffer(data)
|
||||
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
|
||||
buf := make([]byte, 1)
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x01}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x02}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read the remaining frame
|
||||
buf = make([]byte, 2)
|
||||
if n, err := r.Read(buf); err != nil && n != 2 && bytes.Equal(buf, []byte{0x03, 0x04}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read with buffer equal to frame
|
||||
buf = make([]byte, 3)
|
||||
if n, err := r.Read(buf); err != nil && n != 3 && bytes.Equal(buf, []byte{0x05, 0x06, 0x07}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read empty frame
|
||||
buf = make([]byte, 3)
|
||||
if n, err := r.Read(buf); err != nil && n != 0 && bytes.Equal(buf, []byte{}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read with larger buffer than frame
|
||||
buf = make([]byte, 3)
|
||||
if n, err := r.Read(buf); err != nil && n != 1 && bytes.Equal(buf, []byte{0x08}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read EOF
|
||||
if n, err := r.Read(buf); err != io.EOF && n != 0 {
|
||||
t.Fatalf("unexpected: %v %d", err, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadLarge(t *testing.T) {
|
||||
data := []byte{
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
0x00, 0x00, 0x00, 0x03,
|
||||
0x05, 0x06, 0x07,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x08,
|
||||
}
|
||||
b := bytes.NewBuffer(data)
|
||||
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
|
||||
buf := make([]byte, 40)
|
||||
if n, err := r.Read(buf); err != nil && n != 4 && bytes.Equal(buf, []byte{0x01, 0x02, 0x03, 0x04}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil && n != 3 && bytes.Equal(buf, []byte{0x05, 0x06, 0x7}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil && n != 0 && bytes.Equal(buf, []byte{}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil && n != 1 && bytes.Equal(buf, []byte{0x08}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read EOF
|
||||
if n, err := r.Read(buf); err != io.EOF && n != 0 {
|
||||
t.Fatalf("unexpected: %v %d", err, n)
|
||||
}
|
||||
}
|
||||
func TestReadInvalidFrame(t *testing.T) {
|
||||
data := []byte{
|
||||
0x00, 0x00, 0x00, 0x04,
|
||||
0x01, 0x02,
|
||||
}
|
||||
b := bytes.NewBuffer(data)
|
||||
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
|
||||
buf := make([]byte, 1)
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x01}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read the remaining frame
|
||||
buf = make([]byte, 3)
|
||||
if n, err := r.Read(buf); err != io.ErrUnexpectedEOF && n != 1 && bytes.Equal(buf, []byte{0x02}) {
|
||||
t.Fatalf("unexpected: %v %d %v", err, n, buf)
|
||||
}
|
||||
// read EOF
|
||||
if n, err := r.Read(buf); err != io.EOF && n != 0 {
|
||||
t.Fatalf("unexpected: %v %d", err, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFrameReader(t *testing.T) {
|
||||
b := bytes.NewBufferString("{\"test\":true}\n1\n[\"a\"]")
|
||||
r := NewJSONFramedReader(ioutil.NopCloser(b))
|
||||
buf := make([]byte, 20)
|
||||
if n, err := r.Read(buf); err != nil || n != 13 || string(buf[:n]) != `{"test":true}` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `1` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil || n != 5 || string(buf[:n]) != `["a"]` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != io.EOF || n != 0 {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONFrameReaderShortBuffer(t *testing.T) {
|
||||
b := bytes.NewBufferString("{\"test\":true}\n1\n[\"a\"]")
|
||||
r := NewJSONFramedReader(ioutil.NopCloser(b))
|
||||
buf := make([]byte, 3)
|
||||
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `{"t` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `est` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `":t` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `rue` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `}` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
|
||||
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `1` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
|
||||
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `["a` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
if n, err := r.Read(buf); err != nil || n != 2 || string(buf[:n]) != `"]` {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
|
||||
if n, err := r.Read(buf); err != io.EOF || n != 0 {
|
||||
t.Fatalf("unexpected: %v %d %q", err, n, buf)
|
||||
}
|
||||
}
|
44
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/BUILD
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["goroutinemap.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["goroutinemap_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:k8s.io/apimachinery/pkg/util/wait"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/goroutinemap/exponentialbackoff:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
2
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/OWNERS
generated
vendored
Normal file
2
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/OWNERS
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
assignees:
|
||||
- saad-ali
|
27
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/BUILD
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["exponential_backoff.go"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
120
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
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 exponentialbackoff contains logic for implementing exponential
|
||||
// backoff for GoRoutineMap and NestedPendingOperations.
|
||||
package exponentialbackoff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// initialDurationBeforeRetry is the amount of time after an error occurs
|
||||
// that GoroutineMap will refuse to allow another operation to start with
|
||||
// the same target (if exponentialBackOffOnError is enabled). Each
|
||||
// successive error results in a wait 2x times the previous.
|
||||
initialDurationBeforeRetry time.Duration = 500 * time.Millisecond
|
||||
|
||||
// maxDurationBeforeRetry is the maximum amount of time that
|
||||
// durationBeforeRetry will grow to due to exponential backoff.
|
||||
maxDurationBeforeRetry time.Duration = 2 * time.Minute
|
||||
)
|
||||
|
||||
// ExponentialBackoff contains the last occurrence of an error and the duration
|
||||
// that retries are not permitted.
|
||||
type ExponentialBackoff struct {
|
||||
lastError error
|
||||
lastErrorTime time.Time
|
||||
durationBeforeRetry time.Duration
|
||||
}
|
||||
|
||||
// SafeToRetry returns an error if the durationBeforeRetry period for the given
|
||||
// lastErrorTime has not yet expired. Otherwise it returns nil.
|
||||
func (expBackoff *ExponentialBackoff) SafeToRetry(operationName string) error {
|
||||
if time.Since(expBackoff.lastErrorTime) <= expBackoff.durationBeforeRetry {
|
||||
return NewExponentialBackoffError(operationName, *expBackoff)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (expBackoff *ExponentialBackoff) Update(err *error) {
|
||||
if expBackoff.durationBeforeRetry == 0 {
|
||||
expBackoff.durationBeforeRetry = initialDurationBeforeRetry
|
||||
} else {
|
||||
expBackoff.durationBeforeRetry = 2 * expBackoff.durationBeforeRetry
|
||||
if expBackoff.durationBeforeRetry > maxDurationBeforeRetry {
|
||||
expBackoff.durationBeforeRetry = maxDurationBeforeRetry
|
||||
}
|
||||
}
|
||||
|
||||
expBackoff.lastError = *err
|
||||
expBackoff.lastErrorTime = time.Now()
|
||||
}
|
||||
|
||||
func (expBackoff *ExponentialBackoff) GenerateNoRetriesPermittedMsg(
|
||||
operationName string) string {
|
||||
return fmt.Sprintf("Operation for %q failed. No retries permitted until %v (durationBeforeRetry %v). Error: %v",
|
||||
operationName,
|
||||
expBackoff.lastErrorTime.Add(expBackoff.durationBeforeRetry),
|
||||
expBackoff.durationBeforeRetry,
|
||||
expBackoff.lastError)
|
||||
}
|
||||
|
||||
// NewExponentialBackoffError returns a new instance of ExponentialBackoff error.
|
||||
func NewExponentialBackoffError(
|
||||
operationName string, expBackoff ExponentialBackoff) error {
|
||||
return exponentialBackoffError{
|
||||
operationName: operationName,
|
||||
expBackoff: expBackoff,
|
||||
}
|
||||
}
|
||||
|
||||
// IsExponentialBackoff returns true if an error returned from GoroutineMap
|
||||
// indicates that a new operation can not be started because
|
||||
// exponentialBackOffOnError is enabled and a previous operation with the same
|
||||
// operation failed within the durationBeforeRetry period.
|
||||
func IsExponentialBackoff(err error) bool {
|
||||
switch err.(type) {
|
||||
case exponentialBackoffError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// exponentialBackoffError is the error returned returned from GoroutineMap when
|
||||
// a new operation can not be started because exponentialBackOffOnError is
|
||||
// enabled and a previous operation with the same operation failed within the
|
||||
// durationBeforeRetry period.
|
||||
type exponentialBackoffError struct {
|
||||
operationName string
|
||||
expBackoff ExponentialBackoff
|
||||
}
|
||||
|
||||
var _ error = exponentialBackoffError{}
|
||||
|
||||
func (err exponentialBackoffError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Failed to create operation with name %q. An operation with that name failed at %v. No retries permitted until %v (%v). Last error: %q.",
|
||||
err.operationName,
|
||||
err.expBackoff.lastErrorTime,
|
||||
err.expBackoff.lastErrorTime.Add(err.expBackoff.durationBeforeRetry),
|
||||
err.expBackoff.durationBeforeRetry,
|
||||
err.expBackoff.lastError)
|
||||
}
|
243
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap.go
generated
vendored
Normal file
243
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
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 goroutinemap implements a data structure for managing go routines
|
||||
by name. It prevents the creation of new go routines if an existing go routine
|
||||
with the same name exists.
|
||||
*/
|
||||
package goroutinemap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
k8sRuntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
||||
)
|
||||
|
||||
const (
|
||||
// initialDurationBeforeRetry is the amount of time after an error occurs
|
||||
// that GoRoutineMap will refuse to allow another operation to start with
|
||||
// the same operation name (if exponentialBackOffOnError is enabled). Each
|
||||
// successive error results in a wait 2x times the previous.
|
||||
initialDurationBeforeRetry = 500 * time.Millisecond
|
||||
|
||||
// maxDurationBeforeRetry is the maximum amount of time that
|
||||
// durationBeforeRetry will grow to due to exponential backoff.
|
||||
maxDurationBeforeRetry = 2 * time.Minute
|
||||
)
|
||||
|
||||
// GoRoutineMap defines a type that can run named goroutines and track their
|
||||
// state. It prevents the creation of multiple goroutines with the same name
|
||||
// and may prevent recreation of a goroutine until after the a backoff time
|
||||
// has elapsed after the last goroutine with that name finished.
|
||||
type GoRoutineMap interface {
|
||||
// Run adds operation name to the list of running operations and spawns a
|
||||
// new go routine to execute the operation.
|
||||
// If an operation with the same operation name already exists, an
|
||||
// AlreadyExists or ExponentialBackoff error is returned.
|
||||
// Once the operation is complete, the go routine is terminated and the
|
||||
// operation name is removed from the list of executing operations allowing
|
||||
// a new operation to be started with the same operation name without error.
|
||||
Run(operationName string, operationFunc func() error) error
|
||||
|
||||
// Wait blocks until operations map is empty. This is typically
|
||||
// necessary during tests - the test should wait until all operations finish
|
||||
// and evaluate results after that.
|
||||
Wait()
|
||||
|
||||
// WaitForCompletion blocks until either all operations have successfully completed
|
||||
// or have failed but are not pending. The test should wait until operations are either
|
||||
// complete or have failed.
|
||||
WaitForCompletion()
|
||||
|
||||
// IsOperationPending returns true if the operation is pending (currently
|
||||
// running), otherwise returns false.
|
||||
IsOperationPending(operationName string) bool
|
||||
}
|
||||
|
||||
// NewGoRoutineMap returns a new instance of GoRoutineMap.
|
||||
func NewGoRoutineMap(exponentialBackOffOnError bool) GoRoutineMap {
|
||||
g := &goRoutineMap{
|
||||
operations: make(map[string]operation),
|
||||
exponentialBackOffOnError: exponentialBackOffOnError,
|
||||
}
|
||||
|
||||
g.cond = sync.NewCond(&g.lock)
|
||||
return g
|
||||
}
|
||||
|
||||
type goRoutineMap struct {
|
||||
operations map[string]operation
|
||||
exponentialBackOffOnError bool
|
||||
cond *sync.Cond
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// operation holds the state of a single goroutine.
|
||||
type operation struct {
|
||||
operationPending bool
|
||||
expBackoff exponentialbackoff.ExponentialBackoff
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) Run(
|
||||
operationName string,
|
||||
operationFunc func() error) error {
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
existingOp, exists := grm.operations[operationName]
|
||||
if exists {
|
||||
// Operation with name exists
|
||||
if existingOp.operationPending {
|
||||
return NewAlreadyExistsError(operationName)
|
||||
}
|
||||
|
||||
if err := existingOp.expBackoff.SafeToRetry(operationName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
grm.operations[operationName] = operation{
|
||||
operationPending: true,
|
||||
expBackoff: existingOp.expBackoff,
|
||||
}
|
||||
go func() (err error) {
|
||||
// Handle unhandled panics (very unlikely)
|
||||
defer k8sRuntime.HandleCrash()
|
||||
// Handle completion of and error, if any, from operationFunc()
|
||||
defer grm.operationComplete(operationName, &err)
|
||||
// Handle panic, if any, from operationFunc()
|
||||
defer k8sRuntime.RecoverFromPanic(&err)
|
||||
return operationFunc()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// operationComplete handles the completion of a goroutine run in the
|
||||
// goRoutineMap.
|
||||
func (grm *goRoutineMap) operationComplete(
|
||||
operationName string, err *error) {
|
||||
// Defer operations are executed in Last-In is First-Out order. In this case
|
||||
// the lock is acquired first when operationCompletes begins, and is
|
||||
// released when the method finishes, after the lock is released cond is
|
||||
// signaled to wake waiting goroutine.
|
||||
defer grm.cond.Signal()
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
if *err == nil || !grm.exponentialBackOffOnError {
|
||||
// Operation completed without error, or exponentialBackOffOnError disabled
|
||||
delete(grm.operations, operationName)
|
||||
if *err != nil {
|
||||
// Log error
|
||||
glog.Errorf("operation for %q failed with: %v",
|
||||
operationName,
|
||||
*err)
|
||||
}
|
||||
} else {
|
||||
// Operation completed with error and exponentialBackOffOnError Enabled
|
||||
existingOp := grm.operations[operationName]
|
||||
existingOp.expBackoff.Update(err)
|
||||
existingOp.operationPending = false
|
||||
grm.operations[operationName] = existingOp
|
||||
|
||||
// Log error
|
||||
glog.Errorf("%v",
|
||||
existingOp.expBackoff.GenerateNoRetriesPermittedMsg(operationName))
|
||||
}
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) IsOperationPending(operationName string) bool {
|
||||
grm.lock.RLock()
|
||||
defer grm.lock.RUnlock()
|
||||
existingOp, exists := grm.operations[operationName]
|
||||
if exists && existingOp.operationPending {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) Wait() {
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
for len(grm.operations) > 0 {
|
||||
grm.cond.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) WaitForCompletion() {
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
for {
|
||||
if len(grm.operations) == 0 || grm.nothingPending() {
|
||||
break
|
||||
} else {
|
||||
grm.cond.Wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any operation is pending. Already assumes caller has the
|
||||
// necessary locks
|
||||
func (grm *goRoutineMap) nothingPending() bool {
|
||||
nothingIsPending := true
|
||||
for _, operation := range grm.operations {
|
||||
if operation.operationPending {
|
||||
nothingIsPending = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return nothingIsPending
|
||||
}
|
||||
|
||||
// NewAlreadyExistsError returns a new instance of AlreadyExists error.
|
||||
func NewAlreadyExistsError(operationName string) error {
|
||||
return alreadyExistsError{operationName}
|
||||
}
|
||||
|
||||
// IsAlreadyExists returns true if an error returned from GoRoutineMap indicates
|
||||
// a new operation can not be started because an operation with the same
|
||||
// operation name is already executing.
|
||||
func IsAlreadyExists(err error) bool {
|
||||
switch err.(type) {
|
||||
case alreadyExistsError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// alreadyExistsError is the error returned by GoRoutineMap when a new operation
|
||||
// can not be started because an operation with the same operation name is
|
||||
// already executing.
|
||||
type alreadyExistsError struct {
|
||||
operationName string
|
||||
}
|
||||
|
||||
var _ error = alreadyExistsError{}
|
||||
|
||||
func (err alreadyExistsError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Failed to create operation with name %q. An operation with that name is already executing.",
|
||||
err.operationName)
|
||||
}
|
531
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap_test.go
generated
vendored
Normal file
531
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap_test.go
generated
vendored
Normal file
|
@ -0,0 +1,531 @@
|
|||
/*
|
||||
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 goroutinemap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
// testTimeout is a timeout of goroutines to finish. This _should_ be just a
|
||||
// "context switch" and it should take several ms, however, Clayton says "We
|
||||
// have had flakes due to tests that assumed that 15s is long enough to sleep")
|
||||
testTimeout time.Duration = 1 * time.Minute
|
||||
|
||||
// initialOperationWaitTimeShort is the initial amount of time the test will
|
||||
// wait for an operation to complete (each successive failure results in
|
||||
// exponential backoff).
|
||||
initialOperationWaitTimeShort time.Duration = 20 * time.Millisecond
|
||||
|
||||
// initialOperationWaitTimeLong is the initial amount of time the test will
|
||||
// wait for an operation to complete (each successive failure results in
|
||||
// exponential backoff).
|
||||
initialOperationWaitTimeLong time.Duration = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SingleOp(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation := func() error { return nil }
|
||||
|
||||
// Act
|
||||
err := grm.Run(operationName, operation)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_TwoOps(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operation1Name := "operation1-name"
|
||||
operation2Name := "operation2-name"
|
||||
operation := func() error { return nil }
|
||||
|
||||
// Act
|
||||
err1 := grm.Run(operation1Name, operation)
|
||||
err2 := grm.Run(operation2Name, operation)
|
||||
|
||||
// Assert
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine %q failed. Expected: <no error> Actual: <%v>", operation1Name, err1)
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
t.Fatalf("NewGoRoutine %q failed. Expected: <no error> Actual: <%v>", operation2Name, err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SingleOpWithExpBackoff(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation := func() error { return nil }
|
||||
|
||||
// Act
|
||||
err := grm.Run(operationName, operation)
|
||||
|
||||
// Assert
|
||||
if err != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstCompletes(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateCallbackFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
<-operation1DoneCh // Force operation1 to complete
|
||||
|
||||
// Act
|
||||
err2 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeShort),
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation2)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err2 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstCompletesWithExpBackoff(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateCallbackFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
<-operation1DoneCh // Force operation1 to complete
|
||||
|
||||
// Act
|
||||
err2 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeShort),
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation2)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err2 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstPanics(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1 := generatePanicFunc()
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeShort),
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation2)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err2 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_SecondOpAfterFirstPanicsWithExpBackoff(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1 := generatePanicFunc()
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeLong), // Longer duration to accommodate for backoff
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation2)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err2 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Negative_SecondOpBeforeFirstCompletes(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := grm.Run(operationName, operation2)
|
||||
|
||||
// Assert
|
||||
if err2 == nil {
|
||||
t.Fatalf("NewGoRoutine did not fail. Expected: <Failed to create operation with name \"%s\". An operation with that name already exists.> Actual: <no error>", operationName)
|
||||
}
|
||||
if !IsAlreadyExists(err2) {
|
||||
t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Negative_SecondOpBeforeFirstCompletesWithExpBackoff(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := grm.Run(operationName, operation2)
|
||||
|
||||
// Assert
|
||||
if err2 == nil {
|
||||
t.Fatalf("NewGoRoutine did not fail. Expected: <Failed to create operation with name \"%s\". An operation with that name already exists.> Actual: <no error>", operationName)
|
||||
}
|
||||
if !IsAlreadyExists(err2) {
|
||||
t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_ThirdOpAfterFirstCompletes(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
operation3 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := grm.Run(operationName, operation2)
|
||||
|
||||
// Assert
|
||||
if err2 == nil {
|
||||
t.Fatalf("NewGoRoutine did not fail. Expected: <Failed to create operation with name \"%s\". An operation with that name already exists.> Actual: <no error>", operationName)
|
||||
}
|
||||
if !IsAlreadyExists(err2) {
|
||||
t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2)
|
||||
}
|
||||
|
||||
// Act
|
||||
operation1DoneCh <- true // Force operation1 to complete
|
||||
err3 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeShort),
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation3)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err3 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err3)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_ThirdOpAfterFirstCompletesWithExpBackoff(t *testing.T) {
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err1 := grm.Run(operationName, operation1)
|
||||
if err1 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err1)
|
||||
}
|
||||
operation2 := generateNoopFunc()
|
||||
operation3 := generateNoopFunc()
|
||||
|
||||
// Act
|
||||
err2 := grm.Run(operationName, operation2)
|
||||
|
||||
// Assert
|
||||
if err2 == nil {
|
||||
t.Fatalf("NewGoRoutine did not fail. Expected: <Failed to create operation with name \"%s\". An operation with that name already exists.> Actual: <no error>", operationName)
|
||||
}
|
||||
if !IsAlreadyExists(err2) {
|
||||
t.Fatalf("NewGoRoutine did not return alreadyExistsError, got: %v", err2)
|
||||
}
|
||||
|
||||
// Act
|
||||
operation1DoneCh <- true // Force operation1 to complete
|
||||
err3 := retryWithExponentialBackOff(
|
||||
time.Duration(initialOperationWaitTimeShort),
|
||||
func() (bool, error) {
|
||||
err := grm.Run(operationName, operation3)
|
||||
if err != nil {
|
||||
t.Logf("Warning: NewGoRoutine failed with %v. Will retry.", err)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
|
||||
// Assert
|
||||
if err3 != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err3)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_WaitEmpty(t *testing.T) {
|
||||
// Test than Wait() on empty GoRoutineMap always succeeds without blocking
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
|
||||
// Act
|
||||
waitDoneCh := make(chan interface{}, 1)
|
||||
go func() {
|
||||
grm.Wait()
|
||||
waitDoneCh <- true
|
||||
}()
|
||||
|
||||
// Assert
|
||||
err := waitChannelWithTimeout(waitDoneCh, testTimeout)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for GoRoutineMap.Wait: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_WaitEmptyWithExpBackoff(t *testing.T) {
|
||||
// Test than Wait() on empty GoRoutineMap always succeeds without blocking
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
|
||||
// Act
|
||||
waitDoneCh := make(chan interface{}, 1)
|
||||
go func() {
|
||||
grm.Wait()
|
||||
waitDoneCh <- true
|
||||
}()
|
||||
|
||||
// Assert
|
||||
err := waitChannelWithTimeout(waitDoneCh, testTimeout)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for GoRoutineMap.Wait: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_Wait(t *testing.T) {
|
||||
// Test that Wait() really blocks until the last operation succeeds
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(false /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err := grm.Run(operationName, operation1)
|
||||
if err != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
waitDoneCh := make(chan interface{}, 1)
|
||||
go func() {
|
||||
grm.Wait()
|
||||
waitDoneCh <- true
|
||||
}()
|
||||
|
||||
// Finish the operation
|
||||
operation1DoneCh <- true
|
||||
|
||||
// Assert
|
||||
err = waitChannelWithTimeout(waitDoneCh, testTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("Error waiting for GoRoutineMap.Wait: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_Positive_WaitWithExpBackoff(t *testing.T) {
|
||||
// Test that Wait() really blocks until the last operation succeeds
|
||||
// Arrange
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-name"
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateWaitFunc(operation1DoneCh)
|
||||
err := grm.Run(operationName, operation1)
|
||||
if err != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
waitDoneCh := make(chan interface{}, 1)
|
||||
go func() {
|
||||
grm.Wait()
|
||||
waitDoneCh <- true
|
||||
}()
|
||||
|
||||
// Finish the operation
|
||||
operation1DoneCh <- true
|
||||
|
||||
// Assert
|
||||
err = waitChannelWithTimeout(waitDoneCh, testTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("Error waiting for GoRoutineMap.Wait: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_NewGoRoutineMap_WaitForCompletionWithExpBackoff(t *testing.T) {
|
||||
grm := NewGoRoutineMap(true /* exponentialBackOffOnError */)
|
||||
operationName := "operation-err"
|
||||
|
||||
operation1DoneCh := make(chan interface{}, 0 /* bufferSize */)
|
||||
operation1 := generateErrorFunc(operation1DoneCh)
|
||||
err := grm.Run(operationName, operation1)
|
||||
if err != nil {
|
||||
t.Fatalf("NewGoRoutine failed. Expected: <no error> Actual: <%v>", err)
|
||||
}
|
||||
|
||||
// Act
|
||||
waitDoneCh := make(chan interface{}, 1)
|
||||
go func() {
|
||||
grm.WaitForCompletion()
|
||||
waitDoneCh <- true
|
||||
}()
|
||||
|
||||
// Finish the operation
|
||||
operation1DoneCh <- true
|
||||
|
||||
// Assert that WaitForCompletion returns even if scheduled op had error
|
||||
err = waitChannelWithTimeout(waitDoneCh, testTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("Error waiting for GoRoutineMap.Wait: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateCallbackFunc(done chan<- interface{}) func() error {
|
||||
return func() error {
|
||||
done <- true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func generateErrorFunc(done <-chan interface{}) func() error {
|
||||
return func() error {
|
||||
<-done
|
||||
return fmt.Errorf("Generic error")
|
||||
}
|
||||
}
|
||||
|
||||
func generateWaitFunc(done <-chan interface{}) func() error {
|
||||
return func() error {
|
||||
<-done
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func generatePanicFunc() func() error {
|
||||
return func() error {
|
||||
panic("testing panic")
|
||||
}
|
||||
}
|
||||
|
||||
func generateNoopFunc() func() error {
|
||||
return func() error { return nil }
|
||||
}
|
||||
|
||||
func retryWithExponentialBackOff(initialDuration time.Duration, fn wait.ConditionFunc) error {
|
||||
backoff := wait.Backoff{
|
||||
Duration: initialDuration,
|
||||
Factor: 3,
|
||||
Jitter: 0,
|
||||
Steps: 4,
|
||||
}
|
||||
return wait.ExponentialBackoff(backoff, fn)
|
||||
}
|
||||
|
||||
func waitChannelWithTimeout(ch <-chan interface{}, timeout time.Duration) error {
|
||||
timer := time.NewTimer(timeout)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
// Success!
|
||||
return nil
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("timeout after %v", timeout)
|
||||
}
|
||||
}
|
37
vendor/k8s.io/kubernetes/pkg/util/hash/BUILD
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/util/hash/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["hash.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/davecgh/go-spew/spew"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["hash_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//vendor:github.com/davecgh/go-spew/spew"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
37
vendor/k8s.io/kubernetes/pkg/util/hash/hash.go
generated
vendored
Normal file
37
vendor/k8s.io/kubernetes/pkg/util/hash/hash.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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 hash
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// DeepHashObject writes specified object to hash using the spew library
|
||||
// which follows pointers and prints actual values of the nested objects
|
||||
// ensuring the hash does not change when a pointer changes.
|
||||
func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) {
|
||||
hasher.Reset()
|
||||
printer := spew.ConfigState{
|
||||
Indent: " ",
|
||||
SortKeys: true,
|
||||
DisableMethods: true,
|
||||
SpewKeys: true,
|
||||
}
|
||||
printer.Fprintf(hasher, "%#v", objectToWrite)
|
||||
}
|
147
vendor/k8s.io/kubernetes/pkg/util/hash/hash_test.go
generated
vendored
Normal file
147
vendor/k8s.io/kubernetes/pkg/util/hash/hash_test.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
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 hash
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
x int
|
||||
y string
|
||||
}
|
||||
|
||||
type B struct {
|
||||
x []int
|
||||
y map[string]bool
|
||||
}
|
||||
|
||||
type C struct {
|
||||
x int
|
||||
y string
|
||||
}
|
||||
|
||||
func (c C) String() string {
|
||||
return fmt.Sprintf("%d:%s", c.x, c.y)
|
||||
}
|
||||
|
||||
func TestDeepHashObject(t *testing.T) {
|
||||
successCases := []func() interface{}{
|
||||
func() interface{} { return 8675309 },
|
||||
func() interface{} { return "Jenny, I got your number" },
|
||||
func() interface{} { return []string{"eight", "six", "seven"} },
|
||||
func() interface{} { return [...]int{5, 3, 0, 9} },
|
||||
func() interface{} { return map[int]string{8: "8", 6: "6", 7: "7"} },
|
||||
func() interface{} { return map[string]int{"5": 5, "3": 3, "0": 0, "9": 9} },
|
||||
func() interface{} { return A{867, "5309"} },
|
||||
func() interface{} { return &A{867, "5309"} },
|
||||
func() interface{} {
|
||||
return B{[]int{8, 6, 7}, map[string]bool{"5": true, "3": true, "0": true, "9": true}}
|
||||
},
|
||||
func() interface{} { return map[A]bool{A{8675309, "Jenny"}: true, A{9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[C]bool{C{8675309, "Jenny"}: true, C{9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[*A]bool{&A{8675309, "Jenny"}: true, &A{9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[*C]bool{&C{8675309, "Jenny"}: true, &C{9765683, "!Jenny"}: false} },
|
||||
}
|
||||
|
||||
for _, tc := range successCases {
|
||||
hasher1 := adler32.New()
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash1 := hasher1.Sum32()
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash2 := hasher1.Sum32()
|
||||
if hash1 != hash2 {
|
||||
t.Fatalf("hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash2)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
hasher2 := adler32.New()
|
||||
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash1a := hasher1.Sum32()
|
||||
DeepHashObject(hasher2, tc())
|
||||
hash2a := hasher2.Sum32()
|
||||
|
||||
if hash1a != hash1 {
|
||||
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash1a)
|
||||
}
|
||||
if hash2a != hash2 {
|
||||
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash2, hash2a)
|
||||
}
|
||||
if hash1a != hash2a {
|
||||
t.Errorf("hash of the same object produced (%q) different results: %d vs %d", toString(tc()), hash1a, hash2a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toString(obj interface{}) string {
|
||||
return spew.Sprintf("%#v", obj)
|
||||
}
|
||||
|
||||
type wheel struct {
|
||||
radius uint32
|
||||
}
|
||||
|
||||
type unicycle struct {
|
||||
primaryWheel *wheel
|
||||
licencePlateID string
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
func TestDeepObjectPointer(t *testing.T) {
|
||||
// Arrange
|
||||
wheel1 := wheel{radius: 17}
|
||||
wheel2 := wheel{radius: 22}
|
||||
wheel3 := wheel{radius: 17}
|
||||
|
||||
myUni1 := unicycle{licencePlateID: "blah", primaryWheel: &wheel1, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
myUni2 := unicycle{licencePlateID: "blah", primaryWheel: &wheel2, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
myUni3 := unicycle{licencePlateID: "blah", primaryWheel: &wheel3, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
|
||||
// Run it more than once to verify determinism of hasher.
|
||||
for i := 0; i < 100; i++ {
|
||||
hasher1 := adler32.New()
|
||||
hasher2 := adler32.New()
|
||||
hasher3 := adler32.New()
|
||||
// Act
|
||||
DeepHashObject(hasher1, myUni1)
|
||||
hash1 := hasher1.Sum32()
|
||||
DeepHashObject(hasher1, myUni1)
|
||||
hash1a := hasher1.Sum32()
|
||||
DeepHashObject(hasher2, myUni2)
|
||||
hash2 := hasher2.Sum32()
|
||||
DeepHashObject(hasher3, myUni3)
|
||||
hash3 := hasher3.Sum32()
|
||||
|
||||
// Assert
|
||||
if hash1 != hash1a {
|
||||
t.Errorf("repeated hash of the same object produced different results: %d vs %d", hash1, hash1a)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Errorf("hash1 (%d) and hash2(%d) must be different because they have different values for wheel size", hash1, hash2)
|
||||
}
|
||||
|
||||
if hash1 != hash3 {
|
||||
t.Errorf("hash1 (%d) and hash3(%d) must be the same because although they point to different objects, they have the same values for wheel size", hash1, hash3)
|
||||
}
|
||||
}
|
||||
}
|
42
vendor/k8s.io/kubernetes/pkg/util/httpstream/BUILD
generated
vendored
Normal file
42
vendor/k8s.io/kubernetes/pkg/util/httpstream/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"httpstream.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["httpstream_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//pkg/api:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/httpstream/spdy:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/util/httpstream/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/util/httpstream/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 httpstream adds multiplexed streaming support to HTTP requests and
|
||||
// responses via connection upgrades.
|
||||
package httpstream // import "k8s.io/kubernetes/pkg/util/httpstream"
|
149
vendor/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go
generated
vendored
Normal file
149
vendor/k8s.io/kubernetes/pkg/util/httpstream/httpstream.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
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 httpstream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderConnection = "Connection"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
HeaderProtocolVersion = "X-Stream-Protocol-Version"
|
||||
HeaderAcceptedProtocolVersions = "X-Accepted-Stream-Protocol-Versions"
|
||||
)
|
||||
|
||||
// NewStreamHandler defines a function that is called when a new Stream is
|
||||
// received. If no error is returned, the Stream is accepted; otherwise,
|
||||
// the stream is rejected. After the reply frame has been sent, replySent is closed.
|
||||
type NewStreamHandler func(stream Stream, replySent <-chan struct{}) error
|
||||
|
||||
// NoOpNewStreamHandler is a stream handler that accepts a new stream and
|
||||
// performs no other logic.
|
||||
func NoOpNewStreamHandler(stream Stream, replySent <-chan struct{}) error { return nil }
|
||||
|
||||
// Dialer knows how to open a streaming connection to a server.
|
||||
type Dialer interface {
|
||||
|
||||
// Dial opens a streaming connection to a server using one of the protocols
|
||||
// specified (in order of most preferred to least preferred).
|
||||
Dial(protocols ...string) (Connection, string, error)
|
||||
}
|
||||
|
||||
// UpgradeRoundTripper is a type of http.RoundTripper that is able to upgrade
|
||||
// HTTP requests to support multiplexed bidirectional streams. After RoundTrip()
|
||||
// is invoked, if the upgrade is successful, clients may retrieve the upgraded
|
||||
// connection by calling UpgradeRoundTripper.Connection().
|
||||
type UpgradeRoundTripper interface {
|
||||
http.RoundTripper
|
||||
// NewConnection validates the response and creates a new Connection.
|
||||
NewConnection(resp *http.Response) (Connection, error)
|
||||
}
|
||||
|
||||
// ResponseUpgrader knows how to upgrade HTTP requests and responses to
|
||||
// add streaming support to them.
|
||||
type ResponseUpgrader interface {
|
||||
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed
|
||||
// streams. newStreamHandler will be called asynchronously whenever the
|
||||
// other end of the upgraded connection creates a new stream.
|
||||
UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler NewStreamHandler) Connection
|
||||
}
|
||||
|
||||
// Connection represents an upgraded HTTP connection.
|
||||
type Connection interface {
|
||||
// CreateStream creates a new Stream with the supplied headers.
|
||||
CreateStream(headers http.Header) (Stream, error)
|
||||
// Close resets all streams and closes the connection.
|
||||
Close() error
|
||||
// CloseChan returns a channel that is closed when the underlying connection is closed.
|
||||
CloseChan() <-chan bool
|
||||
// SetIdleTimeout sets the amount of time the connection may remain idle before
|
||||
// it is automatically closed.
|
||||
SetIdleTimeout(timeout time.Duration)
|
||||
}
|
||||
|
||||
// Stream represents a bidirectional communications channel that is part of an
|
||||
// upgraded connection.
|
||||
type Stream interface {
|
||||
io.ReadWriteCloser
|
||||
// Reset closes both directions of the stream, indicating that neither client
|
||||
// or server can use it any more.
|
||||
Reset() error
|
||||
// Headers returns the headers used to create the stream.
|
||||
Headers() http.Header
|
||||
// Identifier returns the stream's ID.
|
||||
Identifier() uint32
|
||||
}
|
||||
|
||||
// IsUpgradeRequest returns true if the given request is a connection upgrade request
|
||||
func IsUpgradeRequest(req *http.Request) bool {
|
||||
for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] {
|
||||
if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func negotiateProtocol(clientProtocols, serverProtocols []string) string {
|
||||
for i := range clientProtocols {
|
||||
for j := range serverProtocols {
|
||||
if clientProtocols[i] == serverProtocols[j] {
|
||||
return clientProtocols[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Handshake performs a subprotocol negotiation. If the client did request a
|
||||
// subprotocol, Handshake will select the first common value found in
|
||||
// serverProtocols. If a match is found, Handshake adds a response header
|
||||
// indicating the chosen subprotocol. If no match is found, HTTP forbidden is
|
||||
// returned, along with a response header containing the list of protocols the
|
||||
// server can accept.
|
||||
func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) {
|
||||
clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)]
|
||||
if len(clientProtocols) == 0 {
|
||||
// Kube 1.0 clients didn't support subprotocol negotiation.
|
||||
// TODO require clientProtocols once Kube 1.0 is no longer supported
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(serverProtocols) == 0 {
|
||||
// Kube 1.0 servers didn't support subprotocol negotiation. This is mainly for testing.
|
||||
// TODO require serverProtocols once Kube 1.0 is no longer supported
|
||||
return "", nil
|
||||
}
|
||||
|
||||
negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols)
|
||||
if len(negotiatedProtocol) == 0 {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
for i := range serverProtocols {
|
||||
w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i])
|
||||
}
|
||||
fmt.Fprintf(w, "unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols)
|
||||
return "", fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server supports %v", clientProtocols, serverProtocols)
|
||||
}
|
||||
|
||||
w.Header().Add(HeaderProtocolVersion, negotiatedProtocol)
|
||||
return negotiatedProtocol, nil
|
||||
}
|
127
vendor/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go
generated
vendored
Normal file
127
vendor/k8s.io/kubernetes/pkg/util/httpstream/httpstream_test.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
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 httpstream
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
type responseWriter struct {
|
||||
header http.Header
|
||||
statusCode *int
|
||||
}
|
||||
|
||||
func newResponseWriter() *responseWriter {
|
||||
return &responseWriter{
|
||||
header: make(http.Header),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *responseWriter) Header() http.Header {
|
||||
return r.header
|
||||
}
|
||||
|
||||
func (r *responseWriter) WriteHeader(code int) {
|
||||
r.statusCode = &code
|
||||
}
|
||||
|
||||
func (r *responseWriter) Write([]byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func TestHandshake(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
clientProtocols []string
|
||||
serverProtocols []string
|
||||
expectedProtocol string
|
||||
expectError bool
|
||||
}{
|
||||
"no client protocols": {
|
||||
clientProtocols: []string{},
|
||||
serverProtocols: []string{"a", "b"},
|
||||
expectedProtocol: "",
|
||||
},
|
||||
"no common protocol": {
|
||||
clientProtocols: []string{"c"},
|
||||
serverProtocols: []string{"a", "b"},
|
||||
expectedProtocol: "",
|
||||
expectError: true,
|
||||
},
|
||||
"common protocol": {
|
||||
clientProtocols: []string{"b"},
|
||||
serverProtocols: []string{"a", "b"},
|
||||
expectedProtocol: "b",
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
req, err := http.NewRequest("GET", "http://www.example.com/", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error creating request: %v", name, err)
|
||||
}
|
||||
|
||||
for _, p := range test.clientProtocols {
|
||||
req.Header.Add(HeaderProtocolVersion, p)
|
||||
}
|
||||
|
||||
w := newResponseWriter()
|
||||
negotiated, err := Handshake(req, w, test.serverProtocols)
|
||||
|
||||
// verify negotiated protocol
|
||||
if e, a := test.expectedProtocol, negotiated; e != a {
|
||||
t.Errorf("%s: protocol: expected %q, got %q", name, e, a)
|
||||
}
|
||||
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected error but did not get one", name)
|
||||
}
|
||||
if w.statusCode == nil {
|
||||
t.Errorf("%s: expected w.statusCode to be set", name)
|
||||
} else if e, a := http.StatusForbidden, *w.statusCode; e != a {
|
||||
t.Errorf("%s: w.statusCode: expected %d, got %d", name, e, a)
|
||||
}
|
||||
if e, a := test.serverProtocols, w.Header()[HeaderAcceptedProtocolVersions]; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s: accepted server protocols: expected %v, got %v", name, e, a)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !test.expectError && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if w.statusCode != nil {
|
||||
t.Errorf("%s: unexpected non-nil w.statusCode: %d", name, w.statusCode)
|
||||
}
|
||||
|
||||
if len(test.expectedProtocol) == 0 {
|
||||
if len(w.Header()[HeaderProtocolVersion]) > 0 {
|
||||
t.Errorf("%s: unexpected protocol version response header: %s", name, w.Header()[HeaderProtocolVersion])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// verify response headers
|
||||
if e, a := []string{test.expectedProtocol}, w.Header()[HeaderProtocolVersion]; !api.Semantic.DeepEqual(e, a) {
|
||||
t.Errorf("%s: protocol response header: expected %v, got %v", name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
57
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/BUILD
generated
vendored
Normal file
57
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/BUILD
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"connection.go",
|
||||
"roundtripper.go",
|
||||
"upgrade.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/util/httpstream:go_default_library",
|
||||
"//third_party/forked/golang/netutil:go_default_library",
|
||||
"//vendor:github.com/docker/spdystream",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/apimachinery/pkg/api/errors",
|
||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"connection_test.go",
|
||||
"roundtripper_test.go",
|
||||
"upgrade_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/util/httpstream:go_default_library",
|
||||
"//vendor:github.com/elazarl/goproxy",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
145
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go
generated
vendored
Normal file
145
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/spdystream"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/util/httpstream"
|
||||
)
|
||||
|
||||
// connection maintains state about a spdystream.Connection and its associated
|
||||
// streams.
|
||||
type connection struct {
|
||||
conn *spdystream.Connection
|
||||
streams []httpstream.Stream
|
||||
streamLock sync.Mutex
|
||||
newStreamHandler httpstream.NewStreamHandler
|
||||
}
|
||||
|
||||
// NewClientConnection creates a new SPDY client connection.
|
||||
func NewClientConnection(conn net.Conn) (httpstream.Connection, error) {
|
||||
spdyConn, err := spdystream.NewConnection(conn, false)
|
||||
if err != nil {
|
||||
defer conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newConnection(spdyConn, httpstream.NoOpNewStreamHandler), nil
|
||||
}
|
||||
|
||||
// NewServerConnection creates a new SPDY server connection. newStreamHandler
|
||||
// will be invoked when the server receives a newly created stream from the
|
||||
// client.
|
||||
func NewServerConnection(conn net.Conn, newStreamHandler httpstream.NewStreamHandler) (httpstream.Connection, error) {
|
||||
spdyConn, err := spdystream.NewConnection(conn, true)
|
||||
if err != nil {
|
||||
defer conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newConnection(spdyConn, newStreamHandler), nil
|
||||
}
|
||||
|
||||
// newConnection returns a new connection wrapping conn. newStreamHandler
|
||||
// will be invoked when the server receives a newly created stream from the
|
||||
// client.
|
||||
func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
|
||||
c := &connection{conn: conn, newStreamHandler: newStreamHandler}
|
||||
go conn.Serve(c.newSpdyStream)
|
||||
return c
|
||||
}
|
||||
|
||||
// createStreamResponseTimeout indicates how long to wait for the other side to
|
||||
// acknowledge the new stream before timing out.
|
||||
const createStreamResponseTimeout = 30 * time.Second
|
||||
|
||||
// Close first sends a reset for all of the connection's streams, and then
|
||||
// closes the underlying spdystream.Connection.
|
||||
func (c *connection) Close() error {
|
||||
c.streamLock.Lock()
|
||||
for _, s := range c.streams {
|
||||
// calling Reset instead of Close ensures that all streams are fully torn down
|
||||
s.Reset()
|
||||
}
|
||||
c.streams = make([]httpstream.Stream, 0)
|
||||
c.streamLock.Unlock()
|
||||
|
||||
// now that all streams are fully torn down, it's safe to call close on the underlying connection,
|
||||
// which should be able to terminate immediately at this point, instead of waiting for any
|
||||
// remaining graceful stream termination.
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// CreateStream creates a new stream with the specified headers and registers
|
||||
// it with the connection.
|
||||
func (c *connection) CreateStream(headers http.Header) (httpstream.Stream, error) {
|
||||
stream, err := c.conn.CreateStream(headers, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = stream.WaitTimeout(createStreamResponseTimeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.registerStream(stream)
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// registerStream adds the stream s to the connection's list of streams that
|
||||
// it owns.
|
||||
func (c *connection) registerStream(s httpstream.Stream) {
|
||||
c.streamLock.Lock()
|
||||
c.streams = append(c.streams, s)
|
||||
c.streamLock.Unlock()
|
||||
}
|
||||
|
||||
// CloseChan returns a channel that, when closed, indicates that the underlying
|
||||
// spdystream.Connection has been closed.
|
||||
func (c *connection) CloseChan() <-chan bool {
|
||||
return c.conn.CloseChan()
|
||||
}
|
||||
|
||||
// newSpdyStream is the internal new stream handler used by spdystream.Connection.Serve.
|
||||
// It calls connection's newStreamHandler, giving it the opportunity to accept or reject
|
||||
// the stream. If newStreamHandler returns an error, the stream is rejected. If not, the
|
||||
// stream is accepted and registered with the connection.
|
||||
func (c *connection) newSpdyStream(stream *spdystream.Stream) {
|
||||
replySent := make(chan struct{})
|
||||
err := c.newStreamHandler(stream, replySent)
|
||||
rejectStream := (err != nil)
|
||||
if rejectStream {
|
||||
glog.Warningf("Stream rejected: %v", err)
|
||||
stream.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
c.registerStream(stream)
|
||||
stream.SendReply(http.Header{}, rejectStream)
|
||||
close(replySent)
|
||||
}
|
||||
|
||||
// SetIdleTimeout sets the amount of time the connection may remain idle before
|
||||
// it is automatically closed.
|
||||
func (c *connection) SetIdleTimeout(timeout time.Duration) {
|
||||
c.conn.SetIdleTimeout(timeout)
|
||||
}
|
164
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection_test.go
generated
vendored
Normal file
164
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/connection_test.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/httpstream"
|
||||
)
|
||||
|
||||
func runProxy(t *testing.T, backendUrl string, proxyUrl chan<- string, proxyDone chan<- struct{}) {
|
||||
listener, err := net.Listen("tcp4", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("error listening: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
proxyUrl <- listener.Addr().String()
|
||||
|
||||
clientConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("proxy: error accepting client connection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
backendConn, err := net.Dial("tcp4", backendUrl)
|
||||
if err != nil {
|
||||
t.Errorf("proxy: error dialing backend: %v", err)
|
||||
return
|
||||
}
|
||||
defer backendConn.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(backendConn, clientConn)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(clientConn, backendConn)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
proxyDone <- struct{}{}
|
||||
}
|
||||
|
||||
func runServer(t *testing.T, backendUrl chan<- string, serverDone chan<- struct{}) {
|
||||
listener, err := net.Listen("tcp4", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("server: error listening: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
backendUrl <- listener.Addr().String()
|
||||
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("server: error accepting connection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
streamChan := make(chan httpstream.Stream)
|
||||
replySentChan := make(chan (<-chan struct{}))
|
||||
spdyConn, err := NewServerConnection(conn, func(stream httpstream.Stream, replySent <-chan struct{}) error {
|
||||
streamChan <- stream
|
||||
replySentChan <- replySent
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("server: error creating spdy connection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
stream := <-streamChan
|
||||
replySent := <-replySentChan
|
||||
<-replySent
|
||||
|
||||
buf := make([]byte, 1)
|
||||
_, err = stream.Read(buf)
|
||||
if err != io.EOF {
|
||||
t.Errorf("server: unexpected read error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
<-spdyConn.CloseChan()
|
||||
raw := spdyConn.(*connection).conn
|
||||
if err := raw.Wait(15 * time.Second); err != nil {
|
||||
t.Errorf("server: timed out waiting for connection closure: %v", err)
|
||||
}
|
||||
|
||||
serverDone <- struct{}{}
|
||||
}
|
||||
|
||||
func TestConnectionCloseIsImmediateThroughAProxy(t *testing.T) {
|
||||
serverDone := make(chan struct{})
|
||||
backendUrlChan := make(chan string)
|
||||
go runServer(t, backendUrlChan, serverDone)
|
||||
backendUrl := <-backendUrlChan
|
||||
|
||||
proxyDone := make(chan struct{})
|
||||
proxyUrlChan := make(chan string)
|
||||
go runProxy(t, backendUrl, proxyUrlChan, proxyDone)
|
||||
proxyUrl := <-proxyUrlChan
|
||||
|
||||
conn, err := net.Dial("tcp4", proxyUrl)
|
||||
if err != nil {
|
||||
t.Fatalf("client: error connecting to proxy: %v", err)
|
||||
}
|
||||
|
||||
spdyConn, err := NewClientConnection(conn)
|
||||
if err != nil {
|
||||
t.Fatalf("client: error creating spdy connection: %v", err)
|
||||
}
|
||||
|
||||
if _, err := spdyConn.CreateStream(http.Header{}); err != nil {
|
||||
t.Fatalf("client: error creating stream: %v", err)
|
||||
}
|
||||
|
||||
spdyConn.Close()
|
||||
raw := spdyConn.(*connection).conn
|
||||
if err := raw.Wait(15 * time.Second); err != nil {
|
||||
t.Fatalf("client: timed out waiting for connection closure: %v", err)
|
||||
}
|
||||
|
||||
expired := time.NewTimer(15 * time.Second)
|
||||
defer expired.Stop()
|
||||
i := 0
|
||||
for {
|
||||
select {
|
||||
case <-expired.C:
|
||||
t.Fatalf("timed out waiting for proxy and/or server closure")
|
||||
case <-serverDone:
|
||||
i++
|
||||
case <-proxyDone:
|
||||
i++
|
||||
}
|
||||
if i == 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
267
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go
generated
vendored
Normal file
267
vendor/k8s.io/kubernetes/pkg/util/httpstream/spdy/roundtripper.go
generated
vendored
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/util/httpstream"
|
||||
"k8s.io/kubernetes/third_party/forked/golang/netutil"
|
||||
)
|
||||
|
||||
// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports
|
||||
// multiplexed streams. After RoundTrip() is invoked, Conn will be set
|
||||
// and usable. SpdyRoundTripper implements the UpgradeRoundTripper interface.
|
||||
type SpdyRoundTripper struct {
|
||||
//tlsConfig holds the TLS configuration settings to use when connecting
|
||||
//to the remote server.
|
||||
tlsConfig *tls.Config
|
||||
|
||||
/* TODO according to http://golang.org/pkg/net/http/#RoundTripper, a RoundTripper
|
||||
must be safe for use by multiple concurrent goroutines. If this is absolutely
|
||||
necessary, we could keep a map from http.Request to net.Conn. In practice,
|
||||
a client will create an http.Client, set the transport to a new insteace of
|
||||
SpdyRoundTripper, and use it a single time, so this hopefully won't be an issue.
|
||||
*/
|
||||
// conn is the underlying network connection to the remote server.
|
||||
conn net.Conn
|
||||
|
||||
// Dialer is the dialer used to connect. Used if non-nil.
|
||||
Dialer *net.Dialer
|
||||
|
||||
// proxier knows which proxy to use given a request, defaults to http.ProxyFromEnvironment
|
||||
// Used primarily for mocking the proxy discovery in tests.
|
||||
proxier func(req *http.Request) (*url.URL, error)
|
||||
}
|
||||
|
||||
// NewRoundTripper creates a new SpdyRoundTripper that will use
|
||||
// the specified tlsConfig.
|
||||
func NewRoundTripper(tlsConfig *tls.Config) httpstream.UpgradeRoundTripper {
|
||||
return NewSpdyRoundTripper(tlsConfig)
|
||||
}
|
||||
|
||||
// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use
|
||||
// the specified tlsConfig. This function is mostly meant for unit tests.
|
||||
func NewSpdyRoundTripper(tlsConfig *tls.Config) *SpdyRoundTripper {
|
||||
return &SpdyRoundTripper{tlsConfig: tlsConfig}
|
||||
}
|
||||
|
||||
// implements pkg/util/net.TLSClientConfigHolder for proper TLS checking during proxying with a spdy roundtripper
|
||||
func (s *SpdyRoundTripper) TLSClientConfig() *tls.Config {
|
||||
return s.tlsConfig
|
||||
}
|
||||
|
||||
// dial dials the host specified by req, using TLS if appropriate, optionally
|
||||
// using a proxy server if one is configured via environment variables.
|
||||
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
||||
proxier := s.proxier
|
||||
if proxier == nil {
|
||||
proxier = http.ProxyFromEnvironment
|
||||
}
|
||||
proxyURL, err := proxier(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if proxyURL == nil {
|
||||
return s.dialWithoutProxy(req.URL)
|
||||
}
|
||||
|
||||
// ensure we use a canonical host with proxyReq
|
||||
targetHost := netutil.CanonicalAddr(req.URL)
|
||||
|
||||
// proxying logic adapted from http://blog.h6t.eu/post/74098062923/golang-websocket-with-http-proxy-support
|
||||
proxyReq := http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{},
|
||||
Host: targetHost,
|
||||
}
|
||||
|
||||
if pa := s.proxyAuth(proxyURL); pa != "" {
|
||||
proxyReq.Header = http.Header{}
|
||||
proxyReq.Header.Set("Proxy-Authorization", pa)
|
||||
}
|
||||
|
||||
proxyDialConn, err := s.dialWithoutProxy(proxyURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxyClientConn := httputil.NewProxyClientConn(proxyDialConn, nil)
|
||||
_, err = proxyClientConn.Do(&proxyReq)
|
||||
if err != nil && err != httputil.ErrPersistEOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rwc, _ := proxyClientConn.Hijack()
|
||||
|
||||
if req.URL.Scheme != "https" {
|
||||
return rwc, nil
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(targetHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.tlsConfig == nil {
|
||||
s.tlsConfig = &tls.Config{}
|
||||
}
|
||||
|
||||
if len(s.tlsConfig.ServerName) == 0 {
|
||||
s.tlsConfig.ServerName = host
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(rwc, s.tlsConfig)
|
||||
|
||||
// need to manually call Handshake() so we can call VerifyHostname() below
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return if we were configured to skip validation
|
||||
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
if err := tlsConn.VerifyHostname(host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
// dialWithoutProxy dials the host specified by url, using TLS if appropriate.
|
||||
func (s *SpdyRoundTripper) dialWithoutProxy(url *url.URL) (net.Conn, error) {
|
||||
dialAddr := netutil.CanonicalAddr(url)
|
||||
|
||||
if url.Scheme == "http" {
|
||||
if s.Dialer == nil {
|
||||
return net.Dial("tcp", dialAddr)
|
||||
} else {
|
||||
return s.Dialer.Dial("tcp", dialAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO validate the TLSClientConfig is set up?
|
||||
var conn *tls.Conn
|
||||
var err error
|
||||
if s.Dialer == nil {
|
||||
conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig)
|
||||
} else {
|
||||
conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return if we were configured to skip validation
|
||||
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(dialAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.VerifyHostname(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// proxyAuth returns, for a given proxy URL, the value to be used for the Proxy-Authorization header
|
||||
func (s *SpdyRoundTripper) proxyAuth(proxyURL *url.URL) string {
|
||||
if proxyURL == nil || proxyURL.User == nil {
|
||||
return ""
|
||||
}
|
||||
credentials := proxyURL.User.String()
|
||||
encodedAuth := base64.StdEncoding.EncodeToString([]byte(credentials))
|
||||
return fmt.Sprintf("Basic %s", encodedAuth)
|
||||
}
|
||||
|
||||
// RoundTrip executes the Request and upgrades it. After a successful upgrade,
|
||||
// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded
|
||||
// connection.
|
||||
func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// TODO what's the best way to clone the request?
|
||||
r := *req
|
||||
req = &r
|
||||
req.Header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
|
||||
req.Header.Add(httpstream.HeaderUpgrade, HeaderSpdy31)
|
||||
|
||||
conn, err := s.dial(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = req.Write(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.conn = conn
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// NewConnection validates the upgrade response, creating and returning a new
|
||||
// httpstream.Connection if there were no errors.
|
||||
func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) {
|
||||
connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection))
|
||||
upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade))
|
||||
if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
|
||||
defer resp.Body.Close()
|
||||
responseError := ""
|
||||
responseErrorBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
responseError = "unable to read error from server response"
|
||||
} else {
|
||||
// TODO: I don't belong here, I should be abstracted from this class
|
||||
if obj, _, err := api.Codecs.UniversalDecoder().Decode(responseErrorBytes, nil, &metav1.Status{}); err == nil {
|
||||
if status, ok := obj.(*metav1.Status); ok {
|
||||
return nil, &apierrors.StatusError{ErrStatus: *status}
|
||||
}
|
||||
}
|
||||
responseError = string(responseErrorBytes)
|
||||
responseError = strings.TrimSpace(responseError)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to upgrade connection: %s", responseError)
|
||||
}
|
||||
|
||||
return NewClientConnection(s.conn)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue