vendor files

This commit is contained in:
Serguei Bezverkhi
2018-01-09 13:57:14 -05:00
parent 558bc6c02a
commit 7b24313bd6
16547 changed files with 4527373 additions and 0 deletions

75
vendor/k8s.io/kubernetes/pkg/util/BUILD generated vendored Normal file
View File

@ -0,0 +1,75 @@
package(default_visibility = ["//visibility:public"])
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/config:all-srcs",
"//pkg/util/configz:all-srcs",
"//pkg/util/dbus:all-srcs",
"//pkg/util/ebtables:all-srcs",
"//pkg/util/env:all-srcs",
"//pkg/util/file:all-srcs",
"//pkg/util/filesystem:all-srcs",
"//pkg/util/flock:all-srcs",
"//pkg/util/goroutinemap:all-srcs",
"//pkg/util/hash:all-srcs",
"//pkg/util/initsystem:all-srcs",
"//pkg/util/interrupt:all-srcs",
"//pkg/util/io:all-srcs",
"//pkg/util/ipconfig:all-srcs",
"//pkg/util/ipset:all-srcs",
"//pkg/util/iptables:all-srcs",
"//pkg/util/ipvs:all-srcs",
"//pkg/util/keymutex:all-srcs",
"//pkg/util/labels:all-srcs",
"//pkg/util/limitwriter: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/normalizer:all-srcs",
"//pkg/util/nsenter:all-srcs",
"//pkg/util/oom:all-srcs",
"//pkg/util/parsers:all-srcs",
"//pkg/util/pointer:all-srcs",
"//pkg/util/procfs:all-srcs",
"//pkg/util/reflector/prometheus:all-srcs",
"//pkg/util/removeall:all-srcs",
"//pkg/util/resizefs:all-srcs",
"//pkg/util/resourcecontainer:all-srcs",
"//pkg/util/rlimit:all-srcs",
"//pkg/util/selinux:all-srcs",
"//pkg/util/slice:all-srcs",
"//pkg/util/strings:all-srcs",
"//pkg/util/sysctl:all-srcs",
"//pkg/util/system:all-srcs",
"//pkg/util/tail:all-srcs",
"//pkg/util/taints:all-srcs",
"//pkg/util/template:all-srcs",
"//pkg/util/term:all-srcs",
"//pkg/util/threading:all-srcs",
"//pkg/util/tolerations:all-srcs",
"//pkg/util/version:all-srcs",
"//pkg/util/workqueue/prometheus:all-srcs",
],
tags = ["automanaged"],
)
sh_test(
name = "verify-util-pkg",
size = "small",
srcs = ["verify-util-pkg.sh"],
data = glob(["*.go"]),
)

43
vendor/k8s.io/kubernetes/pkg/util/async/BUILD generated vendored Normal file
View File

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"bounded_frequency_runner.go",
"runner.go",
],
importpath = "k8s.io/kubernetes/pkg/util/async",
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"bounded_frequency_runner_test.go",
"runner_test.go",
],
importpath = "k8s.io/kubernetes/pkg/util/async",
library = ":go_default_library",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,239 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package async
import (
"fmt"
"sync"
"time"
"k8s.io/client-go/util/flowcontrol"
"github.com/golang/glog"
)
// BoundedFrequencyRunner manages runs of a user-provided function.
// See NewBoundedFrequencyRunner for examples.
type BoundedFrequencyRunner struct {
name string // the name of this instance
minInterval time.Duration // the min time between runs, modulo bursts
maxInterval time.Duration // the max time between runs
run chan struct{} // try an async run
mu sync.Mutex // guards runs of fn and all mutations
fn func() // function to run
lastRun time.Time // time of last run
timer timer // timer for deferred runs
limiter rateLimiter // rate limiter for on-demand runs
}
// designed so that flowcontrol.RateLimiter satisfies
type rateLimiter interface {
TryAccept() bool
Stop()
}
type nullLimiter struct{}
func (nullLimiter) TryAccept() bool {
return true
}
func (nullLimiter) Stop() {}
var _ rateLimiter = nullLimiter{}
// for testing
type timer interface {
// C returns the timer's selectable channel.
C() <-chan time.Time
// See time.Timer.Reset.
Reset(d time.Duration) bool
// See time.Timer.Stop.
Stop() bool
// See time.Now.
Now() time.Time
// See time.Since.
Since(t time.Time) time.Duration
// See time.Sleep.
Sleep(d time.Duration)
}
// implement our timer in terms of std time.Timer.
type realTimer struct {
*time.Timer
}
func (rt realTimer) C() <-chan time.Time {
return rt.Timer.C
}
func (rt realTimer) Now() time.Time {
return time.Now()
}
func (rt realTimer) Since(t time.Time) time.Duration {
return time.Since(t)
}
func (rt realTimer) Sleep(d time.Duration) {
time.Sleep(d)
}
var _ timer = realTimer{}
// NewBoundedFrequencyRunner creates a new BoundedFrequencyRunner instance,
// which will manage runs of the specified function.
//
// All runs will be async to the caller of BoundedFrequencyRunner.Run, but
// multiple runs are serialized. If the function needs to hold locks, it must
// take them internally.
//
// Runs of the funtion will have at least minInterval between them (from
// completion to next start), except that up to bursts may be allowed. Burst
// runs are "accumulated" over time, one per minInterval up to burstRuns total.
// This can be used, for example, to mitigate the impact of expensive operations
// being called in response to user-initiated operations. Run requests that
// would violate the minInterval are coallesced and run at the next opportunity.
//
// The function will be run at least once per maxInterval. For example, this can
// force periodic refreshes of state in the absence of anyone calling Run.
//
// Examples:
//
// NewBoundedFrequencyRunner("name", fn, time.Second, 5*time.Second, 1)
// - fn will have at least 1 second between runs
// - fn will have no more than 5 seconds between runs
//
// NewBoundedFrequencyRunner("name", fn, 3*time.Second, 10*time.Second, 3)
// - fn will have at least 3 seconds between runs, with up to 3 burst runs
// - fn will have no more than 10 seconds between runs
//
// The maxInterval must be greater than or equal to the minInterval, If the
// caller passes a maxInterval less than minInterval, this function will panic.
func NewBoundedFrequencyRunner(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int) *BoundedFrequencyRunner {
timer := realTimer{Timer: time.NewTimer(0)} // will tick immediately
<-timer.C() // consume the first tick
return construct(name, fn, minInterval, maxInterval, burstRuns, timer)
}
// Make an instance with dependencies injected.
func construct(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int, timer timer) *BoundedFrequencyRunner {
if maxInterval < minInterval {
panic(fmt.Sprintf("%s: maxInterval (%v) must be >= minInterval (%v)", name, minInterval, maxInterval))
}
if timer == nil {
panic(fmt.Sprintf("%s: timer must be non-nil", name))
}
bfr := &BoundedFrequencyRunner{
name: name,
fn: fn,
minInterval: minInterval,
maxInterval: maxInterval,
run: make(chan struct{}, 1),
timer: timer,
}
if minInterval == 0 {
bfr.limiter = nullLimiter{}
} else {
// allow burst updates in short succession
qps := float32(time.Second) / float32(minInterval)
bfr.limiter = flowcontrol.NewTokenBucketRateLimiterWithClock(qps, burstRuns, timer)
}
return bfr
}
// Loop handles the periodic timer and run requests. This is expected to be
// called as a goroutine.
func (bfr *BoundedFrequencyRunner) Loop(stop <-chan struct{}) {
glog.V(3).Infof("%s Loop running", bfr.name)
bfr.timer.Reset(bfr.maxInterval)
for {
select {
case <-stop:
bfr.stop()
glog.V(3).Infof("%s Loop stopping", bfr.name)
return
case <-bfr.timer.C():
bfr.tryRun()
case <-bfr.run:
bfr.tryRun()
}
}
}
// Run the function as soon as possible. If this is called while Loop is not
// running, the call may be deferred indefinitely.
// If there is already a queued request to call the underlying function, it
// may be dropped - it is just guaranteed that we will try calling the
// underlying function as soon as possible starting from now.
func (bfr *BoundedFrequencyRunner) Run() {
// If it takes a lot of time to run the underlying function, noone is really
// processing elements from <run> channel. So to avoid blocking here on the
// putting element to it, we simply skip it if there is already an element
// in it.
select {
case bfr.run <- struct{}{}:
default:
}
}
// assumes the lock is not held
func (bfr *BoundedFrequencyRunner) stop() {
bfr.mu.Lock()
defer bfr.mu.Unlock()
bfr.limiter.Stop()
bfr.timer.Stop()
}
// assumes the lock is not held
func (bfr *BoundedFrequencyRunner) tryRun() {
bfr.mu.Lock()
defer bfr.mu.Unlock()
if bfr.limiter.TryAccept() {
// We're allowed to run the function right now.
bfr.fn()
bfr.lastRun = bfr.timer.Now()
bfr.timer.Stop()
bfr.timer.Reset(bfr.maxInterval)
glog.V(3).Infof("%s: ran, next possible in %v, periodic in %v", bfr.name, bfr.minInterval, bfr.maxInterval)
return
}
// It can't run right now, figure out when it can run next.
elapsed := bfr.timer.Since(bfr.lastRun) // how long since last run
nextPossible := bfr.minInterval - elapsed // time to next possible run
nextScheduled := bfr.maxInterval - elapsed // time to next periodic run
glog.V(4).Infof("%s: %v since last run, possible in %v, scheduled in %v", bfr.name, elapsed, nextPossible, nextScheduled)
if nextPossible < nextScheduled {
// Set the timer for ASAP, but don't drain here. Assuming Loop is running,
// it might get a delivery in the mean time, but that is OK.
bfr.timer.Stop()
bfr.timer.Reset(nextPossible)
glog.V(3).Infof("%s: throttled, scheduling run in %v", bfr.name, nextPossible)
}
}

View File

@ -0,0 +1,332 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package async
import (
"sync"
"testing"
"time"
)
// Track calls to the managed function.
type receiver struct {
lock sync.Mutex
run bool
}
func (r *receiver) F() {
r.lock.Lock()
defer r.lock.Unlock()
r.run = true
}
func (r *receiver) reset() bool {
r.lock.Lock()
defer r.lock.Unlock()
was := r.run
r.run = false
return was
}
// A single change event in the fake timer.
type timerUpdate struct {
active bool
next time.Duration // iff active == true
}
// Fake time.
type fakeTimer struct {
c chan time.Time
lock sync.Mutex
now time.Time
active bool
updated chan timerUpdate
}
func newFakeTimer() *fakeTimer {
ft := &fakeTimer{
c: make(chan time.Time),
updated: make(chan timerUpdate),
}
return ft
}
func (ft *fakeTimer) C() <-chan time.Time {
return ft.c
}
func (ft *fakeTimer) Reset(in time.Duration) bool {
ft.lock.Lock()
defer ft.lock.Unlock()
was := ft.active
ft.active = true
ft.updated <- timerUpdate{
active: true,
next: in,
}
return was
}
func (ft *fakeTimer) Stop() bool {
ft.lock.Lock()
defer ft.lock.Unlock()
was := ft.active
ft.active = false
ft.updated <- timerUpdate{
active: false,
}
return was
}
func (ft *fakeTimer) Now() time.Time {
ft.lock.Lock()
defer ft.lock.Unlock()
return ft.now
}
func (ft *fakeTimer) Since(t time.Time) time.Duration {
ft.lock.Lock()
defer ft.lock.Unlock()
return ft.now.Sub(t)
}
func (ft *fakeTimer) Sleep(d time.Duration) {
ft.lock.Lock()
defer ft.lock.Unlock()
ft.advance(d)
}
// advance the current time.
func (ft *fakeTimer) advance(d time.Duration) {
ft.lock.Lock()
defer ft.lock.Unlock()
ft.now = ft.now.Add(d)
}
// send a timer tick.
func (ft *fakeTimer) tick() {
ft.lock.Lock()
defer ft.lock.Unlock()
ft.active = false
ft.c <- ft.now
}
// return the calling line number (for printing)
// test the timer's state
func checkTimer(name string, t *testing.T, upd timerUpdate, active bool, next time.Duration) {
if upd.active != active {
t.Fatalf("%s: expected timer active=%v", name, active)
}
if active && upd.next != next {
t.Fatalf("%s: expected timer to be %v, got %v", name, next, upd.next)
}
}
// test and reset the receiver's state
func checkReceiver(name string, t *testing.T, receiver *receiver, expected bool) {
triggered := receiver.reset()
if expected && !triggered {
t.Fatalf("%s: function should have been called", name)
} else if !expected && triggered {
t.Fatalf("%s: function should not have been called", name)
}
}
// Durations embedded in test cases depend on these.
var minInterval = 1 * time.Second
var maxInterval = 10 * time.Second
func waitForReset(name string, t *testing.T, timer *fakeTimer, obj *receiver, expectCall bool, expectNext time.Duration) {
upd := <-timer.updated // wait for stop
checkReceiver(name, t, obj, expectCall)
checkReceiver(name, t, obj, false) // prove post-condition
checkTimer(name, t, upd, false, 0)
upd = <-timer.updated // wait for reset
checkTimer(name, t, upd, true, expectNext)
}
func waitForRun(name string, t *testing.T, timer *fakeTimer, obj *receiver) {
waitForReset(name, t, timer, obj, true, maxInterval)
}
func waitForDefer(name string, t *testing.T, timer *fakeTimer, obj *receiver, expectNext time.Duration) {
waitForReset(name, t, timer, obj, false, expectNext)
}
func Test_BoundedFrequencyRunnerNoBurst(t *testing.T) {
obj := &receiver{}
timer := newFakeTimer()
runner := construct("test-runner", obj.F, minInterval, maxInterval, 1, timer)
stop := make(chan struct{})
var upd timerUpdate
// Start.
go runner.Loop(stop)
upd = <-timer.updated // wait for initial time to be set to max
checkTimer("init", t, upd, true, maxInterval)
checkReceiver("init", t, obj, false)
// Run once, immediately.
// rel=0ms
runner.Run()
waitForRun("first run", t, timer, obj)
// Run again, before minInterval expires.
timer.advance(500 * time.Millisecond) // rel=500ms
runner.Run()
waitForDefer("too soon after first", t, timer, obj, 500*time.Millisecond)
// Run again, before minInterval expires.
timer.advance(499 * time.Millisecond) // rel=999ms
runner.Run()
waitForDefer("still too soon after first", t, timer, obj, 1*time.Millisecond)
// Run again, once minInterval has passed (race with timer).
timer.advance(1 * time.Millisecond) // rel=1000ms
runner.Run()
waitForRun("second run", t, timer, obj)
// Run again, before minInterval expires.
// rel=0ms
runner.Run()
waitForDefer("too soon after second", t, timer, obj, 1*time.Second)
// Run again, before minInterval expires.
timer.advance(1 * time.Millisecond) // rel=1ms
runner.Run()
waitForDefer("still too soon after second", t, timer, obj, 999*time.Millisecond)
// Let the timer tick prematurely.
timer.advance(998 * time.Millisecond) // rel=999ms
timer.tick()
waitForDefer("premature tick", t, timer, obj, 1*time.Millisecond)
// Let the timer tick.
timer.advance(1 * time.Millisecond) // rel=1000ms
timer.tick()
waitForRun("first tick", t, timer, obj)
// Let the timer tick.
timer.advance(10 * time.Second) // rel=10000ms
timer.tick()
waitForRun("second tick", t, timer, obj)
// Run again, before minInterval expires.
timer.advance(1 * time.Millisecond) // rel=1ms
runner.Run()
waitForDefer("too soon after tick", t, timer, obj, 999*time.Millisecond)
// Let the timer tick.
timer.advance(999 * time.Millisecond) // rel=1000ms
timer.tick()
waitForRun("third tick", t, timer, obj)
// Clean up.
stop <- struct{}{}
}
func Test_BoundedFrequencyRunnerBurst(t *testing.T) {
obj := &receiver{}
timer := newFakeTimer()
runner := construct("test-runner", obj.F, minInterval, maxInterval, 2, timer)
stop := make(chan struct{})
var upd timerUpdate
// Start.
go runner.Loop(stop)
upd = <-timer.updated // wait for initial time to be set to max
checkTimer("init", t, upd, true, maxInterval)
checkReceiver("init", t, obj, false)
// Run once, immediately.
// abs=0ms, rel=0ms
runner.Run()
waitForRun("first run", t, timer, obj)
// Run again, before minInterval expires, with burst.
timer.advance(1 * time.Millisecond) // abs=1ms, rel=1ms
runner.Run()
waitForRun("second run", t, timer, obj)
// Run again, before minInterval expires.
timer.advance(498 * time.Millisecond) // abs=499ms, rel=498ms
runner.Run()
waitForDefer("too soon after second", t, timer, obj, 502*time.Millisecond)
// Run again, before minInterval expires.
timer.advance(1 * time.Millisecond) // abs=500ms, rel=499ms
runner.Run()
waitForDefer("too soon after second 2", t, timer, obj, 501*time.Millisecond)
// Run again, before minInterval expires.
timer.advance(1 * time.Millisecond) // abs=501ms, rel=500ms
runner.Run()
waitForDefer("too soon after second 3", t, timer, obj, 500*time.Millisecond)
// Run again, once burst has replenished.
timer.advance(499 * time.Millisecond) // abs=1000ms, rel=999ms
runner.Run()
waitForRun("third run", t, timer, obj)
// Run again, before minInterval expires.
timer.advance(1 * time.Millisecond) // abs=1001ms, rel=1ms
runner.Run()
waitForDefer("too soon after third", t, timer, obj, 999*time.Millisecond)
// Run again, before minInterval expires.
timer.advance(998 * time.Millisecond) // abs=1999ms, rel=999ms
runner.Run()
waitForDefer("too soon after third 2", t, timer, obj, 1*time.Millisecond)
// Run again, once burst has replenished.
timer.advance(1 * time.Millisecond) // abs=2000ms, rel=1000ms
runner.Run()
waitForRun("fourth run", t, timer, obj)
// Run again, once burst has fully replenished.
timer.advance(2 * time.Second) // abs=4000ms, rel=2000ms
runner.Run()
waitForRun("fifth run", t, timer, obj)
runner.Run()
waitForRun("sixth run", t, timer, obj)
runner.Run()
waitForDefer("too soon after sixth", t, timer, obj, 1*time.Second)
// Let the timer tick.
timer.advance(1 * time.Second) // abs=5000ms, rel=1000ms
timer.tick()
waitForRun("first tick", t, timer, obj)
// Let the timer tick.
timer.advance(10 * time.Second) // abs=15000ms, rel=10000ms
timer.tick()
waitForRun("second tick", t, timer, obj)
// Clean up.
stop <- struct{}{}
}

