mirror of https://github.com/k3s-io/k3s
wrapper ipset util
parent
338ee7f5d5
commit
45ad69765e
|
@ -27,6 +27,7 @@ filegroup(
|
|||
"//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",
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"ipset.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/ipset",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/k8s.io/utils/exec:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["ipset_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/ipset",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/util/ipset/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// Interface is an injectable interface for running ipset commands. Implementations must be goroutine-safe.
|
||||
type Interface interface {
|
||||
// FlushSet deletes all entries from a named set.
|
||||
FlushSet(set string) error
|
||||
// DestroySet deletes a named set.
|
||||
DestroySet(set string) error
|
||||
// DestroyAllSets deletes all sets.
|
||||
DestroyAllSets() error
|
||||
// CreateSet creates a new set, it will ignore error when the set already exists if ignoreExistErr=true.
|
||||
CreateSet(set *IPSet, ignoreExistErr bool) error
|
||||
// AddEntry adds a new entry to the named set.
|
||||
AddEntry(entry string, set string, ignoreExistErr bool) error
|
||||
// DelEntry deletes one entry from the named set
|
||||
DelEntry(entry string, set string) error
|
||||
// Test test if an entry exists in the named set
|
||||
TestEntry(entry string, set string) (bool, error)
|
||||
// ListEntries lists all the entries from a named set
|
||||
ListEntries(set string) ([]string, error)
|
||||
// ListSets list all set names from kernel
|
||||
ListSets() ([]string, error)
|
||||
// GetVersion returns the "X.Y" version string for ipset.
|
||||
GetVersion() (string, error)
|
||||
}
|
||||
|
||||
// IPSetCmd represents the ipset util. We use ipset command for ipset execute.
|
||||
const IPSetCmd = "ipset"
|
||||
|
||||
// EntryMemberPattern is the regular expression pattern of ipset member list.
|
||||
// The raw output of ipset command `ipset list {set}` is similar to,
|
||||
//Name: foobar
|
||||
//Type: hash:ip,port
|
||||
//Revision: 2
|
||||
//Header: family inet hashsize 1024 maxelem 65536
|
||||
//Size in memory: 16592
|
||||
//References: 0
|
||||
//Members:
|
||||
//192.168.1.2,tcp:8080
|
||||
//192.168.1.1,udp:53
|
||||
var EntryMemberPattern = "(?m)^(.*\n)*Members:\n"
|
||||
|
||||
// VersionPattern is the regular expression pattern of ipset version string.
|
||||
// ipset version output is similar to "v6.10".
|
||||
var VersionPattern = "v[0-9]+\\.[0-9]+"
|
||||
|
||||
// IPSet implements an Interface to an set.
|
||||
type IPSet struct {
|
||||
// Name is the set name.
|
||||
Name string
|
||||
// SetType specifies the ipset type.
|
||||
SetType Type
|
||||
// HashFamily specifies the protocol family of the IP addresses to be stored in the set.
|
||||
// The default is inet, i.e IPv4. If users want to use IPv6, they should specify inet6.
|
||||
HashFamily string
|
||||
// HashSize specifies the hash table size of ipset.
|
||||
HashSize int
|
||||
// MaxElem specifies the max element number of ipset.
|
||||
MaxElem int
|
||||
// PortRange specifies the port range of bitmap:port type ipset.
|
||||
PortRange string
|
||||
}
|
||||
|
||||
// Entry represents a ipset entry.
|
||||
type Entry struct {
|
||||
// IP is the entry's IP. The IP address protocol corresponds to the HashFamily of IPSet.
|
||||
// All entries' IP addresses in the same ip set has same the protocol, IPv4 or IPv6.
|
||||
IP string
|
||||
// Port is the entry's Port.
|
||||
Port int
|
||||
// Protocol is the entry's Protocol. The protocols of entries in the same ip set are all
|
||||
// the same. The accepted protocols are TCP and UDP.
|
||||
Protocol string
|
||||
// Net is the entry's IP network address. Network address with zero prefix size can NOT
|
||||
// be stored.
|
||||
Net string
|
||||
// IP2 is the entry's second IP. IP2 may not be empty for `hash:ip,port,ip` type ip set.
|
||||
IP2 string
|
||||
// SetType specifies the type of ip set where the entry exists.
|
||||
SetType Type
|
||||
}
|
||||
|
||||
func (e *Entry) String() string {
|
||||
switch e.SetType {
|
||||
case HashIPPort:
|
||||
// Entry{192.168.1.1, udp, 53} -> 192.168.1.1,udp:53
|
||||
// Entry{192.168.1.2, tcp, 8080} -> 192.168.1.2,tcp:8080
|
||||
return fmt.Sprintf("%s,%s:%s", e.IP, e.Protocol, strconv.Itoa(e.Port))
|
||||
case HashIPPortIP:
|
||||
// Entry{192.168.1.1, udp, 53, 10.0.0.1} -> 192.168.1.1,udp:53,10.0.0.1
|
||||
// Entry{192.168.1.2, tcp, 8080, 192.168.1.2} -> 192.168.1.2,tcp:8080,192.168.1.2
|
||||
return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.IP2)
|
||||
case HashIPPortNet:
|
||||
// Entry{192.168.1.2, udp, 80, 10.0.1.0/24} -> 192.168.1.2,udp:80,10.0.1.0/24
|
||||
// Entry{192.168.2,25, tcp, 8080, 10.1.0.0/16} -> 192.168.2,25,tcp:8080,10.1.0.0/16
|
||||
return fmt.Sprintf("%s,%s:%s,%s", e.IP, e.Protocol, strconv.Itoa(e.Port), e.Net)
|
||||
case BitmapPort:
|
||||
// Entry{53} -> 53
|
||||
// Entry{8080} -> 8080
|
||||
return strconv.Itoa(e.Port)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type runner struct {
|
||||
exec utilexec.Interface
|
||||
}
|
||||
|
||||
// New returns a new Interface which will exec ipset.
|
||||
func New(exec utilexec.Interface) Interface {
|
||||
return &runner{
|
||||
exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSet creates a new set, it will ignore error when the set already exists if ignoreExistErr=true.
|
||||
func (runner *runner) CreateSet(set *IPSet, ignoreExistErr bool) error {
|
||||
// Using default values.
|
||||
if set.HashSize == 0 {
|
||||
set.HashSize = 1024
|
||||
}
|
||||
if set.MaxElem == 0 {
|
||||
set.MaxElem = 65536
|
||||
}
|
||||
if set.HashFamily == "" {
|
||||
set.HashFamily = ProtocolFamilyIPV4
|
||||
}
|
||||
if len(set.HashFamily) != 0 && set.HashFamily != ProtocolFamilyIPV4 && set.HashFamily != ProtocolFamilyIPV6 {
|
||||
return fmt.Errorf("Currently supported protocol families are: %s and %s, %s is not supported", ProtocolFamilyIPV4, ProtocolFamilyIPV6, set.HashFamily)
|
||||
}
|
||||
// Default ipset type is "hash:ip,port"
|
||||
if len(set.SetType) == 0 {
|
||||
set.SetType = HashIPPort
|
||||
}
|
||||
// Check if setType is supported
|
||||
if !IsValidIPSetType(set.SetType) {
|
||||
return fmt.Errorf("Currently supported ipset types are: %v, %s is not supported", ValidIPSetTypes, set.SetType)
|
||||
}
|
||||
|
||||
return runner.createSet(set, ignoreExistErr)
|
||||
}
|
||||
|
||||
// If ignoreExistErr is set to true, then the -exist option of ipset will be specified, ipset ignores the error
|
||||
// otherwise raised when the same set (setname and create parameters are identical) already exists.
|
||||
func (runner *runner) createSet(set *IPSet, ignoreExistErr bool) error {
|
||||
args := []string{"create", set.Name, string(set.SetType)}
|
||||
if set.SetType == HashIPPortIP || set.SetType == HashIPPort {
|
||||
args = append(args,
|
||||
"family", set.HashFamily,
|
||||
"hashsize", strconv.Itoa(set.HashSize),
|
||||
"maxelem", strconv.Itoa(set.MaxElem),
|
||||
)
|
||||
}
|
||||
if set.SetType == BitmapPort {
|
||||
if len(set.PortRange) == 0 {
|
||||
set.PortRange = DefaultPortRange
|
||||
}
|
||||
if !validatePortRange(set.PortRange) {
|
||||
return fmt.Errorf("invalid port range for %s type ip set: %s, expect: a-b", BitmapPort, set.PortRange)
|
||||
}
|
||||
args = append(args, "range", set.PortRange)
|
||||
}
|
||||
if ignoreExistErr {
|
||||
args = append(args, "-exist")
|
||||
}
|
||||
if _, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error creating ipset %s, error: %v", set.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddEntry adds a new entry to the named set.
|
||||
// If the -exist option is specified, ipset ignores the error otherwise raised when
|
||||
// the same set (setname and create parameters are identical) already exists.
|
||||
func (runner *runner) AddEntry(entry string, set string, ignoreExistErr bool) error {
|
||||
args := []string{"add", set, entry}
|
||||
if ignoreExistErr {
|
||||
args = append(args, "-exist")
|
||||
}
|
||||
if _, err := runner.exec.Command(IPSetCmd, args...).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error adding entry %s, error: %v", entry, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelEntry is used to delete the specified entry from the set.
|
||||
func (runner *runner) DelEntry(entry string, set string) error {
|
||||
if _, err := runner.exec.Command(IPSetCmd, "del", set, entry).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error deleting entry %s: from set: %s, error: %v", entry, set, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestEntry is used to check whether the specified entry is in the set or not.
|
||||
func (runner *runner) TestEntry(entry string, set string) (bool, error) {
|
||||
if out, err := runner.exec.Command(IPSetCmd, "test", set, entry).CombinedOutput(); err == nil {
|
||||
reg, e := regexp.Compile("NOT")
|
||||
if e == nil && reg.MatchString(string(out)) {
|
||||
return false, nil
|
||||
} else if e == nil {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, fmt.Errorf("error testing entry: %s, error: %v", entry, e)
|
||||
}
|
||||
} else {
|
||||
return false, fmt.Errorf("error testing entry %s: %v (%s)", entry, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
// FlushSet deletes all entries from a named set.
|
||||
func (runner *runner) FlushSet(set string) error {
|
||||
if _, err := runner.exec.Command(IPSetCmd, "flush", set).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error flushing set: %s, error: %v", set, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroySet is used to destroy a named set.
|
||||
func (runner *runner) DestroySet(set string) error {
|
||||
if _, err := runner.exec.Command(IPSetCmd, "destroy", set).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error destroying set %s:, error: %v", set, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroyAllSets is used to destroy all sets.
|
||||
func (runner *runner) DestroyAllSets() error {
|
||||
if _, err := runner.exec.Command(IPSetCmd, "destroy").CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("error destroying all sets, error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSets list all set names from kernel
|
||||
func (runner *runner) ListSets() ([]string, error) {
|
||||
out, err := runner.exec.Command(IPSetCmd, "list", "-n").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing all sets, error: %v", err)
|
||||
}
|
||||
return strings.Split(string(out), "\n"), nil
|
||||
}
|
||||
|
||||
// ListEntries lists all the entries from a named set.
|
||||
func (runner *runner) ListEntries(set string) ([]string, error) {
|
||||
if len(set) == 0 {
|
||||
return nil, fmt.Errorf("set name can't be nil")
|
||||
}
|
||||
out, err := runner.exec.Command(IPSetCmd, "list", set).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing set: %s, error: %v", set, err)
|
||||
}
|
||||
memberMatcher := regexp.MustCompile(EntryMemberPattern)
|
||||
list := memberMatcher.ReplaceAllString(string(out[:]), "")
|
||||
strs := strings.Split(list, "\n")
|
||||
results := make([]string, 0)
|
||||
for i := range strs {
|
||||
if len(strs[i]) > 0 {
|
||||
results = append(results, strs[i])
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetVersion returns the version string.
|
||||
func (runner *runner) GetVersion() (string, error) {
|
||||
return getIPSetVersionString(runner.exec)
|
||||
}
|
||||
|
||||
// getIPSetVersionString runs "ipset --version" to get the version string
|
||||
// in the form of "X.Y", i.e "6.19"
|
||||
func getIPSetVersionString(exec utilexec.Interface) (string, error) {
|
||||
cmd := exec.Command(IPSetCmd, "--version")
|
||||
cmd.SetStdin(bytes.NewReader([]byte{}))
|
||||
bytes, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
versionMatcher := regexp.MustCompile(VersionPattern)
|
||||
match := versionMatcher.FindStringSubmatch(string(bytes))
|
||||
if match == nil {
|
||||
return "", fmt.Errorf("no ipset version found in string: %s", bytes)
|
||||
}
|
||||
return match[0], nil
|
||||
}
|
||||
|
||||
func validatePortRange(portRange string) bool {
|
||||
strs := strings.Split(portRange, "-")
|
||||
if len(strs) != 2 {
|
||||
return false
|
||||
}
|
||||
for i := range strs {
|
||||
if _, err := strconv.Atoi(strs[i]); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var _ = Interface(&runner{})
|
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipset
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func TestCheckIPSetVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
vstring string
|
||||
Expect string
|
||||
Err bool
|
||||
}{
|
||||
{"ipset v4.0, protocol version: 4", "v4.0", false},
|
||||
{"ipset v5.1, protocol version: 5", "v5.1", false},
|
||||
{"ipset v6.0, protocol version: 6", "v6.0", false},
|
||||
{"ipset v6.1, protocol version: 6", "v6.1", false},
|
||||
{"ipset v6.19, protocol version: 6", "v6.19", false},
|
||||
{"total junk", "", true},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// ipset version response
|
||||
func() ([]byte, error) { return []byte(testCases[i].vstring), nil },
|
||||
},
|
||||
}
|
||||
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
|
||||
gotVersion, err := getIPSetVersionString(&fexec)
|
||||
if (err != nil) != testCases[i].Err {
|
||||
t.Errorf("Expected error: %v, Got error: %v", testCases[i].Err, err)
|
||||
}
|
||||
if err == nil {
|
||||
if testCases[i].Expect != gotVersion {
|
||||
t.Errorf("Expected result: %v, Got result: %v", testCases[i].Expect, gotVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlushSet(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success.
|
||||
err := runner.FlushSet("FOOBAR")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "flush", "FOOBAR") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Flush again
|
||||
err = runner.FlushSet("FOOBAR")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDestroySet(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Failure
|
||||
func() ([]byte, error) {
|
||||
return []byte("ipset v6.19: The set with the given name does not exist"), &fakeexec.FakeExitError{Status: 1}
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success
|
||||
err := runner.DestroySet("FOOBAR")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy", "FOOBAR") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Failure
|
||||
err = runner.DestroySet("FOOBAR")
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDestroyAllSets(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success
|
||||
err := runner.DestroyAllSets()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "destroy") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Success
|
||||
err = runner.DestroyAllSets()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected failure: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSet(t *testing.T) {
|
||||
testSet := IPSet{
|
||||
Name: "FOOBAR",
|
||||
SetType: HashIPPort,
|
||||
HashFamily: ProtocolFamilyIPV4,
|
||||
}
|
||||
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Failure
|
||||
func() ([]byte, error) {
|
||||
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Create with ignoreExistErr = false, expect success
|
||||
err := runner.CreateSet(&testSet, false)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Create with ignoreExistErr = true, expect success
|
||||
err = runner.CreateSet(&testSet, true)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 2 {
|
||||
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "create", "FOOBAR", "hash:ip,port", "family", "inet", "hashsize", "1024", "maxelem", "65536", "-exist") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
|
||||
}
|
||||
// Create with ignoreExistErr = false, expect failure
|
||||
err = runner.CreateSet(&testSet, false)
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddEntry(t *testing.T) {
|
||||
testEntry := &Entry{
|
||||
IP: "192.168.1.1",
|
||||
Port: 53,
|
||||
Protocol: ProtocolUDP,
|
||||
SetType: HashIPPort,
|
||||
}
|
||||
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Failure
|
||||
func() ([]byte, error) {
|
||||
return []byte("ipset v6.19: Set cannot be created: set with the same name already exists"), &fakeexec.FakeExitError{Status: 1}
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Create with ignoreExistErr = false, expect success
|
||||
err := runner.AddEntry(testEntry.String(), "FOOBAR", false)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
// Create with ignoreExistErr = true, expect success
|
||||
err = runner.AddEntry(testEntry.String(), "FOOBAR", true)
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 2 {
|
||||
t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("ipset", "add", "FOOBAR", "192.168.1.1,udp:53", "-exist") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
|
||||
}
|
||||
// Create with ignoreExistErr = false, expect failure
|
||||
err = runner.AddEntry(testEntry.String(), "FOOBAR", false)
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelEntry(t *testing.T) {
|
||||
// TODO: Test more set type
|
||||
testEntry := &Entry{
|
||||
IP: "192.168.1.1",
|
||||
Port: 53,
|
||||
Protocol: ProtocolUDP,
|
||||
SetType: HashIPPort,
|
||||
}
|
||||
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte{}, nil },
|
||||
// Failure
|
||||
func() ([]byte, error) {
|
||||
return []byte("ipset v6.19: Element cannot be deleted from the set: it's not added"), &fakeexec.FakeExitError{Status: 1}
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
err := runner.DelEntry(testEntry.String(), "FOOBAR")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "del", "FOOBAR", "192.168.1.1,udp:53") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
err = runner.DelEntry(testEntry.String(), "FOOBAR")
|
||||
if err == nil {
|
||||
t.Errorf("expected failure, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestEntry(t *testing.T) {
|
||||
// TODO: IPv6?
|
||||
testEntry := &Entry{
|
||||
IP: "10.120.7.100",
|
||||
Port: 8080,
|
||||
Protocol: ProtocolTCP,
|
||||
SetType: HashIPPort,
|
||||
}
|
||||
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte("10.120.7.100,tcp:8080 is in set FOOBAR."), nil },
|
||||
// Failure
|
||||
func() ([]byte, error) {
|
||||
return []byte("192.168.1.3,tcp:8080 is NOT in set FOOBAR."), &fakeexec.FakeExitError{Status: 1}
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success
|
||||
ok, err := runner.TestEntry(testEntry.String(), "FOOBAR")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "test", "FOOBAR", "10.120.7.100,tcp:8080") {
|
||||
t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("expect entry exists in test set, got not")
|
||||
}
|
||||
// Failure
|
||||
ok, err = runner.TestEntry(testEntry.String(), "FOOBAR")
|
||||
if err == nil || ok {
|
||||
t.Errorf("expect entry doesn't exist in test set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListEntries(t *testing.T) {
|
||||
|
||||
output := `Name: foobar
|
||||
Type: hash:ip,port
|
||||
Revision: 2
|
||||
Header: family inet hashsize 1024 maxelem 65536
|
||||
Size in memory: 16592
|
||||
References: 0
|
||||
Members:
|
||||
192.168.1.2,tcp:8080
|
||||
192.168.1.1,udp:53`
|
||||
|
||||
emptyOutput := `Name: KUBE-NODE-PORT
|
||||
Type: bitmap:port
|
||||
Revision: 1
|
||||
Header: range 0-65535
|
||||
Size in memory: 524432
|
||||
References: 1
|
||||
Members:
|
||||
|
||||
`
|
||||
|
||||
testCases := []struct {
|
||||
output string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
output: output,
|
||||
expected: []string{"192.168.1.2,tcp:8080", "192.168.1.1,udp:53"},
|
||||
},
|
||||
{
|
||||
output: emptyOutput,
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) {
|
||||
return []byte(testCases[i].output), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd {
|
||||
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
|
||||
},
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success
|
||||
entries, err := runner.ListEntries("foobar")
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got: %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "foobar") {
|
||||
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
if len(entries) != len(testCases[i].expected) {
|
||||
t.Errorf("expected %d ipset entries, got: %d", len(testCases[i].expected), len(entries))
|
||||
}
|
||||
if !reflect.DeepEqual(entries, testCases[i].expected) {
|
||||
t.Errorf("expected entries: %v, got: %v", testCases[i].expected, entries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListSets(t *testing.T) {
|
||||
output := `foo
|
||||
bar
|
||||
baz`
|
||||
|
||||
expected := []string{"foo", "bar", "baz"}
|
||||
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
// Success
|
||||
func() ([]byte, error) { return []byte(output), nil },
|
||||
},
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||
},
|
||||
}
|
||||
runner := New(&fexec)
|
||||
// Success
|
||||
list, err := runner.ListSets()
|
||||
if err != nil {
|
||||
t.Errorf("expected success, got: %v", err)
|
||||
}
|
||||
if fcmd.CombinedOutputCalls != 1 {
|
||||
t.Errorf("expected 1 CombinedOutput() calls, got: %d", fcmd.CombinedOutputCalls)
|
||||
}
|
||||
if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("ipset", "list", "-n") {
|
||||
t.Errorf("wrong CombinedOutput() log, got: %s", fcmd.CombinedOutputLog[0])
|
||||
}
|
||||
if len(list) != len(expected) {
|
||||
t.Errorf("expected %d sets, got: %d", len(expected), len(list))
|
||||
}
|
||||
if !reflect.DeepEqual(list, expected) {
|
||||
t.Errorf("expected sets: %v, got: %v", expected, list)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fake.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/ipset/testing",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//pkg/util/ipset:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/util/ipset"
|
||||
)
|
||||
|
||||
// FakeIPSet is a no-op implementation of ipset Interface
|
||||
type FakeIPSet struct {
|
||||
Lines []byte
|
||||
}
|
||||
|
||||
// NewFake create a new fake ipset interface.
|
||||
func NewFake() *FakeIPSet {
|
||||
return &FakeIPSet{}
|
||||
}
|
||||
|
||||
// GetVersion is part of interface.
|
||||
func (*FakeIPSet) GetVersion() (string, error) {
|
||||
return "0.0", nil
|
||||
}
|
||||
|
||||
// FlushSet is part of interface.
|
||||
func (*FakeIPSet) FlushSet(set string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroySet is part of interface.
|
||||
func (*FakeIPSet) DestroySet(set string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroyAllSets is part of interface.
|
||||
func (*FakeIPSet) DestroyAllSets() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSet is part of interface.
|
||||
func (*FakeIPSet) CreateSet(set *ipset.IPSet, ignoreExistErr bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddEntry is part of interface.
|
||||
func (*FakeIPSet) AddEntry(entry string, set string, ignoreExistErr bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelEntry is part of interface.
|
||||
func (*FakeIPSet) DelEntry(entry string, set string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestEntry is part of interface.
|
||||
func (*FakeIPSet) TestEntry(entry string, set string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ListEntries is part of interface.
|
||||
func (*FakeIPSet) ListEntries(set string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ListSets is part of interface.
|
||||
func (*FakeIPSet) ListSets() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var _ = ipset.Interface(&FakeIPSet{})
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipset
|
||||
|
||||
// Type represents the ipset type
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// HashIPPort represents the `hash:ip,port` type ipset. The hash:ip,port is similar to hash:ip but
|
||||
// you can store IP address and protocol-port pairs in it. TCP, SCTP, UDP, UDPLITE, ICMP and ICMPv6 are supported
|
||||
// with port numbers/ICMP(v6) types and other protocol numbers without port information.
|
||||
HashIPPort Type = "hash:ip,port"
|
||||
// HashIPPortIP represents the `hash:ip,port,ip` type ipset. The hash:ip,port,ip set type uses a hash to store
|
||||
// IP address, port number and a second IP address triples. The port number is interpreted together with a
|
||||
// protocol (default TCP) and zero protocol number cannot be used.
|
||||
HashIPPortIP Type = "hash:ip,port,ip"
|
||||
// HashIPPortNet represents the `hash:ip,port,net` type ipset. The hash:ip,port,net set type uses a hash to store IP address, port number and IP network address triples. The port
|
||||
// number is interpreted together with a protocol (default TCP) and zero protocol number cannot be used. Network address
|
||||
// with zero prefix size cannot be stored either.
|
||||
HashIPPortNet Type = "hash:ip,port,net"
|
||||
// BitmapPort represents the `bitmap:port` type ipset. The bitmap:port set type uses a memory range, where each bit
|
||||
// represents one TCP/UDP port. A bitmap:port type of set can store up to 65535 ports.
|
||||
BitmapPort Type = "bitmap:port"
|
||||
)
|
||||
|
||||
// DefaultPortRange defines the default bitmap:port valid port range.
|
||||
const DefaultPortRange string = "0-65535"
|
||||
|
||||
const (
|
||||
// ProtocolFamilyIPV4 represents IPv4 protocol.
|
||||
ProtocolFamilyIPV4 = "inet"
|
||||
// ProtocolFamilyIPV6 represents IPv6 protocol.
|
||||
ProtocolFamilyIPV6 = "inet6"
|
||||
// ProtocolTCP represents TCP protocol.
|
||||
ProtocolTCP = "tcp"
|
||||
// ProtocolUDP represents UDP protocol.
|
||||
ProtocolUDP = "udp"
|
||||
)
|
||||
|
||||
// ValidIPSetTypes defines the supported ip set type.
|
||||
var ValidIPSetTypes = []Type{
|
||||
HashIPPort,
|
||||
HashIPPortIP,
|
||||
BitmapPort,
|
||||
HashIPPortNet,
|
||||
}
|
||||
|
||||
// IsValidIPSetType checks if the given ipset type is valid.
|
||||
func IsValidIPSetType(set Type) bool {
|
||||
for _, valid := range ValidIPSetTypes {
|
||||
if set == valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue