mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
73
vendor/k8s.io/kubernetes/pkg/util/BUILD
generated
vendored
73
vendor/k8s.io/kubernetes/pkg/util/BUILD
generated
vendored
@ -1,73 +0,0 @@
|
||||
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/conntrack:all-srcs",
|
||||
"//pkg/util/coverage: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/flag: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/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/pod: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/tolerations: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"]),
|
||||
)
|
42
vendor/k8s.io/kubernetes/pkg/util/async/BUILD
generated
vendored
42
vendor/k8s.io/kubernetes/pkg/util/async/BUILD
generated
vendored
@ -1,42 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"bounded_frequency_runner.go",
|
||||
"runner.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/async",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"bounded_frequency_runner_test.go",
|
||||
"runner_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
239
vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go
generated
vendored
239
vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go
generated
vendored
@ -1,239 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// 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 function 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{}) {
|
||||
klog.V(3).Infof("%s Loop running", bfr.name)
|
||||
bfr.timer.Reset(bfr.maxInterval)
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
bfr.stop()
|
||||
klog.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)
|
||||
klog.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
|
||||
klog.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)
|
||||
klog.V(3).Infof("%s: throttled, scheduling run in %v", bfr.name, nextPossible)
|
||||
}
|
||||
}
|
333
vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner_test.go
generated
vendored
333
vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner_test.go
generated
vendored
@ -1,333 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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{
|
||||
now: time.Date(2000, 0, 0, 0, 0, 0, 0, time.UTC),
|
||||
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
58
vendor/k8s.io/kubernetes/pkg/util/async/runner.go
generated
vendored
@ -1,58 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
55
vendor/k8s.io/kubernetes/pkg/util/async/runner_test.go
generated
vendored
@ -1,55 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
63
vendor/k8s.io/kubernetes/pkg/util/bandwidth/BUILD
generated
vendored
63
vendor/k8s.io/kubernetes/pkg/util/bandwidth/BUILD
generated
vendored
@ -1,63 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"fake_shaper.go",
|
||||
"interfaces.go",
|
||||
"linux.go",
|
||||
"unsupported.go",
|
||||
"utils.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/bandwidth",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"linux_test.go",
|
||||
"utils_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//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
18
vendor/k8s.io/kubernetes/pkg/util/bandwidth/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package bandwidth provides utilities for bandwidth shaping
|
||||
package bandwidth // import "k8s.io/kubernetes/pkg/util/bandwidth"
|
49
vendor/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/util/bandwidth/fake_shaper.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
|
||||
}
|
38
vendor/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go
generated
vendored
38
vendor/k8s.io/kubernetes/pkg/util/bandwidth/interfaces.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
339
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux.go
generated
vendored
339
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux.go
generated
vendored
@ -1,339 +0,0 @@
|
||||
// +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"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
klog.V(6).Infof("Running: %s %s", cmdStr, strings.Join(args, " "))
|
||||
cmd := t.e.Command(cmdStr, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
klog.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) (classAndHandleList [][]string, found bool, err error) {
|
||||
data, err := t.e.Command("tc", "filter", "show", "dev", t.iface).CombinedOutput()
|
||||
if err != nil {
|
||||
return classAndHandleList, false, err
|
||||
}
|
||||
|
||||
hex, err := hexCIDR(cidr)
|
||||
if err != nil {
|
||||
return classAndHandleList, 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 classAndHandleList, false, fmt.Errorf("unexpected output from tc: %s %d (%v)", filter, len(parts), parts)
|
||||
} else {
|
||||
resultTmp := []string{parts[18], parts[9]}
|
||||
classAndHandleList = append(classAndHandleList, resultTmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(classAndHandleList) > 0 {
|
||||
return classAndHandleList, true, nil
|
||||
}
|
||||
return classAndHandleList, 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 {
|
||||
klog.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 {
|
||||
classAndHandle, 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)
|
||||
}
|
||||
for i := 0; i < len(classAndHandle); i++ {
|
||||
if err := t.execAndLog("tc", "filter", "del",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"proto", "ip",
|
||||
"prio", "1",
|
||||
"handle", classAndHandle[i][1], "u32"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.execAndLog("tc", "class", "del",
|
||||
"dev", t.iface,
|
||||
"parent", "1:",
|
||||
"classid", classAndHandle[i][0]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
704
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go
generated
vendored
704
vendor/k8s.io/kubernetes/pkg/util/bandwidth/linux_test.go
generated
vendored
@ -1,704 +0,0 @@
|
||||
// +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}
|
||||
classAndHandle, 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", classAndHandle)
|
||||
}
|
||||
} else {
|
||||
if classAndHandle[0][0] != test.expectedClass {
|
||||
t.Errorf("expected class: %s, found %s", test.expectedClass, classAndHandle)
|
||||
}
|
||||
if classAndHandle[0][1] != test.expectedHandle {
|
||||
t.Errorf("expected handle: %s, found %s", test.expectedHandle, classAndHandle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go
generated
vendored
52
vendor/k8s.io/kubernetes/pkg/util/bandwidth/unsupported.go
generated
vendored
@ -1,52 +0,0 @@
|
||||
// +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
65
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
|
||||
}
|
90
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils_test.go
generated
vendored
90
vendor/k8s.io/kubernetes/pkg/util/bandwidth/utils_test.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/util/config/BUILD
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/util/config/BUILD
generated
vendored
@ -1,36 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"doc.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/config",
|
||||
deps = ["//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["config_test.go"],
|
||||
embed = [":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
140
vendor/k8s.io/kubernetes/pkg/util/config/config.go
generated
vendored
@ -1,140 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
120
vendor/k8s.io/kubernetes/pkg/util/config/config_test.go
generated
vendored
@ -1,120 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
20
vendor/k8s.io/kubernetes/pkg/util/config/doc.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package 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"
|
32
vendor/k8s.io/kubernetes/pkg/util/configz/BUILD
generated
vendored
32
vendor/k8s.io/kubernetes/pkg/util/configz/BUILD
generated
vendored
@ -1,32 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["configz.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/configz",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["configz_test.go"],
|
||||
embed = [":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
90
vendor/k8s.io/kubernetes/pkg/util/configz/configz.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package configz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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 http.ResponseWriter) 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)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err = w.Write(b)
|
||||
return err
|
||||
}
|
80
vendor/k8s.io/kubernetes/pkg/util/configz/configz_test.go
generated
vendored
80
vendor/k8s.io/kubernetes/pkg/util/configz/configz_test.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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: %s", body)
|
||||
}
|
||||
|
||||
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: %s", body)
|
||||
}
|
||||
|
||||
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: %s", body)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf("unexpected Content-Type: %s", resp.Header.Get("Content-Type"))
|
||||
}
|
||||
}
|
39
vendor/k8s.io/kubernetes/pkg/util/conntrack/BUILD
generated
vendored
39
vendor/k8s.io/kubernetes/pkg/util/conntrack/BUILD
generated
vendored
@ -1,39 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["conntrack.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/conntrack",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["conntrack_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1: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"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
109
vendor/k8s.io/kubernetes/pkg/util/conntrack/conntrack.go
generated
vendored
109
vendor/k8s.io/kubernetes/pkg/util/conntrack/conntrack.go
generated
vendored
@ -1,109 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// Utilities for dealing with conntrack
|
||||
|
||||
// NoConnectionToDelete is the error string returned by conntrack when no matching connections are found
|
||||
const NoConnectionToDelete = "0 flow entries have been deleted"
|
||||
|
||||
func protoStr(proto v1.Protocol) string {
|
||||
return strings.ToLower(string(proto))
|
||||
}
|
||||
|
||||
func parametersWithFamily(isIPv6 bool, parameters ...string) []string {
|
||||
if isIPv6 {
|
||||
parameters = append(parameters, "-f", "ipv6")
|
||||
}
|
||||
return parameters
|
||||
}
|
||||
|
||||
// ClearEntriesForIP uses the conntrack tool to delete the conntrack entries
|
||||
// for the UDP connections specified by the given service IP
|
||||
func ClearEntriesForIP(execer exec.Interface, ip string, protocol v1.Protocol) error {
|
||||
parameters := parametersWithFamily(utilnet.IsIPv6String(ip), "-D", "--orig-dst", ip, "-p", protoStr(protocol))
|
||||
err := Exec(execer, parameters...)
|
||||
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
|
||||
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
|
||||
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it
|
||||
// is expensive to baby-sit all udp connections to kubernetes services.
|
||||
return fmt.Errorf("error deleting connection tracking state for UDP service IP: %s, error: %v", ip, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exec executes the conntrack tool using the given parameters
|
||||
func Exec(execer exec.Interface, parameters ...string) error {
|
||||
conntrackPath, err := execer.LookPath("conntrack")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking for path of conntrack: %v", err)
|
||||
}
|
||||
output, err := execer.Command(conntrackPath, parameters...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("conntrack command returned: %q, error message: %s", string(output), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists returns true if conntrack binary is installed.
|
||||
func Exists(execer exec.Interface) bool {
|
||||
_, err := execer.LookPath("conntrack")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ClearEntriesForPort uses the conntrack tool to delete the conntrack entries
|
||||
// for connections specified by the port.
|
||||
// When a packet arrives, it will not go through NAT table again, because it is not "the first" packet.
|
||||
// The solution is clearing the conntrack. Known issues:
|
||||
// https://github.com/docker/docker/issues/8795
|
||||
// https://github.com/kubernetes/kubernetes/issues/31983
|
||||
func ClearEntriesForPort(execer exec.Interface, port int, isIPv6 bool, protocol v1.Protocol) error {
|
||||
if port <= 0 {
|
||||
return fmt.Errorf("Wrong port number. The port number must be greater than zero")
|
||||
}
|
||||
parameters := parametersWithFamily(isIPv6, "-D", "-p", protoStr(protocol), "--dport", strconv.Itoa(port))
|
||||
err := Exec(execer, parameters...)
|
||||
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
|
||||
return fmt.Errorf("error deleting conntrack entries for UDP port: %d, error: %v", port, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearEntriesForNAT uses the conntrack tool to delete the conntrack entries
|
||||
// for connections specified by the {origin, dest} IP pair.
|
||||
func ClearEntriesForNAT(execer exec.Interface, origin, dest string, protocol v1.Protocol) error {
|
||||
parameters := parametersWithFamily(utilnet.IsIPv6String(origin), "-D", "--orig-dst", origin, "--dst-nat", dest,
|
||||
"-p", protoStr(protocol))
|
||||
err := Exec(execer, parameters...)
|
||||
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
|
||||
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
|
||||
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it
|
||||
// is expensive to baby sit all udp connections to kubernetes services.
|
||||
return fmt.Errorf("error deleting conntrack entries for UDP peer {%s, %s}, error: %v", origin, dest, err)
|
||||
}
|
||||
return nil
|
||||
}
|
236
vendor/k8s.io/kubernetes/pkg/util/conntrack/conntrack_test.go
generated
vendored
236
vendor/k8s.io/kubernetes/pkg/util/conntrack/conntrack_test.go
generated
vendored
@ -1,236 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package conntrack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func familyParamStr(isIPv6 bool) string {
|
||||
if isIPv6 {
|
||||
return " -f ipv6"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestExecConntrackTool(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) {
|
||||
return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||
},
|
||||
},
|
||||
}
|
||||
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...) },
|
||||
},
|
||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||
}
|
||||
|
||||
testCases := [][]string{
|
||||
{"-L", "-p", "udp"},
|
||||
{"-D", "-p", "udp", "-d", "10.0.240.1"},
|
||||
{"-D", "-p", "udp", "--orig-dst", "10.240.0.2", "--dst-nat", "10.0.10.2"},
|
||||
}
|
||||
|
||||
expectErr := []bool{false, false, true}
|
||||
|
||||
for i := range testCases {
|
||||
err := Exec(&fexec, testCases[i]...)
|
||||
|
||||
if expectErr[i] {
|
||||
if err == nil {
|
||||
t.Errorf("expected err, got %v", err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
execCmd := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
||||
expectCmd := fmt.Sprintf("%s %s", "conntrack", strings.Join(testCases[i], " "))
|
||||
|
||||
if execCmd != expectCmd {
|
||||
t.Errorf("expect execute command: %s, but got: %s", expectCmd, execCmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearUDPConntrackForIP(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) {
|
||||
return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||
},
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), 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...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
ip string
|
||||
}{
|
||||
{"IPv4 success", "10.240.0.3"},
|
||||
{"IPv4 success", "10.240.0.5"},
|
||||
{"IPv4 simulated error", "10.240.0.4"},
|
||||
{"IPv6 success", "2001:db8::10"},
|
||||
}
|
||||
|
||||
svcCount := 0
|
||||
for _, tc := range testCases {
|
||||
if err := ClearEntriesForIP(&fexec, tc.ip, v1.ProtocolUDP); err != nil {
|
||||
t.Errorf("%s test case:, Unexpected error: %v", tc.name, err)
|
||||
}
|
||||
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.ip) + familyParamStr(utilnet.IsIPv6String(tc.ip))
|
||||
execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ")
|
||||
if expectCommand != execCommand {
|
||||
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
||||
}
|
||||
svcCount++
|
||||
}
|
||||
if svcCount != fexec.CommandCalls {
|
||||
t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearUDPConntrackForPort(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) {
|
||||
return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||
},
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), 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...) },
|
||||
},
|
||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
port int
|
||||
isIPv6 bool
|
||||
}{
|
||||
{"IPv4, no error", 8080, false},
|
||||
{"IPv4, simulated error", 9090, false},
|
||||
{"IPv6, no error", 6666, true},
|
||||
}
|
||||
svcCount := 0
|
||||
for _, tc := range testCases {
|
||||
err := ClearEntriesForPort(&fexec, tc.port, tc.isIPv6, v1.ProtocolUDP)
|
||||
if err != nil {
|
||||
t.Errorf("%s test case: Unexpected error: %v", tc.name, err)
|
||||
}
|
||||
expectCommand := fmt.Sprintf("conntrack -D -p udp --dport %d", tc.port) + familyParamStr(tc.isIPv6)
|
||||
execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ")
|
||||
if expectCommand != execCommand {
|
||||
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
||||
}
|
||||
svcCount++
|
||||
}
|
||||
if svcCount != fexec.CommandCalls {
|
||||
t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteUDPConnections(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), nil },
|
||||
func() ([]byte, error) {
|
||||
return []byte(""), fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||
},
|
||||
func() ([]byte, error) { return []byte("1 flow entries have been deleted"), 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...) },
|
||||
},
|
||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
origin string
|
||||
dest string
|
||||
}{
|
||||
{
|
||||
name: "IPv4 success",
|
||||
origin: "1.2.3.4",
|
||||
dest: "10.20.30.40",
|
||||
},
|
||||
{
|
||||
name: "IPv4 simulated failure",
|
||||
origin: "2.3.4.5",
|
||||
dest: "20.30.40.50",
|
||||
},
|
||||
{
|
||||
name: "IPv6 success",
|
||||
origin: "fd00::600d:f00d",
|
||||
dest: "2001:db8::5",
|
||||
},
|
||||
}
|
||||
svcCount := 0
|
||||
for i, tc := range testCases {
|
||||
err := ClearEntriesForNAT(&fexec, tc.origin, tc.dest, v1.ProtocolUDP)
|
||||
if err != nil {
|
||||
t.Errorf("%s test case: unexpected error: %v", tc.name, err)
|
||||
}
|
||||
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(utilnet.IsIPv6String(tc.origin))
|
||||
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
||||
if expectCommand != execCommand {
|
||||
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
||||
}
|
||||
svcCount++
|
||||
}
|
||||
if svcCount != fexec.CommandCalls {
|
||||
t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls)
|
||||
}
|
||||
}
|
25
vendor/k8s.io/kubernetes/pkg/util/coverage/BUILD
generated
vendored
25
vendor/k8s.io/kubernetes/pkg/util/coverage/BUILD
generated
vendored
@ -1,25 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"coverage_disabled.go",
|
||||
"fake_test_deps.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/coverage",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
8
vendor/k8s.io/kubernetes/pkg/util/coverage/OWNERS
generated
vendored
8
vendor/k8s.io/kubernetes/pkg/util/coverage/OWNERS
generated
vendored
@ -1,8 +0,0 @@
|
||||
approvers:
|
||||
- bentheelder
|
||||
- spiffxp
|
||||
reviewers:
|
||||
- bentheelder
|
||||
- spiffxp
|
||||
labels:
|
||||
- sig/testing
|
91
vendor/k8s.io/kubernetes/pkg/util/coverage/coverage.go
generated
vendored
91
vendor/k8s.io/kubernetes/pkg/util/coverage/coverage.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// +build coverage
|
||||
|
||||
/*
|
||||
Copyright 2018 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 coverage provides tools for coverage-instrumented binaries to collect and
|
||||
// flush coverage information.
|
||||
package coverage
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/klog"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var coverageFile string
|
||||
|
||||
// tempCoveragePath returns a temporary file to write coverage information to.
|
||||
// The file is in the same directory as the destination, ensuring os.Rename will work.
|
||||
func tempCoveragePath() string {
|
||||
return coverageFile + ".tmp"
|
||||
}
|
||||
|
||||
// InitCoverage is called from the dummy unit test to prepare Go's coverage framework.
|
||||
// Clients should never need to call it.
|
||||
func InitCoverage(name string) {
|
||||
// We read the coverage destination in from the KUBE_COVERAGE_FILE env var,
|
||||
// or if it's empty we just use a default in /tmp
|
||||
coverageFile = os.Getenv("KUBE_COVERAGE_FILE")
|
||||
if coverageFile == "" {
|
||||
coverageFile = "/tmp/k8s-" + name + ".cov"
|
||||
}
|
||||
fmt.Println("Dumping coverage information to " + coverageFile)
|
||||
|
||||
flushInterval := 5 * time.Second
|
||||
requestedInterval := os.Getenv("KUBE_COVERAGE_FLUSH_INTERVAL")
|
||||
if requestedInterval != "" {
|
||||
if duration, err := time.ParseDuration(requestedInterval); err == nil {
|
||||
flushInterval = duration
|
||||
} else {
|
||||
panic("Invalid KUBE_COVERAGE_FLUSH_INTERVAL value; try something like '30s'.")
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the unit test framework with the required arguments to activate test coverage.
|
||||
flag.CommandLine.Parse([]string{"-test.coverprofile", tempCoveragePath()})
|
||||
|
||||
// Begin periodic logging
|
||||
go wait.Forever(FlushCoverage, flushInterval)
|
||||
}
|
||||
|
||||
// FlushCoverage flushes collected coverage information to disk.
|
||||
// The destination file is configured at startup and cannot be changed.
|
||||
// Calling this function also sends a line like "coverage: 5% of statements" to stdout.
|
||||
func FlushCoverage() {
|
||||
// We're not actually going to run any tests, but we need Go to think we did so it writes
|
||||
// coverage information to disk. To achieve this, we create a bunch of empty test suites and
|
||||
// have it "run" them.
|
||||
tests := []testing.InternalTest{}
|
||||
benchmarks := []testing.InternalBenchmark{}
|
||||
examples := []testing.InternalExample{}
|
||||
|
||||
var deps fakeTestDeps
|
||||
|
||||
dummyRun := testing.MainStart(deps, tests, benchmarks, examples)
|
||||
dummyRun.Run()
|
||||
|
||||
// Once it writes to the temporary path, we move it to the intended path.
|
||||
// This gets us atomic updates from the perspective of another process trying to access
|
||||
// the file.
|
||||
if err := os.Rename(tempCoveragePath(), coverageFile); err != nil {
|
||||
klog.Errorf("Couldn't move coverage file from %s to %s", coverageFile, tempCoveragePath())
|
||||
}
|
||||
}
|
29
vendor/k8s.io/kubernetes/pkg/util/coverage/coverage_disabled.go
generated
vendored
29
vendor/k8s.io/kubernetes/pkg/util/coverage/coverage_disabled.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
// +build !coverage
|
||||
|
||||
/*
|
||||
Copyright 2018 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 coverage
|
||||
|
||||
// InitCoverage is illegal when not running with coverage.
|
||||
func InitCoverage(name string) {
|
||||
panic("Called InitCoverage when not built with coverage instrumentation.")
|
||||
}
|
||||
|
||||
// FlushCoverage is a no-op when not running with coverage.
|
||||
func FlushCoverage() {
|
||||
|
||||
}
|
54
vendor/k8s.io/kubernetes/pkg/util/coverage/fake_test_deps.go
generated
vendored
54
vendor/k8s.io/kubernetes/pkg/util/coverage/fake_test_deps.go
generated
vendored
@ -1,54 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 coverage
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// This is an implementation of testing.testDeps. It doesn't need to do anything, because
|
||||
// no tests are actually run. It does need a concrete implementation of at least ImportPath,
|
||||
// which is called unconditionally when running tests.
|
||||
type fakeTestDeps struct{}
|
||||
|
||||
func (fakeTestDeps) ImportPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fakeTestDeps) MatchString(pat, str string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (fakeTestDeps) StartCPUProfile(io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fakeTestDeps) StopCPUProfile() {}
|
||||
|
||||
func (fakeTestDeps) StartTestLog(io.Writer) {}
|
||||
|
||||
func (fakeTestDeps) StopTestLog() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fakeTestDeps) WriteHeapProfile(io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fakeTestDeps) WriteProfileTo(string, io.Writer, int) error {
|
||||
return nil
|
||||
}
|
38
vendor/k8s.io/kubernetes/pkg/util/dbus/BUILD
generated
vendored
38
vendor/k8s.io/kubernetes/pkg/util/dbus/BUILD
generated
vendored
@ -1,38 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"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"],
|
||||
embed = [":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
133
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go
generated
vendored
@ -1,133 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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...)
|
||||
}
|
243
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go
generated
vendored
243
vendor/k8s.io/kubernetes/pkg/util/dbus/dbus_test.go
generated
vendored
@ -1,243 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
godbus "github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
DBusNameFlagDoNotQueue uint32 = 1 << (iota + 1)
|
||||
)
|
||||
|
||||
const (
|
||||
DBusRequestNameReplyPrimaryOwner uint32 = iota + 1
|
||||
DBusRequestNameReplyAlreadyOwner
|
||||
)
|
||||
|
||||
const (
|
||||
DBusReleaseNameReplyReleased uint32 = iota + 1
|
||||
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}
|
||||
}
|
||||
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
18
vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package dbus provides an injectable interface and implementations for D-Bus communication
|
||||
package dbus // import "k8s.io/kubernetes/pkg/util/dbus"
|
140
vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
generated
vendored
140
vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
generated
vendored
@ -1,140 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
godbus "github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
// Fake is a simple fake Interface type.
|
||||
type Fake struct {
|
||||
systemBus *FakeConnection
|
||||
sessionBus *FakeConnection
|
||||
}
|
||||
|
||||
// FakeConnection represents a fake D-Bus connection
|
||||
type FakeConnection struct {
|
||||
lock sync.Mutex
|
||||
busObject *fakeObject
|
||||
objects map[string]*fakeObject
|
||||
signalHandlers []chan<- *godbus.Signal
|
||||
}
|
||||
|
||||
// FakeHandler is used to handle fake D-Bus method calls
|
||||
type FakeHandler func(method string, args ...interface{}) ([]interface{}, error)
|
||||
|
||||
type fakeObject struct {
|
||||
handler FakeHandler
|
||||
}
|
||||
|
||||
type fakeCall struct {
|
||||
ret []interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// NewFake returns a new Interface which will fake talking to D-Bus
|
||||
func NewFake(systemBus *FakeConnection, sessionBus *FakeConnection) *Fake {
|
||||
return &Fake{systemBus, sessionBus}
|
||||
}
|
||||
|
||||
// NewFakeConnection returns a FakeConnection Interface
|
||||
func NewFakeConnection() *FakeConnection {
|
||||
return &FakeConnection{
|
||||
objects: make(map[string]*fakeObject),
|
||||
}
|
||||
}
|
||||
|
||||
// SystemBus is part of Interface
|
||||
func (db *Fake) SystemBus() (Connection, error) {
|
||||
if db.systemBus != nil {
|
||||
return db.systemBus, nil
|
||||
}
|
||||
return nil, fmt.Errorf("DBus is not running")
|
||||
}
|
||||
|
||||
// SessionBus is part of Interface
|
||||
func (db *Fake) SessionBus() (Connection, error) {
|
||||
if db.sessionBus != nil {
|
||||
return db.sessionBus, nil
|
||||
}
|
||||
return nil, fmt.Errorf("DBus is not running")
|
||||
}
|
||||
|
||||
// BusObject is part of the Connection interface
|
||||
func (conn *FakeConnection) BusObject() Object {
|
||||
return conn.busObject
|
||||
}
|
||||
|
||||
// Object is part of the Connection interface
|
||||
func (conn *FakeConnection) Object(name, path string) Object {
|
||||
return conn.objects[name+path]
|
||||
}
|
||||
|
||||
// Signal is part of the Connection interface
|
||||
func (conn *FakeConnection) 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 *FakeConnection) SetBusObject(handler FakeHandler) {
|
||||
conn.busObject = &fakeObject{handler}
|
||||
}
|
||||
|
||||
// AddObject adds a handler for the Object at name and path
|
||||
func (conn *FakeConnection) AddObject(name, path string, handler FakeHandler) {
|
||||
conn.objects[name+path] = &fakeObject{handler}
|
||||
}
|
||||
|
||||
// EmitSignal emits a signal on conn
|
||||
func (conn *FakeConnection) 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...)
|
||||
}
|
37
vendor/k8s.io/kubernetes/pkg/util/ebtables/BUILD
generated
vendored
37
vendor/k8s.io/kubernetes/pkg/util/ebtables/BUILD
generated
vendored
@ -1,37 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["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"],
|
||||
embed = [":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
190
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables.go
generated
vendored
@ -1,190 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
|
||||
}
|
126
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables_test.go
generated
vendored
126
vendor/k8s.io/kubernetes/pkg/util/ebtables/ebtables_test.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
33
vendor/k8s.io/kubernetes/pkg/util/env/BUILD
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/util/env/BUILD
generated
vendored
@ -1,33 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["env.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/env",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["env_test.go"],
|
||||
embed = [":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
51
vendor/k8s.io/kubernetes/pkg/util/env/env.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
82
vendor/k8s.io/kubernetes/pkg/util/env/env_test.go
generated
vendored
@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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")
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/util/file/BUILD
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/util/file/BUILD
generated
vendored
@ -1,36 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["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"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//vendor/github.com/spf13/afero:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
],
|
||||
)
|
149
vendor/k8s.io/kubernetes/pkg/util/file/file_test.go
generated
vendored
149
vendor/k8s.io/kubernetes/pkg/util/file/file_test.go
generated
vendored
@ -1,149 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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 environment
|
||||
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
34
vendor/k8s.io/kubernetes/pkg/util/filesystem/BUILD
generated
vendored
@ -1,34 +0,0 @@
|
||||
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"],
|
||||
)
|
122
vendor/k8s.io/kubernetes/pkg/util/filesystem/defaultfs.go
generated
vendored
122
vendor/k8s.io/kubernetes/pkg/util/filesystem/defaultfs.go
generated
vendored
@ -1,122 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
|
||||
// TempDir via ioutil.TempDir
|
||||
func (DefaultFs) TempDir(dir, prefix string) (string, error) {
|
||||
return ioutil.TempDir(dir, prefix)
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
128
vendor/k8s.io/kubernetes/pkg/util/filesystem/fakefs.go
generated
vendored
128
vendor/k8s.io/kubernetes/pkg/util/filesystem/fakefs.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
|
||||
// TempDir via afero.TempDir
|
||||
func (fs *fakeFs) TempDir(dir, prefix string) (string, error) {
|
||||
return fs.a.TempDir(dir, prefix)
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/util/filesystem/filesystem.go
generated
vendored
52
vendor/k8s.io/kubernetes/pkg/util/filesystem/filesystem.go
generated
vendored
@ -1,52 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
TempDir(dir, prefix string) (string, 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
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/util/filesystem/watcher.go
generated
vendored
89
vendor/k8s.io/kubernetes/pkg/util/filesystem/watcher.go
generated
vendored
@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
38
vendor/k8s.io/kubernetes/pkg/util/flag/BUILD
generated
vendored
38
vendor/k8s.io/kubernetes/pkg/util/flag/BUILD
generated
vendored
@ -1,38 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["flags.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/flag",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/klog: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 = ["flags_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
||||
)
|
161
vendor/k8s.io/kubernetes/pkg/util/flag/flags.go
generated
vendored
161
vendor/k8s.io/kubernetes/pkg/util/flag/flags.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/klog"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
)
|
||||
|
||||
// PrintFlags logs the flags in the flagset
|
||||
func PrintFlags(flags *pflag.FlagSet) {
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
klog.V(1).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(mikedanese): remove these flag wrapper types when we remove command line flags
|
||||
|
||||
var (
|
||||
_ pflag.Value = &IPVar{}
|
||||
_ pflag.Value = &IPPortVar{}
|
||||
_ pflag.Value = &PortRangeVar{}
|
||||
)
|
||||
|
||||
// IPVar is used for validating a command line option that represents an IP. It implements the pflag.Value interface
|
||||
type IPVar struct {
|
||||
Val *string
|
||||
}
|
||||
|
||||
// Set sets the flag value
|
||||
func (v IPVar) Set(s string) error {
|
||||
if len(s) == 0 {
|
||||
v.Val = nil
|
||||
return nil
|
||||
}
|
||||
if net.ParseIP(s) == nil {
|
||||
return fmt.Errorf("%q is not a valid IP address", s)
|
||||
}
|
||||
if v.Val == nil {
|
||||
// it's okay to panic here since this is programmer error
|
||||
panic("the string pointer passed into IPVar should not be nil")
|
||||
}
|
||||
*v.Val = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the flag value
|
||||
func (v IPVar) String() string {
|
||||
if v.Val == nil {
|
||||
return ""
|
||||
}
|
||||
return *v.Val
|
||||
}
|
||||
|
||||
// Type gets the flag type
|
||||
func (v IPVar) Type() string {
|
||||
return "ip"
|
||||
}
|
||||
|
||||
// IPPortVar is used for validating a command line option that represents an IP and a port. It implements the pflag.Value interface
|
||||
type IPPortVar struct {
|
||||
Val *string
|
||||
}
|
||||
|
||||
// Set sets the flag value
|
||||
func (v IPPortVar) Set(s string) error {
|
||||
if len(s) == 0 {
|
||||
v.Val = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.Val == nil {
|
||||
// it's okay to panic here since this is programmer error
|
||||
panic("the string pointer passed into IPPortVar should not be nil")
|
||||
}
|
||||
|
||||
// Both IP and IP:port are valid.
|
||||
// Attempt to parse into IP first.
|
||||
if net.ParseIP(s) != nil {
|
||||
*v.Val = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// Can not parse into IP, now assume IP:port.
|
||||
host, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q is not in a valid format (ip or ip:port): %v", s, err)
|
||||
}
|
||||
if net.ParseIP(host) == nil {
|
||||
return fmt.Errorf("%q is not a valid IP address", host)
|
||||
}
|
||||
if _, err := strconv.Atoi(port); err != nil {
|
||||
return fmt.Errorf("%q is not a valid number", port)
|
||||
}
|
||||
*v.Val = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the flag value
|
||||
func (v IPPortVar) String() string {
|
||||
if v.Val == nil {
|
||||
return ""
|
||||
}
|
||||
return *v.Val
|
||||
}
|
||||
|
||||
// Type gets the flag type
|
||||
func (v IPPortVar) Type() string {
|
||||
return "ipport"
|
||||
}
|
||||
|
||||
// PortRangeVar is used for validating a command line option that represents a port range. It implements the pflag.Value interface
|
||||
type PortRangeVar struct {
|
||||
Val *string
|
||||
}
|
||||
|
||||
// Set sets the flag value
|
||||
func (v PortRangeVar) Set(s string) error {
|
||||
if _, err := utilnet.ParsePortRange(s); err != nil {
|
||||
return fmt.Errorf("%q is not a valid port range: %v", s, err)
|
||||
}
|
||||
if v.Val == nil {
|
||||
// it's okay to panic here since this is programmer error
|
||||
panic("the string pointer passed into PortRangeVar should not be nil")
|
||||
}
|
||||
*v.Val = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the flag value
|
||||
func (v PortRangeVar) String() string {
|
||||
if v.Val == nil {
|
||||
return ""
|
||||
}
|
||||
return *v.Val
|
||||
}
|
||||
|
||||
// Type gets the flag type
|
||||
func (v PortRangeVar) Type() string {
|
||||
return "port-range"
|
||||
}
|
165
vendor/k8s.io/kubernetes/pkg/util/flag/flags_test.go
generated
vendored
165
vendor/k8s.io/kubernetes/pkg/util/flag/flags_test.go
generated
vendored
@ -1,165 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestIPVar(t *testing.T) {
|
||||
defaultIP := "0.0.0.0"
|
||||
testCases := []struct {
|
||||
argc string
|
||||
expectErr bool
|
||||
expectVal string
|
||||
}{
|
||||
|
||||
{
|
||||
argc: "blah --ip=1.2.3.4",
|
||||
expectVal: "1.2.3.4",
|
||||
},
|
||||
{
|
||||
argc: "blah --ip=1.2.3.4a",
|
||||
expectErr: true,
|
||||
expectVal: defaultIP,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
fs := pflag.NewFlagSet("blah", pflag.PanicOnError)
|
||||
ip := defaultIP
|
||||
fs.Var(IPVar{&ip}, "ip", "the ip")
|
||||
|
||||
var err error
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
fs.Parse(strings.Split(tc.argc, " "))
|
||||
}()
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Errorf("did not observe an expected error")
|
||||
continue
|
||||
}
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Errorf("observed an unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if tc.expectVal != ip {
|
||||
t.Errorf("unexpected ip: expected %q, saw %q", tc.expectVal, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPPortVar(t *testing.T) {
|
||||
defaultIPPort := "0.0.0.0:8080"
|
||||
testCases := []struct {
|
||||
desc string
|
||||
argc string
|
||||
expectErr bool
|
||||
expectVal string
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "valid ipv4 1",
|
||||
argc: "blah --ipport=0.0.0.0",
|
||||
expectVal: "0.0.0.0",
|
||||
},
|
||||
{
|
||||
desc: "valid ipv4 2",
|
||||
argc: "blah --ipport=127.0.0.1",
|
||||
expectVal: "127.0.0.1",
|
||||
},
|
||||
|
||||
{
|
||||
desc: "invalid IP",
|
||||
argc: "blah --ipport=invalidip",
|
||||
expectErr: true,
|
||||
expectVal: defaultIPPort,
|
||||
},
|
||||
{
|
||||
desc: "valid ipv4 with port",
|
||||
argc: "blah --ipport=0.0.0.0:8080",
|
||||
expectVal: "0.0.0.0:8080",
|
||||
},
|
||||
{
|
||||
desc: "invalid ipv4 with invalid port",
|
||||
argc: "blah --ipport=0.0.0.0:invalidport",
|
||||
expectErr: true,
|
||||
expectVal: defaultIPPort,
|
||||
},
|
||||
{
|
||||
desc: "invalid IP with port",
|
||||
argc: "blah --ipport=invalidip:8080",
|
||||
expectErr: true,
|
||||
expectVal: defaultIPPort,
|
||||
},
|
||||
{
|
||||
desc: "valid ipv6 1",
|
||||
argc: "blah --ipport=::1",
|
||||
expectVal: "::1",
|
||||
},
|
||||
{
|
||||
desc: "valid ipv6 2",
|
||||
argc: "blah --ipport=::",
|
||||
expectVal: "::",
|
||||
},
|
||||
{
|
||||
desc: "valid ipv6 with port",
|
||||
argc: "blah --ipport=[::1]:8080",
|
||||
expectVal: "[::1]:8080",
|
||||
},
|
||||
{
|
||||
desc: "invalid ipv6 with port without bracket",
|
||||
argc: "blah --ipport=fd00:f00d:600d:f00d:8080",
|
||||
expectErr: true,
|
||||
expectVal: defaultIPPort,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
fs := pflag.NewFlagSet("blah", pflag.PanicOnError)
|
||||
ipport := defaultIPPort
|
||||
fs.Var(IPPortVar{&ipport}, "ipport", "the ip:port")
|
||||
|
||||
var err error
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
fs.Parse(strings.Split(tc.argc, " "))
|
||||
}()
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Errorf("%q: Did not observe an expected error", tc.desc)
|
||||
continue
|
||||
}
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Errorf("%q: Observed an unexpected error: %v", tc.desc, err)
|
||||
continue
|
||||
}
|
||||
if tc.expectVal != ipport {
|
||||
t.Errorf("%q: Unexpected ipport: expected %q, saw %q", tc.desc, tc.expectVal, ipport)
|
||||
}
|
||||
}
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/util/flock/BUILD
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/util/flock/BUILD
generated
vendored
@ -1,49 +0,0 @@
|
||||
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",
|
||||
"flock_unix.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/flock",
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"//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
24
vendor/k8s.io/kubernetes/pkg/util/flock/flock_other.go
generated
vendored
@ -1,24 +0,0 @@
|
||||
// +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
35
vendor/k8s.io/kubernetes/pkg/util/flock/flock_unix.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
// +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)
|
||||
}
|
41
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/BUILD
generated
vendored
41
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/BUILD
generated
vendored
@ -1,41 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["goroutinemap.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/goroutinemap",
|
||||
deps = [
|
||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["goroutinemap_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//staging/src/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"],
|
||||
)
|
4
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/OWNERS
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/OWNERS
generated
vendored
@ -1,4 +0,0 @@
|
||||
approvers:
|
||||
- saad-ali
|
||||
reviewers:
|
||||
- saad-ali
|
25
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/BUILD
generated
vendored
25
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/BUILD
generated
vendored
@ -1,25 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["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"],
|
||||
)
|
121
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go
generated
vendored
121
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go
generated
vendored
@ -1,121 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package 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)
|
||||
}
|
230
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap.go
generated
vendored
230
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap.go
generated
vendored
@ -1,230 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package 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"
|
||||
|
||||
k8sRuntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
||||
)
|
||||
|
||||
// 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
|
||||
klog.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
|
||||
klog.Errorf("%v",
|
||||
existingOp.expBackoff.GenerateNoRetriesPermittedMsg(operationName))
|
||||
}
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) IsOperationPending(operationName string) bool {
|
||||
grm.lock.RLock()
|
||||
defer grm.lock.RUnlock()
|
||||
existingOp, exists := grm.operations[operationName]
|
||||
if exists && existingOp.operationPending {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) Wait() {
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
for len(grm.operations) > 0 {
|
||||
grm.cond.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (grm *goRoutineMap) WaitForCompletion() {
|
||||
grm.lock.Lock()
|
||||
defer grm.lock.Unlock()
|
||||
|
||||
for {
|
||||
if len(grm.operations) == 0 || grm.nothingPending() {
|
||||
break
|
||||
} else {
|
||||
grm.cond.Wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any operation is pending. Already assumes caller has the
|
||||
// necessary locks
|
||||
func (grm *goRoutineMap) nothingPending() bool {
|
||||
nothingIsPending := true
|
||||
for _, operation := range grm.operations {
|
||||
if operation.operationPending {
|
||||
nothingIsPending = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return nothingIsPending
|
||||
}
|
||||
|
||||
// NewAlreadyExistsError returns a new instance of AlreadyExists error.
|
||||
func NewAlreadyExistsError(operationName string) error {
|
||||
return alreadyExistsError{operationName}
|
||||
}
|
||||
|
||||
// IsAlreadyExists returns true if an error returned from GoRoutineMap indicates
|
||||
// a new operation can not be started because an operation with the same
|
||||
// operation name is already executing.
|
||||
func IsAlreadyExists(err error) bool {
|
||||
switch err.(type) {
|
||||
case alreadyExistsError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// alreadyExistsError is the error returned by GoRoutineMap when a new operation
|
||||
// can not be started because an operation with the same operation name is
|
||||
// already executing.
|
||||
type alreadyExistsError struct {
|
||||
operationName string
|
||||
}
|
||||
|
||||
var _ error = alreadyExistsError{}
|
||||
|
||||
func (err alreadyExistsError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Failed to create operation with name %q. An operation with that name is already executing.",
|
||||
err.operationName)
|
||||
}
|
531
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap_test.go
generated
vendored
531
vendor/k8s.io/kubernetes/pkg/util/goroutinemap/goroutinemap_test.go
generated
vendored
@ -1,531 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
34
vendor/k8s.io/kubernetes/pkg/util/hash/BUILD
generated
vendored
34
vendor/k8s.io/kubernetes/pkg/util/hash/BUILD
generated
vendored
@ -1,34 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_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"],
|
||||
embed = [":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
37
vendor/k8s.io/kubernetes/pkg/util/hash/hash.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
147
vendor/k8s.io/kubernetes/pkg/util/hash/hash_test.go
generated
vendored
@ -1,147 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
25
vendor/k8s.io/kubernetes/pkg/util/initsystem/BUILD
generated
vendored
@ -1,25 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["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"],
|
||||
)
|
179
vendor/k8s.io/kubernetes/pkg/util/initsystem/initsystem.go
generated
vendored
179
vendor/k8s.io/kubernetes/pkg/util/initsystem/initsystem.go
generated
vendored
@ -1,179 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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
|
||||
|
||||
// ServiceRestart tries to reload the environment and restart the specific service
|
||||
ServiceRestart(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) reloadSystemd() error {
|
||||
if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil {
|
||||
return fmt.Errorf("failed to reload systemd: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sysd SystemdInitSystem) ServiceStart(service string) error {
|
||||
// Before we try to start any service, make sure that systemd is ready
|
||||
if err := sysd.reloadSystemd(); err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{"start", service}
|
||||
return exec.Command("systemctl", args...).Run()
|
||||
}
|
||||
|
||||
func (sysd SystemdInitSystem) ServiceRestart(service string) error {
|
||||
// Before we try to restart any service, make sure that systemd is ready
|
||||
if err := sysd.reloadSystemd(); err != nil {
|
||||
return err
|
||||
}
|
||||
args := []string{"restart", service}
|
||||
return exec.Command("systemctl", args...).Run()
|
||||
}
|
||||
|
||||
func (sysd SystemdInitSystem) ServiceStop(service string) error {
|
||||
args := []string{"stop", service}
|
||||
return exec.Command("systemctl", args...).Run()
|
||||
}
|
||||
|
||||
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) ServiceRestart(service string) error {
|
||||
if err := sysd.ServiceStop(service); err != nil {
|
||||
return fmt.Errorf("couldn't stop service: %v", err)
|
||||
}
|
||||
if err := sysd.ServiceStart(service); err != nil {
|
||||
return fmt.Errorf("couldn't start service: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
25
vendor/k8s.io/kubernetes/pkg/util/interrupt/BUILD
generated
vendored
@ -1,25 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["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"],
|
||||
)
|
104
vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go
generated
vendored
104
vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go
generated
vendored
@ -1,104 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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()
|
||||
}
|
25
vendor/k8s.io/kubernetes/pkg/util/io/BUILD
generated
vendored
25
vendor/k8s.io/kubernetes/pkg/util/io/BUILD
generated
vendored
@ -1,25 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["consistentread.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/io",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
40
vendor/k8s.io/kubernetes/pkg/util/ipconfig/BUILD
generated
vendored
40
vendor/k8s.io/kubernetes/pkg/util/ipconfig/BUILD
generated
vendored
@ -1,40 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"ipconfig.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/ipconfig",
|
||||
deps = [
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["ipconfig_test.go"],
|
||||
embed = [":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
18
vendor/k8s.io/kubernetes/pkg/util/ipconfig/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package 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
99
vendor/k8s.io/kubernetes/pkg/util/ipconfig/ipconfig.go
generated
vendored
@ -1,99 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipconfig
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
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" {
|
||||
klog.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 {
|
||||
klog.V(1).Infof("Running %s %s failed: %v", cmdIpconfig, cmdDefaultArgs, err)
|
||||
}
|
||||
|
||||
return suffixList, err
|
||||
}
|
33
vendor/k8s.io/kubernetes/pkg/util/ipconfig/ipconfig_test.go
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/util/ipconfig/ipconfig_test.go
generated
vendored
@ -1,33 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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)
|
||||
}
|
||||
}
|
43
vendor/k8s.io/kubernetes/pkg/util/ipset/BUILD
generated
vendored
43
vendor/k8s.io/kubernetes/pkg/util/ipset/BUILD
generated
vendored
@ -1,43 +0,0 @@
|
||||
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/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["ipset_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/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"],
|
||||
)
|
9
vendor/k8s.io/kubernetes/pkg/util/ipset/OWNERS
generated
vendored
9
vendor/k8s.io/kubernetes/pkg/util/ipset/OWNERS
generated
vendored
@ -1,9 +0,0 @@
|
||||
reviewers:
|
||||
- thockin
|
||||
- brendandburns
|
||||
- m1093782566
|
||||
- islinwb
|
||||
approvers:
|
||||
- thockin
|
||||
- brendandburns
|
||||
- m1093782566
|
528
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset.go
generated
vendored
528
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset.go
generated
vendored
@ -1,528 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
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. It will ignore error when the entry already exists if ignoreExistErr=true.
|
||||
AddEntry(entry string, set *IPSet, 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 a 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
|
||||
// comment message for ipset
|
||||
Comment string
|
||||
}
|
||||
|
||||
// Validate checks if a given ipset is valid or not.
|
||||
func (set *IPSet) Validate() bool {
|
||||
// Check if protocol is valid for `HashIPPort`, `HashIPPortIP` and `HashIPPortNet` type set.
|
||||
if set.SetType == HashIPPort || set.SetType == HashIPPortIP || set.SetType == HashIPPortNet {
|
||||
if valid := validateHashFamily(set.HashFamily); !valid {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// check set type
|
||||
if valid := validateIPSetType(set.SetType); !valid {
|
||||
return false
|
||||
}
|
||||
// check port range for bitmap type set
|
||||
if set.SetType == BitmapPort {
|
||||
if valid := validatePortRange(set.PortRange); !valid {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// check hash size value of ipset
|
||||
if set.HashSize <= 0 {
|
||||
klog.Errorf("Invalid hashsize value %d, should be >0", set.HashSize)
|
||||
return false
|
||||
}
|
||||
// check max elem value of ipset
|
||||
if set.MaxElem <= 0 {
|
||||
klog.Errorf("Invalid maxelem value %d, should be >0", set.MaxElem)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//setIPSetDefaults sets some IPSet fields if not present to their default values.
|
||||
func (set *IPSet) setIPSetDefaults() {
|
||||
// Setting default values if not present
|
||||
if set.HashSize == 0 {
|
||||
set.HashSize = 1024
|
||||
}
|
||||
if set.MaxElem == 0 {
|
||||
set.MaxElem = 65536
|
||||
}
|
||||
// Default protocol is IPv4
|
||||
if set.HashFamily == "" {
|
||||
set.HashFamily = ProtocolFamilyIPV4
|
||||
}
|
||||
// Default ipset type is "hash:ip,port"
|
||||
if len(set.SetType) == 0 {
|
||||
set.SetType = HashIPPort
|
||||
}
|
||||
if len(set.PortRange) == 0 {
|
||||
set.PortRange = DefaultPortRange
|
||||
}
|
||||
}
|
||||
|
||||
// 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, UDP and SCTP.
|
||||
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 is the type of ipset where the entry exists.
|
||||
SetType Type
|
||||
}
|
||||
|
||||
// Validate checks if a given ipset entry is valid or not. The set parameter is the ipset that entry belongs to.
|
||||
func (e *Entry) Validate(set *IPSet) bool {
|
||||
if e.Port < 0 {
|
||||
klog.Errorf("Entry %v port number %d should be >=0 for ipset %v", e, e.Port, set)
|
||||
return false
|
||||
}
|
||||
switch e.SetType {
|
||||
case HashIPPort:
|
||||
//check if IP and Protocol of Entry is valid.
|
||||
if valid := e.checkIPandProtocol(set); !valid {
|
||||
return false
|
||||
}
|
||||
case HashIPPortIP:
|
||||
//check if IP and Protocol of Entry is valid.
|
||||
if valid := e.checkIPandProtocol(set); !valid {
|
||||
return false
|
||||
}
|
||||
|
||||
// IP2 can not be empty for `hash:ip,port,ip` type ip set
|
||||
if net.ParseIP(e.IP2) == nil {
|
||||
klog.Errorf("Error parsing entry %v second ip address %v for ipset %v", e, e.IP2, set)
|
||||
return false
|
||||
}
|
||||
case HashIPPortNet:
|
||||
//check if IP and Protocol of Entry is valid.
|
||||
if valid := e.checkIPandProtocol(set); !valid {
|
||||
return false
|
||||
}
|
||||
|
||||
// Net can not be empty for `hash:ip,port,net` type ip set
|
||||
if _, ipNet, err := net.ParseCIDR(e.Net); ipNet == nil {
|
||||
klog.Errorf("Error parsing entry %v ip net %v for ipset %v, error: %v", e, e.Net, set, err)
|
||||
return false
|
||||
}
|
||||
case BitmapPort:
|
||||
// check if port number satisfies its ipset's requirement of port range
|
||||
if set == nil {
|
||||
klog.Errorf("Unable to reference ip set where the entry %v exists", e)
|
||||
return false
|
||||
}
|
||||
begin, end, err := parsePortRange(set.PortRange)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to parse set %v port range %s for ipset %v, error: %v", set, set.PortRange, set, err)
|
||||
return false
|
||||
}
|
||||
if e.Port < begin || e.Port > end {
|
||||
klog.Errorf("Entry %v port number %d is not in the port range %s of its ipset %v", e, e.Port, set.PortRange, set)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns the string format for ipset entry.
|
||||
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 ""
|
||||
}
|
||||
|
||||
// checkIPandProtocol checks if IP and Protocol of Entry is valid.
|
||||
func (e *Entry) checkIPandProtocol(set *IPSet) bool {
|
||||
// set default protocol to tcp if empty
|
||||
if len(e.Protocol) == 0 {
|
||||
e.Protocol = ProtocolTCP
|
||||
} else if !validateProtocol(e.Protocol) {
|
||||
return false
|
||||
}
|
||||
|
||||
if net.ParseIP(e.IP) == nil {
|
||||
klog.Errorf("Error parsing entry %v ip address %v for ipset %v", e, e.IP, set)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
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 {
|
||||
// sets some IPSet fields if not present to their default values.
|
||||
set.setIPSetDefaults()
|
||||
|
||||
// Validate ipset before creating
|
||||
valid := set.Validate()
|
||||
if !valid {
|
||||
return fmt.Errorf("error creating ipset since it's invalid")
|
||||
}
|
||||
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 || set.SetType == HashIPPortNet {
|
||||
args = append(args,
|
||||
"family", set.HashFamily,
|
||||
"hashsize", strconv.Itoa(set.HashSize),
|
||||
"maxelem", strconv.Itoa(set.MaxElem),
|
||||
)
|
||||
}
|
||||
if set.SetType == BitmapPort {
|
||||
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 *IPSet, ignoreExistErr bool) error {
|
||||
args := []string{"add", set.Name, 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 out, err := runner.exec.Command(IPSetCmd, "destroy", set).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error destroying set %s, error: %v(%s)", set, err, out)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// checks if port range is valid. The begin port number is not necessarily less than
|
||||
// end port number - ipset util can accept it. It means both 1-100 and 100-1 are valid.
|
||||
func validatePortRange(portRange string) bool {
|
||||
strs := strings.Split(portRange, "-")
|
||||
if len(strs) != 2 {
|
||||
klog.Errorf("port range should be in the format of `a-b`")
|
||||
return false
|
||||
}
|
||||
for i := range strs {
|
||||
num, err := strconv.Atoi(strs[i])
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to parse %s, error: %v", strs[i], err)
|
||||
return false
|
||||
}
|
||||
if num < 0 {
|
||||
klog.Errorf("port number %d should be >=0", num)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checks if the given ipset type is valid.
|
||||
func validateIPSetType(set Type) bool {
|
||||
for _, valid := range ValidIPSetTypes {
|
||||
if set == valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
klog.Errorf("Currently supported ipset types are: %v, %s is not supported", ValidIPSetTypes, set)
|
||||
return false
|
||||
}
|
||||
|
||||
// checks if given hash family is supported in ipset
|
||||
func validateHashFamily(family string) bool {
|
||||
if family == ProtocolFamilyIPV4 || family == ProtocolFamilyIPV6 {
|
||||
return true
|
||||
}
|
||||
klog.Errorf("Currently supported ip set hash families are: [%s, %s], %s is not supported", ProtocolFamilyIPV4, ProtocolFamilyIPV6, family)
|
||||
return false
|
||||
}
|
||||
|
||||
// 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, "does not exist") {
|
||||
// set with the same name already exists
|
||||
// xref: https://github.com/Olipro/ipset/blob/master/lib/errcode.c#L32-L33
|
||||
return true
|
||||
}
|
||||
if strings.Contains(es, "element is missing") {
|
||||
// entry is missing from the set
|
||||
// xref: https://github.com/Olipro/ipset/blob/master/lib/parse.c#L1904
|
||||
// https://github.com/Olipro/ipset/blob/master/lib/parse.c#L1925
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checks if given protocol is supported in entry
|
||||
func validateProtocol(protocol string) bool {
|
||||
if protocol == ProtocolTCP || protocol == ProtocolUDP || protocol == ProtocolSCTP {
|
||||
return true
|
||||
}
|
||||
klog.Errorf("Invalid entry's protocol: %s, supported protocols are [%s, %s, %s]", protocol, ProtocolTCP, ProtocolUDP, ProtocolSCTP)
|
||||
return false
|
||||
}
|
||||
|
||||
// parsePortRange parse the begin and end port from a raw string(format: a-b). beginPort <= endPort
|
||||
// in the return value.
|
||||
func parsePortRange(portRange string) (beginPort int, endPort int, err error) {
|
||||
if len(portRange) == 0 {
|
||||
portRange = DefaultPortRange
|
||||
}
|
||||
|
||||
strs := strings.Split(portRange, "-")
|
||||
if len(strs) != 2 {
|
||||
// port number -1 indicates invalid
|
||||
return -1, -1, fmt.Errorf("port range should be in the format of `a-b`")
|
||||
}
|
||||
for i := range strs {
|
||||
num, err := strconv.Atoi(strs[i])
|
||||
if err != nil {
|
||||
// port number -1 indicates invalid
|
||||
return -1, -1, err
|
||||
}
|
||||
if num < 0 {
|
||||
// port number -1 indicates invalid
|
||||
return -1, -1, fmt.Errorf("port number %d should be >=0", num)
|
||||
}
|
||||
if i == 0 {
|
||||
beginPort = num
|
||||
continue
|
||||
}
|
||||
endPort = num
|
||||
// switch when first port number > second port number
|
||||
if beginPort > endPort {
|
||||
endPort = beginPort
|
||||
beginPort = num
|
||||
}
|
||||
}
|
||||
return beginPort, endPort, nil
|
||||
}
|
||||
|
||||
var _ = Interface(&runner{})
|
1647
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset_test.go
generated
vendored
1647
vendor/k8s.io/kubernetes/pkg/util/ipset/ipset_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
36
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/BUILD
generated
vendored
36
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/BUILD
generated
vendored
@ -1,36 +0,0 @@
|
||||
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/ipset/testing",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/util/ipset:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets: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"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["fake_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/util/ipset:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
143
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/fake.go
generated
vendored
143
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/fake.go
generated
vendored
@ -1,143 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/ipset"
|
||||
)
|
||||
|
||||
// FakeIPSet is a no-op implementation of ipset Interface
|
||||
type FakeIPSet struct {
|
||||
// version of ipset util
|
||||
Version string
|
||||
// The key of Sets map is the ip set name
|
||||
Sets map[string]*ipset.IPSet
|
||||
// The key of Entries map is the ip set name where the entries exists
|
||||
Entries map[string]sets.String
|
||||
}
|
||||
|
||||
// NewFake create a new fake ipset interface - it initialize the FakeIPSet.
|
||||
func NewFake(version string) *FakeIPSet {
|
||||
return &FakeIPSet{
|
||||
Version: version,
|
||||
Sets: make(map[string]*ipset.IPSet),
|
||||
Entries: make(map[string]sets.String),
|
||||
}
|
||||
}
|
||||
|
||||
// GetVersion is part of interface.
|
||||
func (f *FakeIPSet) GetVersion() (string, error) {
|
||||
return f.Version, nil
|
||||
}
|
||||
|
||||
// FlushSet is part of interface. It deletes all entries from a named set but keeps the set itself.
|
||||
func (f *FakeIPSet) FlushSet(set string) error {
|
||||
if f.Entries == nil {
|
||||
return fmt.Errorf("entries map can't be nil")
|
||||
}
|
||||
|
||||
// delete all entry elements
|
||||
for true {
|
||||
if _, has := f.Entries[set].PopAny(); has {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroySet is part of interface. It deletes both the entries and the set itself.
|
||||
func (f *FakeIPSet) DestroySet(set string) error {
|
||||
delete(f.Sets, set)
|
||||
delete(f.Entries, set)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroyAllSets is part of interface.
|
||||
func (f *FakeIPSet) DestroyAllSets() error {
|
||||
f.Sets = nil
|
||||
f.Entries = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSet is part of interface.
|
||||
func (f *FakeIPSet) CreateSet(set *ipset.IPSet, ignoreExistErr bool) error {
|
||||
if f.Sets[set.Name] != nil {
|
||||
if !ignoreExistErr {
|
||||
// already exists
|
||||
return fmt.Errorf("Set cannot be created: set with the same name already exists")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
f.Sets[set.Name] = set
|
||||
// initialize entry map
|
||||
f.Entries[set.Name] = sets.NewString()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddEntry is part of interface.
|
||||
func (f *FakeIPSet) AddEntry(entry string, set *ipset.IPSet, ignoreExistErr bool) error {
|
||||
if f.Entries[set.Name].Has(entry) {
|
||||
if !ignoreExistErr {
|
||||
// already exists
|
||||
return fmt.Errorf("Element cannot be added to the set: it's already added")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
f.Entries[set.Name].Insert(entry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelEntry is part of interface.
|
||||
func (f *FakeIPSet) DelEntry(entry string, set string) error {
|
||||
if f.Entries == nil {
|
||||
return fmt.Errorf("entries map can't be nil")
|
||||
}
|
||||
f.Entries[set].Delete(entry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestEntry is part of interface.
|
||||
func (f *FakeIPSet) TestEntry(entry string, set string) (bool, error) {
|
||||
if f.Entries == nil {
|
||||
return false, fmt.Errorf("entries map can't be nil")
|
||||
}
|
||||
found := f.Entries[set].Has(entry)
|
||||
return found, nil
|
||||
}
|
||||
|
||||
// ListEntries is part of interface.
|
||||
func (f *FakeIPSet) ListEntries(set string) ([]string, error) {
|
||||
if f.Entries == nil {
|
||||
return nil, fmt.Errorf("entries map can't be nil")
|
||||
}
|
||||
return f.Entries[set].UnsortedList(), nil
|
||||
}
|
||||
|
||||
// ListSets is part of interface.
|
||||
func (f *FakeIPSet) ListSets() ([]string, error) {
|
||||
res := []string{}
|
||||
for set := range f.Sets {
|
||||
res = append(res, set)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var _ = ipset.Interface(&FakeIPSet{})
|
152
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/fake_test.go
generated
vendored
152
vendor/k8s.io/kubernetes/pkg/util/ipset/testing/fake_test.go
generated
vendored
@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/util/ipset"
|
||||
)
|
||||
|
||||
const testVersion = "v6.19"
|
||||
|
||||
func TestSetEntry(t *testing.T) {
|
||||
fake := NewFake(testVersion)
|
||||
version, err := fake.GetVersion()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if version != testVersion {
|
||||
t.Errorf("Unexpected version mismatch, expected: %s, got: %s", testVersion, version)
|
||||
}
|
||||
// create a set
|
||||
set := &ipset.IPSet{
|
||||
Name: "foo",
|
||||
SetType: ipset.HashIPPort,
|
||||
HashFamily: ipset.ProtocolFamilyIPV4,
|
||||
}
|
||||
if err := fake.CreateSet(set, true); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// add two entries
|
||||
fake.AddEntry("192.168.1.1,tcp:8080", set, true)
|
||||
fake.AddEntry("192.168.1.2,tcp:8081", set, true)
|
||||
entries, err := fake.ListEntries(set.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(entries) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(entries))
|
||||
}
|
||||
expectedEntries := sets.NewString("192.168.1.1,tcp:8080", "192.168.1.2,tcp:8081")
|
||||
if !expectedEntries.Equal(sets.NewString(entries...)) {
|
||||
t.Errorf("Unexpected entries mismatch, expected: %v, got: %v", expectedEntries, entries)
|
||||
}
|
||||
|
||||
// test entries
|
||||
found, err := fake.TestEntry("192.168.1.1,tcp:8080", set.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Unexpected entry 192.168.1.1,tcp:8080 not found")
|
||||
}
|
||||
|
||||
found, err = fake.TestEntry("192.168.1.2,tcp:8081", set.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Unexpected entry 192.168.1.2,tcp:8081 not found")
|
||||
}
|
||||
|
||||
// delete entry from a given set
|
||||
if err := fake.DelEntry("192.168.1.1,tcp:8080", set.Name); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
entries, err = fake.ListEntries(set.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(entries) != 1 {
|
||||
t.Errorf("Expected 1 entries, got %d", len(entries))
|
||||
}
|
||||
expectedEntries = sets.NewString("192.168.1.2,tcp:8081")
|
||||
if !expectedEntries.Equal(sets.NewString(entries...)) {
|
||||
t.Errorf("Unexpected entries mismatch, expected: %v, got: %v", expectedEntries, entries)
|
||||
}
|
||||
|
||||
// Flush set
|
||||
if err := fake.FlushSet(set.Name); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
entries, err = fake.ListEntries(set.Name)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(entries) != 0 {
|
||||
t.Errorf("Expected 0 entries, got %d, entries: %v", len(entries), entries)
|
||||
}
|
||||
|
||||
// create another set
|
||||
set2 := &ipset.IPSet{
|
||||
Name: "bar",
|
||||
SetType: ipset.HashIPPortIP,
|
||||
HashFamily: ipset.ProtocolFamilyIPV6,
|
||||
}
|
||||
if err := fake.CreateSet(set2, true); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
setList, err := fake.ListSets()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(setList) != 2 {
|
||||
t.Errorf("Expected 2 sets, got %d", len(setList))
|
||||
}
|
||||
expectedSets := sets.NewString("foo", "bar")
|
||||
if !expectedSets.Equal(sets.NewString(setList...)) {
|
||||
t.Errorf("Unexpected sets mismatch, expected: %v, got: %v", expectedSets, setList)
|
||||
}
|
||||
|
||||
// Destroy a given set
|
||||
if err := fake.DestroySet(set.Name); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if fake.Sets[set.Name] != nil {
|
||||
t.Errorf("Unexpected set: %v", fake.Sets[set.Name])
|
||||
}
|
||||
if fake.Entries[set.Name] != nil {
|
||||
t.Errorf("Unexpected entries: %v", fake.Entries[set.Name])
|
||||
}
|
||||
|
||||
// Destroy all sets
|
||||
if err := fake.DestroyAllSets(); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(fake.Sets) != 0 {
|
||||
t.Errorf("Expected 0 sets, got %d, sets: %v", len(fake.Sets), fake.Sets)
|
||||
}
|
||||
if len(fake.Entries) != 0 {
|
||||
t.Errorf("Expected 0 entries, got %d, entries: %v", len(fake.Entries), fake.Entries)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test ignoreExistErr=false
|
62
vendor/k8s.io/kubernetes/pkg/util/ipset/types.go
generated
vendored
62
vendor/k8s.io/kubernetes/pkg/util/ipset/types.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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"
|
||||
// ProtocolSCTP represents SCTP protocol.
|
||||
ProtocolSCTP = "sctp"
|
||||
)
|
||||
|
||||
// ValidIPSetTypes defines the supported ip set type.
|
||||
var ValidIPSetTypes = []Type{
|
||||
HashIPPort,
|
||||
HashIPPortIP,
|
||||
BitmapPort,
|
||||
HashIPPortNet,
|
||||
}
|
69
vendor/k8s.io/kubernetes/pkg/util/iptables/BUILD
generated
vendored
69
vendor/k8s.io/kubernetes/pkg/util/iptables/BUILD
generated
vendored
@ -1,69 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"iptables.go",
|
||||
"iptables_linux.go",
|
||||
"iptables_unsupported.go",
|
||||
"save_restore.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/iptables",
|
||||
deps = [
|
||||
"//pkg/util/dbus:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/trace:go_default_library",
|
||||
"//vendor/github.com/godbus/dbus:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"iptables_test.go",
|
||||
"save_restore_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/util/dbus:go_default_library",
|
||||
"//staging/src/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
9
vendor/k8s.io/kubernetes/pkg/util/iptables/OWNERS
generated
vendored
@ -1,9 +0,0 @@
|
||||
reviewers:
|
||||
- dcbw
|
||||
- thockin
|
||||
- eparis
|
||||
- danwinship
|
||||
approvers:
|
||||
- dcbw
|
||||
- thockin
|
||||
- eparis
|
18
vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go
generated
vendored
18
vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package iptables provides an interface and implementations for running iptables commands.
|
||||
package iptables // import "k8s.io/kubernetes/pkg/util/iptables"
|
713
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go
generated
vendored
713
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go
generated
vendored
@ -1,713 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
godbus "github.com/godbus/dbus"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilversion "k8s.io/apimachinery/pkg/util/version"
|
||||
utiltrace "k8s.io/apiserver/pkg/util/trace"
|
||||
"k8s.io/klog"
|
||||
utildbus "k8s.io/kubernetes/pkg/util/dbus"
|
||||
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 WaitSecondsValue = "5"
|
||||
|
||||
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
|
||||
hasListener 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 {
|
||||
klog.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),
|
||||
hasListener: false,
|
||||
waitFlag: getIPTablesWaitFlag(vstring),
|
||||
restoreWaitFlag: getIPTablesRestoreWaitFlag(exec, protocol),
|
||||
lockfilePath: lockfilePath,
|
||||
}
|
||||
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 {
|
||||
klog.V(1).Infof("Could not connect to D-Bus system bus: %s", err)
|
||||
return
|
||||
}
|
||||
runner.hasListener = true
|
||||
|
||||
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()
|
||||
|
||||
trace := utiltrace.New("iptables save")
|
||||
defer trace.LogIfLong(2 * time.Second)
|
||||
|
||||
// run and return
|
||||
iptablesSaveCmd := iptablesSaveCommand(runner.protocol)
|
||||
args := []string{"-t", string(table)}
|
||||
klog.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()
|
||||
|
||||
trace := utiltrace.New("iptables restore")
|
||||
defer trace.LogIfLong(2 * time.Second)
|
||||
|
||||
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
|
||||
}
|
||||
trace.Step("Locks grabbed")
|
||||
defer func(locker iptablesLocker) {
|
||||
if err := locker.Close(); err != nil {
|
||||
klog.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)
|
||||
klog.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) {
|
||||
return runner.runContext(nil, op, args)
|
||||
}
|
||||
|
||||
func (runner *runner) runContext(ctx context.Context, op operation, args []string) ([]byte, error) {
|
||||
iptablesCmd := iptablesCommand(runner.protocol)
|
||||
fullArgs := append(runner.waitFlag, string(op))
|
||||
fullArgs = append(fullArgs, args...)
|
||||
klog.V(5).Infof("running iptables %s %v", string(op), args)
|
||||
if ctx == nil {
|
||||
return runner.exec.Command(iptablesCmd, fullArgs...).CombinedOutput()
|
||||
}
|
||||
return runner.exec.CommandContext(ctx, 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...))
|
||||
}
|
||||
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)
|
||||
klog.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
|
||||
}
|
||||
klog.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) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
out, err := runner.runContext(ctx, opCheckRule, args)
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return false, fmt.Errorf("timed out while checking rules")
|
||||
}
|
||||
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 {
|
||||
klog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err)
|
||||
return true
|
||||
}
|
||||
version, err := utilversion.ParseGeneric(vstring)
|
||||
if err != nil {
|
||||
klog.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 {
|
||||
klog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
minVersion, err := utilversion.ParseGeneric(WaitMinVersion)
|
||||
if err != nil {
|
||||
klog.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 {
|
||||
klog.Errorf("WaitSecondsMinVersion (%s) is not a valid version string: %v", WaitSecondsMinVersion, err)
|
||||
return nil
|
||||
}
|
||||
if version.LessThan(minVersion) {
|
||||
return []string{WaitString}
|
||||
} else {
|
||||
return []string{WaitString, WaitSecondsValue}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
klog.V(3).Infof("couldn't get iptables-restore version; assuming it doesn't support --wait")
|
||||
return nil
|
||||
}
|
||||
if _, err := utilversion.ParseGeneric(vstring); err != nil {
|
||||
klog.V(3).Infof("couldn't parse iptables-restore version; assuming it doesn't support --wait")
|
||||
return nil
|
||||
}
|
||||
|
||||
return []string{WaitString, WaitSecondsValue}
|
||||
}
|
||||
|
||||
// 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.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
|
||||
// We only need to listen to firewalld if there are Reload functions, so lazy
|
||||
// initialize the listener.
|
||||
if !runner.hasListener {
|
||||
runner.connectToFirewallD()
|
||||
}
|
||||
|
||||
runner.reloadFuncs = append(runner.reloadFuncs, reloadFunc)
|
||||
}
|
||||
|
||||
// runs all reload funcs to re-sync iptables rules
|
||||
func (runner *runner) reload() {
|
||||
klog.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
|
||||
}
|
100
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go
generated
vendored
100
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go
generated
vendored
@ -1,100 +0,0 @@
|
||||
// +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)
|
||||
}
|
1310
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go
generated
vendored
1310
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
32
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go
generated
vendored
32
vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go
generated
vendored
@ -1,32 +0,0 @@
|
||||
// +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")
|
||||
}
|
118
vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go
generated
vendored
118
vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go
generated
vendored
@ -1,118 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
commitBytes = []byte("COMMIT")
|
||||
spaceBytes = []byte(" ")
|
||||
)
|
||||
|
||||
// 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 []byte where the []byte is the chain line
|
||||
// from save (with counters etc.).
|
||||
// Note that to avoid allocations memory is SHARED with save.
|
||||
func GetChainLines(table Table, save []byte) map[Chain][]byte {
|
||||
chainsMap := make(map[Chain][]byte)
|
||||
tablePrefix := []byte("*" + string(table))
|
||||
readIndex := 0
|
||||
// find beginning of table
|
||||
for readIndex < len(save) {
|
||||
line, n := readLine(readIndex, save)
|
||||
readIndex = n
|
||||
if bytes.HasPrefix(line, tablePrefix) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// parse table lines
|
||||
for readIndex < len(save) {
|
||||
line, n := readLine(readIndex, save)
|
||||
readIndex = n
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if bytes.HasPrefix(line, commitBytes) || line[0] == '*' {
|
||||
break
|
||||
} else if line[0] == '#' {
|
||||
continue
|
||||
} else if line[0] == ':' && 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:bytes.Index(line, spaceBytes)])
|
||||
chainsMap[chain] = line
|
||||
}
|
||||
}
|
||||
return chainsMap
|
||||
}
|
||||
|
||||
func readLine(readIndex int, byteArray []byte) ([]byte, 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 nil, 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
|
||||
}
|
||||
}
|
||||
// Avoid unnecessary allocation.
|
||||
return byteArray[leftTrimIndex:rightTrimIndex], currentReadIndex + 1
|
||||
} else {
|
||||
// unset rightTrimIndex
|
||||
rightTrimIndex = -1
|
||||
}
|
||||
}
|
||||
return nil, currentReadIndex
|
||||
}
|
53
vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore_test.go
generated
vendored
53
vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore_test.go
generated
vendored
@ -1,53 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadLinesFromByteBuffer(t *testing.T) {
|
||||
testFn := func(byteArray []byte, expected []string) {
|
||||
index := 0
|
||||
readIndex := 0
|
||||
for ; readIndex < len(byteArray); index++ {
|
||||
line, n := readLine(readIndex, byteArray)
|
||||
readIndex = n
|
||||
if expected[index] != string(line) {
|
||||
t.Errorf("expected:%q, actual:%q", expected[index], line)
|
||||
}
|
||||
} // for
|
||||
if readIndex < len(byteArray) {
|
||||
t.Errorf("Byte buffer was only partially read. Buffer length is:%d, readIndex is:%d", len(byteArray), readIndex)
|
||||
}
|
||||
if index < len(expected) {
|
||||
t.Errorf("All expected strings were not compared. expected arr length:%d, matched count:%d", len(expected), index-1)
|
||||
}
|
||||
}
|
||||
|
||||
byteArray1 := []byte("\n Line 1 \n\n\n L ine4 \nLine 5 \n \n")
|
||||
expected1 := []string{"", "Line 1", "", "", "L ine4", "Line 5", ""}
|
||||
testFn(byteArray1, expected1)
|
||||
|
||||
byteArray1 = []byte("")
|
||||
expected1 = []string{}
|
||||
testFn(byteArray1, expected1)
|
||||
|
||||
byteArray1 = []byte("\n\n")
|
||||
expected1 = []string{"", ""}
|
||||
testFn(byteArray1, expected1)
|
||||
}
|
26
vendor/k8s.io/kubernetes/pkg/util/iptables/testing/BUILD
generated
vendored
26
vendor/k8s.io/kubernetes/pkg/util/iptables/testing/BUILD
generated
vendored
@ -1,26 +0,0 @@
|
||||
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"],
|
||||
)
|
128
vendor/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go
generated
vendored
128
vendor/k8s.io/kubernetes/pkg/util/iptables/testing/fake.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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 "
|
||||
MatchSet = "--match-set "
|
||||
)
|
||||
|
||||
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, separator string) string {
|
||||
tokens := strings.Split(line, separator)
|
||||
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, MatchSet} {
|
||||
tok := getToken(l, arg)
|
||||
if tok != "" {
|
||||
newRule[arg] = tok
|
||||
}
|
||||
}
|
||||
rules = append(rules, newRule)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var _ = iptables.Interface(&FakeIPTables{})
|
64
vendor/k8s.io/kubernetes/pkg/util/ipvs/BUILD
generated
vendored
64
vendor/k8s.io/kubernetes/pkg/util/ipvs/BUILD
generated
vendored
@ -1,64 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"ipvs_linux_test.go",
|
||||
"ipvs_test.go",
|
||||
"kernelcheck_linux_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"ipvs.go",
|
||||
"ipvs_linux.go",
|
||||
"ipvs_unsupported.go",
|
||||
"kernelcheck_linux.go",
|
||||
"kernelcheck_unsupported.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/ipvs",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/github.com/docker/libnetwork/ipvs:go_default_library",
|
||||
"//vendor/k8s.io/klog: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"],
|
||||
)
|
7
vendor/k8s.io/kubernetes/pkg/util/ipvs/OWNERS
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/util/ipvs/OWNERS
generated
vendored
@ -1,7 +0,0 @@
|
||||
reviewers:
|
||||
- thockin
|
||||
- m1093782566
|
||||
approvers:
|
||||
- thockin
|
||||
- m1093782566
|
||||
|
150
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs.go
generated
vendored
150
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs.go
generated
vendored
@ -1,150 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipvs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// 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
|
||||
// UpdateRealServer updates the specified real server from the specified virtual server.
|
||||
UpdateRealServer(*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
|
||||
// IPVSProxyMode is match set up cluster with ipvs proxy model
|
||||
IPVSProxyMode = "ipvs"
|
||||
)
|
||||
|
||||
// IPVS required kernel modules.
|
||||
const (
|
||||
// ModIPVS is the kernel module "ip_vs"
|
||||
ModIPVS string = "ip_vs"
|
||||
// ModIPVSRR is the kernel module "ip_vs_rr"
|
||||
ModIPVSRR string = "ip_vs_rr"
|
||||
// ModIPVSWRR is the kernel module "ip_vs_wrr"
|
||||
ModIPVSWRR string = "ip_vs_wrr"
|
||||
// ModIPVSSH is the kernel module "ip_vs_sh"
|
||||
ModIPVSSH string = "ip_vs_sh"
|
||||
// ModNfConntrackIPV4 is the module "nf_conntrack_ipv4"
|
||||
ModNfConntrackIPV4 string = "nf_conntrack_ipv4"
|
||||
// ModNfConntrack is the kernel module "nf_conntrack"
|
||||
ModNfConntrack string = "nf_conntrack"
|
||||
)
|
||||
|
||||
// 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
|
||||
ActiveConn int
|
||||
InactiveConn 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
|
||||
}
|
||||
|
||||
// GetKernelVersionAndIPVSMods returns the linux kernel version and the required ipvs modules
|
||||
func GetKernelVersionAndIPVSMods(Executor exec.Interface) (kernelVersion string, ipvsModules []string, err error) {
|
||||
kernelVersionFile := "/proc/sys/kernel/osrelease"
|
||||
out, err := Executor.Command("cut", "-f1", "-d", " ", kernelVersionFile).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error getting os release kernel version: %v(%s)", err, out)
|
||||
}
|
||||
kernelVersion = strings.TrimSpace(string(out))
|
||||
// parse kernel version
|
||||
ver1, err := version.ParseGeneric(kernelVersion)
|
||||
if err != nil {
|
||||
return kernelVersion, nil, fmt.Errorf("error parsing kernel version: %v(%s)", err, kernelVersion)
|
||||
}
|
||||
// "nf_conntrack_ipv4" has been removed since v4.19
|
||||
// see https://github.com/torvalds/linux/commit/a0ae2562c6c4b2721d9fddba63b7286c13517d9f
|
||||
ver2, _ := version.ParseGeneric("4.19")
|
||||
// get required ipvs modules
|
||||
if ver1.LessThan(ver2) {
|
||||
ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrackIPV4)
|
||||
} else {
|
||||
ipvsModules = append(ipvsModules, ModIPVS, ModIPVSRR, ModIPVSWRR, ModIPVSSH, ModNfConntrack)
|
||||
}
|
||||
|
||||
return kernelVersion, ipvsModules, nil
|
||||
}
|
309
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_linux.go
generated
vendored
309
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_linux.go
generated
vendored
@ -1,309 +0,0 @@
|
||||
// +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"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
libipvs "github.com/docker/libnetwork/ipvs"
|
||||
"k8s.io/klog"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// runner implements ipvs.Interface.
|
||||
type runner struct {
|
||||
exec utilexec.Interface
|
||||
ipvsHandle *libipvs.Handle
|
||||
mu sync.Mutex // Protect Netlink calls
|
||||
}
|
||||
|
||||
// Protocol is the IPVS service protocol type
|
||||
type Protocol uint16
|
||||
|
||||
// New returns a new Interface which will call ipvs APIs.
|
||||
func New(exec utilexec.Interface) Interface {
|
||||
handle, err := libipvs.New("")
|
||||
if err != nil {
|
||||
klog.Errorf("IPVS interface can't be initialized, error: %v", err)
|
||||
return nil
|
||||
}
|
||||
return &runner{
|
||||
exec: exec,
|
||||
ipvsHandle: handle,
|
||||
}
|
||||
}
|
||||
|
||||
// AddVirtualServer is part of ipvs.Interface.
|
||||
func (runner *runner) AddVirtualServer(vs *VirtualServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.NewService(svc)
|
||||
}
|
||||
|
||||
// UpdateVirtualServer is part of ipvs.Interface.
|
||||
func (runner *runner) UpdateVirtualServer(vs *VirtualServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.UpdateService(svc)
|
||||
}
|
||||
|
||||
// DeleteVirtualServer is part of ipvs.Interface.
|
||||
func (runner *runner) DeleteVirtualServer(vs *VirtualServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.DelService(svc)
|
||||
}
|
||||
|
||||
// GetVirtualServer is part of ipvs.Interface.
|
||||
func (runner *runner) GetVirtualServer(vs *VirtualServer) (*VirtualServer, error) {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
ipvsSvc, err := runner.ipvsHandle.GetService(svc)
|
||||
runner.mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vServ, err := toVirtualServer(ipvsSvc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vServ, nil
|
||||
}
|
||||
|
||||
// GetVirtualServers is part of ipvs.Interface.
|
||||
func (runner *runner) GetVirtualServers() ([]*VirtualServer, error) {
|
||||
runner.mu.Lock()
|
||||
ipvsSvcs, err := runner.ipvsHandle.GetServices()
|
||||
runner.mu.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vss := make([]*VirtualServer, 0)
|
||||
for _, ipvsSvc := range ipvsSvcs {
|
||||
vs, err := toVirtualServer(ipvsSvc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vss = append(vss, vs)
|
||||
}
|
||||
return vss, nil
|
||||
}
|
||||
|
||||
// Flush is part of ipvs.Interface. Currently we delete IPVS services one by one
|
||||
func (runner *runner) Flush() error {
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.Flush()
|
||||
}
|
||||
|
||||
// AddRealServer is part of ipvs.Interface.
|
||||
func (runner *runner) AddRealServer(vs *VirtualServer, rs *RealServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst, err := toIPVSDestination(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.NewDestination(svc, dst)
|
||||
}
|
||||
|
||||
// DeleteRealServer is part of ipvs.Interface.
|
||||
func (runner *runner) DeleteRealServer(vs *VirtualServer, rs *RealServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst, err := toIPVSDestination(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.DelDestination(svc, dst)
|
||||
}
|
||||
|
||||
func (runner *runner) UpdateRealServer(vs *VirtualServer, rs *RealServer) error {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst, err := toIPVSDestination(rs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
defer runner.mu.Unlock()
|
||||
return runner.ipvsHandle.UpdateDestination(svc, dst)
|
||||
}
|
||||
|
||||
// GetRealServers is part of ipvs.Interface.
|
||||
func (runner *runner) GetRealServers(vs *VirtualServer) ([]*RealServer, error) {
|
||||
svc, err := toIPVSService(vs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runner.mu.Lock()
|
||||
dsts, err := runner.ipvsHandle.GetDestinations(svc)
|
||||
runner.mu.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rss := make([]*RealServer, 0)
|
||||
for _, dst := range dsts {
|
||||
dst, err := toRealServer(dst)
|
||||
// TODO: aggregate errors?
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rss = append(rss, dst)
|
||||
}
|
||||
return rss, nil
|
||||
}
|
||||
|
||||
// toVirtualServer converts an IPVS Service to the equivalent VirtualServer structure.
|
||||
func toVirtualServer(svc *libipvs.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: protocolToString(Protocol(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 to the equivalent RealServer structure.
|
||||
func toRealServer(dst *libipvs.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,
|
||||
ActiveConn: dst.ActiveConnections,
|
||||
InactiveConn: dst.InactiveConnections,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// toIPVSService converts a VirtualServer to the equivalent IPVS Service structure.
|
||||
func toIPVSService(vs *VirtualServer) (*libipvs.Service, error) {
|
||||
if vs == nil {
|
||||
return nil, errors.New("virtual server should not be empty")
|
||||
}
|
||||
ipvsSvc := &libipvs.Service{
|
||||
Address: vs.Address,
|
||||
Protocol: stringToProtocol(vs.Protocol),
|
||||
Port: vs.Port,
|
||||
SchedName: vs.Scheduler,
|
||||
Flags: uint32(vs.Flags),
|
||||
Timeout: vs.Timeout,
|
||||
}
|
||||
|
||||
if ip4 := vs.Address.To4(); ip4 != nil {
|
||||
ipvsSvc.AddressFamily = syscall.AF_INET
|
||||
ipvsSvc.Netmask = 0xffffffff
|
||||
} else {
|
||||
ipvsSvc.AddressFamily = syscall.AF_INET6
|
||||
ipvsSvc.Netmask = 128
|
||||
}
|
||||
return ipvsSvc, nil
|
||||
}
|
||||
|
||||
// toIPVSDestination converts a RealServer to the equivalent IPVS Destination structure.
|
||||
func toIPVSDestination(rs *RealServer) (*libipvs.Destination, error) {
|
||||
if rs == nil {
|
||||
return nil, errors.New("real server should not be empty")
|
||||
}
|
||||
return &libipvs.Destination{
|
||||
Address: rs.Address,
|
||||
Port: rs.Port,
|
||||
Weight: rs.Weight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// stringToProtocolType returns the protocol type for the given name
|
||||
func stringToProtocol(protocol string) uint16 {
|
||||
switch strings.ToLower(protocol) {
|
||||
case "tcp":
|
||||
return uint16(syscall.IPPROTO_TCP)
|
||||
case "udp":
|
||||
return uint16(syscall.IPPROTO_UDP)
|
||||
case "sctp":
|
||||
return uint16(syscall.IPPROTO_SCTP)
|
||||
}
|
||||
return uint16(0)
|
||||
}
|
||||
|
||||
// protocolTypeToString returns the name for the given protocol.
|
||||
func protocolToString(proto Protocol) string {
|
||||
switch proto {
|
||||
case syscall.IPPROTO_TCP:
|
||||
return "TCP"
|
||||
case syscall.IPPROTO_UDP:
|
||||
return "UDP"
|
||||
case syscall.IPPROTO_SCTP:
|
||||
return "SCTP"
|
||||
}
|
||||
return ""
|
||||
}
|
414
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_linux_test.go
generated
vendored
414
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_linux_test.go
generated
vendored
@ -1,414 +0,0 @@
|
||||
// +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"
|
||||
|
||||
libipvs "github.com/docker/libnetwork/ipvs"
|
||||
)
|
||||
|
||||
func Test_toVirtualServer(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsService libipvs.Service
|
||||
virtualServer VirtualServer
|
||||
expectError bool
|
||||
reason string
|
||||
}{
|
||||
{
|
||||
libipvs.Service{
|
||||
Flags: 0x0,
|
||||
},
|
||||
VirtualServer{},
|
||||
true,
|
||||
fmt.Sprintf("IPVS Service Flags should be >= %d, got 0x0", FlagHashed),
|
||||
},
|
||||
{
|
||||
libipvs.Service{
|
||||
Flags: 0x1,
|
||||
},
|
||||
VirtualServer{},
|
||||
true,
|
||||
fmt.Sprintf("IPVS Service Flags should be >= %d, got 0x1", FlagHashed),
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
"",
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
"",
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
"",
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
"",
|
||||
},
|
||||
{
|
||||
libipvs.Service{
|
||||
Protocol: syscall.IPPROTO_SCTP,
|
||||
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: "SCTP",
|
||||
Port: 80,
|
||||
Scheduler: "",
|
||||
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_toIPVSService(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsService libipvs.Service
|
||||
virtualServer VirtualServer
|
||||
}{
|
||||
{
|
||||
libipvs.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,
|
||||
},
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
},
|
||||
},
|
||||
{
|
||||
libipvs.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,
|
||||
},
|
||||
},
|
||||
{
|
||||
libipvs.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 := toIPVSService(&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_toRealServer(t *testing.T) {
|
||||
Tests := []struct {
|
||||
ipvsDestination libipvs.Destination
|
||||
realServer RealServer
|
||||
}{
|
||||
{
|
||||
libipvs.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,
|
||||
},
|
||||
},
|
||||
{
|
||||
libipvs.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: %v", 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_toIPVSDestination(t *testing.T) {
|
||||
Tests := []struct {
|
||||
realServer RealServer
|
||||
ipvsDestination libipvs.Destination
|
||||
}{
|
||||
{
|
||||
RealServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Port: 54321,
|
||||
Weight: 1,
|
||||
},
|
||||
libipvs.Destination{
|
||||
Port: 54321,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
RealServer{
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
Port: 53,
|
||||
Weight: 1,
|
||||
},
|
||||
libipvs.Destination{
|
||||
Port: 53,
|
||||
ConnectionFlags: 0,
|
||||
Weight: 1,
|
||||
Address: net.ParseIP("2002::cafe"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range Tests {
|
||||
got, err := toIPVSDestination(&Tests[i].realServer)
|
||||
if err != nil {
|
||||
t.Errorf("case %d unexpected error: %v", 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_stringToProtocol(t *testing.T) {
|
||||
tests := []string{
|
||||
"TCP", "UDP", "ICMP", "SCTP",
|
||||
}
|
||||
expected := []uint16{
|
||||
uint16(syscall.IPPROTO_TCP), uint16(syscall.IPPROTO_UDP), uint16(0), uint16(syscall.IPPROTO_SCTP),
|
||||
}
|
||||
for i := range tests {
|
||||
got := stringToProtocol(tests[i])
|
||||
if got != expected[i] {
|
||||
t.Errorf("stringToProtocol() failed - got %#v, want %#v",
|
||||
got, expected[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_protocolToString(t *testing.T) {
|
||||
tests := []Protocol{
|
||||
syscall.IPPROTO_TCP, syscall.IPPROTO_UDP, Protocol(0), syscall.IPPROTO_SCTP,
|
||||
}
|
||||
expected := []string{
|
||||
"TCP", "UDP", "", "SCTP",
|
||||
}
|
||||
for i := range tests {
|
||||
got := protocolToString(tests[i])
|
||||
if got != expected[i] {
|
||||
t.Errorf("protocolToString() failed - got %#v, want %#v",
|
||||
got, expected[i])
|
||||
}
|
||||
}
|
||||
}
|
382
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_test.go
generated
vendored
382
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_test.go
generated
vendored
@ -1,382 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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",
|
||||
},
|
||||
{
|
||||
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: "SCTP",
|
||||
Port: 0,
|
||||
Scheduler: "wrr",
|
||||
Flags: 0,
|
||||
Timeout: 0,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Protocol not equal",
|
||||
},
|
||||
{
|
||||
svcA: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "SCTP",
|
||||
Port: 80,
|
||||
Scheduler: "rr",
|
||||
Flags: 0x1,
|
||||
Timeout: 10800,
|
||||
},
|
||||
svcB: &VirtualServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Protocol: "SCTP",
|
||||
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,
|
||||
},
|
||||
rsB: &RealServer{
|
||||
Address: net.ParseIP("10.20.30.41"),
|
||||
Port: 80,
|
||||
},
|
||||
equal: false,
|
||||
reason: "IPv4 address not equal",
|
||||
},
|
||||
{
|
||||
rsA: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 80,
|
||||
},
|
||||
rsB: &RealServer{
|
||||
Address: net.ParseIP("2017::beef"),
|
||||
Port: 80,
|
||||
},
|
||||
equal: false,
|
||||
reason: "IPv6 address not equal",
|
||||
},
|
||||
{
|
||||
rsA: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 80,
|
||||
},
|
||||
rsB: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 8080,
|
||||
},
|
||||
equal: false,
|
||||
reason: "Port not equal",
|
||||
},
|
||||
{
|
||||
rsA: &RealServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Port: 3080,
|
||||
},
|
||||
rsB: &RealServer{
|
||||
Address: net.ParseIP("1.2.3.4"),
|
||||
Port: 3080,
|
||||
},
|
||||
equal: true,
|
||||
reason: "All fields equal",
|
||||
},
|
||||
{
|
||||
rsA: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 3080,
|
||||
},
|
||||
rsB: &RealServer{
|
||||
Address: net.ParseIP("2012::beef"),
|
||||
Port: 3080,
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_unsupported.go
generated
vendored
75
vendor/k8s.io/kubernetes/pkg/util/ipvs/ipvs_unsupported.go
generated
vendored
@ -1,75 +0,0 @@
|
||||
// +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")
|
||||
}
|
||||
|
||||
func (runner *runner) UpdateRealServer(*VirtualServer, *RealServer) error {
|
||||
return fmt.Errorf("IPVS not supported for this platform")
|
||||
}
|
||||
|
||||
var _ = Interface(&runner{})
|
91
vendor/k8s.io/kubernetes/pkg/util/ipvs/kernelcheck_linux.go
generated
vendored
91
vendor/k8s.io/kubernetes/pkg/util/ipvs/kernelcheck_linux.go
generated
vendored
@ -1,91 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// RequiredIPVSKernelModulesAvailableCheck tests IPVS required kernel modules.
|
||||
type RequiredIPVSKernelModulesAvailableCheck struct {
|
||||
Executor utilsexec.Interface
|
||||
}
|
||||
|
||||
// Name returns label for RequiredIPVSKernelModulesAvailableCheck
|
||||
func (r RequiredIPVSKernelModulesAvailableCheck) Name() string {
|
||||
return "RequiredIPVSKernelModulesAvailable"
|
||||
}
|
||||
|
||||
// Check try to validates IPVS required kernel modules exists or not.
|
||||
// The name of function can not be changed.
|
||||
func (r RequiredIPVSKernelModulesAvailableCheck) Check() (warnings, errors []error) {
|
||||
klog.V(1).Infoln("validating the kernel module IPVS required exists in machine or not")
|
||||
|
||||
kernelVersion, ipvsModules, err := GetKernelVersionAndIPVSMods(r.Executor)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
// Find out loaded kernel modules
|
||||
out, err := r.Executor.Command("cut", "-f1", "-d", " ", "/proc/modules").CombinedOutput()
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("error getting installed ipvs required kernel modules: %v(%s)", err, out))
|
||||
return nil, errors
|
||||
}
|
||||
mods := strings.Split(string(out), "\n")
|
||||
|
||||
wantModules := sets.NewString()
|
||||
loadModules := sets.NewString()
|
||||
wantModules.Insert(ipvsModules...)
|
||||
loadModules.Insert(mods...)
|
||||
modules := wantModules.Difference(loadModules).UnsortedList()
|
||||
|
||||
// Check builtin modules exist or not
|
||||
if len(modules) != 0 {
|
||||
builtinModsFilePath := fmt.Sprintf("/lib/modules/%s/modules.builtin", kernelVersion)
|
||||
out, err := r.Executor.Command("cut", "-f1", "-d", " ", builtinModsFilePath).CombinedOutput()
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("error getting required builtin kernel modules: %v(%s)", err, out))
|
||||
return nil, errors
|
||||
}
|
||||
|
||||
builtInModules := sets.NewString()
|
||||
for _, builtInMode := range ipvsModules {
|
||||
match, _ := regexp.Match(builtInMode+".ko", out)
|
||||
if !match {
|
||||
builtInModules.Insert(string(builtInMode))
|
||||
}
|
||||
}
|
||||
if len(builtInModules) != 0 {
|
||||
warnings = append(warnings, fmt.Errorf(
|
||||
"the IPVS proxier will not be used, because the following required kernel modules are not loaded: %v or no builtin kernel ipvs support: %v\n"+
|
||||
"you can solve this problem with following methods:\n 1. Run 'modprobe -- ' to load missing kernel modules;\n"+
|
||||
"2. Provide the missing builtin kernel ipvs support\n", modules, builtInModules))
|
||||
}
|
||||
}
|
||||
|
||||
return warnings, errors
|
||||
}
|
130
vendor/k8s.io/kubernetes/pkg/util/ipvs/kernelcheck_linux_test.go
generated
vendored
130
vendor/k8s.io/kubernetes/pkg/util/ipvs/kernelcheck_linux_test.go
generated
vendored
@ -1,130 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 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 (
|
||||
"testing"
|
||||
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func TestRequiredIPVSKernelModulesAvailableCheck(t *testing.T) {
|
||||
cases := []struct {
|
||||
caseName string
|
||||
|
||||
loadedKernel string
|
||||
kernelVersion string
|
||||
builtinKernel string
|
||||
|
||||
expectErrors bool
|
||||
expectWarnings bool
|
||||
}{
|
||||
{
|
||||
caseName: "no installed kernel modules and no builtin kernel modules",
|
||||
loadedKernel: "",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "",
|
||||
expectErrors: false,
|
||||
expectWarnings: true,
|
||||
},
|
||||
{
|
||||
caseName: "no installed kernel modules and missing builtin kernel modules",
|
||||
loadedKernel: "",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "kernel/net/netfilter/ipvs/ip_vs.ko\n" +
|
||||
"kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko",
|
||||
expectErrors: false,
|
||||
expectWarnings: true,
|
||||
},
|
||||
{
|
||||
caseName: "no installed kernel modules and own all builtin kernel modules",
|
||||
loadedKernel: "",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "kernel/net/netfilter/ipvs/ip_vs.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_rr.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_wrr.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_sh.ko\n" +
|
||||
"kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko",
|
||||
expectErrors: false,
|
||||
expectWarnings: false,
|
||||
},
|
||||
{
|
||||
caseName: "missing installed kernel modules and no builtin kernel modules",
|
||||
loadedKernel: "ip_vs",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "",
|
||||
expectErrors: false,
|
||||
expectWarnings: true,
|
||||
},
|
||||
{
|
||||
caseName: "own all installed kernel modules and no builtin kernel modules",
|
||||
loadedKernel: "ip_vs\n" + "ip_vs_wrr\n" + "nf_conntrack_ipv4\n" +
|
||||
"ip_vs_rr\n" + "ip_vs_sh",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "",
|
||||
expectErrors: false,
|
||||
expectWarnings: false,
|
||||
},
|
||||
{
|
||||
caseName: "own all installed kernel modules and all builtin kernel modules",
|
||||
loadedKernel: "ip_vs\n" + "ip_vs_wrr\n" + "nf_conntrack_ipv4\n" + "ip_vs_rr\n" + "ip_vs_sh",
|
||||
kernelVersion: "3.13.0-24-generic",
|
||||
builtinKernel: "kernel/net/netfilter/ipvs/ip_vs.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_rr.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_wrr.ko\n" +
|
||||
"kernel/net/netfilter/ipvs/ip_vs_sh.ko\n" +
|
||||
"kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko",
|
||||
expectErrors: false,
|
||||
expectWarnings: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte(cases[i].kernelVersion), nil },
|
||||
func() ([]byte, error) { return []byte(cases[i].loadedKernel), nil },
|
||||
func() ([]byte, error) { return []byte(cases[i].builtinKernel), nil },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) utilsexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) utilsexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) utilsexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
|
||||
check := RequiredIPVSKernelModulesAvailableCheck{
|
||||
Executor: &fexec,
|
||||
}
|
||||
warnings, errors := check.Check()
|
||||
|
||||
switch {
|
||||
case warnings != nil && !tc.expectWarnings:
|
||||
t.Errorf("RequiredIPVSKernelModulesAvailableCheck: unexpected warnings for installed kernel modules %v and builtin kernel modules %v. Warnings: %v", tc.loadedKernel, tc.builtinKernel, warnings)
|
||||
case warnings == nil && tc.expectWarnings:
|
||||
t.Errorf("RequiredIPVSKernelModulesAvailableCheck: expected warnings for installed kernel modules %v and builtin kernel modules %v but got nothing", tc.loadedKernel, tc.builtinKernel)
|
||||
case errors != nil && !tc.expectErrors:
|
||||
t.Errorf("RequiredIPVSKernelModulesAvailableCheck: unexpected errors for installed kernel modules %v and builtin kernel modules %v. errors: %v", tc.loadedKernel, tc.builtinKernel, errors)
|
||||
case errors == nil && tc.expectErrors:
|
||||
t.Errorf("RequiredIPVSKernelModulesAvailableCheck: expected errors for installed kernel modules %v and builtin kernel modules %v but got nothing", tc.loadedKernel, tc.builtinKernel)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user