58
vendor/k8s.io/kubernetes/pkg/util/async/runner.go generated vendored Normal file
View 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
View 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)
}
}

72
vendor/k8s.io/kubernetes/pkg/util/bandwidth/BUILD generated vendored Normal file
View File

@ -0,0 +1,72 @@
package(default_visibility = ["//visibility:public"])
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",
"unsupported.go",
"utils.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"linux.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/bandwidth",
deps = [
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
"//conditions:default": [],
}),
)
go_test(
name = "go_default_test",
srcs = [
"utils_test.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"linux_test.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/bandwidth",
library = ":go_default_library",
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
"//conditions:default": [],
}),
)
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
View 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"

View 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/apimachinery/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
}

View 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/apimachinery/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)
}

325
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux.go generated vendored Normal file
View File

@ -0,0 +1,325 @@
// +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/api/resource"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/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))
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])
if err != nil {
return "", err
}
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
}

View File

@ -0,0 +1,704 @@
// +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/apimachinery/pkg/api/resource"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(test.output), test.err },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd {
return fakeexec.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 {
name string
input string
output string
expectErr bool
}{
{
name: "IPv4 masked",
input: "1.2.3.4/16",
output: "01020000/ffff0000",
},
{
name: "IPv4 host",
input: "172.17.0.2/32",
output: "ac110002/ffffffff",
},
{
name: "IPv6 masked",
input: "2001:dead:beef::cafe/64",
output: "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
},
{
name: "IPv6 host",
input: "2001::5/128",
output: "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
},
{
name: "invalid CIDR",
input: "foo",
expectErr: true,
},
}
for _, test := range tests {
output, err := hexCIDR(test.input)
if test.expectErr {
if err == nil {
t.Errorf("case %s: unexpected non-error", test.name)
}
} else {
if err != nil {
t.Errorf("case %s: unexpected error: %v", test.name, err)
}
if output != test.output {
t.Errorf("case %s: expected: %s, saw: %s",
test.name, test.output, output)
}
}
}
}
func TestAsciiCIDR(t *testing.T) {
tests := []struct {
name string
input string
output string
expectErr bool
}{
{
name: "IPv4",
input: "01020000/ffff0000",
output: "1.2.0.0/16",
},
{
name: "IPv4 host",
input: "ac110002/ffffffff",
output: "172.17.0.2/32",
},
{
name: "IPv6",
input: "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
output: "2001:dead:beef::/64",
},
{
name: "IPv6 host",
input: "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
output: "2001::5/128",
},
{
name: "invalid CIDR",
input: "malformed",
expectErr: true,
},
{
name: "non-hex IP",
input: "nonhex/32",
expectErr: true,
},
{
name: "non-hex mask",
input: "01020000/badmask",
expectErr: true,
},
}
for _, test := range tests {
output, err := asciiCIDR(test.input)
if test.expectErr {
if err == nil {
t.Errorf("case %s: unexpected non-error", test.name)
}
} else {
if err != nil {
t.Errorf("case %s: unexpected error: %v", test.name, err)
}
if output != test.output {
t.Errorf("case %s: expected: %s, saw: %s",
test.name, test.output, output)
}
}
}
}
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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(test.output), test.err },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd {
return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(tcFilterOutput), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd {
return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.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 := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(tcFilterOutput), test.err },
func() ([]byte, error) { return []byte{}, test.err },
func() ([]byte, error) { return []byte{}, test.err },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(tcQdisc), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(output), nil },
func() ([]byte, error) { return []byte(output), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
func() ([]byte, error) { return []byte(test), nil },
func() ([]byte, error) { return []byte("\n"), nil },
func() ([]byte, error) { return []byte("\n"), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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])
}
}
}
}
}

View 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/apimachinery/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
}

65
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
/*
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/apimachinery/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) {
if podAnnotations == nil {
return nil, nil, nil
}
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
}

View File

@ -0,0 +1,90 @@
/*
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/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
api "k8s.io/kubernetes/pkg/apis/core"
)
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: metav1.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)
}
}
}

37
vendor/k8s.io/kubernetes/pkg/util/config/BUILD generated vendored Normal file
View File

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"config.go",
"doc.go",
],
importpath = "k8s.io/kubernetes/pkg/util/config",
deps = ["//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["config_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/config",
library = ":go_default_library",
)
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
View 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
View 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
}

20
vendor/k8s.io/kubernetes/pkg/util/config/doc.go generated vendored Normal file
View 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"

33
vendor/k8s.io/kubernetes/pkg/util/configz/BUILD generated vendored Normal file
View File

@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["configz.go"],
importpath = "k8s.io/kubernetes/pkg/util/configz",
)
go_test(
name = "go_default_test",
srcs = ["configz_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/configz",
library = ":go_default_library",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

90
vendor/k8s.io/kubernetes/pkg/util/configz/configz.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
/*
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 {
var b []byte
var err error
func() {
configsGuard.RLock()
defer configsGuard.RUnlock()
b, err = json.Marshal(configs)
}()
if err != nil {
return fmt.Errorf("error marshaling json: %v", err)
}
_, err = w.Write(b)
return err
}

View 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)
}
}

39
vendor/k8s.io/kubernetes/pkg/util/dbus/BUILD generated vendored Normal file
View File

@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
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",
],
importpath = "k8s.io/kubernetes/pkg/util/dbus",
deps = ["//vendor/github.com/godbus/dbus:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["dbus_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/dbus",
library = ":go_default_library",
deps = ["//vendor/github.com/godbus/dbus:go_default_library"],
)
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
View 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
View 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
View 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"

141
vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go generated vendored Normal file
View File

@ -0,0 +1,141 @@
/*
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"
"sync"
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 {
lock sync.Mutex
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) {
conn.lock.Lock()
defer conn.lock.Unlock()
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{}) {
conn.lock.Lock()
defer conn.lock.Unlock()
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...)
}

38
vendor/k8s.io/kubernetes/pkg/util/ebtables/BUILD generated vendored Normal file
View File

@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["ebtables.go"],
importpath = "k8s.io/kubernetes/pkg/util/ebtables",
deps = ["//vendor/k8s.io/utils/exec:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["ebtables_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/ebtables",
library = ":go_default_library",
deps = [
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing: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
View 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/utils/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
}

View File

@ -0,0 +1,126 @@
/*
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/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
func TestEnsureChain(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Does not Exists
func() ([]byte, error) { return nil, &fakeexec.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, &fakeexec.FakeExitError{Status: 1} },
// Fail to create chain
func() ([]byte, error) { return nil, &fakeexec.FakeExitError{Status: 2} },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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 := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.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, &fakeexec.FakeExitError{Status: 2} },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.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: exit 2, output: "
if err == nil || err.Error() != errStr {
t.Errorf("expected error: %q", errStr)
}
}

34
vendor/k8s.io/kubernetes/pkg/util/env/BUILD generated vendored Normal file
View File

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["env.go"],
importpath = "k8s.io/kubernetes/pkg/util/env",
)
go_test(
name = "go_default_test",
srcs = ["env_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/env",
library = ":go_default_library",
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
)
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
View 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
}

82
vendor/k8s.io/kubernetes/pkg/util/env/env_test.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
/*
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)
if err == nil {
t.Error("expected error")
}
}
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")
}

37
vendor/k8s.io/kubernetes/pkg/util/file/BUILD generated vendored Normal file
View File

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["file.go"],
importpath = "k8s.io/kubernetes/pkg/util/file",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["file_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/file",
library = ":go_default_library",
deps = [
"//vendor/github.com/spf13/afero:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
],
)

57
vendor/k8s.io/kubernetes/pkg/util/file/file.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package file
import (
"os"
)
// FileExists checks if specified file exists.
func FileExists(filename string) (bool, error) {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return false, nil
} else if err != nil {
return false, err
}
return true, nil
}
// FileOrSymlinkExists checks if specified file or symlink exists.
func FileOrSymlinkExists(filename string) (bool, error) {
if _, err := os.Lstat(filename); os.IsNotExist(err) {
return false, nil
} else if err != nil {
return false, err
}
return true, nil
}
// ReadDirNoStat returns a string of files/directories contained
// in dirname without calling lstat on them.
func ReadDirNoStat(dirname string) ([]string, error) {
if dirname == "" {
dirname = "."
}
f, err := os.Open(dirname)
if err != nil {
return nil, err
}
defer f.Close()
return f.Readdirnames(-1)
}

149
vendor/k8s.io/kubernetes/pkg/util/file/file_test.go generated vendored Normal file
View File

@ -0,0 +1,149 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package file
import (
"os"
"path/filepath"
"sort"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)
func RecoverEnv(wd, tmpDir string) {
os.Chdir(wd)
os.RemoveAll(tmpDir)
}
func TestFileUtils(t *testing.T) {
fs := &afero.Afero{Fs: afero.NewOsFs()}
// Create tmp dir
tmpDir, err := fs.TempDir(os.TempDir(), "util_file_test_")
if err != nil {
t.Fatal("Failed to test: failed to create temp dir.")
}
// create tmp file
tmpFile, err := fs.TempFile(tmpDir, "test_file_exists_")
if err != nil {
t.Fatal("Failed to test: failed to create temp file.")
}
// create tmp sym link
tmpSymlinkName := filepath.Join(tmpDir, "test_file_exists_sym_link")
err = os.Symlink(tmpFile.Name(), tmpSymlinkName)
if err != nil {
t.Fatal("Failed to test: failed to create sym link.")
}
// create tmp sub dir
tmpSubDir, err := fs.TempDir(tmpDir, "sub_")
if err != nil {
t.Fatal("Failed to test: failed to create temp sub dir.")
}
// record the current dir
currentDir, err := os.Getwd()
if err != nil {
t.Fatal("Failed to test: failed to get current dir.")
}
// change the work dir to temp dir
err = os.Chdir(tmpDir)
if err != nil {
t.Fatal("Failed to test: failed to change work dir.")
}
// recover test enviroment
defer RecoverEnv(currentDir, tmpDir)
t.Run("TestFileExists", func(t *testing.T) {
tests := []struct {
name string
fileName string
expectedError bool
expectedValue bool
}{
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
{"file_exists", tmpFile.Name(), false, true},
}
for _, test := range tests {
realValued, realError := FileExists(test.fileName)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
}
}
})
t.Run("TestFileOrSymlinkExists", func(t *testing.T) {
tests := []struct {
name string
fileName string
expectedError bool
expectedValue bool
}{
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
{"file_exists", tmpFile.Name(), false, true},
{"symlink_exists", tmpSymlinkName, false, true},
}
for _, test := range tests {
realValued, realError := FileOrSymlinkExists(test.fileName)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
}
}
})
t.Run("TestReadDirNoStat", func(t *testing.T) {
_, tmpFileSimpleName := filepath.Split(tmpFile.Name())
_, tmpSymlinkSimpleName := filepath.Split(tmpSymlinkName)
_, tmpSubDirSimpleName := filepath.Split(tmpSubDir)
tests := []struct {
name string
dirName string
expectedError bool
expectedValue []string
}{
{"dir_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), true, []string{}},
{"dir_is_empty", "", false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
{"dir_exists", tmpDir, false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
}
for _, test := range tests {
realValued, realError := ReadDirNoStat(test.dirName)
// execute sort action before compare
sort.Strings(realValued)
sort.Strings(test.expectedValue)
if test.expectedError {
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.dirName, test.name)
} else {
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.dirName, test.name)
}
}
})
}

34
vendor/k8s.io/kubernetes/pkg/util/filesystem/BUILD generated vendored Normal file
View File

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"defaultfs.go",
"fakefs.go",
"filesystem.go",
"watcher.go",
],
importpath = "k8s.io/kubernetes/pkg/util/filesystem",
deps = [
"//vendor/github.com/fsnotify/fsnotify:go_default_library",
"//vendor/github.com/spf13/afero:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,117 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filesystem
import (
"io/ioutil"
"os"
"path/filepath"
"time"
)
// DefaultFs implements Filesystem using same-named functions from "os" and "io/ioutil"
type DefaultFs struct{}
var _ Filesystem = DefaultFs{}
// Stat via os.Stat
func (DefaultFs) Stat(name string) (os.FileInfo, error) {
return os.Stat(name)
}
// Create via os.Create
func (DefaultFs) Create(name string) (File, error) {
file, err := os.Create(name)
if err != nil {
return nil, err
}
return &defaultFile{file}, nil
}
// Rename via os.Rename
func (DefaultFs) Rename(oldpath, newpath string) error {
return os.Rename(oldpath, newpath)
}
// MkdirAll via os.MkdirAll
func (DefaultFs) MkdirAll(path string, perm os.FileMode) error {
return os.MkdirAll(path, perm)
}
// Chtimes via os.Chtimes
func (DefaultFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
return os.Chtimes(name, atime, mtime)
}
// RemoveAll via os.RemoveAll
func (DefaultFs) RemoveAll(path string) error {
return os.RemoveAll(path)
}
// Remove via os.RemoveAll
func (DefaultFs) Remove(name string) error {
return os.Remove(name)
}
// ReadFile via ioutil.ReadFile
func (DefaultFs) ReadFile(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
// TempFile via ioutil.TempFile
func (DefaultFs) TempFile(dir, prefix string) (File, error) {
file, err := ioutil.TempFile(dir, prefix)
if err != nil {
return nil, err
}
return &defaultFile{file}, nil
}
// ReadDir via ioutil.ReadDir
func (DefaultFs) ReadDir(dirname string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dirname)
}
// Walk via filepath.Walk
func (DefaultFs) Walk(root string, walkFn filepath.WalkFunc) error {
return filepath.Walk(root, walkFn)
}
// defaultFile implements File using same-named functions from "os"
type defaultFile struct {
file *os.File
}
// Name via os.File.Name
func (file *defaultFile) Name() string {
return file.file.Name()
}
// Write via os.File.Write
func (file *defaultFile) Write(b []byte) (n int, err error) {
return file.file.Write(b)
}
// Sync via os.File.Sync
func (file *defaultFile) Sync() error {
return file.file.Sync()
}
// Close via os.File.Close
func (file *defaultFile) Close() error {
return file.file.Close()
}

123
vendor/k8s.io/kubernetes/pkg/util/filesystem/fakefs.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filesystem
import (
"os"
"path/filepath"
"time"
"github.com/spf13/afero"
)
// fakeFs is implemented in terms of afero
type fakeFs struct {
a afero.Afero
}
// NewFakeFs returns a fake Filesystem that exists in-memory, useful for unit tests
func NewFakeFs() Filesystem {
return &fakeFs{a: afero.Afero{Fs: afero.NewMemMapFs()}}
}
// Stat via afero.Fs.Stat
func (fs *fakeFs) Stat(name string) (os.FileInfo, error) {
return fs.a.Fs.Stat(name)
}
// Create via afero.Fs.Create
func (fs *fakeFs) Create(name string) (File, error) {
file, err := fs.a.Fs.Create(name)
if err != nil {
return nil, err
}
return &fakeFile{file}, nil
}
// Rename via afero.Fs.Rename
func (fs *fakeFs) Rename(oldpath, newpath string) error {
return fs.a.Fs.Rename(oldpath, newpath)
}
// MkdirAll via afero.Fs.MkdirAll
func (fs *fakeFs) MkdirAll(path string, perm os.FileMode) error {
return fs.a.Fs.MkdirAll(path, perm)
}
// Chtimes via afero.Fs.Chtimes
func (fs *fakeFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
return fs.a.Fs.Chtimes(name, atime, mtime)
}
// ReadFile via afero.ReadFile
func (fs *fakeFs) ReadFile(filename string) ([]byte, error) {
return fs.a.ReadFile(filename)
}
// TempFile via afero.TempFile
func (fs *fakeFs) TempFile(dir, prefix string) (File, error) {
file, err := fs.a.TempFile(dir, prefix)
if err != nil {
return nil, err
}
return &fakeFile{file}, nil
}
// ReadDir via afero.ReadDir
func (fs *fakeFs) ReadDir(dirname string) ([]os.FileInfo, error) {
return fs.a.ReadDir(dirname)
}
// Walk via afero.Walk
func (fs *fakeFs) Walk(root string, walkFn filepath.WalkFunc) error {
return fs.a.Walk(root, walkFn)
}
// RemoveAll via afero.RemoveAll
func (fs *fakeFs) RemoveAll(path string) error {
return fs.a.RemoveAll(path)
}
// Remove via afero.RemoveAll
func (fs *fakeFs) Remove(name string) error {
return fs.a.Remove(name)
}
// fakeFile implements File; for use with fakeFs
type fakeFile struct {
file afero.File
}
// Name via afero.File.Name
func (file *fakeFile) Name() string {
return file.file.Name()
}
// Write via afero.File.Write
func (file *fakeFile) Write(b []byte) (n int, err error) {
return file.file.Write(b)
}
// Sync via afero.File.Sync
func (file *fakeFile) Sync() error {
return file.file.Sync()
}
// Close via afero.File.Close
func (file *fakeFile) Close() error {
return file.file.Close()
}

View File

@ -0,0 +1,51 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filesystem
import (
"os"
"path/filepath"
"time"
)
// Filesystem is an interface that we can use to mock various filesystem operations
type Filesystem interface {
// from "os"
Stat(name string) (os.FileInfo, error)
Create(name string) (File, error)
Rename(oldpath, newpath string) error
MkdirAll(path string, perm os.FileMode) error
Chtimes(name string, atime time.Time, mtime time.Time) error
RemoveAll(path string) error
Remove(name string) error
// from "io/ioutil"
ReadFile(filename string) ([]byte, error)
TempFile(dir, prefix string) (File, error)
ReadDir(dirname string) ([]os.FileInfo, error)
Walk(root string, walkFn filepath.WalkFunc) error
}
// File is an interface that we can use to mock various filesystem operations typically
// accessed through the File object from the "os" package
type File interface {
// for now, the only os.File methods used are those below, add more as necessary
Name() string
Write(b []byte) (n int, err error)
Sync() error
Close() error
}

View File

@ -0,0 +1,89 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package filesystem
import (
"github.com/fsnotify/fsnotify"
)
// FSWatcher is a callback-based filesystem watcher abstraction for fsnotify.
type FSWatcher interface {
// Initializes the watcher with the given watch handlers.
// Called before all other methods.
Init(FSEventHandler, FSErrorHandler) error
// Starts listening for events and errors.
// When an event or error occurs, the corresponding handler is called.
Run()
// Add a filesystem path to watch
AddWatch(path string) error
}
// FSEventHandler is called when a fsnotify event occurs.
type FSEventHandler func(event fsnotify.Event)
// FSErrorHandler is called when a fsnotify error occurs.
type FSErrorHandler func(err error)
type fsnotifyWatcher struct {
watcher *fsnotify.Watcher
eventHandler FSEventHandler
errorHandler FSErrorHandler
}
var _ FSWatcher = &fsnotifyWatcher{}
// NewFsnotifyWatcher returns an implementation of FSWatcher that continuously listens for
// fsnotify events and calls the event handler as soon as an event is received.
func NewFsnotifyWatcher() FSWatcher {
return &fsnotifyWatcher{}
}
func (w *fsnotifyWatcher) AddWatch(path string) error {
return w.watcher.Add(path)
}
func (w *fsnotifyWatcher) Init(eventHandler FSEventHandler, errorHandler FSErrorHandler) error {
var err error
w.watcher, err = fsnotify.NewWatcher()
if err != nil {
return err
}
w.eventHandler = eventHandler
w.errorHandler = errorHandler
return nil
}
func (w *fsnotifyWatcher) Run() {
go func() {
defer w.watcher.Close()
for {
select {
case event := <-w.watcher.Events:
if w.eventHandler != nil {
w.eventHandler(event)
}
case err := <-w.watcher.Errors:
if w.errorHandler != nil {
w.errorHandler(err)
}
}
}
}()
}

44
vendor/k8s.io/kubernetes/pkg/util/flock/BUILD generated vendored Normal file
View File

@ -0,0 +1,44 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"flock_other.go",
] + select({
"@io_bazel_rules_go//go/platform:darwin_amd64": [
"flock_unix.go",
],
"@io_bazel_rules_go//go/platform:linux_amd64": [
"flock_unix.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/flock",
deps = select({
"@io_bazel_rules_go//go/platform:darwin_amd64": [
"//vendor/golang.org/x/sys/unix:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/golang.org/x/sys/unix:go_default_library",
],
"//conditions:default": [],
}),
)
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
View 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
}

35
vendor/k8s.io/kubernetes/pkg/util/flock/flock_unix.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// +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 "golang.org/x/sys/unix"
// Acquire acquires a lock on a file for the duration of the process. This method
// is reentrant.
func Acquire(path string) error {
fd, err := unix.Open(path, unix.O_CREAT|unix.O_RDWR, 0600)
if err != nil {
return err
}
// We don't need to close the fd since we should hold
// it until the process exits.
return unix.Flock(fd, unix.LOCK_EX)
}

42
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/BUILD generated vendored Normal file
View File

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["goroutinemap.go"],
importpath = "k8s.io/kubernetes/pkg/util/goroutinemap",
deps = [
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["goroutinemap_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/goroutinemap",
library = ":go_default_library",
deps = ["//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/goroutinemap/exponentialbackoff:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -0,0 +1,4 @@
approvers:
- saad-ali
reviewers:
- saad-ali

View File

@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["exponential_backoff.go"],
importpath = "k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,121 @@
/*
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.
// Value is slightly offset from 2 minutes to make timeouts due to this
// constant recognizable.
maxDurationBeforeRetry time.Duration = 2*time.Minute + 2*time.Second
)
// 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: %q",
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)
}

View File

@ -0,0 +1,245 @@
/*
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.
// Value is slightly offset from 2 minutes to make timeouts due to this
// constant recognizable.
maxDurationBeforeRetry = 2*time.Minute + 1*time.Second
)
// 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)
}

View 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)
}
}

35
vendor/k8s.io/kubernetes/pkg/util/hash/BUILD generated vendored Normal file
View File

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["hash.go"],
importpath = "k8s.io/kubernetes/pkg/util/hash",
deps = ["//vendor/github.com/davecgh/go-spew/spew:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["hash_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/hash",
library = ":go_default_library",
deps = ["//vendor/github.com/davecgh/go-spew/spew:go_default_library"],
)
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
View 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
View 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{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[C]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[*A]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
func() interface{} { return map[*C]bool{{8675309, "Jenny"}: true, {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)
}
}
}

25
vendor/k8s.io/kubernetes/pkg/util/initsystem/BUILD generated vendored Normal file
View File

@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["initsystem.go"],
importpath = "k8s.io/kubernetes/pkg/util/initsystem",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,148 @@
/*
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 initsystem
import (
"fmt"
"os/exec"
"strings"
)
type InitSystem interface {
// ServiceStart tries to start a specific service
ServiceStart(service string) error
// ServiceStop tries to stop a specific service
ServiceStop(service string) error
// ServiceExists ensures the service is defined for this init system.
ServiceExists(service string) bool
// ServiceIsEnabled ensures the service is enabled to start on each boot.
ServiceIsEnabled(service string) bool
// ServiceIsActive ensures the service is running, or attempting to run. (crash looping in the case of kubelet)
ServiceIsActive(service string) bool
}
type SystemdInitSystem struct{}
func (sysd SystemdInitSystem) ServiceStart(service string) error {
args := []string{"start", service}
err := exec.Command("systemctl", args...).Run()
return err
}
func (sysd SystemdInitSystem) ServiceStop(service string) error {
args := []string{"stop", service}
err := exec.Command("systemctl", args...).Run()
return err
}
func (sysd SystemdInitSystem) ServiceExists(service string) bool {
args := []string{"status", service}
outBytes, _ := exec.Command("systemctl", args...).Output()
output := string(outBytes)
if strings.Contains(output, "Loaded: not-found") {
return false
}
return true
}
func (sysd SystemdInitSystem) ServiceIsEnabled(service string) bool {
args := []string{"is-enabled", service}
err := exec.Command("systemctl", args...).Run()
if err != nil {
return false
}
return true
}
// ServiceIsActive will check is the service is "active". In the case of
// crash looping services (kubelet in our case) status will return as
// "activating", so we will consider this active as well.
func (sysd SystemdInitSystem) ServiceIsActive(service string) bool {
args := []string{"is-active", service}
// Ignoring error here, command returns non-0 if in "activating" status:
outBytes, _ := exec.Command("systemctl", args...).Output()
output := strings.TrimSpace(string(outBytes))
if output == "active" || output == "activating" {
return true
}
return false
}
// WindowsInitSystem is the windows implementation of InitSystem
type WindowsInitSystem struct{}
func (sysd WindowsInitSystem) ServiceStart(service string) error {
args := []string{"Start-Service", service}
err := exec.Command("powershell", args...).Run()
return err
}
func (sysd WindowsInitSystem) ServiceStop(service string) error {
args := []string{"Stop-Service", service}
err := exec.Command("powershell", args...).Run()
return err
}
func (sysd WindowsInitSystem) ServiceExists(service string) bool {
args := []string{"Get-Service", service}
err := exec.Command("powershell", args...).Run()
if err != nil {
return false
}
return true
}
func (sysd WindowsInitSystem) ServiceIsEnabled(service string) bool {
args := []string{"Get-Service", service + "| select -property starttype"}
outBytes, _ := exec.Command("powershell", args...).Output()
output := strings.TrimSpace(string(outBytes))
if strings.Contains(output, "Automatic") {
return true
}
return false
}
func (sysd WindowsInitSystem) ServiceIsActive(service string) bool {
args := []string{"Get-Service", service + "| select -property status"}
outBytes, _ := exec.Command("powershell", args...).Output()
output := strings.TrimSpace(string(outBytes))
if strings.Contains(output, "Running") {
return true
}
return false
}
// GetInitSystem returns an InitSystem for the current system, or nil
// if we cannot detect a supported init system for pre-flight checks.
// This indicates we will skip init system checks, not an error.
func GetInitSystem() (InitSystem, error) {
// Assume existence of systemctl in path implies this is a systemd system:
_, err := exec.LookPath("systemctl")
if err == nil {
return &SystemdInitSystem{}, nil
}
_, err = exec.LookPath("wininit.exe")
if err == nil {
return &WindowsInitSystem{}, nil
}
return nil, fmt.Errorf("no supported init system detected, skipping checking for services")
}

25
vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD generated vendored Normal file
View File

@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["interrupt.go"],
importpath = "k8s.io/kubernetes/pkg/util/interrupt",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,104 @@
/*
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 interrupt
import (
"os"
"os/signal"
"sync"
"syscall"
)
// terminationSignals are signals that cause the program to exit in the
// supported platforms (linux, darwin, windows).
var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}
// Handler guarantees execution of notifications after a critical section (the function passed
// to a Run method), even in the presence of process termination. It guarantees exactly once
// invocation of the provided notify functions.
type Handler struct {
notify []func()
final func(os.Signal)
once sync.Once
}
// Chain creates a new handler that invokes all notify functions when the critical section exits
// and then invokes the optional handler's notifications. This allows critical sections to be
// nested without losing exactly once invocations. Notify functions can invoke any cleanup needed
// but should not exit (which is the responsibility of the parent handler).
func Chain(handler *Handler, notify ...func()) *Handler {
if handler == nil {
return New(nil, notify...)
}
return New(handler.Signal, append(notify, handler.Close)...)
}
// New creates a new handler that guarantees all notify functions are run after the critical
// section exits (or is interrupted by the OS), then invokes the final handler. If no final
// handler is specified, the default final is `os.Exit(1)`. A handler can only be used for
// one critical section.
func New(final func(os.Signal), notify ...func()) *Handler {
return &Handler{
final: final,
notify: notify,
}
}
// Close executes all the notification handlers if they have not yet been executed.
func (h *Handler) Close() {
h.once.Do(func() {
for _, fn := range h.notify {
fn()
}
})
}
// Signal is called when an os.Signal is received, and guarantees that all notifications
// are executed, then the final handler is executed. This function should only be called once
// per Handler instance.
func (h *Handler) Signal(s os.Signal) {
h.once.Do(func() {
for _, fn := range h.notify {
fn()
}
if h.final == nil {
os.Exit(1)
}
h.final(s)
})
}
// Run ensures that any notifications are invoked after the provided fn exits (even if the
// process is interrupted by an OS termination signal). Notifications are only invoked once
// per Handler instance, so calling Run more than once will not behave as the user expects.
func (h *Handler) Run(fn func() error) error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, terminationSignals...)
defer func() {
signal.Stop(ch)
close(ch)
}()
go func() {
sig, ok := <-ch
if !ok {
return
}
h.Signal(sig)
}()
defer h.Close()
return fn()
}

29
vendor/k8s.io/kubernetes/pkg/util/io/BUILD generated vendored Normal file
View File

@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"consistentread.go",
"writer.go",
],
importpath = "k8s.io/kubernetes/pkg/util/io",
deps = ["//vendor/github.com/golang/glog:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

45
vendor/k8s.io/kubernetes/pkg/util/io/consistentread.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package io
import (
"bytes"
"fmt"
"io/ioutil"
)
// ConsistentRead repeatedly reads a file until it gets the same content twice.
// This is useful when reading files in /proc that are larger than page size
// and kernel may modify them between individual read() syscalls.
func ConsistentRead(filename string, attempts int) ([]byte, error) {
oldContent, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
for i := 0; i < attempts; i++ {
newContent, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
if bytes.Compare(oldContent, newContent) == 0 {
return newContent, nil
}
// Files are different, continue reading
oldContent = newContent
}
return nil, fmt.Errorf("could not get consistent content of %s after %d attempts", filename, attempts)
}

82
vendor/k8s.io/kubernetes/pkg/util/io/writer.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
/*
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 io
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"github.com/golang/glog"
)
// Writer is an interface which allows to write data to a file.
type Writer interface {
// WriteFile mimics ioutil.WriteFile.
WriteFile(filename string, data []byte, perm os.FileMode) error
}
// StdWriter implements Writer interface and uses standard libraries
// for writing data to files.
type StdWriter struct {
}
// WriteFile directly calls ioutil.WriteFile.
func (writer *StdWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
return ioutil.WriteFile(filename, data, perm)
}
// NsenterWriter is implementation of Writer interface that allows writing data
// to file using nsenter command.
// If a program (e.g. kubelet) runs in a container it may want to write data to
// a mounted device. Since in Docker, mount propagation mode is set to private,
// it will not see the mounted device in its own namespace. To work around this
// limitation one has to first enter hosts namespace (by using 'nsenter') and
// only then write data.
type NsenterWriter struct{}
// WriteFile calls 'nsenter cat - > <the file>' and 'nsenter chmod' to create a
// file on the host.
func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
cmd := "nsenter"
baseArgs := []string{
"--mount=/rootfs/proc/1/ns/mnt",
"--",
}
echoArgs := append(baseArgs, "sh", "-c", fmt.Sprintf("cat > %s", filename))
glog.V(5).Infof("Command to write data to file: %v %v", cmd, echoArgs)
command := exec.Command(cmd, echoArgs...)
command.Stdin = bytes.NewBuffer(data)
outputBytes, err := command.CombinedOutput()
if err != nil {
glog.Errorf("Output from writing to %q: %v", filename, string(outputBytes))
return err
}
chmodArgs := append(baseArgs, "chmod", fmt.Sprintf("%o", perm), filename)
glog.V(5).Infof("Command to change permissions to file: %v %v", cmd, chmodArgs)
outputBytes, err = exec.Command(cmd, chmodArgs...).CombinedOutput()
if err != nil {
glog.Errorf("Output from chmod command: %v", string(outputBytes))
return err
}
return nil
}

41
vendor/k8s.io/kubernetes/pkg/util/ipconfig/BUILD generated vendored Normal file
View File

@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"ipconfig.go",
],
importpath = "k8s.io/kubernetes/pkg/util/ipconfig",
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["ipconfig_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/ipconfig",
library = ":go_default_library",
deps = ["//vendor/k8s.io/utils/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/ipconfig/doc.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
/*
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 ipconfig provides an interface and implementations for running Windows ipconfig commands.
package ipconfig // import "k8s.io/kubernetes/pkg/util/ipconfig"

99
vendor/k8s.io/kubernetes/pkg/util/ipconfig/ipconfig.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
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 ipconfig
import (
"runtime"
"strings"
"sync"
"github.com/golang/glog"
utilexec "k8s.io/utils/exec"
)
// Interface is an injectable interface for running ipconfig commands. Implementations must be goroutine-safe.
type Interface interface {
// GetDnsSuffixSearchList returns the list of DNS suffix to search
GetDnsSuffixSearchList() ([]string, error)
}
const (
cmdIpconfig string = "ipconfig"
cmdDefaultArgs string = "/all"
dnsSuffixSearchLisLabel string = "DNS Suffix Search List"
)
// runner implements Interface in terms of exec("ipconfig").
type runner struct {
mu sync.Mutex
exec utilexec.Interface
}
// New returns a new Interface which will exec ipconfig.
func New(exec utilexec.Interface) Interface {
runner := &runner{
exec: exec,
}
return runner
}
// GetDnsSuffixSearchList returns the list of DNS suffix to search
func (runner *runner) GetDnsSuffixSearchList() ([]string, error) {
// Parse the DNS suffix search list from ipconfig output
// ipconfig /all on Windows displays the entry of DNS suffix search list
// An example output contains:
//
// DNS Suffix Search List. . . . . . : example1.com
// example2.com
//
// TODO: this does not work when the label is localized
suffixList := []string{}
if runtime.GOOS != "windows" {
glog.V(1).Infof("ipconfig not supported on GOOS=%s", runtime.GOOS)
return suffixList, nil
}
out, err := runner.exec.Command(cmdIpconfig, cmdDefaultArgs).Output()
if err == nil {
lines := strings.Split(string(out), "\n")
for i, line := range lines {
if trimmed := strings.TrimSpace(line); strings.HasPrefix(trimmed, dnsSuffixSearchLisLabel) {
if parts := strings.Split(trimmed, ":"); len(parts) > 1 {
if trimmed := strings.TrimSpace(parts[1]); trimmed != "" {
suffixList = append(suffixList, strings.TrimSpace(parts[1]))
}
for j := i + 1; j < len(lines); j++ {
if trimmed := strings.TrimSpace(lines[j]); trimmed != "" && !strings.Contains(trimmed, ":") {
suffixList = append(suffixList, trimmed)
} else {
break
}
}
}
break
}
}
} else {
glog.V(1).Infof("Running %s %s failed: %v", cmdIpconfig, cmdDefaultArgs, err)
}
return suffixList, err
}

View File

@ -0,0 +1,33 @@
/*
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 ipconfig
import (
"testing"
"k8s.io/utils/exec"
)
func TestGetDnsSuffixSearchList(t *testing.T) {
// Simple test
ipconfigInterface := New(exec.New())
_, err := ipconfigInterface.GetDnsSuffixSearchList()
if err != nil {
t.Errorf("expected success, got %v", err)
}
}

41
vendor/k8s.io/kubernetes/pkg/util/ipset/BUILD generated vendored Normal file
View File

@ -0,0 +1,41 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"ipset.go",
"types.go",
],
importpath = "k8s.io/kubernetes/pkg/util/ipset",
visibility = ["//visibility:public"],
deps = ["//vendor/k8s.io/utils/exec:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["ipset_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/ipset",
library = ":go_default_library",
deps = [
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/ipset/testing:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

325
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset.go generated vendored Normal file
View File

@ -0,0 +1,325 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipset
import (
"bytes"
"fmt"
"regexp"
"strconv"
"strings"
utilexec "k8s.io/utils/exec"
)
// Interface is an injectable interface for running ipset commands. Implementations must be goroutine-safe.
type Interface interface {
// FlushSet deletes all entries from a named set.
FlushSet(set string) error
// DestroySet deletes a named set.
DestroySet(set string) error
// DestroyAllSets deletes all sets.
DestroyAllSets() error
// CreateSet creates a new set, it will ignore error when the set already exists if ignoreExistErr=true.
CreateSet(set *IPSet, ignoreExistErr bool) error
// AddEntry adds a new entry to the named set.
AddEntry(entry string, set string, ignoreExistErr bool) error
// DelEntry deletes one entry from the named set
DelEntry(entry string, set string) error
// Test test if an entry exists in the named set
TestEntry(entry string, set string) (bool, error)
// ListEntries lists all the entries from a named set
ListEntries(set string) ([]string, error)
// ListSets list all set names from kernel
ListSets() ([]string, error)
// GetVersion returns the "X.Y" version string for ipset.
GetVersion() (string, error)
}
// IPSetCmd represents the ipset util. We use ipset command for ipset execute.
const IPSetCmd = "ipset"
// EntryMemberPattern is the regular expression pattern of ipset member list.
// The raw output of ipset command `ipset list {set}` is similar to,
//Name: foobar
//Type: hash:ip,port
//Revision: 2
//Header: family inet hashsize 1024 maxelem 65536
//Size in memory: 16592
//References: 0
//Members:
//192.168.1.2,tcp:8080
//192.168.1.1,udp:53
var EntryMemberPattern = "(?m)^(.*\n)*Members:\n"
// VersionPattern is the regular expression pattern of ipset version string.
// ipset version output is similar to "v6.10".
var VersionPattern = "v[0-9]+\\.[0-9]+"
// IPSet implements an Interface to an set.
type IPSet struct {
// Name is the set name.
Name string
// SetType specifies the ipset type.
SetType Type
// HashFamily specifies the protocol family of the IP addresses to be stored in the set.
// The default is inet, i.e IPv4. If users want to use IPv6, they should specify inet6.
HashFamily string
// HashSize specifies the hash table size of ipset.
HashSize int
// MaxElem specifies the max element number of ipset.
MaxElem int
// PortRange specifies the port range of bitmap:port type ipset.
PortRange string
}
// Entry represents a ipset entry.
type Entry struct {
// IP is the entry's IP. The IP address protocol corresponds to the HashFamily of IPSet.
// All entries' IP addresses in the same ip set has same the protocol, IPv4 or IPv6.
IP string
// Port is the entry's Port.
Port int
// Protocol is the entry's Protocol. The protocols of entries in the same ip set are all
// the same. The accepted protocols are TCP and UDP.
Protocol string
// Net is the entry's IP network address. Network address with zero prefix size can NOT
// be stored.
Net string
// IP2 is the entry's second IP. IP2 may not be empty for `hash:ip,port,ip` type ip set.
IP2 string
// SetType specifies the type of ip set where the entry exists.
SetType Type
}
func (e *Entry) String() string {
switch e.SetType {
case HashIPPort:
// Entry{192.168.1.1, udp, 53} -> 192.168.1.1,udp:53
// Entry{192.168.1.2, tcp, 8080} -> 192.168.1.2,tcp:8080
return fmt.Sprintf("%s,%s:%s", e.IP, e.Protocol, strconv.Itoa(e.Port))
case HashIPPortIP:
// Entry{192.168.1.1, udp, 53, 10.0.0.1} -> 192.168.1.1,udp:53,10.0.0.1
// Entry{192.168.1.2, tcp, 8080, 192.168.1.2} -> 192.168.1.2,tcp:8080,192.168.1.2
return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.IP2)
case HashIPPortNet:
// Entry{192.168.1.2, udp, 80, 10.0.1.0/24} -> 192.168.1.2,udp:80,10.0.1.0/24
// Entry{192.168.2,25, tcp, 8080, 10.1.0.0/16} -> 192.168.2,25,tcp:8080,10.1.0.0/16
return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.Net)
case BitmapPort:
// Entry{53} -> 53
// Entry{8080} -> 8080
return strconv.Itoa(e.Port)
}
return ""
}
type runner struct {
exec utilexec.Interface
}
// New returns a new Interface which will exec ipset.
func New(exec utilexec.Interface) Interface {
return &runner{
exec: exec,
}
}
// CreateSet creates a new set, it will ignore error when the set already exists if ignoreExistErr=true.
func (runner *runner) CreateSet(set *IPSet, ignoreExistErr bool) error {
// Using default values.
if set.HashSize == 0 {
set.HashSize = 1024
}
if set.MaxElem == 0 {
set.MaxElem = 65536
}
if set.HashFamily == "" {
set.HashFamily = ProtocolFamilyIPV4
}
if len(set.HashFamily) != 0 && set.HashFamily != ProtocolFamilyIPV4 && set.HashFamily != ProtocolFamilyIPV6 {
return fmt.Errorf("Currently supported protocol families are: %s and %s, %s is not supported", ProtocolFamilyIPV4, ProtocolFamilyIPV6, set.HashFamily)
}
// Default ipset type is "hash:ip,port"
if len(set.SetType) == 0 {
set.SetType = HashIPPort
}
// Check if setType is supported
if !IsValidIPSetType(set.SetType) {
return fmt.Errorf("Currently supported ipset types are: %v, %s is not supported", ValidIPSetTypes, set.SetType)
}
return runner.createSet(set, ignoreExistErr)
}
// If ignoreExistErr is set to true, then the -exist option of ipset will be specified, ipset ignores the error
// otherwise raised when the same set (setname and create parameters are identical) already exists.
func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
args := []string{"create", set.Name, string(set.SetType)}
if set.SetType == HashIPPortIP || set.SetType == HashIPPort {
args = append(args,
"family", set.HashFamily,
"hashsize", strconv.Itoa(set.HashSize),
"maxelem", strconv.Itoa(set.MaxElem),
)
}
if set.SetType == BitmapPort {
if len(set.PortRange) == 0 {
set.PortRange = DefaultPortRange
}
if !validatePortRange(set.PortRange) {
return fmt.Errorf("invalid port range for %s type ip set: %s, expect: a-b", BitmapPort, set.PortRange)
}
args = append(args, "range", set.PortRange)
}
if ignoreExistErr {
args = append(args, "-exist")
}
if _, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
return fmt.Errorf("error creating ipset %s, error: %v", set.Name, err)
}
return nil
}
// AddEntry adds a new entry to the named set.
// If the -exist option is specified, ipset ignores the error otherwise raised when
// the same set (setname and create parameters are identical) already exists.
func (runner *runner) AddEntry(entry string, set string, ignoreExistErr bool) error {
args := []string{"add", set, entry}
if ignoreExistErr {
args = append(args, "-exist")
}
if _, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
return fmt.Errorf("error adding entry %s, error: %v", entry, err)
}
return nil
}
// DelEntry is used to delete the specified entry from the set.
func (runner *runner) DelEntry(entry string, set string) error {
if _, err := runner.exec.Command(IPSetCmd, "del", set, entry).CombinedOutput(); err != nil {
return fmt.Errorf("error deleting entry %s: from set: %s, error: %v", entry, set, err)
}
return nil
}
// TestEntry is used to check whether the specified entry is in the set or not.
func (runner *runner) TestEntry(entry string, set string) (bool, error) {
if out, err := runner.exec.Command(IPSetCmd, "test", set, entry).CombinedOutput(); err == nil {
reg, e := regexp.Compile("NOT")
if e == nil && reg.MatchString(string(out)) {
return false, nil
} else if e == nil {
return true, nil
} else {
return false, fmt.Errorf("error testing entry: %s, error: %v", entry, e)
}
} else {
return false, fmt.Errorf("error testing entry %s: %v (%s)", entry, err, out)
}
}
// FlushSet deletes all entries from a named set.
func (runner *runner) FlushSet(set string) error {
if _, err := runner.exec.Command(IPSetCmd, "flush", set).CombinedOutput(); err != nil {
return fmt.Errorf("error flushing set: %s, error: %v", set, err)
}
return nil
}
// DestroySet is used to destroy a named set.
func (runner *runner) DestroySet(set string) error {
if _, err := runner.exec.Command(IPSetCmd, "destroy", set).CombinedOutput(); err != nil {
return fmt.Errorf("error destroying set %s:, error: %v", set, err)
}
return nil
}
// DestroyAllSets is used to destroy all sets.
func (runner *runner) DestroyAllSets() error {
if _, err := runner.exec.Command(IPSetCmd, "destroy").CombinedOutput(); err != nil {
return fmt.Errorf("error destroying all sets, error: %v", err)
}
return nil
}
// ListSets list all set names from kernel
func (runner *runner) ListSets() ([]string, error) {
out, err := runner.exec.Command(IPSetCmd, "list", "-n").CombinedOutput()
if err != nil {
return nil, fmt.Errorf("error listing all sets, error: %v", err)
}
return strings.Split(string(out), "\n"), nil
}
// ListEntries lists all the entries from a named set.
func (runner *runner) ListEntries(set string) ([]string, error) {
if len(set) == 0 {
return nil, fmt.Errorf("set name can't be nil")
}
out, err := runner.exec.Command(IPSetCmd, "list", set).CombinedOutput()
if err != nil {
return nil, fmt.Errorf("error listing set: %s, error: %v", set, err)
}
memberMatcher := regexp.MustCompile(EntryMemberPattern)
list := memberMatcher.ReplaceAllString(string(out[:]), "")
strs := strings.Split(list, "\n")
results := make([]string, 0)
for i := range strs {
if len(strs[i]) > 0 {
results = append(results, strs[i])
}
}
return results, nil
}
// GetVersion returns the version string.
func (runner *runner) GetVersion() (string, error) {
return getIPSetVersionString(runner.exec)
}
// getIPSetVersionString runs "ipset --version" to get the version string
// in the form of "X.Y", i.e "6.19"
func getIPSetVersionString(exec utilexec.Interface) (string, error) {
cmd := exec.Command(IPSetCmd, "--version")
cmd.SetStdin(bytes.NewReader([]byte{}))
bytes, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
versionMatcher := regexp.MustCompile(VersionPattern)
match := versionMatcher.FindStringSubmatch(string(bytes))
if match == nil {
return "", fmt.Errorf("no ipset version found in string: %s", bytes)
}
return match[0], nil
}
func validatePortRange(portRange string) bool {
strs := strings.Split(portRange, "-")
if len(strs) != 2 {
return false
}
for i := range strs {
if _, err := strconv.Atoi(strs[i]); err != nil {
return false
}
}
return true
}
var _ = Interface(&runner{})

483
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset_test.go generated vendored Normal file
View File

@ -0,0 +1,483 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipset
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
func TestCheckIPSetVersion(t *testing.T) {
testCases := []struct {
vstring string
Expect string
Err bool
}{
{"ipset v4.0, protocol version: 4", "v4.0", false},
{"ipset v5.1, protocol version: 5", "v5.1", false},
{"ipset v6.0, protocol version: 6", "v6.0", false},
{"ipset v6.1, protocol version: 6", "v6.1", false},
{"ipset v6.19, protocol version: 6", "v6.19", false},
{"total junk", "", true},
}
for i := range testCases {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// ipset version response
func() ([]byte, error) { return []byte(testCases[i].vstring), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
gotVersion, err := getIPSetVersionString(&fexec)
if (err != nil) != testCases[i].Err {
t.Errorf("Expected error: %v, Got error: %v", testCases[i].Err, err)
}
if err == nil {
if testCases[i].Expect != gotVersion {
t.Errorf("Expected result: %v, Got result: %v", testCases[i].Expect, gotVersion)
}
}
}
}
func TestFlushSet(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Success
func() ([]byte, error) { return []byte{}, nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Success.
err := runner.FlushSet("FOOBAR")
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "flush", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Flush again
err = runner.FlushSet("FOOBAR")
if err != nil {
t.Errorf("expected success, got %v", err)
}
}
func TestDestroySet(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Failure
func() ([]byte, error) {
return []byte("ipset v6.19: The set with the given name does not exist"), &fakeexec.FakeExitError{Status: 1}
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Success
err := runner.DestroySet("FOOBAR")
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy", "FOOBAR") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Failure
err = runner.DestroySet("FOOBAR")
if err == nil {
t.Errorf("expected failure, got nil")
}
}
func TestDestroyAllSets(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Success
func() ([]byte, error) { return []byte{}, nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Success
err := runner.DestroyAllSets()
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Success
err = runner.DestroyAllSets()
if err != nil {
t.Errorf("Unexpected failure: %v", err)
}
}
func TestCreateSet(t *testing.T) {
testSet := IPSet{
Name: "FOOBAR",
SetType: HashIPPort,
HashFamily: ProtocolFamilyIPV4,
}
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Success
func() ([]byte, error) { return []byte{}, nil },
// Failure
func() ([]byte, error) {
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Create with ignoreExistErr = false, expect success
err := runner.CreateSet(&testSet, false)
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Create with ignoreExistErr = true, expect success
err = runner.CreateSet(&testSet, true)
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536", "-exist") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
}
// Create with ignoreExistErr = false, expect failure
err = runner.CreateSet(&testSet, false)
if err == nil {
t.Errorf("expected failure, got nil")
}
}
func TestAddEntry(t *testing.T) {
testEntry := &Entry{
IP: "192.168.1.1",
Port: 53,
Protocol: ProtocolUDP,
SetType: HashIPPort,
}
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Success
func() ([]byte, error) { return []byte{}, nil },
// Failure
func() ([]byte, error) {
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Create with ignoreExistErr = false, expect success
err := runner.AddEntry(testEntry.String(), "FOOBAR", false)
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
// Create with ignoreExistErr = true, expect success
err = runner.AddEntry(testEntry.String(), "FOOBAR", true)
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 2 {
t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53", "-exist") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
}
// Create with ignoreExistErr = false, expect failure
err = runner.AddEntry(testEntry.String(), "FOOBAR", false)
if err == nil {
t.Errorf("expected failure, got nil")
}
}
func TestDelEntry(t *testing.T) {
// TODO: Test more set type
testEntry := &Entry{
IP: "192.168.1.1",
Port: 53,
Protocol: ProtocolUDP,
SetType: HashIPPort,
}
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte{}, nil },
// Failure
func() ([]byte, error) {
return []byte("ipset v6.19: Element cannot be deleted from the set: it's not added"), &fakeexec.FakeExitError{Status: 1}
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
err := runner.DelEntry(testEntry.String(), "FOOBAR")
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "del", "FOOBAR", "192.168.1.1,udp:53") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
err = runner.DelEntry(testEntry.String(), "FOOBAR")
if err == nil {
t.Errorf("expected failure, got nil")
}
}
func TestTestEntry(t *testing.T) {
// TODO: IPv6?
testEntry := &Entry{
IP: "10.120.7.100",
Port: 8080,
Protocol: ProtocolTCP,
SetType: HashIPPort,
}
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte("10.120.7.100,tcp:8080 is in set FOOBAR."), nil },
// Failure
func() ([]byte, error) {
return []byte("192.168.1.3,tcp:8080 is NOT in set FOOBAR."), &fakeexec.FakeExitError{Status: 1}
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Success
ok, err := runner.TestEntry(testEntry.String(), "FOOBAR")
if err != nil {
t.Errorf("expected success, got %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "test", "FOOBAR", "10.120.7.100,tcp:8080") {
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
}
if !ok {
t.Errorf("expect entry exists in test set, got not")
}
// Failure
ok, err = runner.TestEntry(testEntry.String(), "FOOBAR")
if err == nil || ok {
t.Errorf("expect entry doesn't exist in test set")
}
}
func TestListEntries(t *testing.T) {
output := `Name: foobar
Type: hash:ip,port
Revision: 2
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16592
References: 0
Members:
192.168.1.2,tcp:8080
192.168.1.1,udp:53`
emptyOutput := `Name: KUBE-NODE-PORT
Type: bitmap:port
Revision: 1
Header: range 0-65535
Size in memory: 524432
References: 1
Members:
`
testCases := []struct {
output string
expected []string
}{
{
output: output,
expected: []string{"192.168.1.2,tcp:8080", "192.168.1.1,udp:53"},
},
{
output: emptyOutput,
expected: []string{},
},
}
for i := range testCases {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) {
return []byte(testCases[i].output), nil
},
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd {
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
},
},
}
runner := New(&fexec)
// Success
entries, err := runner.ListEntries("foobar")
if err != nil {
t.Errorf("expected success, got: %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "foobar") {
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
}
if len(entries) != len(testCases[i].expected) {
t.Errorf("expected %d ipset entries, got: %d", len(testCases[i].expected), len(entries))
}
if !reflect.DeepEqual(entries, testCases[i].expected) {
t.Errorf("expected entries: %v, got: %v", testCases[i].expected, entries)
}
}
}
func TestListSets(t *testing.T) {
output := `foo
bar
baz`
expected := []string{"foo", "bar", "baz"}
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// Success
func() ([]byte, error) { return []byte(output), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
runner := New(&fexec)
// Success
list, err := runner.ListSets()
if err != nil {
t.Errorf("expected success, got: %v", err)
}
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
}
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "-n") {
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
}
if len(list) != len(expected) {
t.Errorf("expected %d sets, got: %d", len(expected), len(list))
}
if !reflect.DeepEqual(list, expected) {
t.Errorf("expected sets: %v, got: %v", expected, list)
}
}

23
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/BUILD generated vendored Normal file
View File

@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["fake.go"],
importpath = "k8s.io/kubernetes/pkg/util/ipset/testing",
visibility = ["//visibility:public"],
deps = ["//pkg/util/ipset:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,83 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"k8s.io/kubernetes/pkg/util/ipset"
)
// FakeIPSet is a no-op implementation of ipset Interface
type FakeIPSet struct {
Lines []byte
}
// NewFake create a new fake ipset interface.
func NewFake() *FakeIPSet {
return &FakeIPSet{}
}
// GetVersion is part of interface.
func (*FakeIPSet) GetVersion() (string, error) {
return "0.0", nil
}
// FlushSet is part of interface.
func (*FakeIPSet) FlushSet(set string) error {
return nil
}
// DestroySet is part of interface.
func (*FakeIPSet) DestroySet(set string) error {
return nil
}
// DestroyAllSets is part of interface.
func (*FakeIPSet) DestroyAllSets() error {
return nil
}
// CreateSet is part of interface.
func (*FakeIPSet) CreateSet(set *ipset.IPSet, ignoreExistErr bool) error {
return nil
}
// AddEntry is part of interface.
func (*FakeIPSet) AddEntry(entry string, set string, ignoreExistErr bool) error {
return nil
}
// DelEntry is part of interface.
func (*FakeIPSet) DelEntry(entry string, set string) error {
return nil
}
// TestEntry is part of interface.
func (*FakeIPSet) TestEntry(entry string, set string) (bool, error) {
return true, nil
}
// ListEntries is part of interface.
func (*FakeIPSet) ListEntries(set string) ([]string, error) {
return nil, nil
}
// ListSets is part of interface.
func (*FakeIPSet) ListSets() ([]string, error) {
return nil, nil
}
var _ = ipset.Interface(&FakeIPSet{})

70
vendor/k8s.io/kubernetes/pkg/util/ipset/types.go generated vendored Normal file
View File

@ -0,0 +1,70 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipset
// Type represents the ipset type
type Type string
const (
// HashIPPort represents the `hash:ip,port` type ipset. The hash:ip,port is similar to hash:ip but
// you can store IP address and protocol-port pairs in it. TCP, SCTP, UDP, UDPLITE, ICMP and ICMPv6 are supported
// with port numbers/ICMP(v6) types and other protocol numbers without port information.
HashIPPort Type = "hash:ip,port"
// HashIPPortIP represents the `hash:ip,port,ip` type ipset. The hash:ip,port,ip set type uses a hash to store
// IP address, port number and a second IP address triples. The port number is interpreted together with a
// protocol (default TCP) and zero protocol number cannot be used.
HashIPPortIP Type = "hash:ip,port,ip"
// HashIPPortNet represents the `hash:ip,port,net` type ipset. The hash:ip,port,net set type uses a hash to store IP address, port number and IP network address triples. The port
// number is interpreted together with a protocol (default TCP) and zero protocol number cannot be used. Network address
// with zero prefix size cannot be stored either.
HashIPPortNet Type = "hash:ip,port,net"
// BitmapPort represents the `bitmap:port` type ipset. The bitmap:port set type uses a memory range, where each bit
// represents one TCP/UDP port. A bitmap:port type of set can store up to 65535 ports.
BitmapPort Type = "bitmap:port"
)
// DefaultPortRange defines the default bitmap:port valid port range.
const DefaultPortRange string = "0-65535"
const (
// ProtocolFamilyIPV4 represents IPv4 protocol.
ProtocolFamilyIPV4 = "inet"
// ProtocolFamilyIPV6 represents IPv6 protocol.
ProtocolFamilyIPV6 = "inet6"
// ProtocolTCP represents TCP protocol.
ProtocolTCP = "tcp"
// ProtocolUDP represents UDP protocol.
ProtocolUDP = "udp"
)
// ValidIPSetTypes defines the supported ip set type.
var ValidIPSetTypes = []Type{
HashIPPort,
HashIPPortIP,
BitmapPort,
HashIPPortNet,
}
// IsValidIPSetType checks if the given ipset type is valid.
func IsValidIPSetType(set Type) bool {
for _, valid := range ValidIPSetTypes {
if set == valid {
return true
}
}
return false
}

75
vendor/k8s.io/kubernetes/pkg/util/iptables/BUILD generated vendored Normal file
View File

@ -0,0 +1,75 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"iptables.go",
"iptables_unsupported.go",
"save_restore.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"iptables_linux.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/iptables",
deps = [
"//pkg/util/dbus:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/github.com/godbus/dbus:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
],
"//conditions:default": [],
}),
)
go_test(
name = "go_default_test",
srcs = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"iptables_test.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/iptables",
library = ":go_default_library",
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//pkg/util/dbus:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/iptables/testing:all-srcs",
],
tags = ["automanaged"],
)

9
vendor/k8s.io/kubernetes/pkg/util/iptables/OWNERS generated vendored Normal file
View File

@ -0,0 +1,9 @@
reviewers:
- dcbw
- thockin
- eparis
approvers:
- dcbw
- thockin
- eparis

18
vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go generated vendored Normal file
View 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 iptables provides an interface and implementations for running iptables commands.
package iptables // import "k8s.io/kubernetes/pkg/util/iptables"

682
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go generated vendored Normal file
View File

@ -0,0 +1,682 @@
/*
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 iptables
import (
"bytes"
"fmt"
"regexp"
"strings"
"sync"
godbus "github.com/godbus/dbus"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/sets"
utildbus "k8s.io/kubernetes/pkg/util/dbus"
utilversion "k8s.io/kubernetes/pkg/util/version"
utilexec "k8s.io/utils/exec"
)
type RulePosition string
const (
Prepend RulePosition = "-I"
Append RulePosition = "-A"
)
// An injectable interface for running iptables commands. Implementations must be goroutine-safe.
type Interface interface {
// GetVersion returns the "X.Y.Z" version string for iptables.
GetVersion() (string, error)
// EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true.
EnsureChain(table Table, chain Chain) (bool, error)
// FlushChain clears the specified chain. If the chain did not exist, return error.
FlushChain(table Table, chain Chain) error
// DeleteChain deletes the specified chain. If the chain did not exist, return error.
DeleteChain(table Table, chain Chain) error
// EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true.
EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error)
// DeleteRule checks if the specified rule is present and, if so, deletes it.
DeleteRule(table Table, chain Chain, args ...string) error
// IsIpv6 returns true if this is managing ipv6 tables
IsIpv6() bool
// SaveInto calls `iptables-save` for table and stores result in a given buffer.
SaveInto(table Table, buffer *bytes.Buffer) error
// Restore runs `iptables-restore` passing data through []byte.
// table is the Table to restore
// data should be formatted like the output of SaveInto()
// flush sets the presence of the "--noflush" flag. see: FlushFlag
// counters sets the "--counters" flag. see: RestoreCountersFlag
Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error
// RestoreAll is the same as Restore except that no table is specified.
RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error
// AddReloadFunc adds a function to call on iptables reload
AddReloadFunc(reloadFunc func())
// Destroy cleans up resources used by the Interface
Destroy()
}
type Protocol byte
const (
ProtocolIpv4 Protocol = iota + 1
ProtocolIpv6
)
type Table string
const (
TableNAT Table = "nat"
TableFilter Table = "filter"
TableMangle Table = "mangle"
)
type Chain string
const (
ChainPostrouting Chain = "POSTROUTING"
ChainPrerouting Chain = "PREROUTING"
ChainOutput Chain = "OUTPUT"
ChainInput Chain = "INPUT"
ChainForward Chain = "FORWARD"
)
const (
cmdIPTablesSave string = "iptables-save"
cmdIPTablesRestore string = "iptables-restore"
cmdIPTables string = "iptables"
cmdIP6TablesRestore string = "ip6tables-restore"
cmdIP6TablesSave string = "ip6tables-save"
cmdIP6Tables string = "ip6tables"
)
// Option flag for Restore
type RestoreCountersFlag bool
const RestoreCounters RestoreCountersFlag = true
const NoRestoreCounters RestoreCountersFlag = false
// Option flag for Flush
type FlushFlag bool
const FlushTables FlushFlag = true
const NoFlushTables FlushFlag = false
// Versions of iptables less than this do not support the -C / --check flag
// (test whether a rule exists).
const MinCheckVersion = "1.4.11"
// Minimum iptables versions supporting the -w and -w<seconds> flags
const WaitMinVersion = "1.4.20"
const WaitSecondsMinVersion = "1.4.22"
const WaitString = "-w"
const WaitSecondsString = "-w5"
const LockfilePath16x = "/run/xtables.lock"
// runner implements Interface in terms of exec("iptables").
type runner struct {
mu sync.Mutex
exec utilexec.Interface
dbus utildbus.Interface
protocol Protocol
hasCheck bool
waitFlag []string
restoreWaitFlag []string
lockfilePath string
reloadFuncs []func()
signal chan *godbus.Signal
}
// newInternal returns a new Interface which will exec iptables, and allows the
// caller to change the iptables-restore lockfile path
func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol, lockfilePath string) Interface {
vstring, err := getIPTablesVersionString(exec, protocol)
if err != nil {
glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err)
vstring = MinCheckVersion
}
if lockfilePath == "" {
lockfilePath = LockfilePath16x
}
runner := &runner{
exec: exec,
dbus: dbus,
protocol: protocol,
hasCheck: getIPTablesHasCheckCommand(vstring),
waitFlag: getIPTablesWaitFlag(vstring),
restoreWaitFlag: getIPTablesRestoreWaitFlag(exec, protocol),
lockfilePath: lockfilePath,
}
// TODO this needs to be moved to a separate Start() or Run() function so that New() has zero side
// effects.
runner.connectToFirewallD()
return runner
}
// New returns a new Interface which will exec iptables.
func New(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol) Interface {
return newInternal(exec, dbus, protocol, "")
}
// Destroy is part of Interface.
func (runner *runner) Destroy() {
if runner.signal != nil {
runner.signal <- nil
}
}
const (
firewalldName = "org.fedoraproject.FirewallD1"
firewalldPath = "/org/fedoraproject/FirewallD1"
firewalldInterface = "org.fedoraproject.FirewallD1"
)
// Connects to D-Bus and listens for FirewallD start/restart. (On non-FirewallD-using
// systems, this is effectively a no-op; we listen for the signals, but they will never be
// emitted, so reload() will never be called.)
func (runner *runner) connectToFirewallD() {
bus, err := runner.dbus.SystemBus()
if err != nil {
glog.V(1).Infof("Could not connect to D-Bus system bus: %s", err)
return
}
rule := fmt.Sprintf("type='signal',sender='%s',path='%s',interface='%s',member='Reloaded'", firewalldName, firewalldPath, firewalldInterface)
bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", firewalldName)
bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
runner.signal = make(chan *godbus.Signal, 10)
bus.Signal(runner.signal)
go runner.dbusSignalHandler(bus)
}
// GetVersion returns the version string.
func (runner *runner) GetVersion() (string, error) {
return getIPTablesVersionString(runner.exec, runner.protocol)
}
// EnsureChain is part of Interface.
func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) {
fullArgs := makeFullArgs(table, chain)
runner.mu.Lock()
defer runner.mu.Unlock()
out, err := runner.run(opCreateChain, fullArgs)
if err != nil {
if ee, ok := err.(utilexec.ExitError); ok {
if ee.Exited() && ee.ExitStatus() == 1 {
return true, nil
}
}
return false, fmt.Errorf("error creating chain %q: %v: %s", chain, err, out)
}
return false, nil
}
// FlushChain is part of Interface.
func (runner *runner) FlushChain(table Table, chain Chain) error {
fullArgs := makeFullArgs(table, chain)
runner.mu.Lock()
defer runner.mu.Unlock()
out, err := runner.run(opFlushChain, fullArgs)
if err != nil {
return fmt.Errorf("error flushing chain %q: %v: %s", chain, err, out)
}
return nil
}
// DeleteChain is part of Interface.
func (runner *runner) DeleteChain(table Table, chain Chain) error {
fullArgs := makeFullArgs(table, chain)
runner.mu.Lock()
defer runner.mu.Unlock()
// TODO: we could call iptables -S first, ignore the output and check for non-zero return (more like DeleteRule)
out, err := runner.run(opDeleteChain, fullArgs)
if err != nil {
return fmt.Errorf("error deleting chain %q: %v: %s", chain, err, out)
}
return nil
}
// EnsureRule is part of Interface.
func (runner *runner) EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) {
fullArgs := makeFullArgs(table, chain, args...)
runner.mu.Lock()
defer runner.mu.Unlock()
exists, err := runner.checkRule(table, chain, args...)
if err != nil {
return false, err
}
if exists {
return true, nil
}
out, err := runner.run(operation(position), fullArgs)
if err != nil {
return false, fmt.Errorf("error appending rule: %v: %s", err, out)
}
return false, nil
}
// DeleteRule is part of Interface.
func (runner *runner) DeleteRule(table Table, chain Chain, args ...string) error {
fullArgs := makeFullArgs(table, chain, args...)
runner.mu.Lock()
defer runner.mu.Unlock()
exists, err := runner.checkRule(table, chain, args...)
if err != nil {
return err
}
if !exists {
return nil
}
out, err := runner.run(opDeleteRule, fullArgs)
if err != nil {
return fmt.Errorf("error deleting rule: %v: %s", err, out)
}
return nil
}
func (runner *runner) IsIpv6() bool {
return runner.protocol == ProtocolIpv6
}
// SaveInto is part of Interface.
func (runner *runner) SaveInto(table Table, buffer *bytes.Buffer) error {
runner.mu.Lock()
defer runner.mu.Unlock()
// run and return
iptablesSaveCmd := iptablesSaveCommand(runner.protocol)
args := []string{"-t", string(table)}
glog.V(4).Infof("running %s %v", iptablesSaveCmd, args)
cmd := runner.exec.Command(iptablesSaveCmd, args...)
// Since CombinedOutput() doesn't support redirecting it to a buffer,
// we need to workaround it by redirecting stdout and stderr to buffer
// and explicitly calling Run() [CombinedOutput() underneath itself
// creates a new buffer, redirects stdout and stderr to it and also
// calls Run()].
cmd.SetStdout(buffer)
cmd.SetStderr(buffer)
return cmd.Run()
}
// Restore is part of Interface.
func (runner *runner) Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
// setup args
args := []string{"-T", string(table)}
return runner.restoreInternal(args, data, flush, counters)
}
// RestoreAll is part of Interface.
func (runner *runner) RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
// setup args
args := make([]string, 0)
return runner.restoreInternal(args, data, flush, counters)
}
type iptablesLocker interface {
Close() error
}
// restoreInternal is the shared part of Restore/RestoreAll
func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
runner.mu.Lock()
defer runner.mu.Unlock()
if !flush {
args = append(args, "--noflush")
}
if counters {
args = append(args, "--counters")
}
// Grab the iptables lock to prevent iptables-restore and iptables
// from stepping on each other. iptables-restore 1.6.2 will have
// a --wait option like iptables itself, but that's not widely deployed.
if len(runner.restoreWaitFlag) == 0 {
locker, err := grabIptablesLocks(runner.lockfilePath)
if err != nil {
return err
}
defer func(locker iptablesLocker) {
if err := locker.Close(); err != nil {
glog.Errorf("Failed to close iptables locks: %v", err)
}
}(locker)
}
// run the command and return the output or an error including the output and error
fullArgs := append(runner.restoreWaitFlag, args...)
iptablesRestoreCmd := iptablesRestoreCommand(runner.protocol)
glog.V(4).Infof("running %s %v", iptablesRestoreCmd, fullArgs)
cmd := runner.exec.Command(iptablesRestoreCmd, fullArgs...)
cmd.SetStdin(bytes.NewBuffer(data))
b, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("%v (%s)", err, b)
}
return nil
}
func iptablesSaveCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6TablesSave
} else {
return cmdIPTablesSave
}
}
func iptablesRestoreCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6TablesRestore
} else {
return cmdIPTablesRestore
}
}
func iptablesCommand(protocol Protocol) string {
if protocol == ProtocolIpv6 {
return cmdIP6Tables
} else {
return cmdIPTables
}
}
func (runner *runner) run(op operation, args []string) ([]byte, error) {
iptablesCmd := iptablesCommand(runner.protocol)
fullArgs := append(runner.waitFlag, string(op))
fullArgs = append(fullArgs, args...)
glog.V(5).Infof("running iptables %s %v", string(op), args)
return runner.exec.Command(iptablesCmd, fullArgs...).CombinedOutput()
// Don't log err here - callers might not think it is an error.
}
// Returns (bool, nil) if it was able to check the existence of the rule, or
// (<undefined>, error) if the process of checking failed.
func (runner *runner) checkRule(table Table, chain Chain, args ...string) (bool, error) {
if runner.hasCheck {
return runner.checkRuleUsingCheck(makeFullArgs(table, chain, args...))
} else {
return runner.checkRuleWithoutCheck(table, chain, args...)
}
}
var hexnumRE = regexp.MustCompile("0x0+([0-9])")
func trimhex(s string) string {
return hexnumRE.ReplaceAllString(s, "0x$1")
}
// Executes the rule check without using the "-C" flag, instead parsing iptables-save.
// Present for compatibility with <1.4.11 versions of iptables. This is full
// of hack and half-measures. We should nix this ASAP.
func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...string) (bool, error) {
iptablesSaveCmd := iptablesSaveCommand(runner.protocol)
glog.V(1).Infof("running %s -t %s", iptablesSaveCmd, string(table))
out, err := runner.exec.Command(iptablesSaveCmd, "-t", string(table)).CombinedOutput()
if err != nil {
return false, fmt.Errorf("error checking rule: %v", err)
}
// Sadly, iptables has inconsistent quoting rules for comments. Just remove all quotes.
// Also, quoted multi-word comments (which are counted as a single arg)
// will be unpacked into multiple args,
// in order to compare against iptables-save output (which will be split at whitespace boundary)
// e.g. a single arg('"this must be before the NodePort rules"') will be unquoted and unpacked into 7 args.
var argsCopy []string
for i := range args {
tmpField := strings.Trim(args[i], "\"")
tmpField = trimhex(tmpField)
argsCopy = append(argsCopy, strings.Fields(tmpField)...)
}
argset := sets.NewString(argsCopy...)
for _, line := range strings.Split(string(out), "\n") {
var fields = strings.Fields(line)
// Check that this is a rule for the correct chain, and that it has
// the correct number of argument (+2 for "-A <chain name>")
if !strings.HasPrefix(line, fmt.Sprintf("-A %s", string(chain))) || len(fields) != len(argsCopy)+2 {
continue
}
// Sadly, iptables has inconsistent quoting rules for comments.
// Just remove all quotes.
for i := range fields {
fields[i] = strings.Trim(fields[i], "\"")
fields[i] = trimhex(fields[i])
}
// TODO: This misses reorderings e.g. "-x foo ! -y bar" will match "! -x foo -y bar"
if sets.NewString(fields...).IsSuperset(argset) {
return true, nil
}
glog.V(5).Infof("DBG: fields is not a superset of args: fields=%v args=%v", fields, args)
}
return false, nil
}
// Executes the rule check using the "-C" flag
func (runner *runner) checkRuleUsingCheck(args []string) (bool, error) {
out, err := runner.run(opCheckRule, args)
if err == nil {
return true, nil
}
if ee, ok := err.(utilexec.ExitError); ok {
// iptables uses exit(1) to indicate a failure of the operation,
// as compared to a malformed commandline, for example.
if ee.Exited() && ee.ExitStatus() == 1 {
return false, nil
}
}
return false, fmt.Errorf("error checking rule: %v: %s", err, out)
}
type operation string
const (
opCreateChain operation = "-N"
opFlushChain operation = "-F"
opDeleteChain operation = "-X"
opAppendRule operation = "-A"
opCheckRule operation = "-C"
opDeleteRule operation = "-D"
)
func makeFullArgs(table Table, chain Chain, args ...string) []string {
return append([]string{string(chain), "-t", string(table)}, args...)
}
// Checks if iptables has the "-C" flag
func getIPTablesHasCheckCommand(vstring string) bool {
minVersion, err := utilversion.ParseGeneric(MinCheckVersion)
if err != nil {
glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err)
return true
}
version, err := utilversion.ParseGeneric(vstring)
if err != nil {
glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
return true
}
return version.AtLeast(minVersion)
}
// Checks if iptables version has a "wait" flag
func getIPTablesWaitFlag(vstring string) []string {
version, err := utilversion.ParseGeneric(vstring)
if err != nil {
glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
return nil
}
minVersion, err := utilversion.ParseGeneric(WaitMinVersion)
if err != nil {
glog.Errorf("WaitMinVersion (%s) is not a valid version string: %v", WaitMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return nil
}
minVersion, err = utilversion.ParseGeneric(WaitSecondsMinVersion)
if err != nil {
glog.Errorf("WaitSecondsMinVersion (%s) is not a valid version string: %v", WaitSecondsMinVersion, err)
return nil
}
if version.LessThan(minVersion) {
return []string{WaitString}
} else {
return []string{WaitSecondsString}
}
}
// getIPTablesVersionString runs "iptables --version" to get the version string
// in the form "X.X.X"
func getIPTablesVersionString(exec utilexec.Interface, protocol Protocol) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner
iptablesCmd := iptablesCommand(protocol)
bytes, err := exec.Command(iptablesCmd, "--version").CombinedOutput()
if err != nil {
return "", err
}
versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
match := versionMatcher.FindStringSubmatch(string(bytes))
if match == nil {
return "", fmt.Errorf("no iptables version found in string: %s", bytes)
}
return match[1], nil
}
// Checks if iptables-restore has a "wait" flag
// --wait support landed in v1.6.1+ right before --version support, so
// any version of iptables-restore that supports --version will also
// support --wait
func getIPTablesRestoreWaitFlag(exec utilexec.Interface, protocol Protocol) []string {
vstring, err := getIPTablesRestoreVersionString(exec, protocol)
if err != nil || vstring == "" {
glog.V(3).Infof("couldn't get iptables-restore version; assuming it doesn't support --wait")
return nil
}
if _, err := utilversion.ParseGeneric(vstring); err != nil {
glog.V(3).Infof("couldn't parse iptables-restore version; assuming it doesn't support --wait")
return nil
}
return []string{WaitSecondsString}
}
// getIPTablesRestoreVersionString runs "iptables-restore --version" to get the version string
// in the form "X.X.X"
func getIPTablesRestoreVersionString(exec utilexec.Interface, protocol Protocol) (string, error) {
// this doesn't access mutable state so we don't need to use the interface / runner
// iptables-restore hasn't always had --version, and worse complains
// about unrecognized commands but doesn't exit when it gets them.
// Work around that by setting stdin to nothing so it exits immediately.
iptablesRestoreCmd := iptablesRestoreCommand(protocol)
cmd := exec.Command(iptablesRestoreCmd, "--version")
cmd.SetStdin(bytes.NewReader([]byte{}))
bytes, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
match := versionMatcher.FindStringSubmatch(string(bytes))
if match == nil {
return "", fmt.Errorf("no iptables version found in string: %s", bytes)
}
return match[1], nil
}
// goroutine to listen for D-Bus signals
func (runner *runner) dbusSignalHandler(bus utildbus.Connection) {
firewalld := bus.Object(firewalldName, firewalldPath)
for s := range runner.signal {
if s == nil {
// Unregister
bus.Signal(runner.signal)
return
}
switch s.Name {
case "org.freedesktop.DBus.NameOwnerChanged":
name := s.Body[0].(string)
new_owner := s.Body[2].(string)
if name != firewalldName || len(new_owner) == 0 {
continue
}
// FirewallD startup (specifically the part where it deletes
// all existing iptables rules) may not yet be complete when
// we get this signal, so make a dummy request to it to
// synchronize.
firewalld.Call(firewalldInterface+".getDefaultZone", 0)
runner.reload()
case firewalldInterface + ".Reloaded":
runner.reload()
}
}
}
// AddReloadFunc is part of Interface
func (runner *runner) AddReloadFunc(reloadFunc func()) {
runner.reloadFuncs = append(runner.reloadFuncs, reloadFunc)
}
// runs all reload funcs to re-sync iptables rules
func (runner *runner) reload() {
glog.V(1).Infof("reloading iptables rules")
for _, f := range runner.reloadFuncs {
f()
}
}
// IsNotFoundError returns true if the error indicates "not found". It parses
// the error string looking for known values, which is imperfect but works in
// practice.
func IsNotFoundError(err error) bool {
es := err.Error()
if strings.Contains(es, "No such file or directory") {
return true
}
if strings.Contains(es, "No chain/target/match by that name") {
return true
}
return false
}

View File

@ -0,0 +1,100 @@
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iptables
import (
"fmt"
"net"
"os"
"time"
"golang.org/x/sys/unix"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
)
type locker struct {
lock16 *os.File
lock14 *net.UnixListener
}
func (l *locker) Close() error {
errList := []error{}
if l.lock16 != nil {
if err := l.lock16.Close(); err != nil {
errList = append(errList, err)
}
}
if l.lock14 != nil {
if err := l.lock14.Close(); err != nil {
errList = append(errList, err)
}
}
return utilerrors.NewAggregate(errList)
}
func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) {
var err error
var success bool
l := &locker{}
defer func(l *locker) {
// Clean up immediately on failure
if !success {
l.Close()
}
}(l)
// Grab both 1.6.x and 1.4.x-style locks; we don't know what the
// iptables-restore version is if it doesn't support --wait, so we
// can't assume which lock method it'll use.
// Roughly duplicate iptables 1.6.x xtables_lock() function.
l.lock16, err = os.OpenFile(lockfilePath, os.O_CREATE, 0600)
if err != nil {
return nil, fmt.Errorf("failed to open iptables lock %s: %v", lockfilePath, err)
}
if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
if err := grabIptablesFileLock(l.lock16); err != nil {
return false, nil
}
return true, nil
}); err != nil {
return nil, fmt.Errorf("failed to acquire new iptables lock: %v", err)
}
// Roughly duplicate iptables 1.4.x xtables_lock() function.
if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
l.lock14, err = net.ListenUnix("unix", &net.UnixAddr{Name: "@xtables", Net: "unix"})
if err != nil {
return false, nil
}
return true, nil
}); err != nil {
return nil, fmt.Errorf("failed to acquire old iptables lock: %v", err)
}
success = true
return l, nil
}
func grabIptablesFileLock(f *os.File) error {
return unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
// +build !linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iptables
import (
"fmt"
"os"
)
func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) {
return nil, fmt.Errorf("iptables unsupported on this platform")
}
func grabIptablesFileLock(f *os.File) error {
return fmt.Errorf("iptables unsupported on this platform")
}

View File

@ -0,0 +1,110 @@
/*
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 iptables
import (
"fmt"
"strings"
)
// MakeChainLine return an iptables-save/restore formatted chain line given a Chain
func MakeChainLine(chain Chain) string {
return fmt.Sprintf(":%s - [0:0]", chain)
}
// GetChainLines parses a table's iptables-save data to find chains in the table.
// It returns a map of iptables.Chain to string where the string is the chain line from the save (with counters etc).
func GetChainLines(table Table, save []byte) map[Chain]string {
chainsMap := make(map[Chain]string)
tablePrefix := "*" + string(table)
readIndex := 0
// find beginning of table
for readIndex < len(save) {
line, n := ReadLine(readIndex, save)
readIndex = n
if strings.HasPrefix(line, tablePrefix) {
break
}
}
// parse table lines
for readIndex < len(save) {
line, n := ReadLine(readIndex, save)
readIndex = n
if len(line) == 0 {
continue
}
if strings.HasPrefix(line, "COMMIT") || strings.HasPrefix(line, "*") {
break
} else if strings.HasPrefix(line, "#") {
continue
} else if strings.HasPrefix(line, ":") && len(line) > 1 {
// We assume that the <line> contains space - chain lines have 3 fields,
// space delimited. If there is no space, this line will panic.
chain := Chain(line[1:strings.Index(line, " ")])
chainsMap[chain] = line
}
}
return chainsMap
}
func ReadLine(readIndex int, byteArray []byte) (string, int) {
currentReadIndex := readIndex
// consume left spaces
for currentReadIndex < len(byteArray) {
if byteArray[currentReadIndex] == ' ' {
currentReadIndex++
} else {
break
}
}
// leftTrimIndex stores the left index of the line after the line is left-trimmed
leftTrimIndex := currentReadIndex
// rightTrimIndex stores the right index of the line after the line is right-trimmed
// it is set to -1 since the correct value has not yet been determined.
rightTrimIndex := -1
for ; currentReadIndex < len(byteArray); currentReadIndex++ {
if byteArray[currentReadIndex] == ' ' {
// set rightTrimIndex
if rightTrimIndex == -1 {
rightTrimIndex = currentReadIndex
}
} else if (byteArray[currentReadIndex] == '\n') || (currentReadIndex == (len(byteArray) - 1)) {
// end of line or byte buffer is reached
if currentReadIndex <= leftTrimIndex {
return "", currentReadIndex + 1
}
// set the rightTrimIndex
if rightTrimIndex == -1 {
rightTrimIndex = currentReadIndex
if currentReadIndex == (len(byteArray)-1) && (byteArray[currentReadIndex] != '\n') {
// ensure that the last character is part of the returned string,
// unless the last character is '\n'
rightTrimIndex = currentReadIndex + 1
}
}
return string(byteArray[leftTrimIndex:rightTrimIndex]), currentReadIndex + 1
} else {
// unset rightTrimIndex
rightTrimIndex = -1
}
}
return "", currentReadIndex
}

View File

@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["fake.go"],
importpath = "k8s.io/kubernetes/pkg/util/iptables/testing",
deps = ["//pkg/util/iptables:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View 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 testing
import (
"bytes"
"fmt"
"strings"
"k8s.io/kubernetes/pkg/util/iptables"
)
const (
Destination = "-d "
Source = "-s "
DPort = "--dport "
Protocol = "-p "
Jump = "-j "
Reject = "REJECT"
ToDest = "--to-destination "
Recent = "recent "
)
type Rule map[string]string
// no-op implementation of iptables Interface
type FakeIPTables struct {
Lines []byte
}
func NewFake() *FakeIPTables {
return &FakeIPTables{}
}
func (*FakeIPTables) GetVersion() (string, error) {
return "0.0.0", nil
}
func (*FakeIPTables) EnsureChain(table iptables.Table, chain iptables.Chain) (bool, error) {
return true, nil
}
func (*FakeIPTables) FlushChain(table iptables.Table, chain iptables.Chain) error {
return nil
}
func (*FakeIPTables) DeleteChain(table iptables.Table, chain iptables.Chain) error {
return nil
}
func (*FakeIPTables) EnsureRule(position iptables.RulePosition, table iptables.Table, chain iptables.Chain, args ...string) (bool, error) {
return true, nil
}
func (*FakeIPTables) DeleteRule(table iptables.Table, chain iptables.Chain, args ...string) error {
return nil
}
func (*FakeIPTables) IsIpv6() bool {
return false
}
func (f *FakeIPTables) Save(table iptables.Table) ([]byte, error) {
lines := make([]byte, len(f.Lines))
copy(lines, f.Lines)
return lines, nil
}
func (f *FakeIPTables) SaveInto(table iptables.Table, buffer *bytes.Buffer) error {
buffer.Write(f.Lines)
return nil
}
func (*FakeIPTables) Restore(table iptables.Table, data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
return nil
}
func (f *FakeIPTables) RestoreAll(data []byte, flush iptables.FlushFlag, counters iptables.RestoreCountersFlag) error {
f.Lines = data
return nil
}
func (*FakeIPTables) AddReloadFunc(reloadFunc func()) {}
func (*FakeIPTables) Destroy() {}
func getToken(line, seperator string) string {
tokens := strings.Split(line, seperator)
if len(tokens) == 2 {
return strings.Split(tokens[1], " ")[0]
}
return ""
}
// GetChain returns a list of rules for the given chain.
// The chain name must match exactly.
// The matching is pretty dumb, don't rely on it for anything but testing.
func (f *FakeIPTables) GetRules(chainName string) (rules []Rule) {
for _, l := range strings.Split(string(f.Lines), "\n") {
if strings.Contains(l, fmt.Sprintf("-A %v", chainName)) {
newRule := Rule(map[string]string{})
for _, arg := range []string{Destination, Source, DPort, Protocol, Jump, ToDest, Recent} {
tok := getToken(l, arg)
if tok != "" {
newRule[arg] = tok
}
}
rules = append(rules, newRule)
}
}
return
}
var _ = iptables.Interface(&FakeIPTables{})

66
vendor/k8s.io/kubernetes/pkg/util/ipvs/BUILD generated vendored Normal file
View File

@ -0,0 +1,66 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"ipvs_test.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"ipvs_linux_test.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/ipvs",
library = ":go_default_library",
deps = select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
],
"//conditions:default": [],
}),
)
go_library(
name = "go_default_library",
srcs = [
"ipvs.go",
"ipvs_unsupported.go",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"ipvs_linux.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/pkg/util/ipvs",
deps = [
"//vendor/k8s.io/utils/exec:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux_amd64": [
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/util/ipvs/testing:all-srcs",
],
tags = ["automanaged"],
)

98
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"net"
"strconv"
)
// Interface is an injectable interface for running ipvs commands. Implementations must be goroutine-safe.
type Interface interface {
// Flush clears all virtual servers in system. return occurred error immediately.
Flush() error
// AddVirtualServer creates the specified virtual server.
AddVirtualServer(*VirtualServer) error
// UpdateVirtualServer updates an already existing virtual server. If the virtual server does not exist, return error.
UpdateVirtualServer(*VirtualServer) error
// DeleteVirtualServer deletes the specified virtual server. If the virtual server does not exist, return error.
DeleteVirtualServer(*VirtualServer) error
// Given a partial virtual server, GetVirtualServer will return the specified virtual server information in the system.
GetVirtualServer(*VirtualServer) (*VirtualServer, error)
// GetVirtualServers lists all virtual servers in the system.
GetVirtualServers() ([]*VirtualServer, error)
// AddRealServer creates the specified real server for the specified virtual server.
AddRealServer(*VirtualServer, *RealServer) error
// GetRealServers returns all real servers for the specified virtual server.
GetRealServers(*VirtualServer) ([]*RealServer, error)
// DeleteRealServer deletes the specified real server from the specified virtual server.
DeleteRealServer(*VirtualServer, *RealServer) error
}
// VirtualServer is an user-oriented definition of an IPVS virtual server in its entirety.
type VirtualServer struct {
Address net.IP
Protocol string
Port uint16
Scheduler string
Flags ServiceFlags
Timeout uint32
}
// ServiceFlags is used to specify session affinity, ip hash etc.
type ServiceFlags uint32
const (
// FlagPersistent specify IPVS service session affinity
FlagPersistent = 0x1
// FlagHashed specify IPVS service hash flag
FlagHashed = 0x2
)
// Equal check the equality of virtual server.
// We don't use struct == since it doesn't work because of slice.
func (svc *VirtualServer) Equal(other *VirtualServer) bool {
return svc.Address.Equal(other.Address) &&
svc.Protocol == other.Protocol &&
svc.Port == other.Port &&
svc.Scheduler == other.Scheduler &&
svc.Flags == other.Flags &&
svc.Timeout == other.Timeout
}
func (svc *VirtualServer) String() string {
return net.JoinHostPort(svc.Address.String(), strconv.Itoa(int(svc.Port))) + "/" + svc.Protocol
}
// RealServer is an user-oriented definition of an IPVS real server in its entirety.
type RealServer struct {
Address net.IP
Port uint16
Weight int
}
func (rs *RealServer) String() string {
return net.JoinHostPort(rs.Address.String(), strconv.Itoa(int(rs.Port)))
}
// Equal check the equality of real server.
// We don't use struct == since it doesn't work because of slice.
func (rs *RealServer) Equal(other *RealServer) bool {
return rs.Address.Equal(other.Address) &&
rs.Port == other.Port &&
rs.Weight == other.Weight
}

268
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_linux.go generated vendored Normal file
View File

@ -0,0 +1,268 @@
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"errors"
"fmt"
"net"
"strings"
"syscall"
"github.com/docker/libnetwork/ipvs"
"github.com/golang/glog"
utilexec "k8s.io/utils/exec"
)
// runner implements Interface.
type runner struct {
exec utilexec.Interface
ipvsHandle *ipvs.Handle
}
// New returns a new Interface which will call ipvs APIs.
func New(exec utilexec.Interface) Interface {
ihandle, err := ipvs.New("")
if err != nil {
glog.Errorf("IPVS interface can't be initialized, error: %v", err)
return nil
}
return &runner{
exec: exec,
ipvsHandle: ihandle,
}
}
// AddVirtualServer is part of Interface.
func (runner *runner) AddVirtualServer(vs *VirtualServer) error {
eSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.NewService(eSvc)
}
// UpdateVirtualServer is part of Interface.
func (runner *runner) UpdateVirtualServer(vs *VirtualServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.UpdateService(bSvc)
}
// DeleteVirtualServer is part of Interface.
func (runner *runner) DeleteVirtualServer(vs *VirtualServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
return runner.ipvsHandle.DelService(bSvc)
}
// GetVirtualServer is part of Interface.
func (runner *runner) GetVirtualServer(vs *VirtualServer) (*VirtualServer, error) {
bSvc, err := toBackendService(vs)
if err != nil {
return nil, err
}
ipvsService, err := runner.ipvsHandle.GetService(bSvc)
if err != nil {
return nil, err
}
virtualServer, err := toVirtualServer(ipvsService)
if err != nil {
return nil, err
}
return virtualServer, nil
}
// GetVirtualServers is part of Interface.
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
ipvsServices, err := runner.ipvsHandle.GetServices()
if err != nil {
return nil, err
}
vss := make([]*VirtualServer, 0)
for _, ipvsService := range ipvsServices {
vs, err := toVirtualServer(ipvsService)
if err != nil {
return nil, err
}
vss = append(vss, vs)
}
return vss, nil
}
// Flush is part of Interface. Currently we delete IPVS services one by one
func (runner *runner) Flush() error {
return runner.ipvsHandle.Flush()
}
// AddRealServer is part of Interface.
func (runner *runner) AddRealServer(vs *VirtualServer, rs *RealServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
bDst, err := toBackendDestination(rs)
if err != nil {
return err
}
return runner.ipvsHandle.NewDestination(bSvc, bDst)
}
// DeleteRealServer is part of Interface.
func (runner *runner) DeleteRealServer(vs *VirtualServer, rs *RealServer) error {
bSvc, err := toBackendService(vs)
if err != nil {
return err
}
bDst, err := toBackendDestination(rs)
if err != nil {
return err
}
return runner.ipvsHandle.DelDestination(bSvc, bDst)
}
// GetRealServers is part of Interface.
func (runner *runner) GetRealServers(vs *VirtualServer) ([]*RealServer, error) {
bSvc, err := toBackendService(vs)
if err != nil {
return nil, err
}
bDestinations, err := runner.ipvsHandle.GetDestinations(bSvc)
if err != nil {
return nil, err
}
realServers := make([]*RealServer, 0)
for _, dest := range bDestinations {
dst, err := toRealServer(dest)
// TODO: aggregate errors?
if err != nil {
return nil, err
}
realServers = append(realServers, dst)
}
return realServers, nil
}
// toVirtualServer converts an IPVS service representation to the equivalent virtual server structure.
func toVirtualServer(svc *ipvs.Service) (*VirtualServer, error) {
if svc == nil {
return nil, errors.New("ipvs svc should not be empty")
}
vs := &VirtualServer{
Address: svc.Address,
Port: svc.Port,
Scheduler: svc.SchedName,
Protocol: protocolNumbeToString(ProtoType(svc.Protocol)),
Timeout: svc.Timeout,
}
// Test Flags >= 0x2, valid Flags ranges [0x2, 0x3]
if svc.Flags&FlagHashed == 0 {
return nil, fmt.Errorf("Flags of successfully created IPVS service should be >= %d since every service is hashed into the service table", FlagHashed)
}
// Sub Flags to 0x2
// 011 -> 001, 010 -> 000
vs.Flags = ServiceFlags(svc.Flags &^ uint32(FlagHashed))
if vs.Address == nil {
if svc.AddressFamily == syscall.AF_INET {
vs.Address = net.IPv4zero
} else {
vs.Address = net.IPv6zero
}
}
return vs, nil
}
// toRealServer converts an IPVS destination representation to the equivalent real server structure.
func toRealServer(dst *ipvs.Destination) (*RealServer, error) {
if dst == nil {
return nil, errors.New("ipvs destination should not be empty")
}
return &RealServer{
Address: dst.Address,
Port: dst.Port,
Weight: dst.Weight,
}, nil
}
// toBackendService converts an IPVS real server representation to the equivalent "backend" service structure.
func toBackendService(vs *VirtualServer) (*ipvs.Service, error) {
if vs == nil {
return nil, errors.New("virtual server should not be empty")
}
bakSvc := &ipvs.Service{
Address: vs.Address,
Protocol: stringToProtocolNumber(vs.Protocol),
Port: vs.Port,
SchedName: vs.Scheduler,
Flags: uint32(vs.Flags),
Timeout: vs.Timeout,
}
if ip4 := vs.Address.To4(); ip4 != nil {
bakSvc.AddressFamily = syscall.AF_INET
bakSvc.Netmask = 0xffffffff
} else {
bakSvc.AddressFamily = syscall.AF_INET6
bakSvc.Netmask = 128
}
return bakSvc, nil
}
// toBackendDestination converts an IPVS real server representation to the equivalent "backend" destination structure.
func toBackendDestination(rs *RealServer) (*ipvs.Destination, error) {
if rs == nil {
return nil, errors.New("real server should not be empty")
}
return &ipvs.Destination{
Address: rs.Address,
Port: rs.Port,
Weight: rs.Weight,
}, nil
}
// stringToProtocolNumber returns the protocol value for the given name
func stringToProtocolNumber(protocol string) uint16 {
switch strings.ToLower(protocol) {
case "tcp":
return uint16(syscall.IPPROTO_TCP)
case "udp":
return uint16(syscall.IPPROTO_UDP)
}
return uint16(0)
}
// protocolNumbeToString returns the name for the given protocol value.
func protocolNumbeToString(proto ProtoType) string {
switch proto {
case syscall.IPPROTO_TCP:
return "TCP"
case syscall.IPPROTO_UDP:
return "UDP"
}
return ""
}
// ProtoType is IPVS service protocol type
type ProtoType uint16

View File

@ -0,0 +1,390 @@
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"fmt"
"net"
"reflect"
"syscall"
"testing"
"github.com/docker/libnetwork/ipvs"
)
func Test_toVirtualServer(t *testing.T) {
Tests := []struct {
ipvsService ipvs.Service
virtualServer VirtualServer
expectError bool
reason string
}{
{
ipvs.Service{
Flags: 0x0,
},
VirtualServer{},
true,
fmt.Sprintf("IPVS Service Flags should be >= %d, got 0x0", FlagHashed),
},
{
ipvs.Service{
Flags: 0x1,
},
VirtualServer{},
true,
fmt.Sprintf("IPVS Service Flags should be >= %d, got 0x1", FlagHashed),
},
{
ipvs.Service{
Protocol: syscall.IPPROTO_TCP,
Port: 80,
FWMark: 0,
SchedName: "",
Flags: uint32(FlagPersistent + FlagHashed),
Timeout: 0,
Netmask: 0xffffffff,
AddressFamily: syscall.AF_INET,
Address: nil,
PEName: "",
},
VirtualServer{
Address: net.ParseIP("0.0.0.0"),
Protocol: "TCP",
Port: 80,
Scheduler: "",
Flags: ServiceFlags(FlagPersistent),
Timeout: 0,
},
false,
"",
},
{
ipvs.Service{
Protocol: syscall.IPPROTO_UDP,
Port: 33434,
FWMark: 0,
SchedName: "wlc",
Flags: uint32(0 + FlagHashed),
Timeout: 100,
Netmask: 128,
AddressFamily: syscall.AF_INET6,
Address: net.ParseIP("2012::beef"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "UDP",
Port: 33434,
Scheduler: "wlc",
Flags: ServiceFlags(0),
Timeout: 100,
},
false,
"",
},
{
ipvs.Service{
Protocol: 0,
Port: 0,
FWMark: 0,
SchedName: "lc",
Flags: uint32(0 + FlagHashed),
Timeout: 0,
Netmask: 0xffffffff,
AddressFamily: syscall.AF_INET,
Address: net.ParseIP("1.2.3.4"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "",
Port: 0,
Scheduler: "lc",
Flags: ServiceFlags(0),
Timeout: 0,
},
false,
"",
},
{
ipvs.Service{
Protocol: 0,
Port: 0,
FWMark: 0,
SchedName: "wrr",
Flags: uint32(FlagPersistent + FlagHashed),
Timeout: 0,
Netmask: 128,
AddressFamily: syscall.AF_INET6,
Address: nil,
PEName: "",
},
VirtualServer{
Address: net.ParseIP("::0"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: ServiceFlags(FlagPersistent),
Timeout: 0,
},
false,
"",
},
}
for i := range Tests {
got, err := toVirtualServer(&Tests[i].ipvsService)
if Tests[i].expectError && err == nil {
t.Errorf("case: %d, expected error: %s, got nil", i, Tests[i].reason)
}
if !Tests[i].expectError && err != nil {
t.Errorf("case: %d, unexpected error: %v", i, err)
}
if got != nil && &Tests[i].virtualServer != nil {
if !reflect.DeepEqual(*got, Tests[i].virtualServer) {
t.Errorf("case: %d, got %#v, want %#v", i, *got, Tests[i].virtualServer)
}
}
}
}
func Test_toBackendService(t *testing.T) {
Tests := []struct {
ipvsService ipvs.Service
virtualServer VirtualServer
}{
{
ipvs.Service{
Protocol: syscall.IPPROTO_TCP,
Port: 80,
FWMark: 0,
SchedName: "",
Flags: 0,
Timeout: 0,
Netmask: 0xffffffff,
AddressFamily: syscall.AF_INET,
Address: net.ParseIP("0.0.0.0"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("0.0.0.0"),
Protocol: "TCP",
Port: 80,
Scheduler: "",
Flags: 0,
Timeout: 0,
},
},
{
ipvs.Service{
Protocol: syscall.IPPROTO_UDP,
Port: 33434,
FWMark: 0,
SchedName: "wlc",
Flags: 1234,
Timeout: 100,
Netmask: 128,
AddressFamily: syscall.AF_INET6,
Address: net.ParseIP("2012::beef"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "UDP",
Port: 33434,
Scheduler: "wlc",
Flags: 1234,
Timeout: 100,
},
},
{
ipvs.Service{
Protocol: 0,
Port: 0,
FWMark: 0,
SchedName: "lc",
Flags: 0,
Timeout: 0,
Netmask: 0xffffffff,
AddressFamily: syscall.AF_INET,
Address: net.ParseIP("1.2.3.4"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "",
Port: 0,
Scheduler: "lc",
Flags: 0,
Timeout: 0,
},
},
{
ipvs.Service{
Protocol: 0,
Port: 0,
FWMark: 0,
SchedName: "wrr",
Flags: 0,
Timeout: 0,
Netmask: 128,
AddressFamily: syscall.AF_INET6,
Address: net.ParseIP("::0"),
PEName: "",
},
VirtualServer{
Address: net.ParseIP("::0"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
},
}
for i := range Tests {
got, err := toBackendService(&Tests[i].virtualServer)
if err != nil {
t.Errorf("case: %d, unexpected error: %v", i, err)
}
if !reflect.DeepEqual(*got, Tests[i].ipvsService) {
t.Errorf("case: %d - got %#v, want %#v", i, *got, Tests[i].ipvsService)
}
}
}
func Test_toFrontendDestination(t *testing.T) {
Tests := []struct {
ipvsDestination ipvs.Destination
realServer RealServer
}{
{
ipvs.Destination{
Port: 54321,
ConnectionFlags: 0,
Weight: 1,
Address: net.ParseIP("1.2.3.4"),
},
RealServer{
Address: net.ParseIP("1.2.3.4"),
Port: 54321,
Weight: 1,
},
},
{
ipvs.Destination{
Port: 53,
ConnectionFlags: 0,
Weight: 1,
Address: net.ParseIP("2002::cafe"),
},
RealServer{
Address: net.ParseIP("2002::cafe"),
Port: 53,
Weight: 1,
},
},
}
for i := range Tests {
got, err := toRealServer(&Tests[i].ipvsDestination)
if err != nil {
t.Errorf("case %d unexpected error: %d", i, err)
}
if !reflect.DeepEqual(*got, Tests[i].realServer) {
t.Errorf("case %d Failed to translate Destination - got %#v, want %#v", i, *got, Tests[i].realServer)
}
}
}
func Test_toBackendDestination(t *testing.T) {
Tests := []struct {
realServer RealServer
ipvsDestination ipvs.Destination
}{
{
RealServer{
Address: net.ParseIP("1.2.3.4"),
Port: 54321,
Weight: 1,
},
ipvs.Destination{
Port: 54321,
ConnectionFlags: 0,
Weight: 1,
Address: net.ParseIP("1.2.3.4"),
},
},
{
RealServer{
Address: net.ParseIP("2002::cafe"),
Port: 53,
Weight: 1,
},
ipvs.Destination{
Port: 53,
ConnectionFlags: 0,
Weight: 1,
Address: net.ParseIP("2002::cafe"),
},
},
}
for i := range Tests {
got, err := toBackendDestination(&Tests[i].realServer)
if err != nil {
t.Errorf("case %d unexpected error: %d", i, err)
}
if !reflect.DeepEqual(*got, Tests[i].ipvsDestination) {
t.Errorf("case %d Failed to translate Destination - got %#v, want %#v", i, *got, Tests[i].ipvsDestination)
}
}
}
func Test_stringToProtocolNumber(t *testing.T) {
tests := []string{
"TCP", "UDP", "ICMP",
}
expecteds := []uint16{
uint16(syscall.IPPROTO_TCP), uint16(syscall.IPPROTO_UDP), uint16(0),
}
for i := range tests {
got := stringToProtocolNumber(tests[i])
if got != expecteds[i] {
t.Errorf("stringToProtocolNumber() failed - got %#v, want %#v",
got, expecteds[i])
}
}
}
func Test_protocolNumberToString(t *testing.T) {
tests := []ProtoType{
syscall.IPPROTO_TCP, syscall.IPPROTO_UDP, ProtoType(0),
}
expecteds := []string{
"TCP", "UDP", "",
}
for i := range tests {
got := protocolNumbeToString(tests[i])
if got != expecteds[i] {
t.Errorf("protocolNumbeToString() failed - got %#v, want %#v",
got, expecteds[i])
}
}
}

366
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_test.go generated vendored Normal file
View File

@ -0,0 +1,366 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"net"
"testing"
)
func TestVirtualServerEqual(t *testing.T) {
Tests := []struct {
svcA *VirtualServer
svcB *VirtualServer
equal bool
reason string
}{
{
svcA: &VirtualServer{
Address: net.ParseIP("10.20.30.40"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("10.20.30.41"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "IPv4 address not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2017::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "IPv6 address not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beeef"),
Protocol: "UDP",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Protocol not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 80,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "TCP",
Port: 8080,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Port not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "wlc",
Flags: 0,
Timeout: 0,
},
equal: false,
reason: "Scheduler not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 2,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 3,
Timeout: 0,
},
equal: false,
reason: "Flags not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 0,
},
svcB: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "",
Port: 0,
Scheduler: "wrr",
Flags: 0,
Timeout: 10800,
},
equal: false,
reason: "Timeout not equal",
},
{
svcA: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0x1,
Timeout: 10800,
},
svcB: &VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Protocol: "TCP",
Port: 80,
Scheduler: "rr",
Flags: 0x1,
Timeout: 10800,
},
equal: true,
reason: "All fields equal",
},
}
for i := range Tests {
equal := Tests[i].svcA.Equal(Tests[i].svcB)
if equal != Tests[i].equal {
t.Errorf("case: %d got %v, expected %v, reason: %s", i, equal, Tests[i].equal, Tests[i].reason)
}
}
}
func TestRealServerEqual(t *testing.T) {
Tests := []struct {
rsA *RealServer
rsB *RealServer
equal bool
reason string
}{
{
rsA: &RealServer{
Address: net.ParseIP("10.20.30.40"),
Port: 80,
Weight: 1,
},
rsB: &RealServer{
Address: net.ParseIP("10.20.30.41"),
Port: 80,
Weight: 1,
},
equal: false,
reason: "IPv4 address not equal",
},
{
rsA: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 80,
Weight: 1,
},
rsB: &RealServer{
Address: net.ParseIP("2017::beef"),
Port: 80,
Weight: 1,
},
equal: false,
reason: "IPv6 address not equal",
},
{
rsA: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 80,
Weight: 1,
},
rsB: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 8080,
Weight: 1,
},
equal: false,
reason: "Port not equal",
},
{
rsA: &RealServer{
Address: net.ParseIP("10.20.30.40"),
Port: 8080,
Weight: 1,
},
rsB: &RealServer{
Address: net.ParseIP("10.20.30.40"),
Port: 8080,
Weight: 10,
},
equal: false,
reason: "Weight not equal",
},
{
rsA: &RealServer{
Address: net.ParseIP("1.2.3.4"),
Port: 3080,
Weight: 10,
},
rsB: &RealServer{
Address: net.ParseIP("1.2.3.4"),
Port: 3080,
Weight: 10,
},
equal: true,
reason: "All fields equal",
},
{
rsA: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 3080,
Weight: 10,
},
rsB: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 3080,
Weight: 10,
},
equal: true,
reason: "All fields equal",
},
}
for i := range Tests {
equal := Tests[i].rsA.Equal(Tests[i].rsB)
if equal != Tests[i].equal {
t.Errorf("case: %d got %v, expected %v, reason: %s", i, equal, Tests[i].equal, Tests[i].reason)
}
}
}
func TestFrontendServiceString(t *testing.T) {
Tests := []struct {
svc *VirtualServer
expected string
}{
{
svc: &VirtualServer{
Address: net.ParseIP("10.20.30.40"),
Protocol: "TCP",
Port: 80,
},
expected: "10.20.30.40:80/TCP",
},
{
svc: &VirtualServer{
Address: net.ParseIP("2012::beef"),
Protocol: "UDP",
Port: 8080,
},
expected: "[2012::beef]:8080/UDP",
},
{
svc: &VirtualServer{
Address: net.ParseIP("10.20.30.41"),
Protocol: "ESP",
Port: 1234,
},
expected: "10.20.30.41:1234/ESP",
},
}
for i := range Tests {
if Tests[i].expected != Tests[i].svc.String() {
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
}
}
}
func TestFrontendDestinationString(t *testing.T) {
Tests := []struct {
svc *RealServer
expected string
}{
{
svc: &RealServer{
Address: net.ParseIP("10.20.30.40"),
Port: 80,
},
expected: "10.20.30.40:80",
},
{
svc: &RealServer{
Address: net.ParseIP("2012::beef"),
Port: 8080,
},
expected: "[2012::beef]:8080",
},
}
for i := range Tests {
if Tests[i].expected != Tests[i].svc.String() {
t.Errorf("case: %d got %v, expected %v", i, Tests[i].svc.String(), Tests[i].expected)
}
}
}

View File

@ -0,0 +1,71 @@
// +build !linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"fmt"
utilexec "k8s.io/utils/exec"
)
// New returns a dummy Interface for unsupported platform.
func New(utilexec.Interface) Interface {
return &runner{}
}
type runner struct {
}
func (runner *runner) Flush() error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) AddVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) UpdateVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) DeleteVirtualServer(*VirtualServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetVirtualServer(*VirtualServer) (*VirtualServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) AddRealServer(*VirtualServer, *RealServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) GetRealServers(*VirtualServer) ([]*RealServer, error) {
return nil, fmt.Errorf("IPVS not supported for this platform")
}
func (runner *runner) DeleteRealServer(*VirtualServer, *RealServer) error {
return fmt.Errorf("IPVS not supported for this platform")
}
var _ = Interface(&runner{})

35
vendor/k8s.io/kubernetes/pkg/util/ipvs/testing/BUILD generated vendored Normal file
View File

@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["fake.go"],
importpath = "k8s.io/kubernetes/pkg/util/ipvs/testing",
deps = ["//pkg/util/ipvs:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["fake_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/ipvs/testing",
library = ":go_default_library",
deps = ["//pkg/util/ipvs:go_default_library"],
)

177
vendor/k8s.io/kubernetes/pkg/util/ipvs/testing/fake.go generated vendored Normal file
View File

@ -0,0 +1,177 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"fmt"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
)
//FakeIPVS no-op implementation of ipvs Interface
type FakeIPVS struct {
Scheduler string
Services map[serviceKey]*utilipvs.VirtualServer
Destinations map[serviceKey][]*utilipvs.RealServer
}
type serviceKey struct {
IP string
Port uint16
Protocol string
}
func (s *serviceKey) String() string {
return fmt.Sprintf("%s:%d/%s", s.IP, s.Port, s.Protocol)
}
//NewFake creates a fake ipvs implementation - a cache store.
func NewFake() *FakeIPVS {
return &FakeIPVS{
Services: make(map[serviceKey]*utilipvs.VirtualServer),
Destinations: make(map[serviceKey][]*utilipvs.RealServer),
}
}
func toServiceKey(serv *utilipvs.VirtualServer) serviceKey {
return serviceKey{
IP: serv.Address.String(),
Port: serv.Port,
Protocol: serv.Protocol,
}
}
//AddVirtualServer is a fake implementation, it simply adds the VirtualServer into the cache store.
func (f *FakeIPVS) AddVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to add virtual server, error: virtual server can't be nil")
}
key := toServiceKey(serv)
f.Services[key] = serv
// make sure no destination present when creating new service
f.Destinations[key] = make([]*utilipvs.RealServer, 0)
return nil
}
//UpdateVirtualServer is a fake implementation, it updates the VirtualServer in the cache store.
func (f *FakeIPVS) UpdateVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to update service, service can't be nil")
}
key := toServiceKey(serv)
f.Services[key] = serv
return nil
}
//DeleteVirtualServer is a fake implementation, it simply deletes the VirtualServer from the cache store.
func (f *FakeIPVS) DeleteVirtualServer(serv *utilipvs.VirtualServer) error {
if serv == nil {
return fmt.Errorf("Failed to delete service: service can't be nil")
}
key := toServiceKey(serv)
delete(f.Services, key)
// clear specific destinations as well
f.Destinations[key] = nil
return nil
}
//GetVirtualServer is a fake implementation, it tries to find a specific VirtualServer from the cache store.
func (f *FakeIPVS) GetVirtualServer(serv *utilipvs.VirtualServer) (*utilipvs.VirtualServer, error) {
if serv == nil {
return nil, fmt.Errorf("Failed to get service: service can't be nil")
}
key := toServiceKey(serv)
svc, found := f.Services[key]
if found {
return svc, nil
}
return nil, fmt.Errorf("Not found serv: %v", key.String())
}
//GetVirtualServers is a fake implementation, it simply returns all VirtualServers in the cache store.
func (f *FakeIPVS) GetVirtualServers() ([]*utilipvs.VirtualServer, error) {
res := make([]*utilipvs.VirtualServer, 0)
for _, svc := range f.Services {
res = append(res, svc)
}
return res, nil
}
//Flush is a fake implementation, it simply clears the cache store.
func (f *FakeIPVS) Flush() error {
// directly drop old data
f.Services = nil
f.Destinations = nil
return nil
}
//AddRealServer is a fake implementation, it simply creates a RealServer for a VirtualServer in the cache store.
func (f *FakeIPVS) AddRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
if serv == nil || dest == nil {
return fmt.Errorf("Failed to add destination for service, neither service nor destination shouldn't be nil")
}
key := toServiceKey(serv)
if _, ok := f.Services[key]; !ok {
return fmt.Errorf("Failed to add destination for service %v, service not found", key.String())
}
dests := f.Destinations[key]
if dests == nil {
dests = make([]*utilipvs.RealServer, 0)
f.Destinations[key] = dests
}
f.Destinations[key] = append(f.Destinations[key], dest)
return nil
}
//GetRealServers is a fake implementation, it simply returns all RealServers in the cache store.
func (f *FakeIPVS) GetRealServers(serv *utilipvs.VirtualServer) ([]*utilipvs.RealServer, error) {
if serv == nil {
return nil, fmt.Errorf("Failed to get destination for nil service")
}
key := toServiceKey(serv)
if _, ok := f.Services[key]; !ok {
return nil, fmt.Errorf("Failed to get destinations for service %v, service not found", key.String())
}
return f.Destinations[key], nil
}
//DeleteRealServer is a fake implementation, it deletes the real server in the cache store.
func (f *FakeIPVS) DeleteRealServer(serv *utilipvs.VirtualServer, dest *utilipvs.RealServer) error {
if serv == nil || dest == nil {
return fmt.Errorf("Failed to delete destination, neither service nor destination can't be nil")
}
key := toServiceKey(serv)
if _, ok := f.Services[key]; !ok {
return fmt.Errorf("Failed to delete destination for service %v, service not found", key.String())
}
dests := f.Destinations[key]
var i int
for i = range dests {
if dests[i].Equal(dest) {
break
}
}
// Not Found
if i >= len(f.Destinations[key]) {
return fmt.Errorf("Failed to delete real server for service %v, real server not found", key.String())
}
// Delete one element
f.Destinations[key] = append(f.Destinations[key][:i], f.Destinations[key][i+1:]...)
return nil
}
var _ = utilipvs.Interface(&FakeIPVS{})

View File

@ -0,0 +1,166 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"net"
"testing"
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
)
func TestVirtualServer(t *testing.T) {
// Initialize
fake := NewFake()
// Add a virtual server
vs1 := &utilipvs.VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Port: uint16(80),
Protocol: string("TCP"),
Flags: utilipvs.FlagHashed,
}
err := fake.AddVirtualServer(vs1)
if err != nil {
t.Errorf("Fail to add virutal server, error: %v", err)
}
// Get a specific virtual server
got1, err := fake.GetVirtualServer(vs1)
if err != nil {
t.Errorf("Fail to get virutal server, error: %v", err)
}
if !vs1.Equal(got1) {
t.Errorf("Expect virtual server: %v, got: %v", vs1, got1)
}
// Update virtual server
vs12 := &utilipvs.VirtualServer{
Address: net.ParseIP("1.2.3.4"),
Port: uint16(80),
Protocol: string("TCP"),
Flags: utilipvs.FlagPersistent,
}
err = fake.UpdateVirtualServer(vs12)
if err != nil {
t.Errorf("Fail to update virutal server, error: %v", err)
}
// Check the updated virtual server
got12, err := fake.GetVirtualServer(vs1)
if !got12.Equal(vs12) {
t.Errorf("Expect virutal server: %v, got: %v", vs12, got12)
}
// Add another virtual server
vs2 := &utilipvs.VirtualServer{
Address: net.ParseIP("10::40"),
Port: uint16(8080),
Protocol: string("UDP"),
}
err = fake.AddVirtualServer(vs2)
if err != nil {
t.Errorf("Unexpected error when add virutal server, error: %v", err)
}
// List all virtual servers
list, err := fake.GetVirtualServers()
if err != nil {
t.Errorf("Fail to list virutal servers, error: %v", err)
}
if len(list) != 2 {
t.Errorf("Expect 2 virutal servers, got: %d", len(list))
}
// Delete a virtual server
err = fake.DeleteVirtualServer(vs1)
if err != nil {
t.Errorf("Fail to delete virutal server: %v, error: %v", vs1, err)
}
// Check the deleted virtual server no longer exists
got, _ := fake.GetVirtualServer(vs1)
if got != nil {
t.Errorf("Expect nil, got: %v", got)
}
// Flush all virtual servers
err = fake.Flush()
if err != nil {
t.Errorf("Fail to flush virutal servers, error: %v", err)
}
// List all virtual servers
list, err = fake.GetVirtualServers()
if err != nil {
t.Errorf("Fail to list virutal servers, error: %v", err)
}
if len(list) != 0 {
t.Errorf("Expect 0 virutal servers, got: %d", len(list))
}
}
func TestRealServer(t *testing.T) {
// Initialize
fake := NewFake()
// Add a virtual server
vs := &utilipvs.VirtualServer{
Address: net.ParseIP("10.20.30.40"),
Port: uint16(80),
Protocol: string("TCP"),
}
err := fake.AddVirtualServer(vs)
if err != nil {
t.Errorf("Fail to add virutal server, error: %v", err)
}
// Add a real server to the virtual server
rs1 := &utilipvs.RealServer{
Address: net.ParseIP("172.16.2.1"),
Port: uint16(8080),
Weight: 1,
}
err = fake.AddRealServer(vs, rs1)
if err != nil {
t.Errorf("Fail to add real server, error: %v", err)
}
// Add another real server to the virtual server
rs2 := &utilipvs.RealServer{
Address: net.ParseIP("172.16.3.2"),
Port: uint16(8080),
Weight: 2,
}
err = fake.AddRealServer(vs, rs2)
if err != nil {
t.Errorf("Fail to add real server, error: %v", err)
}
// List all real servers of the virtual server
list, err := fake.GetRealServers(vs)
if err != nil {
t.Errorf("Fail to get real servers of the virtual server, error: %v", err)
}
if len(list) != 2 {
t.Errorf("Expect 2 virutal servers, got: %d", len(list))
}
// Delete a real server of the virtual server
err = fake.DeleteRealServer(vs, rs2)
list, err = fake.GetRealServers(vs)
if err != nil {
t.Errorf("Fail to get real servers of the virtual server, error: %v", err)
}
if len(list) != 1 {
t.Errorf("Expect 1 real server, got: %d", len(list))
}
// Delete the virtual server
err = fake.DeleteVirtualServer(vs)
if err != nil {
t.Errorf("Fail to delete virtual server, error: %v", err)
}
_, err = fake.GetRealServers(vs)
if err == nil {
t.Errorf("Expect error, got nil")
}
}

34
vendor/k8s.io/kubernetes/pkg/util/keymutex/BUILD generated vendored Normal file
View File

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["keymutex.go"],
importpath = "k8s.io/kubernetes/pkg/util/keymutex",
deps = ["//vendor/github.com/golang/glog:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["keymutex_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/keymutex",
library = ":go_default_library",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

83
vendor/k8s.io/kubernetes/pkg/util/keymutex/keymutex.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
/*
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 keymutex
import (
"fmt"
"sync"
"github.com/golang/glog"
)
// KeyMutex is a thread-safe interface for acquiring locks on arbitrary strings.
type KeyMutex interface {
// Acquires a lock associated with the specified ID, creates the lock if one doesn't already exist.
LockKey(id string)
// Releases the lock associated with the specified ID.
// Returns an error if the specified ID doesn't exist.
UnlockKey(id string) error
}
// Returns a new instance of a key mutex.
func NewKeyMutex() KeyMutex {
return &keyMutex{
mutexMap: make(map[string]*sync.Mutex),
}
}
type keyMutex struct {
sync.RWMutex
mutexMap map[string]*sync.Mutex
}
// Acquires a lock associated with the specified ID (creates the lock if one doesn't already exist).
func (km *keyMutex) LockKey(id string) {
glog.V(5).Infof("LockKey(...) called for id %q\r\n", id)
mutex := km.getOrCreateLock(id)
mutex.Lock()
glog.V(5).Infof("LockKey(...) for id %q completed.\r\n", id)
}
// Releases the lock associated with the specified ID.
// Returns an error if the specified ID doesn't exist.
func (km *keyMutex) UnlockKey(id string) error {
glog.V(5).Infof("UnlockKey(...) called for id %q\r\n", id)
km.RLock()
defer km.RUnlock()
mutex, exists := km.mutexMap[id]
if !exists {
return fmt.Errorf("id %q not found", id)
}
glog.V(5).Infof("UnlockKey(...) for id. Mutex found, trying to unlock it. %q\r\n", id)
mutex.Unlock()
glog.V(5).Infof("UnlockKey(...) for id %q completed.\r\n", id)
return nil
}
// Returns lock associated with the specified ID, or creates the lock if one doesn't already exist.
func (km *keyMutex) getOrCreateLock(id string) *sync.Mutex {
km.Lock()
defer km.Unlock()
if _, exists := km.mutexMap[id]; !exists {
km.mutexMap[id] = &sync.Mutex{}
}
return km.mutexMap[id]
}

View File

@ -0,0 +1,93 @@
/*
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 keymutex
import (
"testing"
"time"
)
const (
callbackTimeout = 1 * time.Second
)
func Test_SingleLock_NoUnlock(t *testing.T) {
// Arrange
km := NewKeyMutex()
key := "fakeid"
callbackCh := make(chan interface{})
// Act
go lockAndCallback(km, key, callbackCh)
// Assert
verifyCallbackHappens(t, callbackCh)
}
func Test_SingleLock_SingleUnlock(t *testing.T) {
// Arrange
km := NewKeyMutex()
key := "fakeid"
callbackCh := make(chan interface{})
// Act & Assert
go lockAndCallback(km, key, callbackCh)
verifyCallbackHappens(t, callbackCh)
km.UnlockKey(key)
}
func Test_DoubleLock_DoubleUnlock(t *testing.T) {
// Arrange
km := NewKeyMutex()
key := "fakeid"
callbackCh1stLock := make(chan interface{})
callbackCh2ndLock := make(chan interface{})
// Act & Assert
go lockAndCallback(km, key, callbackCh1stLock)
verifyCallbackHappens(t, callbackCh1stLock)
go lockAndCallback(km, key, callbackCh2ndLock)
verifyCallbackDoesntHappens(t, callbackCh2ndLock)
km.UnlockKey(key)
verifyCallbackHappens(t, callbackCh2ndLock)
km.UnlockKey(key)
}
func lockAndCallback(km KeyMutex, id string, callbackCh chan<- interface{}) {
km.LockKey(id)
callbackCh <- true
}
func verifyCallbackHappens(t *testing.T, callbackCh <-chan interface{}) bool {
select {
case <-callbackCh:
return true
case <-time.After(callbackTimeout):
t.Fatalf("Timed out waiting for callback.")
return false
}
}
func verifyCallbackDoesntHappens(t *testing.T, callbackCh <-chan interface{}) bool {
select {
case <-callbackCh:
t.Fatalf("Unexpected callback.")
return false
case <-time.After(callbackTimeout):
return true
}
}

0
vendor/k8s.io/kubernetes/pkg/util/labels/.readonly generated vendored Normal file
View File

38
vendor/k8s.io/kubernetes/pkg/util/labels/BUILD generated vendored Normal file
View File

@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"labels.go",
],
importpath = "k8s.io/kubernetes/pkg/util/labels",
deps = ["//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["labels_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/labels",
library = ":go_default_library",
deps = ["//vendor/k8s.io/apimachinery/pkg/apis/meta/v1: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/labels/doc.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
/*
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 labels provides utilities to work with Kubernetes labels.
package labels // import "k8s.io/kubernetes/pkg/util/labels"

124
vendor/k8s.io/kubernetes/pkg/util/labels/labels.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
/*
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 labels
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Clones the given map and returns a new map with the given key and value added.
// Returns the given map, if labelKey is empty.
func CloneAndAddLabel(labels map[string]string, labelKey, labelValue string) map[string]string {
if labelKey == "" {
// Don't need to add a label.
return labels
}
// Clone.
newLabels := map[string]string{}
for key, value := range labels {
newLabels[key] = value
}
newLabels[labelKey] = labelValue
return newLabels
}
// CloneAndRemoveLabel clones the given map and returns a new map with the given key removed.
// Returns the given map, if labelKey is empty.
func CloneAndRemoveLabel(labels map[string]string, labelKey string) map[string]string {
if labelKey == "" {
// Don't need to add a label.
return labels
}
// Clone.
newLabels := map[string]string{}
for key, value := range labels {
newLabels[key] = value
}
delete(newLabels, labelKey)
return newLabels
}
// AddLabel returns a map with the given key and value added to the given map.
func AddLabel(labels map[string]string, labelKey, labelValue string) map[string]string {
if labelKey == "" {
// Don't need to add a label.
return labels
}
if labels == nil {
labels = make(map[string]string)
}
labels[labelKey] = labelValue
return labels
}
// Clones the given selector and returns a new selector with the given key and value added.
// Returns the given selector, if labelKey is empty.
func CloneSelectorAndAddLabel(selector *metav1.LabelSelector, labelKey, labelValue string) *metav1.LabelSelector {
if labelKey == "" {
// Don't need to add a label.
return selector
}
// Clone.
newSelector := new(metav1.LabelSelector)
// TODO(madhusudancs): Check if you can use deepCopy_extensions_LabelSelector here.
newSelector.MatchLabels = make(map[string]string)
if selector.MatchLabels != nil {
for key, val := range selector.MatchLabels {
newSelector.MatchLabels[key] = val
}
}
newSelector.MatchLabels[labelKey] = labelValue
if selector.MatchExpressions != nil {
newMExps := make([]metav1.LabelSelectorRequirement, len(selector.MatchExpressions))
for i, me := range selector.MatchExpressions {
newMExps[i].Key = me.Key
newMExps[i].Operator = me.Operator
if me.Values != nil {
newMExps[i].Values = make([]string, len(me.Values))
copy(newMExps[i].Values, me.Values)
} else {
newMExps[i].Values = nil
}
}
newSelector.MatchExpressions = newMExps
} else {
newSelector.MatchExpressions = nil
}
return newSelector
}
// AddLabelToSelector returns a selector with the given key and value added to the given selector's MatchLabels.
func AddLabelToSelector(selector *metav1.LabelSelector, labelKey, labelValue string) *metav1.LabelSelector {
if labelKey == "" {
// Don't need to add a label.
return selector
}
if selector.MatchLabels == nil {
selector.MatchLabels = make(map[string]string)
}
selector.MatchLabels[labelKey] = labelValue
return selector
}
// SelectorHasLabel checks if the given selector contains the given label key in its MatchLabels
func SelectorHasLabel(selector *metav1.LabelSelector, labelKey string) bool {
return len(selector.MatchLabels[labelKey]) > 0
}

211
vendor/k8s.io/kubernetes/pkg/util/labels/labels_test.go generated vendored Normal file
View File

@ -0,0 +1,211 @@
/*
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 labels
import (
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestCloneAndAddLabel(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "42",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "42",
},
},
}
for _, tc := range cases {
got := CloneAndAddLabel(tc.labels, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("[Add] got %v, want %v", got, tc.want)
}
// now test the inverse.
got_rm := CloneAndRemoveLabel(got, tc.labelKey)
if !reflect.DeepEqual(got_rm, tc.labels) {
t.Errorf("[RM] got %v, want %v", got_rm, tc.labels)
}
}
}
func TestAddLabel(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "food",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "food",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "food",
want: map[string]string{
"foo4": "food",
},
},
}
for _, tc := range cases {
got := AddLabel(tc.labels, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}
func TestCloneSelectorAndAddLabel(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := metav1.LabelSelector{MatchLabels: tc.labels}
ls_out := metav1.LabelSelector{MatchLabels: tc.want}
got := CloneSelectorAndAddLabel(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}
func TestAddLabelToSelector(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := metav1.LabelSelector{MatchLabels: tc.labels}
ls_out := metav1.LabelSelector{MatchLabels: tc.want}
got := AddLabelToSelector(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}

36
vendor/k8s.io/kubernetes/pkg/util/limitwriter/BUILD generated vendored Normal file
View File

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"limitwriter.go",
],
importpath = "k8s.io/kubernetes/pkg/util/limitwriter",
)
go_test(
name = "go_default_test",
srcs = ["limitwriter_test.go"],
importpath = "k8s.io/kubernetes/pkg/util/limitwriter",
library = ":go_default_library",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

19
vendor/k8s.io/kubernetes/pkg/util/limitwriter/doc.go generated vendored Normal file
View 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 limitwriter provides a writer that only allows a certain number of bytes to be
// written.
package limitwriter // import "k8s.io/kubernetes/pkg/util/limitwriter"

View File

@ -0,0 +1,53 @@
/*
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 limitwriter
import (
"errors"
"io"
)
// New creates a writer that is limited to writing at most n bytes to w. This writer is not
// thread safe.
func New(w io.Writer, n int64) io.Writer {
return &limitWriter{
w: w,
n: n,
}
}
// ErrMaximumWrite is returned when all bytes have been written.
var ErrMaximumWrite = errors.New("maximum write")
type limitWriter struct {
w io.Writer
n int64
}
func (w *limitWriter) Write(p []byte) (n int, err error) {
if int64(len(p)) > w.n {
p = p[:w.n]
}
if w.n > 0 {
n, err = w.w.Write(p)
w.n -= int64(n)
}
if w.n == 0 {
err = ErrMaximumWrite
}
return
}

Some files were not shown because too many files have changed in this diff Show More