mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
vendor update for CSI 0.3.0
This commit is contained in:
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/BUILD
generated
vendored
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/BUILD
generated
vendored
@ -21,56 +21,67 @@ go_library(
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"docker_image_linux.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_linux.go",
|
||||
"helpers_linux.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"docker_image_unsupported.go",
|
||||
"docker_sandbox_others.go",
|
||||
"docker_stats_unsupported.go",
|
||||
"helpers_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"docker_image_windows.go",
|
||||
"docker_sandbox_windows.go",
|
||||
"docker_stats_windows.go",
|
||||
"helpers_windows.go",
|
||||
],
|
||||
@ -82,26 +93,25 @@ go_library(
|
||||
"//pkg/credentialprovider:go_default_library",
|
||||
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/cm:go_default_library",
|
||||
"//pkg/kubelet/checkpointmanager:go_default_library",
|
||||
"//pkg/kubelet/checkpointmanager/checksum:go_default_library",
|
||||
"//pkg/kubelet/checkpointmanager/errors:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/cm:go_default_library",
|
||||
"//pkg/kubelet/dockershim/libdocker:go_default_library",
|
||||
"//pkg/kubelet/dockershim/metrics:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/cni:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/kubenet:go_default_library",
|
||||
"//pkg/kubelet/kuberuntime:go_default_library",
|
||||
"//pkg/kubelet/leaky:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/cni:go_default_library",
|
||||
"//pkg/kubelet/network/hostport:go_default_library",
|
||||
"//pkg/kubelet/network/kubenet:go_default_library",
|
||||
"//pkg/kubelet/qos:go_default_library",
|
||||
"//pkg/kubelet/server/streaming:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/cache:go_default_library",
|
||||
"//pkg/kubelet/util/ioutils:go_default_library",
|
||||
"//pkg/kubelet/util/store:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
"//pkg/util/hash:go_default_library",
|
||||
"//pkg/util/parsers:go_default_library",
|
||||
"//vendor/github.com/armon/circbuf:go_default_library",
|
||||
"//vendor/github.com/blang/semver:go_default_library",
|
||||
@ -112,7 +122,6 @@ go_library(
|
||||
"//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library",
|
||||
"//vendor/github.com/docker/go-connections/nat:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/net/context:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
@ -150,12 +159,12 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
|
||||
"//pkg/kubelet/checkpointmanager:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/libdocker:go_default_library",
|
||||
"//pkg/kubelet/dockershim/testing:go_default_library",
|
||||
"//pkg/kubelet/network:go_default_library",
|
||||
"//pkg/kubelet/network/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/testing:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/kubelet/util/cache:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
@ -167,9 +176,13 @@ go_test(
|
||||
"//vendor/github.com/golang/mock/gomock:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||
"//vendor/golang.org/x/net/context:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
],
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
@ -186,8 +199,8 @@ filegroup(
|
||||
"//pkg/kubelet/dockershim/cm:all-srcs",
|
||||
"//pkg/kubelet/dockershim/libdocker:all-srcs",
|
||||
"//pkg/kubelet/dockershim/metrics:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network:all-srcs",
|
||||
"//pkg/kubelet/dockershim/remote:all-srcs",
|
||||
"//pkg/kubelet/dockershim/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
|
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/convert.go
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/convert.go
generated
vendored
@ -164,13 +164,14 @@ func containerToRuntimeAPISandbox(c *dockertypes.Container) (*runtimeapi.PodSand
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkpointToRuntimeAPISandbox(id string, checkpoint *PodSandboxCheckpoint) *runtimeapi.PodSandbox {
|
||||
func checkpointToRuntimeAPISandbox(id string, checkpoint DockershimCheckpoint) *runtimeapi.PodSandbox {
|
||||
state := runtimeapi.PodSandboxState_SANDBOX_NOTREADY
|
||||
_, name, namespace, _, _ := checkpoint.GetData()
|
||||
return &runtimeapi.PodSandbox{
|
||||
Id: id,
|
||||
Metadata: &runtimeapi.PodSandboxMetadata{
|
||||
Name: checkpoint.Name,
|
||||
Namespace: checkpoint.Namespace,
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
State: state,
|
||||
}
|
||||
|
108
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_checkpoint.go
generated
vendored
108
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_checkpoint.go
generated
vendored
@ -18,14 +18,9 @@ package dockershim
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/glog"
|
||||
utilstore "k8s.io/kubernetes/pkg/kubelet/util/store"
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
hashutil "k8s.io/kubernetes/pkg/util/hash"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/checksum"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -36,6 +31,11 @@ const (
|
||||
schemaVersion = "v1"
|
||||
)
|
||||
|
||||
type DockershimCheckpoint interface {
|
||||
checkpointmanager.Checkpoint
|
||||
GetData() (string, string, string, []*PortMapping, bool)
|
||||
}
|
||||
|
||||
type Protocol string
|
||||
|
||||
// PortMapping is the port mapping configurations of a sandbox.
|
||||
@ -65,89 +65,31 @@ type PodSandboxCheckpoint struct {
|
||||
// Data to checkpoint for pod sandbox.
|
||||
Data *CheckpointData `json:"data,omitempty"`
|
||||
// Checksum is calculated with fnv hash of the checkpoint object with checksum field set to be zero
|
||||
CheckSum uint64 `json:"checksum"`
|
||||
Checksum checksum.Checksum `json:"checksum"`
|
||||
}
|
||||
|
||||
// CheckpointHandler provides the interface to manage PodSandbox checkpoint
|
||||
type CheckpointHandler interface {
|
||||
// CreateCheckpoint persists sandbox checkpoint in CheckpointStore.
|
||||
CreateCheckpoint(podSandboxID string, checkpoint *PodSandboxCheckpoint) error
|
||||
// GetCheckpoint retrieves sandbox checkpoint from CheckpointStore.
|
||||
GetCheckpoint(podSandboxID string) (*PodSandboxCheckpoint, error)
|
||||
// RemoveCheckpoint removes sandbox checkpoint form CheckpointStore.
|
||||
// WARNING: RemoveCheckpoint will not return error if checkpoint does not exist.
|
||||
RemoveCheckpoint(podSandboxID string) error
|
||||
// ListCheckpoint returns the list of existing checkpoints.
|
||||
ListCheckpoints() ([]string, error)
|
||||
}
|
||||
|
||||
// PersistentCheckpointHandler is an implementation of CheckpointHandler. It persists checkpoint in CheckpointStore
|
||||
type PersistentCheckpointHandler struct {
|
||||
store utilstore.Store
|
||||
}
|
||||
|
||||
func NewPersistentCheckpointHandler(dockershimRootDir string) (CheckpointHandler, error) {
|
||||
fstore, err := utilstore.NewFileStore(filepath.Join(dockershimRootDir, sandboxCheckpointDir), utilfs.DefaultFs{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PersistentCheckpointHandler{store: fstore}, nil
|
||||
}
|
||||
|
||||
func (handler *PersistentCheckpointHandler) CreateCheckpoint(podSandboxID string, checkpoint *PodSandboxCheckpoint) error {
|
||||
checkpoint.CheckSum = calculateChecksum(*checkpoint)
|
||||
blob, err := json.Marshal(checkpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return handler.store.Write(podSandboxID, blob)
|
||||
}
|
||||
|
||||
func (handler *PersistentCheckpointHandler) GetCheckpoint(podSandboxID string) (*PodSandboxCheckpoint, error) {
|
||||
blob, err := handler.store.Read(podSandboxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var checkpoint PodSandboxCheckpoint
|
||||
//TODO: unmarhsal into a struct with just Version, check version, unmarshal into versioned type.
|
||||
err = json.Unmarshal(blob, &checkpoint)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to unmarshal checkpoint %q, removing checkpoint. Checkpoint content: %q. ErrMsg: %v", podSandboxID, string(blob), err)
|
||||
handler.RemoveCheckpoint(podSandboxID)
|
||||
return nil, fmt.Errorf("failed to unmarshal checkpoint")
|
||||
}
|
||||
if checkpoint.CheckSum != calculateChecksum(checkpoint) {
|
||||
glog.Errorf("Checksum of checkpoint %q is not valid, removing checkpoint", podSandboxID)
|
||||
handler.RemoveCheckpoint(podSandboxID)
|
||||
return nil, fmt.Errorf("checkpoint is corrupted")
|
||||
}
|
||||
return &checkpoint, nil
|
||||
}
|
||||
|
||||
func (handler *PersistentCheckpointHandler) RemoveCheckpoint(podSandboxID string) error {
|
||||
return handler.store.Delete(podSandboxID)
|
||||
}
|
||||
|
||||
func (handler *PersistentCheckpointHandler) ListCheckpoints() ([]string, error) {
|
||||
keys, err := handler.store.List()
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("failed to list checkpoint store: %v", err)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func NewPodSandboxCheckpoint(namespace, name string) *PodSandboxCheckpoint {
|
||||
func NewPodSandboxCheckpoint(namespace, name string, data *CheckpointData) DockershimCheckpoint {
|
||||
return &PodSandboxCheckpoint{
|
||||
Version: schemaVersion,
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
Data: &CheckpointData{},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func calculateChecksum(checkpoint PodSandboxCheckpoint) uint64 {
|
||||
checkpoint.CheckSum = 0
|
||||
hash := fnv.New32a()
|
||||
hashutil.DeepHashObject(hash, checkpoint)
|
||||
return uint64(hash.Sum32())
|
||||
func (cp *PodSandboxCheckpoint) MarshalCheckpoint() ([]byte, error) {
|
||||
cp.Checksum = checksum.New(*cp.Data)
|
||||
return json.Marshal(*cp)
|
||||
}
|
||||
|
||||
func (cp *PodSandboxCheckpoint) UnmarshalCheckpoint(blob []byte) error {
|
||||
return json.Unmarshal(blob, cp)
|
||||
}
|
||||
|
||||
func (cp *PodSandboxCheckpoint) VerifyChecksum() error {
|
||||
return cp.Checksum.Verify(*cp.Data)
|
||||
}
|
||||
|
||||
func (cp *PodSandboxCheckpoint) GetData() (string, string, string, []*PortMapping, bool) {
|
||||
return cp.Version, cp.Name, cp.Namespace, cp.Data.PortMappings, cp.Data.HostNetwork
|
||||
}
|
||||
|
85
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_checkpoint_test.go
generated
vendored
85
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_checkpoint_test.go
generated
vendored
@ -17,86 +17,17 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
utilstore "k8s.io/kubernetes/pkg/kubelet/dockershim/testing"
|
||||
)
|
||||
|
||||
func NewTestPersistentCheckpointHandler() CheckpointHandler {
|
||||
return &PersistentCheckpointHandler{store: utilstore.NewMemStore()}
|
||||
}
|
||||
|
||||
func TestPersistentCheckpointHandler(t *testing.T) {
|
||||
var err error
|
||||
handler := NewTestPersistentCheckpointHandler()
|
||||
port80 := int32(80)
|
||||
port443 := int32(443)
|
||||
proto := protocolTCP
|
||||
|
||||
checkpoint1 := NewPodSandboxCheckpoint("ns1", "sandbox1")
|
||||
checkpoint1.Data.PortMappings = []*PortMapping{
|
||||
{
|
||||
&proto,
|
||||
&port80,
|
||||
&port80,
|
||||
},
|
||||
{
|
||||
&proto,
|
||||
&port443,
|
||||
&port443,
|
||||
},
|
||||
}
|
||||
checkpoint1.Data.HostNetwork = true
|
||||
|
||||
checkpoints := []struct {
|
||||
podSandboxID string
|
||||
checkpoint *PodSandboxCheckpoint
|
||||
expectHostNetwork bool
|
||||
}{
|
||||
{
|
||||
"id1",
|
||||
checkpoint1,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"id2",
|
||||
NewPodSandboxCheckpoint("ns2", "sandbox2"),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range checkpoints {
|
||||
// Test CreateCheckpoints
|
||||
err = handler.CreateCheckpoint(tc.podSandboxID, tc.checkpoint)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test GetCheckpoints
|
||||
checkpoint, err := handler.GetCheckpoint(tc.podSandboxID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, *checkpoint, *tc.checkpoint)
|
||||
assert.Equal(t, checkpoint.Data.HostNetwork, tc.expectHostNetwork)
|
||||
}
|
||||
// Test ListCheckpoints
|
||||
keys, err := handler.ListCheckpoints()
|
||||
assert.NoError(t, err)
|
||||
sort.Strings(keys)
|
||||
assert.Equal(t, keys, []string{"id1", "id2"})
|
||||
|
||||
// Test RemoveCheckpoints
|
||||
err = handler.RemoveCheckpoint("id1")
|
||||
assert.NoError(t, err)
|
||||
// Test Remove Nonexisted Checkpoints
|
||||
err = handler.RemoveCheckpoint("id1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test ListCheckpoints
|
||||
keys, err = handler.ListCheckpoints()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, keys, []string{"id2"})
|
||||
|
||||
// Test Get NonExisted Checkpoint
|
||||
_, err = handler.GetCheckpoint("id1")
|
||||
assert.Error(t, err)
|
||||
func TestPodSandboxCheckpoint(t *testing.T) {
|
||||
data := &CheckpointData{HostNetwork: true}
|
||||
checkpoint := NewPodSandboxCheckpoint("ns1", "sandbox1", data)
|
||||
version, name, namespace, _, hostNetwork := checkpoint.GetData()
|
||||
assert.Equal(t, schemaVersion, version)
|
||||
assert.Equal(t, "ns1", namespace)
|
||||
assert.Equal(t, "sandbox1", name)
|
||||
assert.Equal(t, true, hostNetwork)
|
||||
}
|
||||
|
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_container.go
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_container.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -27,7 +28,6 @@ import (
|
||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
||||
dockerstrslice "github.com/docker/docker/api/types/strslice"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
@ -140,7 +140,10 @@ func (ds *dockerService) CreateContainer(_ context.Context, r *runtimeapi.Create
|
||||
}
|
||||
|
||||
hc := createConfig.HostConfig
|
||||
ds.updateCreateConfig(&createConfig, config, sandboxConfig, podSandboxID, securityOptSeparator, apiVersion)
|
||||
err = ds.updateCreateConfig(&createConfig, config, sandboxConfig, podSandboxID, securityOptSeparator, apiVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update container create config: %v", err)
|
||||
}
|
||||
// Set devices for container.
|
||||
devices := make([]dockercontainer.DeviceMapping, len(config.Devices))
|
||||
for i, device := range config.Devices {
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_container_test.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_container_test.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -26,7 +27,6 @@ import (
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
|
5
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image.go
generated
vendored
5
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image.go
generated
vendored
@ -17,14 +17,15 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/golang/glog"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
)
|
||||
@ -51,7 +52,7 @@ func (ds *dockerService) ListImages(_ context.Context, r *runtimeapi.ListImagesR
|
||||
for _, i := range images {
|
||||
apiImage, err := imageToRuntimeAPIImage(&i)
|
||||
if err != nil {
|
||||
// TODO: log an error message?
|
||||
glog.V(5).Infof("Failed to convert docker API image %+v to runtime API image: %v", i, err)
|
||||
continue
|
||||
}
|
||||
result = append(result, apiImage)
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_linux.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_linux.go
generated
vendored
@ -19,10 +19,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_unsupported.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_unsupported.go
generated
vendored
@ -19,10 +19,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_windows.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_image_windows.go
generated
vendored
@ -19,10 +19,10 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/winstats"
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_logs.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_logs.go
generated
vendored
@ -17,10 +17,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
|
85
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go
generated
vendored
85
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@ -26,10 +27,11 @@ import (
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/errors"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
@ -37,7 +39,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSandboxImage = "k8s.gcr.io/pause-amd64:3.1"
|
||||
defaultSandboxImage = "k8s.gcr.io/pause:3.1"
|
||||
|
||||
// Various default sandbox resources requests/limits.
|
||||
defaultSandboxCPUshares int64 = 2
|
||||
@ -118,7 +120,7 @@ func (ds *dockerService) RunPodSandbox(ctx context.Context, r *runtimeapi.RunPod
|
||||
}(&err)
|
||||
|
||||
// Step 3: Create Sandbox Checkpoint.
|
||||
if err = ds.checkpointHandler.CreateCheckpoint(createResp.ID, constructPodSandboxCheckpoint(config)); err != nil {
|
||||
if err = ds.checkpointManager.CreateCheckpoint(createResp.ID, constructPodSandboxCheckpoint(config)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -162,12 +164,24 @@ func (ds *dockerService) RunPodSandbox(ctx context.Context, r *runtimeapi.RunPod
|
||||
cID := kubecontainer.BuildContainerID(runtimeName, createResp.ID)
|
||||
err = ds.network.SetUpPod(config.GetMetadata().Namespace, config.GetMetadata().Name, cID, config.Annotations)
|
||||
if err != nil {
|
||||
// TODO(random-liu): Do we need to teardown network here?
|
||||
if err := ds.client.StopContainer(createResp.ID, defaultSandboxGracePeriod); err != nil {
|
||||
glog.Warningf("Failed to stop sandbox container %q for pod %q: %v", createResp.ID, config.Metadata.Name, err)
|
||||
errList := []error{fmt.Errorf("failed to set up sandbox container %q network for pod %q: %v", createResp.ID, config.Metadata.Name, err)}
|
||||
|
||||
// Ensure network resources are cleaned up even if the plugin
|
||||
// succeeded but an error happened between that success and here.
|
||||
err = ds.network.TearDownPod(config.GetMetadata().Namespace, config.GetMetadata().Name, cID)
|
||||
if err != nil {
|
||||
errList = append(errList, fmt.Errorf("failed to clean up sandbox container %q network for pod %q: %v", createResp.ID, config.Metadata.Name, err))
|
||||
}
|
||||
|
||||
err = ds.client.StopContainer(createResp.ID, defaultSandboxGracePeriod)
|
||||
if err != nil {
|
||||
errList = append(errList, fmt.Errorf("failed to stop sandbox container %q for pod %q: %v", createResp.ID, config.Metadata.Name, err))
|
||||
}
|
||||
|
||||
return resp, utilerrors.NewAggregate(errList)
|
||||
}
|
||||
return resp, err
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
||||
@ -189,12 +203,19 @@ func (ds *dockerService) StopPodSandbox(ctx context.Context, r *runtimeapi.StopP
|
||||
name = metadata.Name
|
||||
hostNetwork = (networkNamespaceMode(inspectResult) == runtimeapi.NamespaceMode_NODE)
|
||||
} else {
|
||||
checkpoint, checkpointErr := ds.checkpointHandler.GetCheckpoint(podSandboxID)
|
||||
checkpoint := NewPodSandboxCheckpoint("", "", &CheckpointData{})
|
||||
checkpointErr := ds.checkpointManager.GetCheckpoint(podSandboxID, checkpoint)
|
||||
|
||||
// Proceed if both sandbox container and checkpoint could not be found. This means that following
|
||||
// actions will only have sandbox ID and not have pod namespace and name information.
|
||||
// Return error if encounter any unexpected error.
|
||||
if checkpointErr != nil {
|
||||
if checkpointErr != errors.ErrCheckpointNotFound {
|
||||
err := ds.checkpointManager.RemoveCheckpoint(podSandboxID)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to delete corrupt checkpoint for sandbox %q: %v", podSandboxID, err)
|
||||
}
|
||||
}
|
||||
if libdocker.IsContainerNotFoundError(statusErr) {
|
||||
glog.Warningf("Both sandbox container and checkpoint for id %q could not be found. "+
|
||||
"Proceed without further sandbox information.", podSandboxID)
|
||||
@ -204,9 +225,7 @@ func (ds *dockerService) StopPodSandbox(ctx context.Context, r *runtimeapi.StopP
|
||||
fmt.Errorf("failed to get sandbox status: %v", statusErr)})
|
||||
}
|
||||
} else {
|
||||
namespace = checkpoint.Namespace
|
||||
name = checkpoint.Name
|
||||
hostNetwork = checkpoint.Data != nil && checkpoint.Data.HostNetwork
|
||||
_, name, namespace, _, hostNetwork = checkpoint.GetData()
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +256,7 @@ func (ds *dockerService) StopPodSandbox(ctx context.Context, r *runtimeapi.StopP
|
||||
errList = append(errList, err)
|
||||
} else {
|
||||
// remove the checkpoint for any sandbox that is not found in the runtime
|
||||
ds.checkpointHandler.RemoveCheckpoint(podSandboxID)
|
||||
ds.checkpointManager.RemoveCheckpoint(podSandboxID)
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +303,7 @@ func (ds *dockerService) RemovePodSandbox(ctx context.Context, r *runtimeapi.Rem
|
||||
}
|
||||
|
||||
// Remove the checkpoint of the sandbox.
|
||||
if err := ds.checkpointHandler.RemoveCheckpoint(podSandboxID); err != nil {
|
||||
if err := ds.checkpointManager.RemoveCheckpoint(podSandboxID); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
@ -465,7 +484,7 @@ func (ds *dockerService) ListPodSandbox(_ context.Context, r *runtimeapi.ListPod
|
||||
var err error
|
||||
checkpoints := []string{}
|
||||
if filter == nil {
|
||||
checkpoints, err = ds.checkpointHandler.ListCheckpoints()
|
||||
checkpoints, err = ds.checkpointManager.ListCheckpoints()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to list checkpoints: %v", err)
|
||||
}
|
||||
@ -501,9 +520,16 @@ func (ds *dockerService) ListPodSandbox(_ context.Context, r *runtimeapi.ListPod
|
||||
if _, ok := sandboxIDs[id]; ok {
|
||||
continue
|
||||
}
|
||||
checkpoint, err := ds.checkpointHandler.GetCheckpoint(id)
|
||||
checkpoint := NewPodSandboxCheckpoint("", "", &CheckpointData{})
|
||||
err := ds.checkpointManager.GetCheckpoint(id, checkpoint)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to retrieve checkpoint for sandbox %q: %v", id, err)
|
||||
if err == errors.ErrCorruptCheckpoint {
|
||||
err = ds.checkpointManager.RemoveCheckpoint(id)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to delete corrupt checkpoint for sandbox %q: %v", id, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
result = append(result, checkpointToRuntimeAPISandbox(id, checkpoint))
|
||||
@ -512,21 +538,6 @@ func (ds *dockerService) ListPodSandbox(_ context.Context, r *runtimeapi.ListPod
|
||||
return &runtimeapi.ListPodSandboxResponse{Items: result}, nil
|
||||
}
|
||||
|
||||
// applySandboxLinuxOptions applies LinuxPodSandboxConfig to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig.
|
||||
func (ds *dockerService) applySandboxLinuxOptions(hc *dockercontainer.HostConfig, lc *runtimeapi.LinuxPodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error {
|
||||
if lc == nil {
|
||||
return nil
|
||||
}
|
||||
// Apply security context.
|
||||
if err := applySandboxSecurityContext(lc, createConfig.Config, hc, ds.network, separator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set sysctls.
|
||||
hc.Sysctls = lc.Sysctls
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *dockerService) applySandboxResources(hc *dockercontainer.HostConfig, lc *runtimeapi.LinuxPodSandboxConfig) error {
|
||||
hc.Resources = dockercontainer.Resources{
|
||||
MemorySwap: DefaultMemorySwap(),
|
||||
@ -567,8 +578,8 @@ func (ds *dockerService) makeSandboxDockerConfig(c *runtimeapi.PodSandboxConfig,
|
||||
HostConfig: hc,
|
||||
}
|
||||
|
||||
// Apply linux-specific options.
|
||||
if err := ds.applySandboxLinuxOptions(hc, c.GetLinux(), createConfig, image, securityOptSeparator); err != nil {
|
||||
// Apply platform-specific options.
|
||||
if err := ds.applySandboxPlatformOptions(hc, c, createConfig, image, securityOptSeparator); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -624,20 +635,20 @@ func ipcNamespaceMode(container *dockertypes.ContainerJSON) runtimeapi.Namespace
|
||||
return runtimeapi.NamespaceMode_POD
|
||||
}
|
||||
|
||||
func constructPodSandboxCheckpoint(config *runtimeapi.PodSandboxConfig) *PodSandboxCheckpoint {
|
||||
checkpoint := NewPodSandboxCheckpoint(config.Metadata.Namespace, config.Metadata.Name)
|
||||
func constructPodSandboxCheckpoint(config *runtimeapi.PodSandboxConfig) checkpointmanager.Checkpoint {
|
||||
data := CheckpointData{}
|
||||
for _, pm := range config.GetPortMappings() {
|
||||
proto := toCheckpointProtocol(pm.Protocol)
|
||||
checkpoint.Data.PortMappings = append(checkpoint.Data.PortMappings, &PortMapping{
|
||||
data.PortMappings = append(data.PortMappings, &PortMapping{
|
||||
HostPort: &pm.HostPort,
|
||||
ContainerPort: &pm.ContainerPort,
|
||||
Protocol: &proto,
|
||||
})
|
||||
}
|
||||
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetNetwork() == runtimeapi.NamespaceMode_NODE {
|
||||
checkpoint.Data.HostNetwork = true
|
||||
data.HostNetwork = true
|
||||
}
|
||||
return checkpoint
|
||||
return NewPodSandboxCheckpoint(config.Metadata.Namespace, config.Metadata.Name, &data)
|
||||
}
|
||||
|
||||
func toCheckpointProtocol(protocol runtimeapi.Protocol) Protocol {
|
||||
|
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_others.go
generated
vendored
Normal file
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_others.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 dockershim
|
||||
|
||||
import (
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// applySandboxPlatformOptions applies platform specific options to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig.
|
||||
func (ds *dockerService) applySandboxPlatformOptions(hc *dockercontainer.HostConfig, config *runtimeapi.PodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error {
|
||||
lc := config.GetLinux()
|
||||
if lc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply security context.
|
||||
if err := applySandboxSecurityContext(lc, createConfig.Config, hc, ds.network, separator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set sysctls.
|
||||
hc.Sysctls = lc.Sysctls
|
||||
return nil
|
||||
}
|
4
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_test.go
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_test.go
generated
vendored
@ -30,7 +30,7 @@ import (
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
@ -277,6 +277,8 @@ func TestSetUpPodFailure(t *testing.T) {
|
||||
cID := kubecontainer.ContainerID{Type: runtimeName, ID: libdocker.GetFakeContainerID(fmt.Sprintf("/%v", makeSandboxName(c)))}
|
||||
mockPlugin.EXPECT().Name().Return("mockNetworkPlugin").AnyTimes()
|
||||
mockPlugin.EXPECT().SetUpPod(ns, name, cID).Return(errors.New("setup pod error")).AnyTimes()
|
||||
// If SetUpPod() fails, we expect TearDownPod() to immediately follow
|
||||
mockPlugin.EXPECT().TearDownPod(ns, name, cID)
|
||||
// Assume network plugin doesn't return error, dockershim should still be able to return not ready correctly.
|
||||
mockPlugin.EXPECT().GetPodNetworkStatus(ns, name, cID).Return(&network.PodNetworkStatus{IP: net.IP("127.0.0.01")}, nil).AnyTimes()
|
||||
|
||||
|
39
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_windows.go
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_sandbox_windows.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
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 dockershim
|
||||
|
||||
import (
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
// applySandboxPlatformOptions applies platform specific options to dockercontainer.HostConfig and dockercontainer.ContainerCreateConfig.
|
||||
func (ds *dockerService) applySandboxPlatformOptions(hc *dockercontainer.HostConfig, config *runtimeapi.PodSandboxConfig, createConfig *dockertypes.ContainerCreateConfig, image string, separator rune) error {
|
||||
dnsConfig := config.GetDnsConfig()
|
||||
if dnsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setup DNS.
|
||||
hc.DNS = dnsConfig.GetServers()
|
||||
hc.DNSSearch = dnsConfig.GetSearches()
|
||||
hc.DNSOptions = dnsConfig.GetOptions()
|
||||
return nil
|
||||
}
|
127
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_service.go
generated
vendored
127
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_service.go
generated
vendored
@ -17,29 +17,31 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecm "k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager/errors"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network/cni"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network/hostport"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network/kubenet"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server/streaming"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/cache"
|
||||
utilstore "k8s.io/kubernetes/pkg/kubelet/util/store"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/metrics"
|
||||
@ -110,23 +112,19 @@ type NetworkPluginSettings struct {
|
||||
NonMasqueradeCIDR string
|
||||
// PluginName is the name of the plugin, runtime shim probes for
|
||||
PluginName string
|
||||
// PluginBinDir is the directory in which the binaries for the plugin with
|
||||
// PluginName is kept. The admin is responsible for provisioning these
|
||||
// binaries before-hand.
|
||||
PluginBinDir string
|
||||
// PluginBinDirsString is a list of directiores delimited by commas, in
|
||||
// which the binaries for the plugin with PluginName may be found.
|
||||
PluginBinDirString string
|
||||
// PluginBinDirs is an array of directories in which the binaries for
|
||||
// the plugin with PluginName may be found. The admin is responsible for
|
||||
// provisioning these binaries before-hand.
|
||||
PluginBinDirs []string
|
||||
// PluginConfDir is the directory in which the admin places a CNI conf.
|
||||
// Depending on the plugin, this may be an optional field, eg: kubenet
|
||||
// generates its own plugin conf.
|
||||
PluginConfDir string
|
||||
// MTU is the desired MTU for network devices created by the plugin.
|
||||
MTU int
|
||||
|
||||
// RuntimeHost is an interface that serves as a trap-door from plugin back
|
||||
// into the kubelet.
|
||||
// TODO: This shouldn't be required, remove once we move host ports into CNI
|
||||
// and figure out bandwidth shaping. See corresponding comments above
|
||||
// network.Host interface.
|
||||
LegacyRuntimeHost network.LegacyHost
|
||||
}
|
||||
|
||||
// namespaceGetter is a wrapper around the dockerService that implements
|
||||
@ -153,7 +151,6 @@ func (p *portMappingGetter) GetPodPortMappings(containerID string) ([]*hostport.
|
||||
// and dockerServices which implements the rest of the network host interfaces.
|
||||
// The legacy host methods are slated for deletion.
|
||||
type dockerNetworkHost struct {
|
||||
network.LegacyHost
|
||||
*namespaceGetter
|
||||
*portMappingGetter
|
||||
}
|
||||
@ -191,12 +188,14 @@ func NewDockerClientFromConfig(config *ClientConfig) libdocker.Interface {
|
||||
|
||||
// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
|
||||
func NewDockerService(config *ClientConfig, podSandboxImage string, streamingConfig *streaming.Config,
|
||||
pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, dockershimRootDir string, disableSharedPID bool) (DockerService, error) {
|
||||
pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, dockershimRootDir string,
|
||||
disableSharedPID, startLocalStreamingServer bool) (DockerService, error) {
|
||||
|
||||
client := NewDockerClientFromConfig(config)
|
||||
|
||||
c := libdocker.NewInstrumentedInterface(client)
|
||||
checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir)
|
||||
|
||||
checkpointManager, err := checkpointmanager.NewCheckpointManager(filepath.Join(dockershimRootDir, sandboxCheckpointDir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -209,10 +208,11 @@ func NewDockerService(config *ClientConfig, podSandboxImage string, streamingCon
|
||||
client: client,
|
||||
execHandler: &NativeExecHandler{},
|
||||
},
|
||||
containerManager: cm.NewContainerManager(cgroupsName, client),
|
||||
checkpointHandler: checkpointHandler,
|
||||
disableSharedPID: disableSharedPID,
|
||||
networkReady: make(map[string]bool),
|
||||
containerManager: cm.NewContainerManager(cgroupsName, client),
|
||||
checkpointManager: checkpointManager,
|
||||
disableSharedPID: disableSharedPID,
|
||||
startLocalStreamingServer: startLocalStreamingServer,
|
||||
networkReady: make(map[string]bool),
|
||||
}
|
||||
|
||||
// check docker version compatibility.
|
||||
@ -228,11 +228,20 @@ func NewDockerService(config *ClientConfig, podSandboxImage string, streamingCon
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the hairpin mode.
|
||||
if err := effectiveHairpinMode(pluginSettings); err != nil {
|
||||
// This is a non-recoverable error. Returning it up the callstack will just
|
||||
// lead to retries of the same failure, so just fail hard.
|
||||
return nil, err
|
||||
}
|
||||
glog.Infof("Hairpin mode set to %q", pluginSettings.HairpinMode)
|
||||
|
||||
// dockershim currently only supports CNI plugins.
|
||||
cniPlugins := cni.ProbeNetworkPlugins(pluginSettings.PluginConfDir, pluginSettings.PluginBinDir)
|
||||
cniPlugins = append(cniPlugins, kubenet.NewPlugin(pluginSettings.PluginBinDir))
|
||||
pluginSettings.PluginBinDirs = cni.SplitDirs(pluginSettings.PluginBinDirString)
|
||||
cniPlugins := cni.ProbeNetworkPlugins(pluginSettings.PluginConfDir, pluginSettings.PluginBinDirs)
|
||||
cniPlugins = append(cniPlugins, kubenet.NewPlugin(pluginSettings.PluginBinDirs))
|
||||
netHost := &dockerNetworkHost{
|
||||
pluginSettings.LegacyRuntimeHost,
|
||||
&namespaceGetter{ds},
|
||||
&portMappingGetter{ds},
|
||||
}
|
||||
@ -289,7 +298,7 @@ type dockerService struct {
|
||||
containerManager cm.ContainerManager
|
||||
// cgroup driver used by Docker runtime.
|
||||
cgroupDriver string
|
||||
checkpointHandler CheckpointHandler
|
||||
checkpointManager checkpointmanager.CheckpointManager
|
||||
// caches the version of the runtime.
|
||||
// To be compatible with multiple docker versions, we need to perform
|
||||
// version checking for some operations. Use this cache to avoid querying
|
||||
@ -300,6 +309,9 @@ type dockerService struct {
|
||||
// See proposals/pod-pid-namespace.md for details.
|
||||
// TODO: Remove once the escape hatch is no longer used (https://issues.k8s.io/41938)
|
||||
disableSharedPID bool
|
||||
// startLocalStreamingServer indicates whether dockershim should start a
|
||||
// streaming server on localhost.
|
||||
startLocalStreamingServer bool
|
||||
}
|
||||
|
||||
// TODO: handle context.
|
||||
@ -361,17 +373,22 @@ func (ds *dockerService) GetNetNS(podSandboxID string) (string, error) {
|
||||
// GetPodPortMappings returns the port mappings of the given podSandbox ID.
|
||||
func (ds *dockerService) GetPodPortMappings(podSandboxID string) ([]*hostport.PortMapping, error) {
|
||||
// TODO: get portmappings from docker labels for backward compatibility
|
||||
checkpoint, err := ds.checkpointHandler.GetCheckpoint(podSandboxID)
|
||||
checkpoint := NewPodSandboxCheckpoint("", "", &CheckpointData{})
|
||||
err := ds.checkpointManager.GetCheckpoint(podSandboxID, checkpoint)
|
||||
// Return empty portMappings if checkpoint is not found
|
||||
if err != nil {
|
||||
if err == utilstore.ErrKeyNotFound {
|
||||
if err == errors.ErrCheckpointNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
errRem := ds.checkpointManager.RemoveCheckpoint(podSandboxID)
|
||||
if errRem != nil {
|
||||
glog.Errorf("Failed to delete corrupt checkpoint for sandbox %q: %v", podSandboxID, errRem)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
portMappings := make([]*hostport.PortMapping, 0, len(checkpoint.Data.PortMappings))
|
||||
for _, pm := range checkpoint.Data.PortMappings {
|
||||
_, _, _, checkpointedPortMappings, _ := checkpoint.GetData()
|
||||
portMappings := make([]*hostport.PortMapping, 0, len(checkpointedPortMappings))
|
||||
for _, pm := range checkpointedPortMappings {
|
||||
proto := toAPIProtocol(*pm.Protocol)
|
||||
portMappings = append(portMappings, &hostport.PortMapping{
|
||||
HostPort: *pm.HostPort,
|
||||
@ -385,11 +402,17 @@ func (ds *dockerService) GetPodPortMappings(podSandboxID string) ([]*hostport.Po
|
||||
// Start initializes and starts components in dockerService.
|
||||
func (ds *dockerService) Start() error {
|
||||
// Initialize the legacy cleanup flag.
|
||||
if ds.startLocalStreamingServer {
|
||||
go func() {
|
||||
if err := ds.streamingServer.Start(true); err != nil {
|
||||
glog.Fatalf("Streaming server stopped unexpectedly: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return ds.containerManager.Start()
|
||||
}
|
||||
|
||||
// Status returns the status of the runtime.
|
||||
// TODO(random-liu): Set network condition accordingly here.
|
||||
func (ds *dockerService) Status(_ context.Context, r *runtimeapi.StatusRequest) (*runtimeapi.StatusResponse, error) {
|
||||
runtimeReady := &runtimeapi.RuntimeCondition{
|
||||
Type: runtimeapi.RuntimeReady,
|
||||
@ -424,17 +447,14 @@ func (ds *dockerService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// GenerateExpectedCgroupParent returns cgroup parent in syntax expected by cgroup driver
|
||||
func (ds *dockerService) GenerateExpectedCgroupParent(cgroupParent string) (string, error) {
|
||||
if len(cgroupParent) > 0 {
|
||||
if cgroupParent != "" {
|
||||
// if docker uses the systemd cgroup driver, it expects *.slice style names for cgroup parent.
|
||||
// if we configured kubelet to use --cgroup-driver=cgroupfs, and docker is configured to use systemd driver
|
||||
// docker will fail to launch the container because the name we provide will not be a valid slice.
|
||||
// this is a very good thing.
|
||||
if ds.cgroupDriver == "systemd" {
|
||||
systemdCgroupParent, err := kubecm.ConvertCgroupFsNameToSystemd(cgroupParent)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cgroupParent = systemdCgroupParent
|
||||
// Pass only the last component of the cgroup path to systemd.
|
||||
cgroupParent = path.Base(cgroupParent)
|
||||
}
|
||||
}
|
||||
glog.V(3).Infof("Setting cgroup parent to: %q", cgroupParent)
|
||||
@ -506,3 +526,28 @@ func toAPIProtocol(protocol Protocol) v1.Protocol {
|
||||
glog.Warningf("Unknown protocol %q: defaulting to TCP", protocol)
|
||||
return v1.ProtocolTCP
|
||||
}
|
||||
|
||||
// effectiveHairpinMode determines the effective hairpin mode given the
|
||||
// configured mode, and whether cbr0 should be configured.
|
||||
func effectiveHairpinMode(s *NetworkPluginSettings) error {
|
||||
// The hairpin mode setting doesn't matter if:
|
||||
// - We're not using a bridge network. This is hard to check because we might
|
||||
// be using a plugin.
|
||||
// - It's set to hairpin-veth for a container runtime that doesn't know how
|
||||
// to set the hairpin flag on the veth's of containers. Currently the
|
||||
// docker runtime is the only one that understands this.
|
||||
// - It's set to "none".
|
||||
if s.HairpinMode == kubeletconfig.PromiscuousBridge || s.HairpinMode == kubeletconfig.HairpinVeth {
|
||||
if s.HairpinMode == kubeletconfig.PromiscuousBridge && s.PluginName != "kubenet" {
|
||||
// This is not a valid combination, since promiscuous-bridge only works on kubenet. Users might be using the
|
||||
// default values (from before the hairpin-mode flag existed) and we
|
||||
// should keep the old behavior.
|
||||
glog.Warningf("Hairpin mode set to %q but kubenet is not enabled, falling back to %q", s.HairpinMode, kubeletconfig.HairpinVeth)
|
||||
s.HairpinMode = kubeletconfig.HairpinVeth
|
||||
return nil
|
||||
}
|
||||
} else if s.HairpinMode != kubeletconfig.HairpinNone {
|
||||
return fmt.Errorf("unknown value: %q", s.HairpinMode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_service_test.go
generated
vendored
42
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_service_test.go
generated
vendored
@ -30,10 +30,11 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
nettest "k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/cache"
|
||||
)
|
||||
|
||||
@ -43,15 +44,50 @@ func newTestNetworkPlugin(t *testing.T) *nettest.MockNetworkPlugin {
|
||||
return nettest.NewMockNetworkPlugin(ctrl)
|
||||
}
|
||||
|
||||
type mockCheckpointManager struct {
|
||||
checkpoint map[string]*PodSandboxCheckpoint
|
||||
}
|
||||
|
||||
func (ckm *mockCheckpointManager) CreateCheckpoint(checkpointKey string, checkpoint checkpointmanager.Checkpoint) error {
|
||||
ckm.checkpoint[checkpointKey] = checkpoint.(*PodSandboxCheckpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckm *mockCheckpointManager) GetCheckpoint(checkpointKey string, checkpoint checkpointmanager.Checkpoint) error {
|
||||
*(checkpoint.(*PodSandboxCheckpoint)) = *(ckm.checkpoint[checkpointKey])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckm *mockCheckpointManager) RemoveCheckpoint(checkpointKey string) error {
|
||||
_, ok := ckm.checkpoint[checkpointKey]
|
||||
if ok {
|
||||
delete(ckm.checkpoint, "moo")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ckm *mockCheckpointManager) ListCheckpoints() ([]string, error) {
|
||||
var keys []string
|
||||
for key := range ckm.checkpoint {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func newMockCheckpointManager() checkpointmanager.CheckpointManager {
|
||||
return &mockCheckpointManager{checkpoint: make(map[string]*PodSandboxCheckpoint)}
|
||||
}
|
||||
|
||||
func newTestDockerService() (*dockerService, *libdocker.FakeDockerClient, *clock.FakeClock) {
|
||||
fakeClock := clock.NewFakeClock(time.Time{})
|
||||
c := libdocker.NewFakeDockerClient().WithClock(fakeClock).WithVersion("1.11.2", "1.23").WithRandSource(rand.NewSource(0))
|
||||
pm := network.NewPluginManager(&network.NoopNetworkPlugin{})
|
||||
ckm := newMockCheckpointManager()
|
||||
return &dockerService{
|
||||
client: c,
|
||||
os: &containertest.FakeOS{},
|
||||
network: pm,
|
||||
checkpointHandler: NewTestPersistentCheckpointHandler(),
|
||||
checkpointManager: ckm,
|
||||
networkReady: make(map[string]bool),
|
||||
}, c, fakeClock
|
||||
}
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_linux.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_linux.go
generated
vendored
@ -19,9 +19,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_unsupported.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_unsupported.go
generated
vendored
@ -19,10 +19,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
|
9
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_windows.go
generated
vendored
9
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_stats_windows.go
generated
vendored
@ -19,10 +19,9 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
@ -65,6 +64,11 @@ func (ds *dockerService) ListContainerStats(ctx context.Context, r *runtimeapi.L
|
||||
}
|
||||
|
||||
func (ds *dockerService) getContainerStats(containerID string) (*runtimeapi.ContainerStats, error) {
|
||||
info, err := ds.client.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statsJSON, err := ds.client.GetContainerStats(containerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -102,6 +106,7 @@ func (ds *dockerService) getContainerStats(containerID string) (*runtimeapi.Cont
|
||||
},
|
||||
WritableLayer: &runtimeapi.FilesystemUsage{
|
||||
Timestamp: timestamp,
|
||||
FsId: &runtimeapi.FilesystemIdentifier{Mountpoint: info.DockerRootDir},
|
||||
UsedBytes: &runtimeapi.UInt64Value{Value: uint64(*containerJSON.SizeRw)},
|
||||
},
|
||||
}
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_streaming.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/docker_streaming.go
generated
vendored
@ -18,6 +18,7 @@ package dockershim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@ -27,7 +28,6 @@ import (
|
||||
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
|
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_linux.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_linux.go
generated
vendored
@ -30,6 +30,7 @@ import (
|
||||
"github.com/blang/semver"
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
"k8s.io/api/core/v1"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
)
|
||||
|
||||
@ -53,7 +54,7 @@ func getSeccompDockerOpts(seccompProfile string) ([]dockerOpt, error) {
|
||||
return defaultSeccompOpt, nil
|
||||
}
|
||||
|
||||
if seccompProfile == "docker/default" {
|
||||
if seccompProfile == v1.SeccompProfileRuntimeDefault || seccompProfile == v1.DeprecatedSeccompProfileDockerDefault {
|
||||
// return nil so docker will load the default seccomp profile
|
||||
return nil, nil
|
||||
}
|
||||
|
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_linux_test.go
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_linux_test.go
generated
vendored
@ -27,6 +27,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestGetSeccompSecurityOpts(t *testing.T) {
|
||||
@ -44,7 +45,11 @@ func TestGetSeccompSecurityOpts(t *testing.T) {
|
||||
expectedOpts: []string{"seccomp=unconfined"},
|
||||
}, {
|
||||
msg: "Seccomp default",
|
||||
seccompProfile: "docker/default",
|
||||
seccompProfile: v1.SeccompProfileRuntimeDefault,
|
||||
expectedOpts: nil,
|
||||
}, {
|
||||
msg: "Seccomp deprecated default",
|
||||
seccompProfile: v1.DeprecatedSeccompProfileDockerDefault,
|
||||
expectedOpts: nil,
|
||||
}}
|
||||
|
||||
|
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_test.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_test.go
generated
vendored
@ -342,5 +342,5 @@ func TestGenerateMountBindings(t *testing.T) {
|
||||
}
|
||||
result := generateMountBindings(mounts)
|
||||
|
||||
assert.Equal(t, result, expectedResult)
|
||||
assert.Equal(t, expectedResult, result)
|
||||
}
|
||||
|
56
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_windows.go
generated
vendored
56
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/helpers_windows.go
generated
vendored
@ -76,6 +76,9 @@ func (ds *dockerService) updateCreateConfig(
|
||||
CPUPercent: rOpts.CpuMaximum,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply security context.
|
||||
applyWindowsContainerSecurityContext(wc.GetSecurityContext(), createConfig.Config, createConfig.HostConfig)
|
||||
}
|
||||
|
||||
applyExperimentalCreateConfig(createConfig, sandboxConfig.Annotations)
|
||||
@ -83,6 +86,17 @@ func (ds *dockerService) updateCreateConfig(
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyWindowsContainerSecurityContext updates docker container options according to security context.
|
||||
func applyWindowsContainerSecurityContext(wsc *runtimeapi.WindowsContainerSecurityContext, config *dockercontainer.Config, hc *dockercontainer.HostConfig) {
|
||||
if wsc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if wsc.GetRunAsUsername() != "" {
|
||||
config.User = wsc.GetRunAsUsername()
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *dockerService) determinePodIPBySandboxID(sandboxID string) string {
|
||||
opts := dockertypes.ContainerListOptions{
|
||||
All: true,
|
||||
@ -115,6 +129,17 @@ func (ds *dockerService) determinePodIPBySandboxID(sandboxID string) string {
|
||||
// Todo: Add a kernel version check for more validation
|
||||
|
||||
if networkMode := os.Getenv("CONTAINER_NETWORK"); networkMode == "" {
|
||||
// On Windows, every container that is created in a Sandbox, needs to invoke CNI plugin again for adding the Network,
|
||||
// with the shared container name as NetNS info,
|
||||
// This is passed down to the platform to replicate some necessary information to the new container
|
||||
|
||||
//
|
||||
// This place is chosen as a hack for now, since ds.getIP would end up calling CNI's addToNetwork
|
||||
// That is why addToNetwork is required to be idempotent
|
||||
|
||||
// Instead of relying on this call, an explicit call to addToNetwork should be
|
||||
// done immediately after ContainerCreation, in case of Windows only. TBD Issue # to handle this
|
||||
|
||||
if r.HostConfig.Isolation == kubeletapis.HypervIsolationValue {
|
||||
// Hyper-V only supports one container per Pod yet and the container will have a different
|
||||
// IP address from sandbox. Return the first non-sandbox container IP as POD IP.
|
||||
@ -123,22 +148,16 @@ func (ds *dockerService) determinePodIPBySandboxID(sandboxID string) string {
|
||||
return containerIP
|
||||
}
|
||||
} else {
|
||||
// Do not return any IP, so that we would continue and get the IP of the Sandbox
|
||||
// Do not return any IP, so that we would continue and get the IP of the Sandbox.
|
||||
// Windows 1709 and 1803 doesn't have the Namespace support, so getIP() is called
|
||||
// to replicate the DNS registry key to the Workload container (IP/Gateway/MAC is
|
||||
// set separately than DNS).
|
||||
// TODO(feiskyer): remove this workaround after Namespace is supported in Windows RS5.
|
||||
ds.getIP(sandboxID, r)
|
||||
}
|
||||
} else {
|
||||
// On Windows, every container that is created in a Sandbox, needs to invoke CNI plugin again for adding the Network,
|
||||
// with the shared container name as NetNS info,
|
||||
// This is passed down to the platform to replicate some necessary information to the new container
|
||||
|
||||
//
|
||||
// This place is chosen as a hack for now, since getContainerIP would end up calling CNI's addToNetwork
|
||||
// That is why addToNetwork is required to be idempotent
|
||||
|
||||
// Instead of relying on this call, an explicit call to addToNetwork should be
|
||||
// done immediately after ContainerCreation, in case of Windows only. TBD Issue # to handle this
|
||||
|
||||
if containerIP := getContainerIP(r); containerIP != "" {
|
||||
// ds.getIP will call the CNI plugin to fetch the IP
|
||||
if containerIP := ds.getIP(c.ID, r); containerIP != "" {
|
||||
return containerIP
|
||||
}
|
||||
}
|
||||
@ -153,14 +172,3 @@ func getNetworkNamespace(c *dockertypes.ContainerJSON) (string, error) {
|
||||
// so returning the docker networkMode (which holds container:<ref containerid> for network namespace here
|
||||
return string(c.HostConfig.NetworkMode), nil
|
||||
}
|
||||
|
||||
func getContainerIP(container *dockertypes.ContainerJSON) string {
|
||||
if container.NetworkSettings != nil {
|
||||
for _, network := range container.NetworkSettings.Networks {
|
||||
if network.IPAddress != "" {
|
||||
return network.IPAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
1
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker/BUILD
generated
vendored
1
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker/BUILD
generated
vendored
@ -40,7 +40,6 @@ go_library(
|
||||
"//vendor/github.com/docker/docker/pkg/stdcopy:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/opencontainers/go-digest:go_default_library",
|
||||
"//vendor/golang.org/x/net/context:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
],
|
||||
|
4
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker/kube_docker_client.go
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker/kube_docker_client.go
generated
vendored
@ -18,6 +18,7 @@ package libdocker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -35,7 +36,6 @@ import (
|
||||
dockerapi "github.com/docker/docker/client"
|
||||
dockermessage "github.com/docker/docker/pkg/jsonmessage"
|
||||
dockerstdcopy "github.com/docker/docker/pkg/stdcopy"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// kubeDockerClient is a wrapped layer of docker client for kubelet internal use. This layer is added to:
|
||||
@ -337,7 +337,7 @@ func (p *progressReporter) start() {
|
||||
case <-ticker.C:
|
||||
progress, timestamp := p.progress.get()
|
||||
// If there is no progress for p.imagePullProgressDeadline, cancel the operation.
|
||||
if time.Now().Sub(timestamp) > p.imagePullProgressDeadline {
|
||||
if time.Since(timestamp) > p.imagePullProgressDeadline {
|
||||
glog.Errorf("Cancel pulling image %q because of no progress for %v, latest progress: %q", p.image, p.imagePullProgressDeadline, progress)
|
||||
p.cancel()
|
||||
return
|
||||
|
46
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"network.go",
|
||||
"plugins.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/metrics:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//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",
|
||||
"//pkg/kubelet/dockershim/network/cni:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network/hairpin:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network/hostport:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network/kubenet:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network/metrics:all-srcs",
|
||||
"//pkg/kubelet/dockershim/network/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
9
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/OWNERS
generated
vendored
Normal file
9
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/OWNERS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
approvers:
|
||||
- thockin
|
||||
- dchen1107
|
||||
- matchstick
|
||||
- freehan
|
||||
- dcbw
|
||||
reviewers:
|
||||
- sig-network-reviewers
|
||||
|
110
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/BUILD
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/BUILD
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cni.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"cni_others.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"cni_windows.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni",
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/libcni:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types/020:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"cni_test.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
embed = [":go_default_library"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/cni/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/testing:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types/020:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/mock:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/testing: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/kubelet/dockershim/network/cni/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
325
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni.go
generated
vendored
Normal file
325
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni.go
generated
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
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 cni
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
cnitypes "github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
CNIPluginName = "cni"
|
||||
DefaultConfDir = "/etc/cni/net.d"
|
||||
DefaultBinDir = "/opt/cni/bin"
|
||||
)
|
||||
|
||||
type cniNetworkPlugin struct {
|
||||
network.NoopNetworkPlugin
|
||||
|
||||
loNetwork *cniNetwork
|
||||
|
||||
sync.RWMutex
|
||||
defaultNetwork *cniNetwork
|
||||
|
||||
host network.Host
|
||||
execer utilexec.Interface
|
||||
nsenterPath string
|
||||
confDir string
|
||||
binDirs []string
|
||||
}
|
||||
|
||||
type cniNetwork struct {
|
||||
name string
|
||||
NetworkConfig *libcni.NetworkConfigList
|
||||
CNIConfig libcni.CNI
|
||||
}
|
||||
|
||||
// cniPortMapping maps to the standard CNI portmapping Capability
|
||||
// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md
|
||||
type cniPortMapping struct {
|
||||
HostPort int32 `json:"hostPort"`
|
||||
ContainerPort int32 `json:"containerPort"`
|
||||
Protocol string `json:"protocol"`
|
||||
HostIP string `json:"hostIP"`
|
||||
}
|
||||
|
||||
func SplitDirs(dirs string) []string {
|
||||
// Use comma rather than colon to work better with Windows too
|
||||
return strings.Split(dirs, ",")
|
||||
}
|
||||
|
||||
func ProbeNetworkPlugins(confDir string, binDirs []string) []network.NetworkPlugin {
|
||||
old := binDirs
|
||||
binDirs = make([]string, 0, len(binDirs))
|
||||
for _, dir := range old {
|
||||
if dir != "" {
|
||||
binDirs = append(binDirs, dir)
|
||||
}
|
||||
}
|
||||
if len(binDirs) == 0 {
|
||||
binDirs = []string{DefaultBinDir}
|
||||
}
|
||||
|
||||
if confDir == "" {
|
||||
confDir = DefaultConfDir
|
||||
}
|
||||
|
||||
plugin := &cniNetworkPlugin{
|
||||
defaultNetwork: nil,
|
||||
loNetwork: getLoNetwork(binDirs),
|
||||
execer: utilexec.New(),
|
||||
confDir: confDir,
|
||||
binDirs: binDirs,
|
||||
}
|
||||
|
||||
// sync NetworkConfig in best effort during probing.
|
||||
plugin.syncNetworkConfig()
|
||||
return []network.NetworkPlugin{plugin}
|
||||
}
|
||||
|
||||
func getDefaultCNINetwork(confDir string, binDirs []string) (*cniNetwork, error) {
|
||||
files, err := libcni.ConfFiles(confDir, []string{".conf", ".conflist", ".json"})
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case len(files) == 0:
|
||||
return nil, fmt.Errorf("No networks found in %s", confDir)
|
||||
}
|
||||
|
||||
sort.Strings(files)
|
||||
for _, confFile := range files {
|
||||
var confList *libcni.NetworkConfigList
|
||||
if strings.HasSuffix(confFile, ".conflist") {
|
||||
confList, err = libcni.ConfListFromFile(confFile)
|
||||
if err != nil {
|
||||
glog.Warningf("Error loading CNI config list file %s: %v", confFile, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
conf, err := libcni.ConfFromFile(confFile)
|
||||
if err != nil {
|
||||
glog.Warningf("Error loading CNI config file %s: %v", confFile, err)
|
||||
continue
|
||||
}
|
||||
// Ensure the config has a "type" so we know what plugin to run.
|
||||
// Also catches the case where somebody put a conflist into a conf file.
|
||||
if conf.Network.Type == "" {
|
||||
glog.Warningf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile)
|
||||
continue
|
||||
}
|
||||
|
||||
confList, err = libcni.ConfListFromConf(conf)
|
||||
if err != nil {
|
||||
glog.Warningf("Error converting CNI config file %s to list: %v", confFile, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(confList.Plugins) == 0 {
|
||||
glog.Warningf("CNI config list %s has no networks, skipping", confFile)
|
||||
continue
|
||||
}
|
||||
|
||||
network := &cniNetwork{
|
||||
name: confList.Name,
|
||||
NetworkConfig: confList,
|
||||
CNIConfig: &libcni.CNIConfig{Path: binDirs},
|
||||
}
|
||||
return network, nil
|
||||
}
|
||||
return nil, fmt.Errorf("No valid networks found in %s", confDir)
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) Init(host network.Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
err := plugin.platformInit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plugin.host = host
|
||||
|
||||
plugin.syncNetworkConfig()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) syncNetworkConfig() {
|
||||
network, err := getDefaultCNINetwork(plugin.confDir, plugin.binDirs)
|
||||
if err != nil {
|
||||
glog.Warningf("Unable to update cni config: %s", err)
|
||||
return
|
||||
}
|
||||
plugin.setDefaultNetwork(network)
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork {
|
||||
plugin.RLock()
|
||||
defer plugin.RUnlock()
|
||||
return plugin.defaultNetwork
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) setDefaultNetwork(n *cniNetwork) {
|
||||
plugin.Lock()
|
||||
defer plugin.Unlock()
|
||||
plugin.defaultNetwork = n
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) checkInitialized() error {
|
||||
if plugin.getDefaultNetwork() == nil {
|
||||
return errors.New("cni config uninitialized")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) Name() string {
|
||||
return CNIPluginName
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) Status() error {
|
||||
// sync network config from confDir periodically to detect network config updates
|
||||
plugin.syncNetworkConfig()
|
||||
|
||||
// Can't set up pods if we don't have any CNI network configs yet
|
||||
return plugin.checkInitialized()
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
if err := plugin.checkInitialized(); err != nil {
|
||||
return err
|
||||
}
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CNI failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
|
||||
// Windows doesn't have loNetwork. It comes only with Linux
|
||||
if plugin.loNetwork != nil {
|
||||
if _, err = plugin.addToNetwork(plugin.loNetwork, name, namespace, id, netnsPath); err != nil {
|
||||
glog.Errorf("Error while adding to cni lo network: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = plugin.addToNetwork(plugin.getDefaultNetwork(), name, namespace, id, netnsPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Error while adding to cni network: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) TearDownPod(namespace string, name string, id kubecontainer.ContainerID) error {
|
||||
if err := plugin.checkInitialized(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lack of namespace should not be fatal on teardown
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
glog.Warningf("CNI failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
|
||||
return plugin.deleteFromNetwork(plugin.getDefaultNetwork(), name, namespace, id, netnsPath)
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) addToNetwork(network *cniNetwork, podName string, podNamespace string, podSandboxID kubecontainer.ContainerID, podNetnsPath string) (cnitypes.Result, error) {
|
||||
rt, err := plugin.buildCNIRuntimeConf(podName, podNamespace, podSandboxID, podNetnsPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Error adding network when building cni runtime conf: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netConf, cniNet := network.NetworkConfig, network.CNIConfig
|
||||
glog.V(4).Infof("About to add CNI network %v (type=%v)", netConf.Name, netConf.Plugins[0].Network.Type)
|
||||
res, err := cniNet.AddNetworkList(netConf, rt)
|
||||
if err != nil {
|
||||
glog.Errorf("Error adding network: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) deleteFromNetwork(network *cniNetwork, podName string, podNamespace string, podSandboxID kubecontainer.ContainerID, podNetnsPath string) error {
|
||||
rt, err := plugin.buildCNIRuntimeConf(podName, podNamespace, podSandboxID, podNetnsPath)
|
||||
if err != nil {
|
||||
glog.Errorf("Error deleting network when building cni runtime conf: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
netConf, cniNet := network.NetworkConfig, network.CNIConfig
|
||||
glog.V(4).Infof("About to del CNI network %v (type=%v)", netConf.Name, netConf.Plugins[0].Network.Type)
|
||||
err = cniNet.DelNetworkList(netConf, rt)
|
||||
// The pod may not get deleted successfully at the first time.
|
||||
// Ignore "no such file or directory" error in case the network has already been deleted in previous attempts.
|
||||
if err != nil && !strings.Contains(err.Error(), "no such file or directory") {
|
||||
glog.Errorf("Error deleting network: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) buildCNIRuntimeConf(podName string, podNs string, podSandboxID kubecontainer.ContainerID, podNetnsPath string) (*libcni.RuntimeConf, error) {
|
||||
glog.V(4).Infof("Got netns path %v", podNetnsPath)
|
||||
glog.V(4).Infof("Using podns path %v", podNs)
|
||||
|
||||
rt := &libcni.RuntimeConf{
|
||||
ContainerID: podSandboxID.ID,
|
||||
NetNS: podNetnsPath,
|
||||
IfName: network.DefaultInterfaceName,
|
||||
Args: [][2]string{
|
||||
{"IgnoreUnknown", "1"},
|
||||
{"K8S_POD_NAMESPACE", podNs},
|
||||
{"K8S_POD_NAME", podName},
|
||||
{"K8S_POD_INFRA_CONTAINER_ID", podSandboxID.ID},
|
||||
},
|
||||
}
|
||||
|
||||
// port mappings are a cni capability-based args, rather than parameters
|
||||
// to a specific plugin
|
||||
portMappings, err := plugin.host.GetPodPortMappings(podSandboxID.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not retrieve port mappings: %v", err)
|
||||
}
|
||||
portMappingsParam := make([]cniPortMapping, 0, len(portMappings))
|
||||
for _, p := range portMappings {
|
||||
if p.HostPort <= 0 {
|
||||
continue
|
||||
}
|
||||
portMappingsParam = append(portMappingsParam, cniPortMapping{
|
||||
HostPort: p.HostPort,
|
||||
ContainerPort: p.ContainerPort,
|
||||
Protocol: strings.ToLower(string(p.Protocol)),
|
||||
HostIP: p.HostIP,
|
||||
})
|
||||
}
|
||||
rt.CapabilityArgs = map[string]interface{}{
|
||||
"portMappings": portMappingsParam,
|
||||
}
|
||||
|
||||
return rt, nil
|
||||
}
|
77
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_others.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_others.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 cni
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
)
|
||||
|
||||
func getLoNetwork(binDirs []string) *cniNetwork {
|
||||
loConfig, err := libcni.ConfListFromBytes([]byte(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "cni-loopback",
|
||||
"plugins":[{
|
||||
"type": "loopback"
|
||||
}]
|
||||
}`))
|
||||
if err != nil {
|
||||
// The hardcoded config above should always be valid and unit tests will
|
||||
// catch this
|
||||
panic(err)
|
||||
}
|
||||
loNetwork := &cniNetwork{
|
||||
name: "lo",
|
||||
NetworkConfig: loConfig,
|
||||
CNIConfig: &libcni.CNIConfig{Path: binDirs},
|
||||
}
|
||||
|
||||
return loNetwork
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) platformInit() error {
|
||||
var err error
|
||||
plugin.nsenterPath, err = plugin.execer.LookPath("nsenter")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Use the addToNetwork function to obtain the IP of the Pod. That will assume idempotent ADD call to the plugin.
|
||||
// Also fix the runtime's call to Status function to be done only in the case that the IP is lost, no need to do periodic calls
|
||||
func (plugin *cniNetworkPlugin) GetPodNetworkStatus(namespace string, name string, id kubecontainer.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CNI failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
if netnsPath == "" {
|
||||
return nil, fmt.Errorf("Cannot find the network namespace, skipping pod network status for container %q", id)
|
||||
}
|
||||
|
||||
ip, err := network.GetPodIP(plugin.execer, plugin.nsenterPath, netnsPath, network.DefaultInterfaceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &network.PodNetworkStatus{IP: ip}, nil
|
||||
}
|
301
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_test.go
generated
vendored
Normal file
301
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_test.go
generated
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
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 cni
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
types020 "github.com/containernetworking/cni/pkg/types/020"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/api/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
networktest "k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
// Returns .in file path, .out file path, and .env file path
|
||||
func installPluginUnderTest(t *testing.T, testBinDir, testConfDir, testDataDir, binName string, confName string) (string, string, string) {
|
||||
for _, dir := range []string{testBinDir, testConfDir, testDataDir} {
|
||||
err := os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test plugin dir %s: %v", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
confFile := path.Join(testConfDir, confName+".conf")
|
||||
f, err := os.Create(confFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to install plugin %s: %v", confFile, err)
|
||||
}
|
||||
networkConfig := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, confName, binName)
|
||||
_, err = f.WriteString(networkConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write network config file (%v)", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
pluginExec := path.Join(testBinDir, binName)
|
||||
f, err = os.Create(pluginExec)
|
||||
|
||||
const execScriptTempl = `#!/usr/bin/env bash
|
||||
cat > {{.InputFile}}
|
||||
env > {{.OutputEnv}}
|
||||
echo "%@" >> {{.OutputEnv}}
|
||||
export $(echo ${CNI_ARGS} | sed 's/;/ /g') &> /dev/null
|
||||
mkdir -p {{.OutputDir}} &> /dev/null
|
||||
echo -n "$CNI_COMMAND $CNI_NETNS $K8S_POD_NAMESPACE $K8S_POD_NAME $K8S_POD_INFRA_CONTAINER_ID" >& {{.OutputFile}}
|
||||
echo -n "{ \"ip4\": { \"ip\": \"10.1.0.23/24\" } }"
|
||||
`
|
||||
inputFile := path.Join(testDataDir, binName+".in")
|
||||
outputFile := path.Join(testDataDir, binName+".out")
|
||||
envFile := path.Join(testDataDir, binName+".env")
|
||||
execTemplateData := &map[string]interface{}{
|
||||
"InputFile": inputFile,
|
||||
"OutputFile": outputFile,
|
||||
"OutputEnv": envFile,
|
||||
"OutputDir": testDataDir,
|
||||
}
|
||||
|
||||
tObj := template.Must(template.New("test").Parse(execScriptTempl))
|
||||
buf := &bytes.Buffer{}
|
||||
if err := tObj.Execute(buf, *execTemplateData); err != nil {
|
||||
t.Fatalf("Error in executing script template - %v", err)
|
||||
}
|
||||
execScript := buf.String()
|
||||
_, err = f.WriteString(execScript)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write plugin exec - %v", err)
|
||||
}
|
||||
|
||||
err = f.Chmod(0777)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to set exec perms on plugin")
|
||||
}
|
||||
|
||||
f.Close()
|
||||
|
||||
return inputFile, outputFile, envFile
|
||||
}
|
||||
|
||||
func tearDownPlugin(tmpDir string) {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in cleaning up test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeNetworkHost struct {
|
||||
networktest.FakePortMappingGetter
|
||||
kubeClient clientset.Interface
|
||||
runtime kubecontainer.Runtime
|
||||
}
|
||||
|
||||
func NewFakeHost(kubeClient clientset.Interface, pods []*containertest.FakePod, ports map[string][]*hostport.PortMapping) *fakeNetworkHost {
|
||||
host := &fakeNetworkHost{
|
||||
networktest.FakePortMappingGetter{PortMaps: ports},
|
||||
kubeClient,
|
||||
&containertest.FakeRuntime{
|
||||
AllPodList: pods,
|
||||
},
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetPodByName(name, namespace string) (*v1.Pod, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetKubeClient() clientset.Interface {
|
||||
return fnh.kubeClient
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetRuntime() kubecontainer.Runtime {
|
||||
return fnh.runtime
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetNetNS(containerID string) (string, error) {
|
||||
return fnh.GetRuntime().GetNetNS(kubecontainer.ContainerID{Type: "test", ID: containerID})
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) SupportsLegacyFeatures() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestCNIPlugin(t *testing.T) {
|
||||
// install some random plugin
|
||||
netName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
binName := fmt.Sprintf("test_vendor%d", rand.Intn(1000))
|
||||
|
||||
podIP := "10.0.0.2"
|
||||
podIPOutput := fmt.Sprintf("4: eth0 inet %s/24 scope global dynamic eth0\\ valid_lft forever preferred_lft forever", podIP)
|
||||
fakeCmds := []fakeexec.FakeCommandAction{
|
||||
func(cmd string, args ...string) exec.Cmd {
|
||||
return fakeexec.InitFakeCmd(&fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) {
|
||||
return []byte(podIPOutput), nil
|
||||
},
|
||||
},
|
||||
}, cmd, args...)
|
||||
},
|
||||
}
|
||||
|
||||
fexec := &fakeexec.FakeExec{
|
||||
CommandScript: fakeCmds,
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
|
||||
mockLoCNI := &mock_cni.MockCNI{}
|
||||
// TODO mock for the test plugin too
|
||||
|
||||
tmpDir := utiltesting.MkTmpdirOrDie("cni-test")
|
||||
testConfDir := path.Join(tmpDir, "etc", "cni", "net.d")
|
||||
testBinDir := path.Join(tmpDir, "opt", "cni", "bin")
|
||||
testDataDir := path.Join(tmpDir, "output")
|
||||
defer tearDownPlugin(tmpDir)
|
||||
inputFile, outputFile, outputEnv := installPluginUnderTest(t, testBinDir, testConfDir, testDataDir, binName, netName)
|
||||
|
||||
containerID := kubecontainer.ContainerID{Type: "test", ID: "test_infra_container"}
|
||||
pods := []*containertest.FakePod{{
|
||||
Pod: &kubecontainer.Pod{
|
||||
Containers: []*kubecontainer.Container{
|
||||
{ID: containerID},
|
||||
},
|
||||
},
|
||||
NetnsPath: "/proc/12345/ns/net",
|
||||
}}
|
||||
|
||||
plugins := ProbeNetworkPlugins(testConfDir, []string{testBinDir})
|
||||
if len(plugins) != 1 {
|
||||
t.Fatalf("Expected only one network plugin, got %d", len(plugins))
|
||||
}
|
||||
if plugins[0].Name() != "cni" {
|
||||
t.Fatalf("Expected CNI network plugin, got %q", plugins[0].Name())
|
||||
}
|
||||
|
||||
cniPlugin, ok := plugins[0].(*cniNetworkPlugin)
|
||||
if !ok {
|
||||
t.Fatalf("Not a CNI network plugin!")
|
||||
}
|
||||
cniPlugin.execer = fexec
|
||||
cniPlugin.loNetwork.CNIConfig = mockLoCNI
|
||||
|
||||
mockLoCNI.On("AddNetworkList", cniPlugin.loNetwork.NetworkConfig, mock.AnythingOfType("*libcni.RuntimeConf")).Return(&types020.Result{IP4: &types020.IPConfig{IP: net.IPNet{IP: []byte{127, 0, 0, 1}}}}, nil)
|
||||
|
||||
ports := map[string][]*hostport.PortMapping{
|
||||
containerID.ID: {
|
||||
{
|
||||
Name: "name",
|
||||
HostPort: 8008,
|
||||
ContainerPort: 80,
|
||||
Protocol: "UDP",
|
||||
HostIP: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeHost := NewFakeHost(nil, pods, ports)
|
||||
|
||||
plug, err := network.InitNetworkPlugin(plugins, "cni", fakeHost, kubeletconfig.HairpinNone, "10.0.0.0/8", network.UseDefaultMTU)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to select the desired plugin: %v", err)
|
||||
}
|
||||
|
||||
// Set up the pod
|
||||
err = plug.SetUpPod("podNamespace", "podName", containerID, map[string]string{})
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil: %v", err)
|
||||
}
|
||||
eo, eerr := ioutil.ReadFile(outputEnv)
|
||||
output, err := ioutil.ReadFile(outputFile)
|
||||
if err != nil || eerr != nil {
|
||||
t.Errorf("Failed to read output file %s: %v (env %s err %v)", outputFile, err, eo, eerr)
|
||||
}
|
||||
|
||||
expectedOutput := "ADD /proc/12345/ns/net podNamespace podName test_infra_container"
|
||||
if string(output) != expectedOutput {
|
||||
t.Errorf("Mismatch in expected output for setup hook. Expected '%s', got '%s'", expectedOutput, string(output))
|
||||
}
|
||||
|
||||
// Verify the correct network configuration was passed
|
||||
inputConfig := struct {
|
||||
RuntimeConfig struct {
|
||||
PortMappings []map[string]interface{} `json:"portMappings"`
|
||||
} `json:"runtimeConfig"`
|
||||
}{}
|
||||
inputBytes, inerr := ioutil.ReadFile(inputFile)
|
||||
parseerr := json.Unmarshal(inputBytes, &inputConfig)
|
||||
if inerr != nil || parseerr != nil {
|
||||
t.Errorf("failed to parse reported cni input config %s: (%v %v)", inputFile, inerr, parseerr)
|
||||
}
|
||||
expectedMappings := []map[string]interface{}{
|
||||
// hah, golang always unmarshals unstructured json numbers as float64
|
||||
{"hostPort": 8008.0, "containerPort": 80.0, "protocol": "udp", "hostIP": "0.0.0.0"},
|
||||
}
|
||||
if !reflect.DeepEqual(inputConfig.RuntimeConfig.PortMappings, expectedMappings) {
|
||||
t.Errorf("mismatch in expected port mappings. expected %v got %v", expectedMappings, inputConfig.RuntimeConfig.PortMappings)
|
||||
}
|
||||
|
||||
// Get its IP address
|
||||
status, err := plug.GetPodNetworkStatus("podNamespace", "podName", containerID)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read pod network status: %v", err)
|
||||
}
|
||||
if status.IP.String() != podIP {
|
||||
t.Errorf("Expected pod IP %q but got %q", podIP, status.IP.String())
|
||||
}
|
||||
|
||||
// Tear it down
|
||||
err = plug.TearDownPod("podNamespace", "podName", containerID)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil: %v", err)
|
||||
}
|
||||
output, err = ioutil.ReadFile(outputFile)
|
||||
expectedOutput = "DEL /proc/12345/ns/net podNamespace podName test_infra_container"
|
||||
if string(output) != expectedOutput {
|
||||
t.Errorf("Mismatch in expected output for setup hook. Expected '%s', got '%s'", expectedOutput, string(output))
|
||||
}
|
||||
|
||||
mockLoCNI.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestLoNetNonNil(t *testing.T) {
|
||||
if conf := getLoNetwork(nil); conf == nil {
|
||||
t.Error("Expected non-nil lo network")
|
||||
}
|
||||
}
|
61
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_windows.go
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/cni_windows.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
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 cni
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cniTypes020 "github.com/containernetworking/cni/pkg/types/020"
|
||||
"github.com/golang/glog"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
)
|
||||
|
||||
func getLoNetwork(binDirs []string) *cniNetwork {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *cniNetworkPlugin) platformInit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPodNetworkStatus : Assuming addToNetwork is idempotent, we can call this API as many times as required to get the IPAddress
|
||||
func (plugin *cniNetworkPlugin) GetPodNetworkStatus(namespace string, name string, id kubecontainer.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CNI failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
|
||||
result, err := plugin.addToNetwork(plugin.getDefaultNetwork(), name, namespace, id, netnsPath)
|
||||
|
||||
glog.V(5).Infof("GetPodNetworkStatus result %+v", result)
|
||||
if err != nil {
|
||||
glog.Errorf("error while adding to cni network: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the result and get the IPAddress
|
||||
var result020 *cniTypes020.Result
|
||||
result020, err = cniTypes020.GetResult(result)
|
||||
if err != nil {
|
||||
glog.Errorf("error while cni parsing result: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return &network.PodNetworkStatus{IP: result020.IP4.IP.IP}, nil
|
||||
}
|
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing/BUILD
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing/BUILD
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["mock_cni.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing",
|
||||
deps = [
|
||||
"//vendor/github.com/containernetworking/cni/libcni:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/mock:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing/mock_cni.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing/mock_cni.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// mock_cni is a mock of the `libcni.CNI` interface. It's a handwritten mock
|
||||
// because there are only two functions to deal with.
|
||||
package mock_cni
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockCNI struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockCNI) AddNetwork(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) (types.Result, error) {
|
||||
args := m.Called(net, rt)
|
||||
return args.Get(0).(types.Result), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockCNI) DelNetwork(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) error {
|
||||
args := m.Called(net, rt)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockCNI) DelNetworkList(net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) error {
|
||||
args := m.Called(net, rt)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockCNI) AddNetworkList(net *libcni.NetworkConfigList, rt *libcni.RuntimeConf) (types.Result, error) {
|
||||
args := m.Called(net, rt)
|
||||
return args.Get(0).(types.Result), args.Error(1)
|
||||
}
|
40
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/BUILD
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["hairpin.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin",
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["hairpin_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"],
|
||||
)
|
87
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/hairpin.go
generated
vendored
Normal file
87
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/hairpin.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 hairpin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
sysfsNetPath = "/sys/devices/virtual/net"
|
||||
brportRelativePath = "brport"
|
||||
hairpinModeRelativePath = "hairpin_mode"
|
||||
hairpinEnable = "1"
|
||||
)
|
||||
|
||||
var (
|
||||
ethtoolOutputRegex = regexp.MustCompile("peer_ifindex: (\\d+)")
|
||||
)
|
||||
|
||||
func findPairInterfaceOfContainerInterface(e exec.Interface, containerInterfaceName, containerDesc string, nsenterArgs []string) (string, error) {
|
||||
nsenterPath, err := e.LookPath("nsenter")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ethtoolPath, err := e.LookPath("ethtool")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nsenterArgs = append(nsenterArgs, "-F", "--", ethtoolPath, "--statistics", containerInterfaceName)
|
||||
output, err := e.Command(nsenterPath, nsenterArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to query interface %s of container %s: %v: %s", containerInterfaceName, containerDesc, err, string(output))
|
||||
}
|
||||
// look for peer_ifindex
|
||||
match := ethtoolOutputRegex.FindSubmatch(output)
|
||||
if match == nil {
|
||||
return "", fmt.Errorf("No peer_ifindex in interface statistics for %s of container %s", containerInterfaceName, containerDesc)
|
||||
}
|
||||
peerIfIndex, err := strconv.Atoi(string(match[1]))
|
||||
if err != nil { // seems impossible (\d+ not numeric)
|
||||
return "", fmt.Errorf("peer_ifindex wasn't numeric: %s: %v", match[1], err)
|
||||
}
|
||||
iface, err := net.InterfaceByIndex(peerIfIndex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return iface.Name, nil
|
||||
}
|
||||
|
||||
func setUpInterface(ifName string) error {
|
||||
glog.V(3).Infof("Enabling hairpin on interface %s", ifName)
|
||||
ifPath := path.Join(sysfsNetPath, ifName)
|
||||
if _, err := os.Stat(ifPath); err != nil {
|
||||
return err
|
||||
}
|
||||
brportPath := path.Join(ifPath, brportRelativePath)
|
||||
if _, err := os.Stat(brportPath); err != nil && os.IsNotExist(err) {
|
||||
// Device is not on a bridge, so doesn't need hairpin mode
|
||||
return nil
|
||||
}
|
||||
hairpinModeFile := path.Join(brportPath, hairpinModeRelativePath)
|
||||
return ioutil.WriteFile(hairpinModeFile, []byte(hairpinEnable), 0644)
|
||||
}
|
109
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/hairpin_test.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hairpin/hairpin_test.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 hairpin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func TestFindPairInterfaceOfContainerInterface(t *testing.T) {
|
||||
// there should be at least "lo" on any system
|
||||
interfaces, _ := net.Interfaces()
|
||||
validOutput := fmt.Sprintf("garbage\n peer_ifindex: %d", interfaces[0].Index)
|
||||
invalidOutput := fmt.Sprintf("garbage\n unknown: %d", interfaces[0].Index)
|
||||
|
||||
tests := []struct {
|
||||
output string
|
||||
err error
|
||||
expectedName string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
output: validOutput,
|
||||
expectedName: interfaces[0].Name,
|
||||
},
|
||||
{
|
||||
output: invalidOutput,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
output: validOutput,
|
||||
err: errors.New("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...)
|
||||
},
|
||||
},
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
nsenterArgs := []string{"-t", "123", "-n"}
|
||||
name, err := findPairInterfaceOfContainerInterface(&fexec, "eth0", "123", nsenterArgs)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
if name != test.expectedName {
|
||||
t.Errorf("unexpected name: %s (expected: %s)", name, test.expectedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUpInterfaceNonExistent(t *testing.T) {
|
||||
err := setUpInterface("non-existent")
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
deviceDir := fmt.Sprintf("%s/%s", sysfsNetPath, "non-existent")
|
||||
if !strings.Contains(fmt.Sprintf("%v", err), deviceDir) {
|
||||
t.Errorf("should have tried to open %s", deviceDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetUpInterfaceNotBridged(t *testing.T) {
|
||||
err := setUpInterface("lo")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Skipf("'lo' device does not exist??? (%v)", err)
|
||||
}
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
62
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/BUILD
generated
vendored
Normal file
62
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/BUILD
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"fake_iptables.go",
|
||||
"hostport.go",
|
||||
"hostport_manager.go",
|
||||
"hostport_syncer.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport",
|
||||
deps = [
|
||||
"//pkg/proxy/iptables:go_default_library",
|
||||
"//pkg/util/conntrack:go_default_library",
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//pkg/util/net:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"fake_iptables_test.go",
|
||||
"hostport_manager_test.go",
|
||||
"hostport_syncer_test.go",
|
||||
"hostport_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//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",
|
||||
"//pkg/kubelet/dockershim/network/hostport/testing:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
353
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/fake_iptables.go
generated
vendored
Normal file
353
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/fake_iptables.go
generated
vendored
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
type fakeChain struct {
|
||||
name utiliptables.Chain
|
||||
rules []string
|
||||
}
|
||||
|
||||
type fakeTable struct {
|
||||
name utiliptables.Table
|
||||
chains map[string]*fakeChain
|
||||
}
|
||||
|
||||
type fakeIPTables struct {
|
||||
tables map[string]*fakeTable
|
||||
builtinChains map[string]sets.String
|
||||
}
|
||||
|
||||
func NewFakeIPTables() *fakeIPTables {
|
||||
return &fakeIPTables{
|
||||
tables: make(map[string]*fakeTable, 0),
|
||||
builtinChains: map[string]sets.String{
|
||||
string(utiliptables.TableFilter): sets.NewString("INPUT", "FORWARD", "OUTPUT"),
|
||||
string(utiliptables.TableNAT): sets.NewString("PREROUTING", "INPUT", "OUTPUT", "POSTROUTING"),
|
||||
string(utiliptables.TableMangle): sets.NewString("PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) GetVersion() (string, error) {
|
||||
return "1.4.21", nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) getTable(tableName utiliptables.Table) (*fakeTable, error) {
|
||||
table, ok := f.tables[string(tableName)]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Table %s does not exist", tableName)
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) getChain(tableName utiliptables.Table, chainName utiliptables.Chain) (*fakeTable, *fakeChain, error) {
|
||||
table, err := f.getTable(tableName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
chain, ok := table.chains[string(chainName)]
|
||||
if !ok {
|
||||
return table, nil, fmt.Errorf("Chain %s/%s does not exist", tableName, chainName)
|
||||
}
|
||||
|
||||
return table, chain, nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) ensureChain(tableName utiliptables.Table, chainName utiliptables.Chain) (bool, *fakeChain) {
|
||||
table, chain, err := f.getChain(tableName, chainName)
|
||||
if err != nil {
|
||||
// either table or table+chain don't exist yet
|
||||
if table == nil {
|
||||
table = &fakeTable{
|
||||
name: tableName,
|
||||
chains: make(map[string]*fakeChain),
|
||||
}
|
||||
f.tables[string(tableName)] = table
|
||||
}
|
||||
chain := &fakeChain{
|
||||
name: chainName,
|
||||
rules: make([]string, 0),
|
||||
}
|
||||
table.chains[string(chainName)] = chain
|
||||
return false, chain
|
||||
}
|
||||
return true, chain
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) EnsureChain(tableName utiliptables.Table, chainName utiliptables.Chain) (bool, error) {
|
||||
existed, _ := f.ensureChain(tableName, chainName)
|
||||
return existed, nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) FlushChain(tableName utiliptables.Table, chainName utiliptables.Chain) error {
|
||||
_, chain, err := f.getChain(tableName, chainName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chain.rules = make([]string, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) DeleteChain(tableName utiliptables.Table, chainName utiliptables.Chain) error {
|
||||
table, _, err := f.getChain(tableName, chainName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(table.chains, string(chainName))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns index of rule in array; < 0 if rule is not found
|
||||
func findRule(chain *fakeChain, rule string) int {
|
||||
for i, candidate := range chain.rules {
|
||||
if rule == candidate {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) ensureRule(position utiliptables.RulePosition, tableName utiliptables.Table, chainName utiliptables.Chain, rule string) (bool, error) {
|
||||
_, chain, err := f.getChain(tableName, chainName)
|
||||
if err != nil {
|
||||
_, chain = f.ensureChain(tableName, chainName)
|
||||
}
|
||||
|
||||
rule, err = normalizeRule(rule)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ruleIdx := findRule(chain, rule)
|
||||
if ruleIdx >= 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if position == utiliptables.Prepend {
|
||||
chain.rules = append([]string{rule}, chain.rules...)
|
||||
} else if position == utiliptables.Append {
|
||||
chain.rules = append(chain.rules, rule)
|
||||
} else {
|
||||
return false, fmt.Errorf("Unknown position argument %q", position)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func normalizeRule(rule string) (string, error) {
|
||||
normalized := ""
|
||||
remaining := strings.TrimSpace(rule)
|
||||
for {
|
||||
var end int
|
||||
|
||||
if strings.HasPrefix(remaining, "--to-destination=") {
|
||||
remaining = strings.Replace(remaining, "=", " ", 1)
|
||||
}
|
||||
|
||||
if remaining[0] == '"' {
|
||||
end = strings.Index(remaining[1:], "\"")
|
||||
if end < 0 {
|
||||
return "", fmt.Errorf("Invalid rule syntax: mismatched quotes")
|
||||
}
|
||||
end += 2
|
||||
} else {
|
||||
end = strings.Index(remaining, " ")
|
||||
if end < 0 {
|
||||
end = len(remaining)
|
||||
}
|
||||
}
|
||||
arg := remaining[:end]
|
||||
|
||||
// Normalize un-prefixed IP addresses like iptables does
|
||||
if net.ParseIP(arg) != nil {
|
||||
arg = arg + "/32"
|
||||
}
|
||||
|
||||
if len(normalized) > 0 {
|
||||
normalized += " "
|
||||
}
|
||||
normalized += strings.TrimSpace(arg)
|
||||
if len(remaining) == end {
|
||||
break
|
||||
}
|
||||
remaining = remaining[end+1:]
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) EnsureRule(position utiliptables.RulePosition, tableName utiliptables.Table, chainName utiliptables.Chain, args ...string) (bool, error) {
|
||||
ruleArgs := make([]string, 0)
|
||||
for _, arg := range args {
|
||||
// quote args with internal spaces (like comments)
|
||||
if strings.Index(arg, " ") >= 0 {
|
||||
arg = fmt.Sprintf("\"%s\"", arg)
|
||||
}
|
||||
ruleArgs = append(ruleArgs, arg)
|
||||
}
|
||||
return f.ensureRule(position, tableName, chainName, strings.Join(ruleArgs, " "))
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) DeleteRule(tableName utiliptables.Table, chainName utiliptables.Chain, args ...string) error {
|
||||
_, chain, err := f.getChain(tableName, chainName)
|
||||
if err == nil {
|
||||
rule := strings.Join(args, " ")
|
||||
ruleIdx := findRule(chain, rule)
|
||||
if ruleIdx < 0 {
|
||||
return nil
|
||||
}
|
||||
chain.rules = append(chain.rules[:ruleIdx], chain.rules[ruleIdx+1:]...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) IsIpv6() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func saveChain(chain *fakeChain, data *bytes.Buffer) {
|
||||
for _, rule := range chain.rules {
|
||||
data.WriteString(fmt.Sprintf("-A %s %s\n", chain.name, rule))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) SaveInto(tableName utiliptables.Table, buffer *bytes.Buffer) error {
|
||||
table, err := f.getTable(tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buffer.WriteString(fmt.Sprintf("*%s\n", table.name))
|
||||
|
||||
rules := bytes.NewBuffer(nil)
|
||||
for _, chain := range table.chains {
|
||||
buffer.WriteString(fmt.Sprintf(":%s - [0:0]\n", string(chain.name)))
|
||||
saveChain(chain, rules)
|
||||
}
|
||||
buffer.Write(rules.Bytes())
|
||||
buffer.WriteString("COMMIT\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) restore(restoreTableName utiliptables.Table, data []byte, flush utiliptables.FlushFlag) error {
|
||||
allLines := string(data)
|
||||
buf := bytes.NewBuffer(data)
|
||||
var tableName utiliptables.Table
|
||||
for {
|
||||
line, err := buf.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
line = strings.TrimSuffix(line, "\n")
|
||||
if strings.HasPrefix(line, "*") {
|
||||
tableName = utiliptables.Table(line[1:])
|
||||
}
|
||||
if tableName != "" {
|
||||
if restoreTableName != "" && restoreTableName != tableName {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, ":") {
|
||||
chainName := utiliptables.Chain(strings.Split(line[1:], " ")[0])
|
||||
if flush == utiliptables.FlushTables {
|
||||
table, chain, _ := f.getChain(tableName, chainName)
|
||||
if chain != nil {
|
||||
delete(table.chains, string(chainName))
|
||||
}
|
||||
}
|
||||
_, _ = f.ensureChain(tableName, chainName)
|
||||
// The --noflush option for iptables-restore doesn't work for user-defined chains, only builtin chains.
|
||||
// We should flush user-defined chains if the chain is not to be deleted
|
||||
if !f.isBuiltinChain(tableName, chainName) && !strings.Contains(allLines, "-X "+string(chainName)) {
|
||||
if err := f.FlushChain(tableName, chainName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(line, "-A") {
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("Invalid iptables rule '%s'", line)
|
||||
}
|
||||
chainName := utiliptables.Chain(parts[1])
|
||||
rule := strings.TrimPrefix(line, fmt.Sprintf("-A %s ", chainName))
|
||||
_, err := f.ensureRule(utiliptables.Append, tableName, chainName, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(line, "-I") {
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("Invalid iptables rule '%s'", line)
|
||||
}
|
||||
chainName := utiliptables.Chain(parts[1])
|
||||
rule := strings.TrimPrefix(line, fmt.Sprintf("-I %s ", chainName))
|
||||
_, err := f.ensureRule(utiliptables.Prepend, tableName, chainName, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(line, "-X") {
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("Invalid iptables rule '%s'", line)
|
||||
}
|
||||
if err := f.DeleteChain(tableName, utiliptables.Chain(parts[1])); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if line == "COMMIT" {
|
||||
if restoreTableName == tableName {
|
||||
return nil
|
||||
}
|
||||
tableName = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) Restore(tableName utiliptables.Table, data []byte, flush utiliptables.FlushFlag, counters utiliptables.RestoreCountersFlag) error {
|
||||
return f.restore(tableName, data, flush)
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) RestoreAll(data []byte, flush utiliptables.FlushFlag, counters utiliptables.RestoreCountersFlag) error {
|
||||
return f.restore("", data, flush)
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) AddReloadFunc(reloadFunc func()) {
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) Destroy() {
|
||||
}
|
||||
|
||||
func (f *fakeIPTables) isBuiltinChain(tableName utiliptables.Table, chainName utiliptables.Chain) bool {
|
||||
if builtinChains, ok := f.builtinChains[string(tableName)]; ok && builtinChains.Has(string(chainName)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/fake_iptables_test.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/fake_iptables_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
func TestRestoreFlushRules(t *testing.T) {
|
||||
iptables := NewFakeIPTables()
|
||||
rules := [][]string{
|
||||
{"-A", "KUBE-HOSTPORTS", "-m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j KUBE-HP-5N7UH5JAXCVP5UJR"},
|
||||
{"-A", "POSTROUTING", "-m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE"},
|
||||
}
|
||||
natRules := bytes.NewBuffer(nil)
|
||||
writeLine(natRules, "*nat")
|
||||
for _, rule := range rules {
|
||||
_, err := iptables.EnsureChain(utiliptables.TableNAT, utiliptables.Chain(rule[1]))
|
||||
assert.NoError(t, err)
|
||||
_, err = iptables.ensureRule(utiliptables.RulePosition(rule[0]), utiliptables.TableNAT, utiliptables.Chain(rule[1]), rule[2])
|
||||
assert.NoError(t, err)
|
||||
|
||||
writeLine(natRules, utiliptables.MakeChainLine(utiliptables.Chain(rule[1])))
|
||||
}
|
||||
writeLine(natRules, "COMMIT")
|
||||
assert.NoError(t, iptables.Restore(utiliptables.TableNAT, natRules.Bytes(), utiliptables.NoFlushTables, utiliptables.RestoreCounters))
|
||||
natTable, ok := iptables.tables[string(utiliptables.TableNAT)]
|
||||
assert.True(t, ok)
|
||||
// check KUBE-HOSTPORTS chain, should have been cleaned up
|
||||
hostportChain, ok := natTable.chains["KUBE-HOSTPORTS"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, len(hostportChain.rules))
|
||||
|
||||
// check builtin chains, should not been cleaned up
|
||||
postroutingChain, ok := natTable.chains["POSTROUTING"]
|
||||
assert.True(t, ok, string(postroutingChain.name))
|
||||
assert.Equal(t, 1, len(postroutingChain.rules))
|
||||
}
|
143
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport.go
generated
vendored
Normal file
143
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
const (
|
||||
// the hostport chain
|
||||
kubeHostportsChain utiliptables.Chain = "KUBE-HOSTPORTS"
|
||||
// prefix for hostport chains
|
||||
kubeHostportChainPrefix string = "KUBE-HP-"
|
||||
)
|
||||
|
||||
// PortMapping represents a network port in a container
|
||||
type PortMapping struct {
|
||||
Name string
|
||||
HostPort int32
|
||||
ContainerPort int32
|
||||
Protocol v1.Protocol
|
||||
HostIP string
|
||||
}
|
||||
|
||||
// PodPortMapping represents a pod's network state and associated container port mappings
|
||||
type PodPortMapping struct {
|
||||
Namespace string
|
||||
Name string
|
||||
PortMappings []*PortMapping
|
||||
HostNetwork bool
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
type hostport struct {
|
||||
port int32
|
||||
protocol string
|
||||
}
|
||||
|
||||
type hostportOpener func(*hostport) (closeable, error)
|
||||
|
||||
type closeable interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
func openLocalPort(hp *hostport) (closeable, error) {
|
||||
// For ports on node IPs, open the actual port and hold it, even though we
|
||||
// use iptables to redirect traffic.
|
||||
// This ensures a) that it's safe to use that port and b) that (a) stays
|
||||
// true. The risk is that some process on the node (e.g. sshd or kubelet)
|
||||
// is using a port and we give that same port out to a Service. That would
|
||||
// be bad because iptables would silently claim the traffic but the process
|
||||
// would never know.
|
||||
// NOTE: We should not need to have a real listen()ing socket - bind()
|
||||
// should be enough, but I can't figure out a way to e2e test without
|
||||
// it. Tools like 'ss' and 'netstat' do not show sockets that are
|
||||
// bind()ed but not listen()ed, and at least the default debian netcat
|
||||
// has no way to avoid about 10 seconds of retries.
|
||||
var socket closeable
|
||||
switch hp.protocol {
|
||||
case "tcp":
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", hp.port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket = listener
|
||||
case "udp":
|
||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", hp.port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket = conn
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown protocol %q", hp.protocol)
|
||||
}
|
||||
glog.V(3).Infof("Opened local port %s", hp.String())
|
||||
return socket, nil
|
||||
}
|
||||
|
||||
// portMappingToHostport creates hostport structure based on input portmapping
|
||||
func portMappingToHostport(portMapping *PortMapping) hostport {
|
||||
return hostport{
|
||||
port: portMapping.HostPort,
|
||||
protocol: strings.ToLower(string(portMapping.Protocol)),
|
||||
}
|
||||
}
|
||||
|
||||
// ensureKubeHostportChains ensures the KUBE-HOSTPORTS chain is setup correctly
|
||||
func ensureKubeHostportChains(iptables utiliptables.Interface, natInterfaceName string) error {
|
||||
glog.V(4).Info("Ensuring kubelet hostport chains")
|
||||
// Ensure kubeHostportChain
|
||||
if _, err := iptables.EnsureChain(utiliptables.TableNAT, kubeHostportsChain); err != nil {
|
||||
return fmt.Errorf("Failed to ensure that %s chain %s exists: %v", utiliptables.TableNAT, kubeHostportsChain, err)
|
||||
}
|
||||
tableChainsNeedJumpServices := []struct {
|
||||
table utiliptables.Table
|
||||
chain utiliptables.Chain
|
||||
}{
|
||||
{utiliptables.TableNAT, utiliptables.ChainOutput},
|
||||
{utiliptables.TableNAT, utiliptables.ChainPrerouting},
|
||||
}
|
||||
args := []string{"-m", "comment", "--comment", "kube hostport portals",
|
||||
"-m", "addrtype", "--dst-type", "LOCAL",
|
||||
"-j", string(kubeHostportsChain)}
|
||||
for _, tc := range tableChainsNeedJumpServices {
|
||||
// KUBE-HOSTPORTS chain needs to be appended to the system chains.
|
||||
// This ensures KUBE-SERVICES chain gets processed first.
|
||||
// Since rules in KUBE-HOSTPORTS chain matches broader cases, allow the more specific rules to be processed first.
|
||||
if _, err := iptables.EnsureRule(utiliptables.Append, tc.table, tc.chain, args...); err != nil {
|
||||
return fmt.Errorf("Failed to ensure that %s chain %s jumps to %s: %v", tc.table, tc.chain, kubeHostportsChain, err)
|
||||
}
|
||||
}
|
||||
// Need to SNAT traffic from localhost
|
||||
args = []string{"-m", "comment", "--comment", "SNAT for localhost access to hostports", "-o", natInterfaceName, "-s", "127.0.0.0/8", "-j", "MASQUERADE"}
|
||||
if _, err := iptables.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainPostrouting, args...); err != nil {
|
||||
return fmt.Errorf("Failed to ensure that %s chain %s jumps to MASQUERADE: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, err)
|
||||
}
|
||||
return nil
|
||||
}
|
383
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_manager.go
generated
vendored
Normal file
383
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_manager.go
generated
vendored
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
iptablesproxy "k8s.io/kubernetes/pkg/proxy/iptables"
|
||||
"k8s.io/kubernetes/pkg/util/conntrack"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
utilnet "k8s.io/kubernetes/pkg/util/net"
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// HostPortManager is an interface for adding and removing hostport for a given pod sandbox.
|
||||
type HostPortManager interface {
|
||||
// Add implements port mappings.
|
||||
// id should be a unique identifier for a pod, e.g. podSandboxID.
|
||||
// podPortMapping is the associated port mapping information for the pod.
|
||||
// natInterfaceName is the interface that localhost used to talk to the given pod.
|
||||
Add(id string, podPortMapping *PodPortMapping, natInterfaceName string) error
|
||||
// Remove cleans up matching port mappings
|
||||
// Remove must be able to clean up port mappings without pod IP
|
||||
Remove(id string, podPortMapping *PodPortMapping) error
|
||||
}
|
||||
|
||||
type hostportManager struct {
|
||||
hostPortMap map[hostport]closeable
|
||||
execer exec.Interface
|
||||
conntrackFound bool
|
||||
iptables utiliptables.Interface
|
||||
portOpener hostportOpener
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewHostportManager(iptables utiliptables.Interface) HostPortManager {
|
||||
h := &hostportManager{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
execer: exec.New(),
|
||||
iptables: iptables,
|
||||
portOpener: openLocalPort,
|
||||
}
|
||||
h.conntrackFound = conntrack.Exists(h.execer)
|
||||
if !h.conntrackFound {
|
||||
glog.Warningf("The binary conntrack is not installed, this can cause failures in network connection cleanup.")
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func (hm *hostportManager) Add(id string, podPortMapping *PodPortMapping, natInterfaceName string) (err error) {
|
||||
if podPortMapping == nil || podPortMapping.HostNetwork {
|
||||
return nil
|
||||
}
|
||||
podFullName := getPodFullName(podPortMapping)
|
||||
|
||||
// skip if there is no hostport needed
|
||||
hostportMappings := gatherHostportMappings(podPortMapping)
|
||||
if len(hostportMappings) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if podPortMapping.IP.To4() == nil {
|
||||
return fmt.Errorf("invalid or missing IP of pod %s", podFullName)
|
||||
}
|
||||
podIP := podPortMapping.IP.String()
|
||||
|
||||
if err = ensureKubeHostportChains(hm.iptables, natInterfaceName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure atomicity for port opening and iptables operations
|
||||
hm.mu.Lock()
|
||||
defer hm.mu.Unlock()
|
||||
|
||||
// try to open hostports
|
||||
ports, err := hm.openHostports(podPortMapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for hostport, socket := range ports {
|
||||
hm.hostPortMap[hostport] = socket
|
||||
}
|
||||
|
||||
natChains := bytes.NewBuffer(nil)
|
||||
natRules := bytes.NewBuffer(nil)
|
||||
writeLine(natChains, "*nat")
|
||||
|
||||
existingChains, existingRules, err := getExistingHostportIPTablesRules(hm.iptables)
|
||||
if err != nil {
|
||||
// clean up opened host port if encounter any error
|
||||
return utilerrors.NewAggregate([]error{err, hm.closeHostports(hostportMappings)})
|
||||
}
|
||||
|
||||
newChains := []utiliptables.Chain{}
|
||||
conntrackPortsToRemove := []int{}
|
||||
for _, pm := range hostportMappings {
|
||||
protocol := strings.ToLower(string(pm.Protocol))
|
||||
chain := getHostportChain(id, pm)
|
||||
newChains = append(newChains, chain)
|
||||
if pm.Protocol == v1.ProtocolUDP {
|
||||
conntrackPortsToRemove = append(conntrackPortsToRemove, int(pm.HostPort))
|
||||
}
|
||||
|
||||
// Add new hostport chain
|
||||
writeLine(natChains, utiliptables.MakeChainLine(chain))
|
||||
|
||||
// Prepend the new chain to KUBE-HOSTPORTS
|
||||
// This avoids any leaking iptables rule that takes up the same port
|
||||
writeLine(natRules, "-I", string(kubeHostportsChain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, podFullName, pm.HostPort),
|
||||
"-m", protocol, "-p", protocol, "--dport", fmt.Sprintf("%d", pm.HostPort),
|
||||
"-j", string(chain),
|
||||
)
|
||||
|
||||
// SNAT if the traffic comes from the pod itself
|
||||
writeLine(natRules, "-A", string(chain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, podFullName, pm.HostPort),
|
||||
"-s", podIP,
|
||||
"-j", string(iptablesproxy.KubeMarkMasqChain))
|
||||
|
||||
// DNAT to the podIP:containerPort
|
||||
writeLine(natRules, "-A", string(chain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, podFullName, pm.HostPort),
|
||||
"-m", protocol, "-p", protocol,
|
||||
"-j", "DNAT", fmt.Sprintf("--to-destination=%s:%d", podIP, pm.ContainerPort))
|
||||
}
|
||||
|
||||
// getHostportChain should be able to provide unique hostport chain name using hash
|
||||
// if there is a chain conflict or multiple Adds have been triggered for a single pod,
|
||||
// filtering should be able to avoid further problem
|
||||
filterChains(existingChains, newChains)
|
||||
existingRules = filterRules(existingRules, newChains)
|
||||
|
||||
for _, chain := range existingChains {
|
||||
writeLine(natChains, chain)
|
||||
}
|
||||
for _, rule := range existingRules {
|
||||
writeLine(natRules, rule)
|
||||
}
|
||||
writeLine(natRules, "COMMIT")
|
||||
|
||||
if err = hm.syncIPTables(append(natChains.Bytes(), natRules.Bytes()...)); err != nil {
|
||||
// clean up opened host port if encounter any error
|
||||
return utilerrors.NewAggregate([]error{err, hm.closeHostports(hostportMappings)})
|
||||
}
|
||||
isIpv6 := utilnet.IsIPv6(podPortMapping.IP)
|
||||
|
||||
// Remove conntrack entries just after adding the new iptables rules. If the conntrack entry is removed along with
|
||||
// the IP tables rule, it can be the case that the packets received by the node after iptables rule removal will
|
||||
// create a new conntrack entry without any DNAT. That will result in blackhole of the traffic even after correct
|
||||
// iptables rules have been added back.
|
||||
if hm.execer != nil && hm.conntrackFound {
|
||||
glog.Infof("Starting to delete udp conntrack entries: %v, isIPv6 - %v", conntrackPortsToRemove, isIpv6)
|
||||
for _, port := range conntrackPortsToRemove {
|
||||
err = conntrack.ClearEntriesForPort(hm.execer, port, isIpv6, v1.ProtocolUDP)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to clear udp conntrack for port %d, error: %v", port, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hm *hostportManager) Remove(id string, podPortMapping *PodPortMapping) (err error) {
|
||||
if podPortMapping == nil || podPortMapping.HostNetwork {
|
||||
return nil
|
||||
}
|
||||
|
||||
hostportMappings := gatherHostportMappings(podPortMapping)
|
||||
if len(hostportMappings) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure atomicity for port closing and iptables operations
|
||||
hm.mu.Lock()
|
||||
defer hm.mu.Unlock()
|
||||
|
||||
var existingChains map[utiliptables.Chain]string
|
||||
var existingRules []string
|
||||
existingChains, existingRules, err = getExistingHostportIPTablesRules(hm.iptables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Gather target hostport chains for removal
|
||||
chainsToRemove := []utiliptables.Chain{}
|
||||
for _, pm := range hostportMappings {
|
||||
chainsToRemove = append(chainsToRemove, getHostportChain(id, pm))
|
||||
}
|
||||
|
||||
// remove rules that consists of target chains
|
||||
remainingRules := filterRules(existingRules, chainsToRemove)
|
||||
|
||||
// gather target hostport chains that exists in iptables-save result
|
||||
existingChainsToRemove := []utiliptables.Chain{}
|
||||
for _, chain := range chainsToRemove {
|
||||
if _, ok := existingChains[chain]; ok {
|
||||
existingChainsToRemove = append(existingChainsToRemove, chain)
|
||||
}
|
||||
}
|
||||
|
||||
natChains := bytes.NewBuffer(nil)
|
||||
natRules := bytes.NewBuffer(nil)
|
||||
writeLine(natChains, "*nat")
|
||||
for _, chain := range existingChains {
|
||||
writeLine(natChains, chain)
|
||||
}
|
||||
for _, rule := range remainingRules {
|
||||
writeLine(natRules, rule)
|
||||
}
|
||||
for _, chain := range existingChainsToRemove {
|
||||
writeLine(natRules, "-X", string(chain))
|
||||
}
|
||||
writeLine(natRules, "COMMIT")
|
||||
|
||||
if err = hm.syncIPTables(append(natChains.Bytes(), natRules.Bytes()...)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// clean up opened pod host ports
|
||||
return hm.closeHostports(hostportMappings)
|
||||
}
|
||||
|
||||
// syncIPTables executes iptables-restore with given lines
|
||||
func (hm *hostportManager) syncIPTables(lines []byte) error {
|
||||
glog.V(3).Infof("Restoring iptables rules: %s", lines)
|
||||
err := hm.iptables.RestoreAll(lines, utiliptables.NoFlushTables, utiliptables.RestoreCounters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to execute iptables-restore: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// openHostports opens all given hostports using the given hostportOpener
|
||||
// If encounter any error, clean up and return the error
|
||||
// If all ports are opened successfully, return the hostport and socket mapping
|
||||
func (hm *hostportManager) openHostports(podPortMapping *PodPortMapping) (map[hostport]closeable, error) {
|
||||
var retErr error
|
||||
ports := make(map[hostport]closeable)
|
||||
for _, pm := range podPortMapping.PortMappings {
|
||||
if pm.HostPort <= 0 {
|
||||
continue
|
||||
}
|
||||
hp := portMappingToHostport(pm)
|
||||
socket, err := hm.portOpener(&hp)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("cannot open hostport %d for pod %s: %v", pm.HostPort, getPodFullName(podPortMapping), err)
|
||||
break
|
||||
}
|
||||
ports[hp] = socket
|
||||
}
|
||||
|
||||
// If encounter any error, close all hostports that just got opened.
|
||||
if retErr != nil {
|
||||
for hp, socket := range ports {
|
||||
if err := socket.Close(); err != nil {
|
||||
glog.Errorf("Cannot clean up hostport %d for pod %s: %v", hp.port, getPodFullName(podPortMapping), err)
|
||||
}
|
||||
}
|
||||
return nil, retErr
|
||||
}
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
// closeHostports tries to close all the listed host ports
|
||||
func (hm *hostportManager) closeHostports(hostportMappings []*PortMapping) error {
|
||||
errList := []error{}
|
||||
for _, pm := range hostportMappings {
|
||||
hp := portMappingToHostport(pm)
|
||||
if socket, ok := hm.hostPortMap[hp]; ok {
|
||||
glog.V(2).Infof("Closing host port %s", hp.String())
|
||||
if err := socket.Close(); err != nil {
|
||||
errList = append(errList, fmt.Errorf("failed to close host port %s: %v", hp.String(), err))
|
||||
continue
|
||||
}
|
||||
delete(hm.hostPortMap, hp)
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
// getHostportChain takes id, hostport and protocol for a pod and returns associated iptables chain.
|
||||
// This is computed by hashing (sha256) then encoding to base32 and truncating with the prefix
|
||||
// "KUBE-HP-". We do this because IPTables Chain Names must be <= 28 chars long, and the longer
|
||||
// they are the harder they are to read.
|
||||
// WARNING: Please do not change this function. Otherwise, HostportManager may not be able to
|
||||
// identify existing iptables chains.
|
||||
func getHostportChain(id string, pm *PortMapping) utiliptables.Chain {
|
||||
hash := sha256.Sum256([]byte(id + strconv.Itoa(int(pm.HostPort)) + string(pm.Protocol)))
|
||||
encoded := base32.StdEncoding.EncodeToString(hash[:])
|
||||
return utiliptables.Chain(kubeHostportChainPrefix + encoded[:16])
|
||||
}
|
||||
|
||||
// gatherHostportMappings returns all the PortMappings which has hostport for a pod
|
||||
func gatherHostportMappings(podPortMapping *PodPortMapping) []*PortMapping {
|
||||
mappings := []*PortMapping{}
|
||||
for _, pm := range podPortMapping.PortMappings {
|
||||
if pm.HostPort <= 0 {
|
||||
continue
|
||||
}
|
||||
mappings = append(mappings, pm)
|
||||
}
|
||||
return mappings
|
||||
}
|
||||
|
||||
// getExistingHostportIPTablesRules retrieves raw data from iptables-save, parse it,
|
||||
// return all the hostport related chains and rules
|
||||
func getExistingHostportIPTablesRules(iptables utiliptables.Interface) (map[utiliptables.Chain]string, []string, error) {
|
||||
iptablesData := bytes.NewBuffer(nil)
|
||||
err := iptables.SaveInto(utiliptables.TableNAT, iptablesData)
|
||||
if err != nil { // if we failed to get any rules
|
||||
return nil, nil, fmt.Errorf("failed to execute iptables-save: %v", err)
|
||||
}
|
||||
existingNATChains := utiliptables.GetChainLines(utiliptables.TableNAT, iptablesData.Bytes())
|
||||
|
||||
existingHostportChains := make(map[utiliptables.Chain]string)
|
||||
existingHostportRules := []string{}
|
||||
|
||||
for chain := range existingNATChains {
|
||||
if strings.HasPrefix(string(chain), string(kubeHostportsChain)) || strings.HasPrefix(string(chain), kubeHostportChainPrefix) {
|
||||
existingHostportChains[chain] = existingNATChains[chain]
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(iptablesData.Bytes()), "\n") {
|
||||
if strings.HasPrefix(line, fmt.Sprintf("-A %s", kubeHostportChainPrefix)) ||
|
||||
strings.HasPrefix(line, fmt.Sprintf("-A %s", string(kubeHostportsChain))) {
|
||||
existingHostportRules = append(existingHostportRules, line)
|
||||
}
|
||||
}
|
||||
return existingHostportChains, existingHostportRules, nil
|
||||
}
|
||||
|
||||
// filterRules filters input rules with input chains. Rules that did not involve any filter chain will be returned.
|
||||
// The order of the input rules is important and is preserved.
|
||||
func filterRules(rules []string, filters []utiliptables.Chain) []string {
|
||||
filtered := []string{}
|
||||
for _, rule := range rules {
|
||||
skip := false
|
||||
for _, filter := range filters {
|
||||
if strings.Contains(rule, string(filter)) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !skip {
|
||||
filtered = append(filtered, rule)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// filterChains deletes all entries of filter chains from chain map
|
||||
func filterChains(chains map[utiliptables.Chain]string, filterChains []utiliptables.Chain) {
|
||||
for _, chain := range filterChains {
|
||||
if _, ok := chains[chain]; ok {
|
||||
delete(chains, chain)
|
||||
}
|
||||
}
|
||||
}
|
335
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go
generated
vendored
Normal file
335
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go
generated
vendored
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
"k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
func TestOpenCloseHostports(t *testing.T) {
|
||||
openPortCases := []struct {
|
||||
podPortMapping *PodPortMapping
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
&PodPortMapping{
|
||||
Namespace: "ns1",
|
||||
Name: "n0",
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
&PodPortMapping{
|
||||
Namespace: "ns1",
|
||||
Name: "n1",
|
||||
PortMappings: []*PortMapping{
|
||||
{HostPort: 80, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 8080, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 443, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
&PodPortMapping{
|
||||
Namespace: "ns1",
|
||||
Name: "n2",
|
||||
PortMappings: []*PortMapping{
|
||||
{HostPort: 80, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
&PodPortMapping{
|
||||
Namespace: "ns1",
|
||||
Name: "n3",
|
||||
PortMappings: []*PortMapping{
|
||||
{HostPort: 8081, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 8080, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
&PodPortMapping{
|
||||
Namespace: "ns1",
|
||||
Name: "n3",
|
||||
PortMappings: []*PortMapping{
|
||||
{HostPort: 8081, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
iptables := NewFakeIPTables()
|
||||
portOpener := NewFakeSocketManager()
|
||||
manager := &hostportManager{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
iptables: iptables,
|
||||
portOpener: portOpener.openFakeSocket,
|
||||
execer: exec.New(),
|
||||
}
|
||||
|
||||
for _, tc := range openPortCases {
|
||||
mapping, err := manager.openHostports(tc.podPortMapping)
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, len(mapping), len(tc.podPortMapping.PortMappings))
|
||||
}
|
||||
|
||||
// We have 4 ports: 80, 443, 8080, 8081 open now.
|
||||
closePortCases := []struct {
|
||||
portMappings []*PortMapping
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
portMappings: nil,
|
||||
},
|
||||
{
|
||||
|
||||
portMappings: []*PortMapping{
|
||||
{HostPort: 80, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 8080, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 443, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
portMappings: []*PortMapping{
|
||||
{HostPort: 80, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
{
|
||||
portMappings: []*PortMapping{
|
||||
{HostPort: 8081, Protocol: v1.Protocol("TCP")},
|
||||
{HostPort: 8080, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
{
|
||||
portMappings: []*PortMapping{
|
||||
{HostPort: 8081, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
{
|
||||
portMappings: []*PortMapping{
|
||||
{HostPort: 7070, Protocol: v1.Protocol("TCP")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range closePortCases {
|
||||
err := manager.closeHostports(tc.portMappings)
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
// Clear all elements in hostPortMap
|
||||
assert.Zero(t, len(manager.hostPortMap))
|
||||
}
|
||||
|
||||
func TestHostportManager(t *testing.T) {
|
||||
iptables := NewFakeIPTables()
|
||||
portOpener := NewFakeSocketManager()
|
||||
manager := &hostportManager{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
iptables: iptables,
|
||||
portOpener: portOpener.openFakeSocket,
|
||||
execer: exec.New(),
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
mapping *PodPortMapping
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
mapping: &PodPortMapping{
|
||||
Name: "pod1",
|
||||
Namespace: "ns1",
|
||||
IP: net.ParseIP("10.1.1.2"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 8080,
|
||||
ContainerPort: 80,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
HostPort: 8081,
|
||||
ContainerPort: 81,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
mapping: &PodPortMapping{
|
||||
Name: "pod2",
|
||||
Namespace: "ns1",
|
||||
IP: net.ParseIP("10.1.1.3"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 8082,
|
||||
ContainerPort: 80,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
HostPort: 8081,
|
||||
ContainerPort: 81,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
mapping: &PodPortMapping{
|
||||
Name: "pod3",
|
||||
Namespace: "ns1",
|
||||
IP: net.ParseIP("10.1.1.4"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 8443,
|
||||
ContainerPort: 443,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Add Hostports
|
||||
for _, tc := range testCases {
|
||||
err := manager.Add("id", tc.mapping, "cbr0")
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
continue
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Check port opened
|
||||
expectedPorts := []hostport{{8080, "tcp"}, {8081, "udp"}, {8443, "tcp"}}
|
||||
openedPorts := make(map[hostport]bool)
|
||||
for hp, port := range portOpener.mem {
|
||||
if !port.closed {
|
||||
openedPorts[hp] = true
|
||||
}
|
||||
}
|
||||
assert.EqualValues(t, len(openedPorts), len(expectedPorts))
|
||||
for _, hp := range expectedPorts {
|
||||
_, ok := openedPorts[hp]
|
||||
assert.EqualValues(t, true, ok)
|
||||
}
|
||||
|
||||
// Check Iptables-save result after adding hostports
|
||||
raw := bytes.NewBuffer(nil)
|
||||
err := iptables.SaveInto(utiliptables.TableNAT, raw)
|
||||
assert.NoError(t, err)
|
||||
|
||||
lines := strings.Split(string(raw.Bytes()), "\n")
|
||||
expectedLines := map[string]bool{
|
||||
`*nat`: true,
|
||||
`:KUBE-HOSTPORTS - [0:0]`: true,
|
||||
`:OUTPUT - [0:0]`: true,
|
||||
`:PREROUTING - [0:0]`: true,
|
||||
`:POSTROUTING - [0:0]`: true,
|
||||
`:KUBE-HP-IJHALPHTORMHHPPK - [0:0]`: true,
|
||||
`:KUBE-HP-63UPIDJXVRSZGSUZ - [0:0]`: true,
|
||||
`:KUBE-HP-WFBOALXEP42XEMJK - [0:0]`: true,
|
||||
"-A KUBE-HOSTPORTS -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j KUBE-HP-WFBOALXEP42XEMJK": true,
|
||||
"-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp --dport 8081 -j KUBE-HP-63UPIDJXVRSZGSUZ": true,
|
||||
"-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp --dport 8080 -j KUBE-HP-IJHALPHTORMHHPPK": true,
|
||||
"-A OUTPUT -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
|
||||
"-A PREROUTING -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
|
||||
"-A POSTROUTING -m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE": true,
|
||||
"-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
|
||||
"-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80": true,
|
||||
"-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
|
||||
"-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81": true,
|
||||
"-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -s 10.1.1.4/32 -j KUBE-MARK-MASQ": true,
|
||||
"-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.4:443": true,
|
||||
`COMMIT`: true,
|
||||
}
|
||||
for _, line := range lines {
|
||||
if len(strings.TrimSpace(line)) > 0 {
|
||||
_, ok := expectedLines[strings.TrimSpace(line)]
|
||||
assert.EqualValues(t, true, ok)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all added hostports
|
||||
for _, tc := range testCases {
|
||||
if !tc.expectError {
|
||||
err := manager.Remove("id", tc.mapping)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check Iptables-save result after deleting hostports
|
||||
raw.Reset()
|
||||
err = iptables.SaveInto(utiliptables.TableNAT, raw)
|
||||
assert.NoError(t, err)
|
||||
lines = strings.Split(string(raw.Bytes()), "\n")
|
||||
remainingChains := make(map[string]bool)
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, ":") {
|
||||
remainingChains[strings.TrimSpace(line)] = true
|
||||
}
|
||||
}
|
||||
expectDeletedChains := []string{"KUBE-HP-4YVONL46AKYWSKS3", "KUBE-HP-7THKRFSEH4GIIXK7", "KUBE-HP-5N7UH5JAXCVP5UJR"}
|
||||
for _, chain := range expectDeletedChains {
|
||||
_, ok := remainingChains[chain]
|
||||
assert.EqualValues(t, false, ok)
|
||||
}
|
||||
|
||||
// check if all ports are closed
|
||||
for _, port := range portOpener.mem {
|
||||
assert.EqualValues(t, true, port.closed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHostportChain(t *testing.T) {
|
||||
m := make(map[string]int)
|
||||
chain := getHostportChain("testrdma-2", &PortMapping{HostPort: 57119, Protocol: "TCP", ContainerPort: 57119})
|
||||
m[string(chain)] = 1
|
||||
chain = getHostportChain("testrdma-2", &PortMapping{HostPort: 55429, Protocol: "TCP", ContainerPort: 55429})
|
||||
m[string(chain)] = 1
|
||||
chain = getHostportChain("testrdma-2", &PortMapping{HostPort: 56833, Protocol: "TCP", ContainerPort: 56833})
|
||||
m[string(chain)] = 1
|
||||
if len(m) != 3 {
|
||||
t.Fatal(m)
|
||||
}
|
||||
}
|
304
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go
generated
vendored
Normal file
304
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
iptablesproxy "k8s.io/kubernetes/pkg/proxy/iptables"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
// HostportSyncer takes a list of PodPortMappings and implements hostport all at once
|
||||
type HostportSyncer interface {
|
||||
// SyncHostports gathers all hostports on node and setup iptables rules to enable them.
|
||||
// On each invocation existing ports are synced and stale rules are deleted.
|
||||
SyncHostports(natInterfaceName string, activePodPortMappings []*PodPortMapping) error
|
||||
// OpenPodHostportsAndSync opens hostports for a new PodPortMapping, gathers all hostports on
|
||||
// node, sets up iptables rules enable them. On each invocation existing ports are synced and stale rules are deleted.
|
||||
// 'newPortMapping' must also be present in 'activePodPortMappings'.
|
||||
OpenPodHostportsAndSync(newPortMapping *PodPortMapping, natInterfaceName string, activePodPortMappings []*PodPortMapping) error
|
||||
}
|
||||
|
||||
type hostportSyncer struct {
|
||||
hostPortMap map[hostport]closeable
|
||||
iptables utiliptables.Interface
|
||||
portOpener hostportOpener
|
||||
}
|
||||
|
||||
func NewHostportSyncer(iptables utiliptables.Interface) HostportSyncer {
|
||||
return &hostportSyncer{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
iptables: iptables,
|
||||
portOpener: openLocalPort,
|
||||
}
|
||||
}
|
||||
|
||||
type targetPod struct {
|
||||
podFullName string
|
||||
podIP string
|
||||
}
|
||||
|
||||
func (hp *hostport) String() string {
|
||||
return fmt.Sprintf("%s:%d", hp.protocol, hp.port)
|
||||
}
|
||||
|
||||
// openHostports opens all hostport for pod and returns the map of hostport and socket
|
||||
func (h *hostportSyncer) openHostports(podHostportMapping *PodPortMapping) error {
|
||||
var retErr error
|
||||
ports := make(map[hostport]closeable)
|
||||
for _, port := range podHostportMapping.PortMappings {
|
||||
if port.HostPort <= 0 {
|
||||
// Assume hostport is not specified in this portmapping. So skip
|
||||
continue
|
||||
}
|
||||
hp := hostport{
|
||||
port: port.HostPort,
|
||||
protocol: strings.ToLower(string(port.Protocol)),
|
||||
}
|
||||
socket, err := h.portOpener(&hp)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("cannot open hostport %d for pod %s: %v", port.HostPort, getPodFullName(podHostportMapping), err)
|
||||
break
|
||||
}
|
||||
ports[hp] = socket
|
||||
}
|
||||
|
||||
// If encounter any error, close all hostports that just got opened.
|
||||
if retErr != nil {
|
||||
for hp, socket := range ports {
|
||||
if err := socket.Close(); err != nil {
|
||||
glog.Errorf("Cannot clean up hostport %d for pod %s: %v", hp.port, getPodFullName(podHostportMapping), err)
|
||||
}
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
for hostPort, socket := range ports {
|
||||
h.hostPortMap[hostPort] = socket
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPodFullName(pod *PodPortMapping) string {
|
||||
// Use underscore as the delimiter because it is not allowed in pod name
|
||||
// (DNS subdomain format), while allowed in the container name format.
|
||||
return pod.Name + "_" + pod.Namespace
|
||||
}
|
||||
|
||||
// gatherAllHostports returns all hostports that should be presented on node,
|
||||
// given the list of pods running on that node and ignoring host network
|
||||
// pods (which don't need hostport <-> container port mapping).
|
||||
func gatherAllHostports(activePodPortMappings []*PodPortMapping) (map[*PortMapping]targetPod, error) {
|
||||
podHostportMap := make(map[*PortMapping]targetPod)
|
||||
for _, pm := range activePodPortMappings {
|
||||
if pm.IP.To4() == nil {
|
||||
return nil, fmt.Errorf("Invalid or missing pod %s IP", getPodFullName(pm))
|
||||
}
|
||||
// should not handle hostports for hostnetwork pods
|
||||
if pm.HostNetwork {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, port := range pm.PortMappings {
|
||||
if port.HostPort != 0 {
|
||||
podHostportMap[port] = targetPod{podFullName: getPodFullName(pm), podIP: pm.IP.String()}
|
||||
}
|
||||
}
|
||||
}
|
||||
return podHostportMap, nil
|
||||
}
|
||||
|
||||
// Join all words with spaces, terminate with newline and write to buf.
|
||||
func writeLine(buf *bytes.Buffer, words ...string) {
|
||||
buf.WriteString(strings.Join(words, " ") + "\n")
|
||||
}
|
||||
|
||||
//hostportChainName takes containerPort for a pod and returns associated iptables chain.
|
||||
// This is computed by hashing (sha256)
|
||||
// then encoding to base32 and truncating with the prefix "KUBE-SVC-". We do
|
||||
// this because IPTables Chain Names must be <= 28 chars long, and the longer
|
||||
// they are the harder they are to read.
|
||||
func hostportChainName(pm *PortMapping, podFullName string) utiliptables.Chain {
|
||||
hash := sha256.Sum256([]byte(strconv.Itoa(int(pm.HostPort)) + string(pm.Protocol) + podFullName))
|
||||
encoded := base32.StdEncoding.EncodeToString(hash[:])
|
||||
return utiliptables.Chain(kubeHostportChainPrefix + encoded[:16])
|
||||
}
|
||||
|
||||
// OpenPodHostportsAndSync opens hostports for a new PodPortMapping, gathers all hostports on
|
||||
// node, sets up iptables rules enable them. And finally clean up stale hostports.
|
||||
// 'newPortMapping' must also be present in 'activePodPortMappings'.
|
||||
func (h *hostportSyncer) OpenPodHostportsAndSync(newPortMapping *PodPortMapping, natInterfaceName string, activePodPortMappings []*PodPortMapping) error {
|
||||
// try to open pod host port if specified
|
||||
if err := h.openHostports(newPortMapping); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the new pod to active pods if it's not present.
|
||||
var found bool
|
||||
for _, pm := range activePodPortMappings {
|
||||
if pm.Namespace == newPortMapping.Namespace && pm.Name == newPortMapping.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
activePodPortMappings = append(activePodPortMappings, newPortMapping)
|
||||
}
|
||||
|
||||
return h.SyncHostports(natInterfaceName, activePodPortMappings)
|
||||
}
|
||||
|
||||
// SyncHostports gathers all hostports on node and setup iptables rules enable them. And finally clean up stale hostports
|
||||
func (h *hostportSyncer) SyncHostports(natInterfaceName string, activePodPortMappings []*PodPortMapping) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
glog.V(4).Infof("syncHostportsRules took %v", time.Since(start))
|
||||
}()
|
||||
|
||||
hostportPodMap, err := gatherAllHostports(activePodPortMappings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure KUBE-HOSTPORTS chains
|
||||
ensureKubeHostportChains(h.iptables, natInterfaceName)
|
||||
|
||||
// Get iptables-save output so we can check for existing chains and rules.
|
||||
// This will be a map of chain name to chain with rules as stored in iptables-save/iptables-restore
|
||||
existingNATChains := make(map[utiliptables.Chain]string)
|
||||
iptablesData := bytes.NewBuffer(nil)
|
||||
err = h.iptables.SaveInto(utiliptables.TableNAT, iptablesData)
|
||||
if err != nil { // if we failed to get any rules
|
||||
glog.Errorf("Failed to execute iptables-save, syncing all rules: %v", err)
|
||||
} else { // otherwise parse the output
|
||||
existingNATChains = utiliptables.GetChainLines(utiliptables.TableNAT, iptablesData.Bytes())
|
||||
}
|
||||
|
||||
natChains := bytes.NewBuffer(nil)
|
||||
natRules := bytes.NewBuffer(nil)
|
||||
writeLine(natChains, "*nat")
|
||||
// Make sure we keep stats for the top-level chains, if they existed
|
||||
// (which most should have because we created them above).
|
||||
if chain, ok := existingNATChains[kubeHostportsChain]; ok {
|
||||
writeLine(natChains, chain)
|
||||
} else {
|
||||
writeLine(natChains, utiliptables.MakeChainLine(kubeHostportsChain))
|
||||
}
|
||||
|
||||
// Accumulate NAT chains to keep.
|
||||
activeNATChains := map[utiliptables.Chain]bool{} // use a map as a set
|
||||
|
||||
for port, target := range hostportPodMap {
|
||||
protocol := strings.ToLower(string(port.Protocol))
|
||||
hostportChain := hostportChainName(port, target.podFullName)
|
||||
if chain, ok := existingNATChains[hostportChain]; ok {
|
||||
writeLine(natChains, chain)
|
||||
} else {
|
||||
writeLine(natChains, utiliptables.MakeChainLine(hostportChain))
|
||||
}
|
||||
|
||||
activeNATChains[hostportChain] = true
|
||||
|
||||
// Redirect to hostport chain
|
||||
args := []string{
|
||||
"-A", string(kubeHostportsChain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, target.podFullName, port.HostPort),
|
||||
"-m", protocol, "-p", protocol,
|
||||
"--dport", fmt.Sprintf("%d", port.HostPort),
|
||||
"-j", string(hostportChain),
|
||||
}
|
||||
writeLine(natRules, args...)
|
||||
|
||||
// Assuming kubelet is syncing iptables KUBE-MARK-MASQ chain
|
||||
// If the request comes from the pod that is serving the hostport, then SNAT
|
||||
args = []string{
|
||||
"-A", string(hostportChain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, target.podFullName, port.HostPort),
|
||||
"-s", target.podIP, "-j", string(iptablesproxy.KubeMarkMasqChain),
|
||||
}
|
||||
writeLine(natRules, args...)
|
||||
|
||||
// Create hostport chain to DNAT traffic to final destination
|
||||
// IPTables will maintained the stats for this chain
|
||||
args = []string{
|
||||
"-A", string(hostportChain),
|
||||
"-m", "comment", "--comment", fmt.Sprintf(`"%s hostport %d"`, target.podFullName, port.HostPort),
|
||||
"-m", protocol, "-p", protocol,
|
||||
"-j", "DNAT", fmt.Sprintf("--to-destination=%s:%d", target.podIP, port.ContainerPort),
|
||||
}
|
||||
writeLine(natRules, args...)
|
||||
}
|
||||
|
||||
// Delete chains no longer in use.
|
||||
for chain := range existingNATChains {
|
||||
if !activeNATChains[chain] {
|
||||
chainString := string(chain)
|
||||
if !strings.HasPrefix(chainString, kubeHostportChainPrefix) {
|
||||
// Ignore chains that aren't ours.
|
||||
continue
|
||||
}
|
||||
// We must (as per iptables) write a chain-line for it, which has
|
||||
// the nice effect of flushing the chain. Then we can remove the
|
||||
// chain.
|
||||
writeLine(natChains, existingNATChains[chain])
|
||||
writeLine(natRules, "-X", chainString)
|
||||
}
|
||||
}
|
||||
writeLine(natRules, "COMMIT")
|
||||
|
||||
natLines := append(natChains.Bytes(), natRules.Bytes()...)
|
||||
glog.V(3).Infof("Restoring iptables rules: %s", natLines)
|
||||
err = h.iptables.RestoreAll(natLines, utiliptables.NoFlushTables, utiliptables.RestoreCounters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to execute iptables-restore: %v", err)
|
||||
}
|
||||
|
||||
h.cleanupHostportMap(hostportPodMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupHostportMap closes obsolete hostports
|
||||
func (h *hostportSyncer) cleanupHostportMap(containerPortMap map[*PortMapping]targetPod) {
|
||||
// compute hostports that are supposed to be open
|
||||
currentHostports := make(map[hostport]bool)
|
||||
for containerPort := range containerPortMap {
|
||||
hp := hostport{
|
||||
port: containerPort.HostPort,
|
||||
protocol: strings.ToLower(string(containerPort.Protocol)),
|
||||
}
|
||||
currentHostports[hp] = true
|
||||
}
|
||||
|
||||
// close and delete obsolete hostports
|
||||
for hp, socket := range h.hostPortMap {
|
||||
if _, ok := currentHostports[hp]; !ok {
|
||||
socket.Close()
|
||||
glog.V(3).Infof("Closed local port %s", hp.String())
|
||||
delete(h.hostPortMap, hp)
|
||||
}
|
||||
}
|
||||
}
|
312
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_syncer_test.go
generated
vendored
Normal file
312
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_syncer_test.go
generated
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
type ruleMatch struct {
|
||||
hostport int
|
||||
chain string
|
||||
match string
|
||||
}
|
||||
|
||||
func TestOpenPodHostports(t *testing.T) {
|
||||
fakeIPTables := NewFakeIPTables()
|
||||
fakeOpener := NewFakeSocketManager()
|
||||
|
||||
h := &hostportSyncer{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
iptables: fakeIPTables,
|
||||
portOpener: fakeOpener.openFakeSocket,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
mapping *PodPortMapping
|
||||
matches []*ruleMatch
|
||||
}{
|
||||
// New pod that we are going to add
|
||||
{
|
||||
&PodPortMapping{
|
||||
Name: "test-pod",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
IP: net.ParseIP("10.1.1.2"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 4567,
|
||||
ContainerPort: 80,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
HostPort: 5678,
|
||||
ContainerPort: 81,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]*ruleMatch{
|
||||
{
|
||||
-1,
|
||||
"KUBE-HOSTPORTS",
|
||||
"-m comment --comment \"test-pod_default hostport 4567\" -m tcp -p tcp --dport 4567",
|
||||
},
|
||||
{
|
||||
4567,
|
||||
"",
|
||||
"-m comment --comment \"test-pod_default hostport 4567\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ",
|
||||
},
|
||||
{
|
||||
4567,
|
||||
"",
|
||||
"-m comment --comment \"test-pod_default hostport 4567\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80",
|
||||
},
|
||||
{
|
||||
-1,
|
||||
"KUBE-HOSTPORTS",
|
||||
"-m comment --comment \"test-pod_default hostport 5678\" -m udp -p udp --dport 5678",
|
||||
},
|
||||
{
|
||||
5678,
|
||||
"",
|
||||
"-m comment --comment \"test-pod_default hostport 5678\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ",
|
||||
},
|
||||
{
|
||||
5678,
|
||||
"",
|
||||
"-m comment --comment \"test-pod_default hostport 5678\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Already running pod
|
||||
{
|
||||
&PodPortMapping{
|
||||
Name: "another-test-pod",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
IP: net.ParseIP("10.1.1.5"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 123,
|
||||
ContainerPort: 654,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]*ruleMatch{
|
||||
{
|
||||
-1,
|
||||
"KUBE-HOSTPORTS",
|
||||
"-m comment --comment \"another-test-pod_default hostport 123\" -m tcp -p tcp --dport 123",
|
||||
},
|
||||
{
|
||||
123,
|
||||
"",
|
||||
"-m comment --comment \"another-test-pod_default hostport 123\" -s 10.1.1.5/32 -j KUBE-MARK-MASQ",
|
||||
},
|
||||
{
|
||||
123,
|
||||
"",
|
||||
"-m comment --comment \"another-test-pod_default hostport 123\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.5:654",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
activePodPortMapping := make([]*PodPortMapping, 0)
|
||||
|
||||
// Fill in any match rules missing chain names
|
||||
for _, test := range tests {
|
||||
for _, match := range test.matches {
|
||||
if match.hostport >= 0 {
|
||||
found := false
|
||||
for _, pm := range test.mapping.PortMappings {
|
||||
if int(pm.HostPort) == match.hostport {
|
||||
match.chain = string(hostportChainName(pm, getPodFullName(test.mapping)))
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Failed to find ContainerPort for match %d/'%s'", match.hostport, match.match)
|
||||
}
|
||||
}
|
||||
}
|
||||
activePodPortMapping = append(activePodPortMapping, test.mapping)
|
||||
|
||||
}
|
||||
|
||||
// Already running pod's host port
|
||||
hp := hostport{
|
||||
tests[1].mapping.PortMappings[0].HostPort,
|
||||
strings.ToLower(string(tests[1].mapping.PortMappings[0].Protocol)),
|
||||
}
|
||||
h.hostPortMap[hp] = &fakeSocket{
|
||||
tests[1].mapping.PortMappings[0].HostPort,
|
||||
strings.ToLower(string(tests[1].mapping.PortMappings[0].Protocol)),
|
||||
false,
|
||||
}
|
||||
|
||||
err := h.OpenPodHostportsAndSync(tests[0].mapping, "br0", activePodPortMapping)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to OpenPodHostportsAndSync: %v", err)
|
||||
}
|
||||
|
||||
// Generic rules
|
||||
genericRules := []*ruleMatch{
|
||||
{-1, "POSTROUTING", "-m comment --comment \"SNAT for localhost access to hostports\" -o br0 -s 127.0.0.0/8 -j MASQUERADE"},
|
||||
{-1, "PREROUTING", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
||||
{-1, "OUTPUT", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
||||
}
|
||||
|
||||
for _, rule := range genericRules {
|
||||
_, chain, err := fakeIPTables.getChain(utiliptables.TableNAT, utiliptables.Chain(rule.chain))
|
||||
if err != nil {
|
||||
t.Fatalf("Expected NAT chain %s did not exist", rule.chain)
|
||||
}
|
||||
if !matchRule(chain, rule.match) {
|
||||
t.Fatalf("Expected %s chain rule match '%s' not found", rule.chain, rule.match)
|
||||
}
|
||||
}
|
||||
|
||||
// Pod rules
|
||||
for _, test := range tests {
|
||||
for _, match := range test.matches {
|
||||
// Ensure chain exists
|
||||
_, chain, err := fakeIPTables.getChain(utiliptables.TableNAT, utiliptables.Chain(match.chain))
|
||||
if err != nil {
|
||||
t.Fatalf("Expected NAT chain %s did not exist", match.chain)
|
||||
}
|
||||
if !matchRule(chain, match.match) {
|
||||
t.Fatalf("Expected NAT chain %s rule containing '%s' not found", match.chain, match.match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Socket
|
||||
hostPortMap := map[hostport]closeable{
|
||||
{123, "tcp"}: &fakeSocket{123, "tcp", false},
|
||||
{4567, "tcp"}: &fakeSocket{4567, "tcp", false},
|
||||
{5678, "udp"}: &fakeSocket{5678, "udp", false},
|
||||
}
|
||||
if !reflect.DeepEqual(hostPortMap, h.hostPortMap) {
|
||||
t.Fatalf("Mismatch in expected hostPortMap. Expected '%v', got '%v'", hostPortMap, h.hostPortMap)
|
||||
}
|
||||
}
|
||||
|
||||
func matchRule(chain *fakeChain, match string) bool {
|
||||
for _, rule := range chain.rules {
|
||||
if strings.Contains(rule, match) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestHostportChainName(t *testing.T) {
|
||||
m := make(map[string]int)
|
||||
chain := hostportChainName(&PortMapping{HostPort: 57119, Protocol: "TCP", ContainerPort: 57119}, "testrdma-2")
|
||||
m[string(chain)] = 1
|
||||
chain = hostportChainName(&PortMapping{HostPort: 55429, Protocol: "TCP", ContainerPort: 55429}, "testrdma-2")
|
||||
m[string(chain)] = 1
|
||||
chain = hostportChainName(&PortMapping{HostPort: 56833, Protocol: "TCP", ContainerPort: 56833}, "testrdma-2")
|
||||
m[string(chain)] = 1
|
||||
if len(m) != 3 {
|
||||
t.Fatal(m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostPortSyncerRemoveLegacyRules(t *testing.T) {
|
||||
iptables := NewFakeIPTables()
|
||||
legacyRules := [][]string{
|
||||
{"-A", "KUBE-HOSTPORTS", "-m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j KUBE-HP-5N7UH5JAXCVP5UJR"},
|
||||
{"-A", "KUBE-HOSTPORTS", "-m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp --dport 8081 -j KUBE-HP-7THKRFSEH4GIIXK7"},
|
||||
{"-A", "KUBE-HOSTPORTS", "-m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp --dport 8080 -j KUBE-HP-4YVONL46AKYWSKS3"},
|
||||
{"-A", "OUTPUT", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
||||
{"-A", "PREROUTING", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
||||
{"-A", "POSTROUTING", "-m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE"},
|
||||
{"-A", "KUBE-HP-4YVONL46AKYWSKS3", "-m comment --comment \"pod1_ns1 hostport 8080\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ"},
|
||||
{"-A", "KUBE-HP-4YVONL46AKYWSKS3", "-m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80"},
|
||||
{"-A", "KUBE-HP-7THKRFSEH4GIIXK7", "-m comment --comment \"pod1_ns1 hostport 8081\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ"},
|
||||
{"-A", "KUBE-HP-7THKRFSEH4GIIXK7", "-m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81"},
|
||||
{"-A", "KUBE-HP-5N7UH5JAXCVP5UJR", "-m comment --comment \"pod3_ns1 hostport 8443\" -s 10.1.1.4/32 -j KUBE-MARK-MASQ"},
|
||||
{"-A", "KUBE-HP-5N7UH5JAXCVP5UJR", "-m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.4:443"},
|
||||
}
|
||||
for _, rule := range legacyRules {
|
||||
_, err := iptables.EnsureChain(utiliptables.TableNAT, utiliptables.Chain(rule[1]))
|
||||
assert.NoError(t, err)
|
||||
_, err = iptables.ensureRule(utiliptables.RulePosition(rule[0]), utiliptables.TableNAT, utiliptables.Chain(rule[1]), rule[2])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
portOpener := NewFakeSocketManager()
|
||||
h := &hostportSyncer{
|
||||
hostPortMap: make(map[hostport]closeable),
|
||||
iptables: iptables,
|
||||
portOpener: portOpener.openFakeSocket,
|
||||
}
|
||||
// check preserve pod3's rules and remove pod1's rules
|
||||
pod3PortMapping := &PodPortMapping{
|
||||
Name: "pod3",
|
||||
Namespace: "ns1",
|
||||
IP: net.ParseIP("10.1.1.4"),
|
||||
HostNetwork: false,
|
||||
PortMappings: []*PortMapping{
|
||||
{
|
||||
HostPort: 8443,
|
||||
ContainerPort: 443,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
}
|
||||
h.SyncHostports("cbr0", []*PodPortMapping{pod3PortMapping})
|
||||
|
||||
newChainName := string(hostportChainName(pod3PortMapping.PortMappings[0], getPodFullName(pod3PortMapping)))
|
||||
expectRules := [][]string{
|
||||
{"KUBE-HOSTPORTS", "-m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j " + newChainName},
|
||||
{newChainName, "-m comment --comment \"pod3_ns1 hostport 8443\" -s 10.1.1.4/32 -j KUBE-MARK-MASQ"},
|
||||
{newChainName, "-m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.4:443"},
|
||||
}
|
||||
|
||||
natTable, ok := iptables.tables[string(utiliptables.TableNAT)]
|
||||
assert.True(t, ok)
|
||||
// check pod1's rules in KUBE-HOSTPORTS chain should be cleaned up
|
||||
hostportChain, ok := natTable.chains["KUBE-HOSTPORTS"]
|
||||
assert.True(t, ok, string(hostportChain.name))
|
||||
assert.Equal(t, 1, len(hostportChain.rules), "%v", hostportChain.rules)
|
||||
|
||||
// check pod3's rules left
|
||||
assert.Equal(t, expectRules[0][1], hostportChain.rules[0])
|
||||
chain, ok := natTable.chains[newChainName]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 2, len(chain.rules))
|
||||
assert.Equal(t, expectRules[1][1], chain.rules[0])
|
||||
assert.Equal(t, expectRules[2][1], chain.rules[1])
|
||||
|
||||
// check legacy KUBE-HP-* chains should be deleted
|
||||
for _, name := range []string{"KUBE-HP-4YVONL46AKYWSKS3", "KUBE-HP-7THKRFSEH4GIIXK7", "KUBE-HP-5N7UH5JAXCVP5UJR"} {
|
||||
_, ok := natTable.chains[name]
|
||||
assert.False(t, ok)
|
||||
}
|
||||
}
|
82
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_test.go
generated
vendored
Normal file
82
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/hostport_test.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 hostport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
)
|
||||
|
||||
type fakeSocket struct {
|
||||
port int32
|
||||
protocol string
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (f *fakeSocket) Close() error {
|
||||
if f.closed {
|
||||
return fmt.Errorf("Socket %q.%s already closed!", f.port, f.protocol)
|
||||
}
|
||||
f.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFakeSocketManager() *fakeSocketManager {
|
||||
return &fakeSocketManager{mem: make(map[hostport]*fakeSocket)}
|
||||
}
|
||||
|
||||
type fakeSocketManager struct {
|
||||
mem map[hostport]*fakeSocket
|
||||
}
|
||||
|
||||
func (f *fakeSocketManager) openFakeSocket(hp *hostport) (closeable, error) {
|
||||
if socket, ok := f.mem[*hp]; ok && !socket.closed {
|
||||
return nil, fmt.Errorf("hostport is occupied")
|
||||
}
|
||||
fs := &fakeSocket{hp.port, hp.protocol, false}
|
||||
f.mem[*hp] = fs
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
func TestEnsureKubeHostportChains(t *testing.T) {
|
||||
interfaceName := "cbr0"
|
||||
builtinChains := []string{"PREROUTING", "OUTPUT"}
|
||||
jumpRule := "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"
|
||||
masqRule := "-m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE"
|
||||
|
||||
fakeIPTables := NewFakeIPTables()
|
||||
assert.NoError(t, ensureKubeHostportChains(fakeIPTables, interfaceName))
|
||||
|
||||
_, _, err := fakeIPTables.getChain(utiliptables.TableNAT, utiliptables.Chain("KUBE-HOSTPORTS"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, chain, err := fakeIPTables.getChain(utiliptables.TableNAT, utiliptables.ChainPostrouting)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, len(chain.rules), 1)
|
||||
assert.Contains(t, chain.rules[0], masqRule)
|
||||
|
||||
for _, chainName := range builtinChains {
|
||||
_, chain, err := fakeIPTables.getChain(utiliptables.TableNAT, utiliptables.Chain(chainName))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, len(chain.rules), 1)
|
||||
assert.Contains(t, chain.rules[0], jumpRule)
|
||||
}
|
||||
|
||||
}
|
@ -7,8 +7,9 @@ load(
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/testing",
|
||||
srcs = ["fake.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/testing",
|
||||
deps = ["//pkg/kubelet/dockershim/network/hostport:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/testing/fake.go
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/testing/fake.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
)
|
||||
|
||||
type fakeSyncer struct{}
|
||||
|
||||
func NewFakeHostportSyncer() hostport.HostportSyncer {
|
||||
return &fakeSyncer{}
|
||||
}
|
||||
|
||||
func (h *fakeSyncer) OpenPodHostportsAndSync(newPortMapping *hostport.PodPortMapping, natInterfaceName string, activePortMapping []*hostport.PodPortMapping) error {
|
||||
return h.SyncHostports(natInterfaceName, activePortMapping)
|
||||
}
|
||||
|
||||
func (h *fakeSyncer) SyncHostports(natInterfaceName string, activePortMapping []*hostport.PodPortMapping) error {
|
||||
for _, r := range activePortMapping {
|
||||
if r.IP.To4() == nil {
|
||||
return fmt.Errorf("Invalid or missing pod %s/%s IP", r.Namespace, r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
166
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/BUILD
generated
vendored
Normal file
166
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/BUILD
generated
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"kubenet.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"kubenet_linux.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"kubenet_unsupported.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet",
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport:go_default_library",
|
||||
"//pkg/util/bandwidth:go_default_library",
|
||||
"//pkg/util/dbus:go_default_library",
|
||||
"//pkg/util/ebtables:go_default_library",
|
||||
"//pkg/util/iptables:go_default_library",
|
||||
"//pkg/util/sysctl:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/libcni:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types:go_default_library",
|
||||
"//vendor/github.com/containernetworking/cni/pkg/types/020:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/vishvananda/netlink:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"kubenet_linux_test.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
embed = [":go_default_library"],
|
||||
deps = select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/cni/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/testing:go_default_library",
|
||||
"//pkg/util/bandwidth:go_default_library",
|
||||
"//pkg/util/iptables/testing:go_default_library",
|
||||
"//pkg/util/sysctl/testing:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/mock: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"],
|
||||
tags = ["automanaged"],
|
||||
)
|
21
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet.go
generated
vendored
Normal file
21
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
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 kubenet
|
||||
|
||||
const (
|
||||
KubenetPluginName = "kubenet"
|
||||
)
|
688
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_linux.go
generated
vendored
Normal file
688
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_linux.go
generated
vendored
Normal file
@ -0,0 +1,688 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
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 kubenet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
cnitypes "github.com/containernetworking/cni/pkg/types"
|
||||
cnitypes020 "github.com/containernetworking/cni/pkg/types/020"
|
||||
"github.com/golang/glog"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
utilsets "k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
"k8s.io/kubernetes/pkg/util/bandwidth"
|
||||
utildbus "k8s.io/kubernetes/pkg/util/dbus"
|
||||
utilebtables "k8s.io/kubernetes/pkg/util/ebtables"
|
||||
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
||||
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const (
|
||||
BridgeName = "cbr0"
|
||||
DefaultCNIDir = "/opt/cni/bin"
|
||||
|
||||
sysctlBridgeCallIPTables = "net/bridge/bridge-nf-call-iptables"
|
||||
|
||||
// fallbackMTU is used if an MTU is not specified, and we cannot determine the MTU
|
||||
fallbackMTU = 1460
|
||||
|
||||
// ebtables Chain to store dedup rules
|
||||
dedupChain = utilebtables.Chain("KUBE-DEDUP")
|
||||
|
||||
// defaultIPAMDir is the default location for the checkpoint files stored by host-local ipam
|
||||
// https://github.com/containernetworking/cni/tree/master/plugins/ipam/host-local#backends
|
||||
defaultIPAMDir = "/var/lib/cni/networks"
|
||||
)
|
||||
|
||||
// CNI plugins required by kubenet in /opt/cni/bin or user-specified directory
|
||||
var requiredCNIPlugins = [...]string{"bridge", "host-local", "loopback"}
|
||||
|
||||
type kubenetNetworkPlugin struct {
|
||||
network.NoopNetworkPlugin
|
||||
|
||||
host network.Host
|
||||
netConfig *libcni.NetworkConfig
|
||||
loConfig *libcni.NetworkConfig
|
||||
cniConfig libcni.CNI
|
||||
bandwidthShaper bandwidth.BandwidthShaper
|
||||
mu sync.Mutex //Mutex for protecting podIPs map, netConfig, and shaper initialization
|
||||
podIPs map[kubecontainer.ContainerID]string
|
||||
mtu int
|
||||
execer utilexec.Interface
|
||||
nsenterPath string
|
||||
hairpinMode kubeletconfig.HairpinMode
|
||||
// kubenet can use either hostportSyncer and hostportManager to implement hostports
|
||||
// Currently, if network host supports legacy features, hostportSyncer will be used,
|
||||
// otherwise, hostportManager will be used.
|
||||
hostportSyncer hostport.HostportSyncer
|
||||
hostportManager hostport.HostPortManager
|
||||
iptables utiliptables.Interface
|
||||
sysctl utilsysctl.Interface
|
||||
ebtables utilebtables.Interface
|
||||
// binDirs is passed by kubelet cni-bin-dir parameter.
|
||||
// kubenet will search for CNI binaries in DefaultCNIDir first, then continue to binDirs.
|
||||
binDirs []string
|
||||
nonMasqueradeCIDR string
|
||||
podCidr string
|
||||
gateway net.IP
|
||||
}
|
||||
|
||||
func NewPlugin(networkPluginDirs []string) network.NetworkPlugin {
|
||||
protocol := utiliptables.ProtocolIpv4
|
||||
execer := utilexec.New()
|
||||
dbus := utildbus.New()
|
||||
sysctl := utilsysctl.New()
|
||||
iptInterface := utiliptables.New(execer, dbus, protocol)
|
||||
return &kubenetNetworkPlugin{
|
||||
podIPs: make(map[kubecontainer.ContainerID]string),
|
||||
execer: utilexec.New(),
|
||||
iptables: iptInterface,
|
||||
sysctl: sysctl,
|
||||
binDirs: append([]string{DefaultCNIDir}, networkPluginDirs...),
|
||||
hostportSyncer: hostport.NewHostportSyncer(iptInterface),
|
||||
hostportManager: hostport.NewHostportManager(iptInterface),
|
||||
nonMasqueradeCIDR: "10.0.0.0/8",
|
||||
}
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Init(host network.Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
plugin.host = host
|
||||
plugin.hairpinMode = hairpinMode
|
||||
plugin.nonMasqueradeCIDR = nonMasqueradeCIDR
|
||||
plugin.cniConfig = &libcni.CNIConfig{Path: plugin.binDirs}
|
||||
|
||||
if mtu == network.UseDefaultMTU {
|
||||
if link, err := findMinMTU(); err == nil {
|
||||
plugin.mtu = link.MTU
|
||||
glog.V(5).Infof("Using interface %s MTU %d as bridge MTU", link.Name, link.MTU)
|
||||
} else {
|
||||
plugin.mtu = fallbackMTU
|
||||
glog.Warningf("Failed to find default bridge MTU, using %d: %v", fallbackMTU, err)
|
||||
}
|
||||
} else {
|
||||
plugin.mtu = mtu
|
||||
}
|
||||
|
||||
// Since this plugin uses a Linux bridge, set bridge-nf-call-iptables=1
|
||||
// is necessary to ensure kube-proxy functions correctly.
|
||||
//
|
||||
// This will return an error on older kernel version (< 3.18) as the module
|
||||
// was built-in, we simply ignore the error here. A better thing to do is
|
||||
// to check the kernel version in the future.
|
||||
plugin.execer.Command("modprobe", "br-netfilter").CombinedOutput()
|
||||
err := plugin.sysctl.SetSysctl(sysctlBridgeCallIPTables, 1)
|
||||
if err != nil {
|
||||
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIPTables, err)
|
||||
}
|
||||
|
||||
plugin.loConfig, err = libcni.ConfFromBytes([]byte(`{
|
||||
"cniVersion": "0.1.0",
|
||||
"name": "kubenet-loopback",
|
||||
"type": "loopback"
|
||||
}`))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to generate loopback config: %v", err)
|
||||
}
|
||||
|
||||
plugin.nsenterPath, err = plugin.execer.LookPath("nsenter")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to find nsenter binary: %v", err)
|
||||
}
|
||||
|
||||
// Need to SNAT outbound traffic from cluster
|
||||
if err = plugin.ensureMasqRule(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: move thic logic into cni bridge plugin and remove this from kubenet
|
||||
func (plugin *kubenetNetworkPlugin) ensureMasqRule() error {
|
||||
if plugin.nonMasqueradeCIDR != "0.0.0.0/0" {
|
||||
if _, err := plugin.iptables.EnsureRule(utiliptables.Append, utiliptables.TableNAT, utiliptables.ChainPostrouting,
|
||||
"-m", "comment", "--comment", "kubenet: SNAT for outbound traffic from cluster",
|
||||
"-m", "addrtype", "!", "--dst-type", "LOCAL",
|
||||
"!", "-d", plugin.nonMasqueradeCIDR,
|
||||
"-j", "MASQUERADE"); err != nil {
|
||||
return fmt.Errorf("Failed to ensure that %s chain %s jumps to MASQUERADE: %v", utiliptables.TableNAT, utiliptables.ChainPostrouting, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findMinMTU() (*net.Interface, error) {
|
||||
intfs, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mtu := 999999
|
||||
defIntfIndex := -1
|
||||
for i, intf := range intfs {
|
||||
if ((intf.Flags & net.FlagUp) != 0) && (intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) == 0) {
|
||||
if intf.MTU < mtu {
|
||||
mtu = intf.MTU
|
||||
defIntfIndex = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mtu >= 999999 || mtu < 576 || defIntfIndex < 0 {
|
||||
return nil, fmt.Errorf("no suitable interface: %v", BridgeName)
|
||||
}
|
||||
|
||||
return &intfs[defIntfIndex], nil
|
||||
}
|
||||
|
||||
const NET_CONFIG_TEMPLATE = `{
|
||||
"cniVersion": "0.1.0",
|
||||
"name": "kubenet",
|
||||
"type": "bridge",
|
||||
"bridge": "%s",
|
||||
"mtu": %d,
|
||||
"addIf": "%s",
|
||||
"isGateway": true,
|
||||
"ipMasq": false,
|
||||
"hairpinMode": %t,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "%s",
|
||||
"gateway": "%s",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Event(name string, details map[string]interface{}) {
|
||||
if name != network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE {
|
||||
return
|
||||
}
|
||||
|
||||
plugin.mu.Lock()
|
||||
defer plugin.mu.Unlock()
|
||||
|
||||
podCIDR, ok := details[network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR].(string)
|
||||
if !ok {
|
||||
glog.Warningf("%s event didn't contain pod CIDR", network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE)
|
||||
return
|
||||
}
|
||||
|
||||
if plugin.netConfig != nil {
|
||||
glog.Warningf("Ignoring subsequent pod CIDR update to %s", podCIDR)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(5).Infof("PodCIDR is set to %q", podCIDR)
|
||||
_, cidr, err := net.ParseCIDR(podCIDR)
|
||||
if err == nil {
|
||||
setHairpin := plugin.hairpinMode == kubeletconfig.HairpinVeth
|
||||
// Set bridge address to first address in IPNet
|
||||
cidr.IP[len(cidr.IP)-1] += 1
|
||||
|
||||
json := fmt.Sprintf(NET_CONFIG_TEMPLATE, BridgeName, plugin.mtu, network.DefaultInterfaceName, setHairpin, podCIDR, cidr.IP.String())
|
||||
glog.V(2).Infof("CNI network config set to %v", json)
|
||||
plugin.netConfig, err = libcni.ConfFromBytes([]byte(json))
|
||||
if err == nil {
|
||||
glog.V(5).Infof("CNI network config:\n%s", json)
|
||||
|
||||
// Ensure cbr0 has no conflicting addresses; CNI's 'bridge'
|
||||
// plugin will bail out if the bridge has an unexpected one
|
||||
plugin.clearBridgeAddressesExcept(cidr)
|
||||
}
|
||||
plugin.podCidr = podCIDR
|
||||
plugin.gateway = cidr.IP
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to generate CNI network config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) clearBridgeAddressesExcept(keep *net.IPNet) {
|
||||
bridge, err := netlink.LinkByName(BridgeName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
addrs, err := netlink.AddrList(bridge, unix.AF_INET)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if !utilnet.IPNetEqual(addr.IPNet, keep) {
|
||||
glog.V(2).Infof("Removing old address %s from %s", addr.IPNet.String(), BridgeName)
|
||||
netlink.AddrDel(bridge, &addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Name() string {
|
||||
return KubenetPluginName
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Capabilities() utilsets.Int {
|
||||
return utilsets.NewInt()
|
||||
}
|
||||
|
||||
// setup sets up networking through CNI using the given ns/name and sandbox ID.
|
||||
func (plugin *kubenetNetworkPlugin) setup(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
// Disable DAD so we skip the kernel delay on bringing up new interfaces.
|
||||
if err := plugin.disableContainerDAD(id); err != nil {
|
||||
glog.V(3).Infof("Failed to disable DAD in container: %v", err)
|
||||
}
|
||||
|
||||
// Bring up container loopback interface
|
||||
if _, err := plugin.addContainerToNetwork(plugin.loConfig, "lo", namespace, name, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hook container up with our bridge
|
||||
resT, err := plugin.addContainerToNetwork(plugin.netConfig, network.DefaultInterfaceName, namespace, name, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Coerce the CNI result version
|
||||
res, err := cnitypes020.GetResult(resT)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to understand network config: %v", err)
|
||||
}
|
||||
if res.IP4 == nil {
|
||||
return fmt.Errorf("CNI plugin reported no IPv4 address for container %v.", id)
|
||||
}
|
||||
ip4 := res.IP4.IP.IP.To4()
|
||||
if ip4 == nil {
|
||||
return fmt.Errorf("CNI plugin reported an invalid IPv4 address for container %v: %+v.", id, res.IP4)
|
||||
}
|
||||
|
||||
// Put the container bridge into promiscuous mode to force it to accept hairpin packets.
|
||||
// TODO: Remove this once the kernel bug (#20096) is fixed.
|
||||
if plugin.hairpinMode == kubeletconfig.PromiscuousBridge {
|
||||
link, err := netlink.LinkByName(BridgeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", BridgeName, err)
|
||||
}
|
||||
if link.Attrs().Promisc != 1 {
|
||||
// promiscuous mode is not on, then turn it on.
|
||||
err := netlink.SetPromiscOn(link)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error setting promiscuous mode on %s: %v", BridgeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// configure the ebtables rules to eliminate duplicate packets by best effort
|
||||
plugin.syncEbtablesDedupRules(link.Attrs().HardwareAddr)
|
||||
}
|
||||
|
||||
plugin.podIPs[id] = ip4.String()
|
||||
|
||||
// The first SetUpPod call creates the bridge; get a shaper for the sake of initialization
|
||||
// TODO: replace with CNI traffic shaper plugin
|
||||
shaper := plugin.shaper()
|
||||
ingress, egress, err := bandwidth.ExtractPodBandwidthResources(annotations)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading pod bandwidth annotations: %v", err)
|
||||
}
|
||||
if egress != nil || ingress != nil {
|
||||
if err := shaper.ReconcileCIDR(fmt.Sprintf("%s/32", ip4.String()), egress, ingress); err != nil {
|
||||
return fmt.Errorf("Failed to add pod to shaper: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace with CNI port-forwarding plugin
|
||||
portMappings, err := plugin.host.GetPodPortMappings(id.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if portMappings != nil && len(portMappings) > 0 {
|
||||
if err := plugin.hostportManager.Add(id.ID, &hostport.PodPortMapping{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
PortMappings: portMappings,
|
||||
IP: ip4,
|
||||
HostNetwork: false,
|
||||
}, BridgeName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
plugin.mu.Lock()
|
||||
defer plugin.mu.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
glog.V(4).Infof("SetUpPod took %v for %s/%s", time.Since(start), namespace, name)
|
||||
}()
|
||||
|
||||
if err := plugin.Status(); err != nil {
|
||||
return fmt.Errorf("Kubenet cannot SetUpPod: %v", err)
|
||||
}
|
||||
|
||||
if err := plugin.setup(namespace, name, id, annotations); err != nil {
|
||||
// Make sure everything gets cleaned up on errors
|
||||
podIP, _ := plugin.podIPs[id]
|
||||
if err := plugin.teardown(namespace, name, id, podIP); err != nil {
|
||||
// Not a hard error or warning
|
||||
glog.V(4).Infof("Failed to clean up %s/%s after SetUpPod failure: %v", namespace, name, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Need to SNAT outbound traffic from cluster
|
||||
if err := plugin.ensureMasqRule(); err != nil {
|
||||
glog.Errorf("Failed to ensure MASQ rule: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tears down as much of a pod's network as it can even if errors occur. Returns
|
||||
// an aggregate error composed of all errors encountered during the teardown.
|
||||
func (plugin *kubenetNetworkPlugin) teardown(namespace string, name string, id kubecontainer.ContainerID, podIP string) error {
|
||||
errList := []error{}
|
||||
|
||||
if podIP != "" {
|
||||
glog.V(5).Infof("Removing pod IP %s from shaper", podIP)
|
||||
// shaper wants /32
|
||||
if err := plugin.shaper().Reset(fmt.Sprintf("%s/32", podIP)); err != nil {
|
||||
// Possible bandwidth shaping wasn't enabled for this pod anyways
|
||||
glog.V(4).Infof("Failed to remove pod IP %s from shaper: %v", podIP, err)
|
||||
}
|
||||
|
||||
delete(plugin.podIPs, id)
|
||||
}
|
||||
|
||||
if err := plugin.delContainerFromNetwork(plugin.netConfig, network.DefaultInterfaceName, namespace, name, id); err != nil {
|
||||
// This is to prevent returning error when TearDownPod is called twice on the same pod. This helps to reduce event pollution.
|
||||
if podIP != "" {
|
||||
glog.Warningf("Failed to delete container from kubenet: %v", err)
|
||||
} else {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
|
||||
portMappings, err := plugin.host.GetPodPortMappings(id.ID)
|
||||
if err != nil {
|
||||
errList = append(errList, err)
|
||||
} else if portMappings != nil && len(portMappings) > 0 {
|
||||
if err = plugin.hostportManager.Remove(id.ID, &hostport.PodPortMapping{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
PortMappings: portMappings,
|
||||
HostNetwork: false,
|
||||
}); err != nil {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errList)
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) TearDownPod(namespace string, name string, id kubecontainer.ContainerID) error {
|
||||
plugin.mu.Lock()
|
||||
defer plugin.mu.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
glog.V(4).Infof("TearDownPod took %v for %s/%s", time.Since(start), namespace, name)
|
||||
}()
|
||||
|
||||
if plugin.netConfig == nil {
|
||||
return fmt.Errorf("Kubenet needs a PodCIDR to tear down pods")
|
||||
}
|
||||
|
||||
// no cached IP is Ok during teardown
|
||||
podIP, _ := plugin.podIPs[id]
|
||||
if err := plugin.teardown(namespace, name, id, podIP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Need to SNAT outbound traffic from cluster
|
||||
if err := plugin.ensureMasqRule(); err != nil {
|
||||
glog.Errorf("Failed to ensure MASQ rule: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Use the addToNetwork function to obtain the IP of the Pod. That will assume idempotent ADD call to the plugin.
|
||||
// Also fix the runtime's call to Status function to be done only in the case that the IP is lost, no need to do periodic calls
|
||||
func (plugin *kubenetNetworkPlugin) GetPodNetworkStatus(namespace string, name string, id kubecontainer.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
plugin.mu.Lock()
|
||||
defer plugin.mu.Unlock()
|
||||
// Assuming the ip of pod does not change. Try to retrieve ip from kubenet map first.
|
||||
if podIP, ok := plugin.podIPs[id]; ok {
|
||||
return &network.PodNetworkStatus{IP: net.ParseIP(podIP)}, nil
|
||||
}
|
||||
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Kubenet failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
if netnsPath == "" {
|
||||
return nil, fmt.Errorf("Cannot find the network namespace, skipping pod network status for container %q", id)
|
||||
}
|
||||
ip, err := network.GetPodIP(plugin.execer, plugin.nsenterPath, netnsPath, network.DefaultInterfaceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plugin.podIPs[id] = ip.String()
|
||||
return &network.PodNetworkStatus{IP: ip}, nil
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Status() error {
|
||||
// Can't set up pods if we don't have a PodCIDR yet
|
||||
if plugin.netConfig == nil {
|
||||
return fmt.Errorf("Kubenet does not have netConfig. This is most likely due to lack of PodCIDR")
|
||||
}
|
||||
|
||||
if !plugin.checkRequiredCNIPlugins() {
|
||||
return fmt.Errorf("could not locate kubenet required CNI plugins %v at %q", requiredCNIPlugins, plugin.binDirs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkRequiredCNIPlugins returns if all kubenet required cni plugins can be found at /opt/cni/bin or user specified NetworkPluginDir.
|
||||
func (plugin *kubenetNetworkPlugin) checkRequiredCNIPlugins() bool {
|
||||
for _, dir := range plugin.binDirs {
|
||||
if plugin.checkRequiredCNIPluginsInOneDir(dir) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkRequiredCNIPluginsInOneDir returns true if all required cni plugins are placed in dir
|
||||
func (plugin *kubenetNetworkPlugin) checkRequiredCNIPluginsInOneDir(dir string) bool {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, cniPlugin := range requiredCNIPlugins {
|
||||
found := false
|
||||
for _, file := range files {
|
||||
if strings.TrimSpace(file.Name()) == cniPlugin {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) buildCNIRuntimeConf(ifName string, id kubecontainer.ContainerID, needNetNs bool) (*libcni.RuntimeConf, error) {
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if needNetNs && err != nil {
|
||||
glog.Errorf("Kubenet failed to retrieve network namespace path: %v", err)
|
||||
}
|
||||
|
||||
return &libcni.RuntimeConf{
|
||||
ContainerID: id.ID,
|
||||
NetNS: netnsPath,
|
||||
IfName: ifName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) addContainerToNetwork(config *libcni.NetworkConfig, ifName, namespace, name string, id kubecontainer.ContainerID) (cnitypes.Result, error) {
|
||||
rt, err := plugin.buildCNIRuntimeConf(ifName, id, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error building CNI config: %v", err)
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Adding %s/%s to '%s' with CNI '%s' plugin and runtime: %+v", namespace, name, config.Network.Name, config.Network.Type, rt)
|
||||
// The network plugin can take up to 3 seconds to execute,
|
||||
// so yield the lock while it runs.
|
||||
plugin.mu.Unlock()
|
||||
res, err := plugin.cniConfig.AddNetwork(config, rt)
|
||||
plugin.mu.Lock()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error adding container to network: %v", err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) delContainerFromNetwork(config *libcni.NetworkConfig, ifName, namespace, name string, id kubecontainer.ContainerID) error {
|
||||
rt, err := plugin.buildCNIRuntimeConf(ifName, id, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error building CNI config: %v", err)
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Removing %s/%s from '%s' with CNI '%s' plugin and runtime: %+v", namespace, name, config.Network.Name, config.Network.Type, rt)
|
||||
err = plugin.cniConfig.DelNetwork(config, rt)
|
||||
// The pod may not get deleted successfully at the first time.
|
||||
// Ignore "no such file or directory" error in case the network has already been deleted in previous attempts.
|
||||
if err != nil && !strings.Contains(err.Error(), "no such file or directory") {
|
||||
return fmt.Errorf("Error removing container from network: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// shaper retrieves the bandwidth shaper and, if it hasn't been fetched before,
|
||||
// initializes it and ensures the bridge is appropriately configured
|
||||
// This function should only be called while holding the `plugin.mu` lock
|
||||
func (plugin *kubenetNetworkPlugin) shaper() bandwidth.BandwidthShaper {
|
||||
if plugin.bandwidthShaper == nil {
|
||||
plugin.bandwidthShaper = bandwidth.NewTCShaper(BridgeName)
|
||||
plugin.bandwidthShaper.ReconcileInterface()
|
||||
}
|
||||
return plugin.bandwidthShaper
|
||||
}
|
||||
|
||||
//TODO: make this into a goroutine and rectify the dedup rules periodically
|
||||
func (plugin *kubenetNetworkPlugin) syncEbtablesDedupRules(macAddr net.HardwareAddr) {
|
||||
if plugin.ebtables == nil {
|
||||
plugin.ebtables = utilebtables.New(plugin.execer)
|
||||
glog.V(3).Infof("Flushing dedup chain")
|
||||
if err := plugin.ebtables.FlushChain(utilebtables.TableFilter, dedupChain); err != nil {
|
||||
glog.Errorf("Failed to flush dedup chain: %v", err)
|
||||
}
|
||||
}
|
||||
_, err := plugin.ebtables.GetVersion()
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to get ebtables version. Skip syncing ebtables dedup rules: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(3).Infof("Filtering packets with ebtables on mac address: %v, gateway: %v, pod CIDR: %v", macAddr.String(), plugin.gateway.String(), plugin.podCidr)
|
||||
_, err = plugin.ebtables.EnsureChain(utilebtables.TableFilter, dedupChain)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to ensure %v chain %v", utilebtables.TableFilter, dedupChain)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = plugin.ebtables.EnsureRule(utilebtables.Append, utilebtables.TableFilter, utilebtables.ChainOutput, "-j", string(dedupChain))
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to ensure %v chain %v jump to %v chain: %v", utilebtables.TableFilter, utilebtables.ChainOutput, dedupChain, err)
|
||||
return
|
||||
}
|
||||
|
||||
commonArgs := []string{"-p", "IPv4", "-s", macAddr.String(), "-o", "veth+"}
|
||||
_, err = plugin.ebtables.EnsureRule(utilebtables.Prepend, utilebtables.TableFilter, dedupChain, append(commonArgs, "--ip-src", plugin.gateway.String(), "-j", "ACCEPT")...)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to ensure packets from cbr0 gateway to be accepted")
|
||||
return
|
||||
|
||||
}
|
||||
_, err = plugin.ebtables.EnsureRule(utilebtables.Append, utilebtables.TableFilter, dedupChain, append(commonArgs, "--ip-src", plugin.podCidr, "-j", "DROP")...)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to ensure packets from podCidr but has mac address of cbr0 to get dropped.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// disableContainerDAD disables duplicate address detection in the container.
|
||||
// DAD has a negative affect on pod creation latency, since we have to wait
|
||||
// a second or more for the addresses to leave the "tentative" state. Since
|
||||
// we're sure there won't be an address conflict (since we manage them manually),
|
||||
// this is safe. See issue 54651.
|
||||
//
|
||||
// This sets net.ipv6.conf.default.dad_transmits to 0. It must be run *before*
|
||||
// the CNI plugins are run.
|
||||
func (plugin *kubenetNetworkPlugin) disableContainerDAD(id kubecontainer.ContainerID) error {
|
||||
key := "net/ipv6/conf/default/dad_transmits"
|
||||
|
||||
sysctlBin, err := plugin.execer.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not find sysctl binary: %s", err)
|
||||
}
|
||||
|
||||
netnsPath, err := plugin.host.GetNetNS(id.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get netns: %v", err)
|
||||
}
|
||||
if netnsPath == "" {
|
||||
return fmt.Errorf("Pod has no network namespace")
|
||||
}
|
||||
|
||||
// If the sysctl doesn't exist, it means ipv6 is disabled; log and move on
|
||||
if _, err := plugin.sysctl.GetSysctl(key); err != nil {
|
||||
return fmt.Errorf("Ipv6 not enabled: %v", err)
|
||||
}
|
||||
|
||||
output, err := plugin.execer.Command(plugin.nsenterPath,
|
||||
fmt.Sprintf("--net=%s", netnsPath), "-F", "--",
|
||||
sysctlBin, "-w", fmt.Sprintf("%s=%s", key, "0"),
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to write sysctl: output: %s error: %s",
|
||||
output, err)
|
||||
}
|
||||
return nil
|
||||
}
|
262
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_linux_test.go
generated
vendored
Normal file
262
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_linux_test.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
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 kubenet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni/testing"
|
||||
hostporttest "k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport/testing"
|
||||
nettest "k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing"
|
||||
"k8s.io/kubernetes/pkg/util/bandwidth"
|
||||
ipttest "k8s.io/kubernetes/pkg/util/iptables/testing"
|
||||
sysctltest "k8s.io/kubernetes/pkg/util/sysctl/testing"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
// test it fulfills the NetworkPlugin interface
|
||||
var _ network.NetworkPlugin = &kubenetNetworkPlugin{}
|
||||
|
||||
func newFakeKubenetPlugin(initMap map[kubecontainer.ContainerID]string, execer exec.Interface, host network.Host) *kubenetNetworkPlugin {
|
||||
return &kubenetNetworkPlugin{
|
||||
podIPs: initMap,
|
||||
execer: execer,
|
||||
mtu: 1460,
|
||||
host: host,
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodNetworkStatus(t *testing.T) {
|
||||
podIPMap := make(map[kubecontainer.ContainerID]string)
|
||||
podIPMap[kubecontainer.ContainerID{ID: "1"}] = "10.245.0.2"
|
||||
podIPMap[kubecontainer.ContainerID{ID: "2"}] = "10.245.0.3"
|
||||
|
||||
testCases := []struct {
|
||||
id string
|
||||
expectError bool
|
||||
expectIP string
|
||||
}{
|
||||
//in podCIDR map
|
||||
{
|
||||
"1",
|
||||
false,
|
||||
"10.245.0.2",
|
||||
},
|
||||
{
|
||||
"2",
|
||||
false,
|
||||
"10.245.0.3",
|
||||
},
|
||||
//not in podCIDR map
|
||||
{
|
||||
"3",
|
||||
true,
|
||||
"",
|
||||
},
|
||||
//TODO: add test cases for retrieving ip inside container network namespace
|
||||
}
|
||||
|
||||
fakeCmds := make([]fakeexec.FakeCommandAction, 0)
|
||||
for _, t := range testCases {
|
||||
// the fake commands return the IP from the given index, or an error
|
||||
fCmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) {
|
||||
ip, ok := podIPMap[kubecontainer.ContainerID{ID: t.id}]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Pod IP %q not found", t.id)
|
||||
}
|
||||
return []byte(ip), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeCmds = append(fakeCmds, func(cmd string, args ...string) exec.Cmd {
|
||||
return fakeexec.InitFakeCmd(&fCmd, cmd, args...)
|
||||
})
|
||||
}
|
||||
fexec := fakeexec.FakeExec{
|
||||
CommandScript: fakeCmds,
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
|
||||
fhost := nettest.NewFakeHost(nil)
|
||||
fakeKubenet := newFakeKubenetPlugin(podIPMap, &fexec, fhost)
|
||||
|
||||
for i, tc := range testCases {
|
||||
out, err := fakeKubenet.GetPodNetworkStatus("", "", kubecontainer.ContainerID{ID: tc.id})
|
||||
if tc.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("Test case %d expects error but got none", i)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Test case %d expects error but got error: %v", i, err)
|
||||
}
|
||||
}
|
||||
if tc.expectIP != out.IP.String() {
|
||||
t.Errorf("Test case %d expects ip %s but got %s", i, tc.expectIP, out.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTeardownCallsShaper tests that a `TearDown` call does call
|
||||
// `shaper.Reset`
|
||||
func TestTeardownCallsShaper(t *testing.T) {
|
||||
fexec := &fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{},
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
fhost := nettest.NewFakeHost(nil)
|
||||
fshaper := &bandwidth.FakeShaper{}
|
||||
mockcni := &mock_cni.MockCNI{}
|
||||
kubenet := newFakeKubenetPlugin(map[kubecontainer.ContainerID]string{}, fexec, fhost)
|
||||
kubenet.cniConfig = mockcni
|
||||
kubenet.iptables = ipttest.NewFake()
|
||||
kubenet.bandwidthShaper = fshaper
|
||||
kubenet.hostportSyncer = hostporttest.NewFakeHostportSyncer()
|
||||
|
||||
mockcni.On("DelNetwork", mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil)
|
||||
|
||||
details := make(map[string]interface{})
|
||||
details[network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR] = "10.0.0.1/24"
|
||||
kubenet.Event(network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE, details)
|
||||
|
||||
existingContainerID := kubecontainer.BuildContainerID("docker", "123")
|
||||
kubenet.podIPs[existingContainerID] = "10.0.0.1"
|
||||
|
||||
if err := kubenet.TearDownPod("namespace", "name", existingContainerID); err != nil {
|
||||
t.Fatalf("Unexpected error in TearDownPod: %v", err)
|
||||
}
|
||||
assert.Equal(t, []string{"10.0.0.1/32"}, fshaper.ResetCIDRs, "shaper.Reset should have been called")
|
||||
|
||||
mockcni.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// TestInit tests that a `Init` call with an MTU sets the MTU
|
||||
func TestInit_MTU(t *testing.T) {
|
||||
var fakeCmds []fakeexec.FakeCommandAction
|
||||
{
|
||||
// modprobe br-netfilter
|
||||
fCmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) {
|
||||
return make([]byte, 0), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeCmds = append(fakeCmds, func(cmd string, args ...string) exec.Cmd {
|
||||
return fakeexec.InitFakeCmd(&fCmd, cmd, args...)
|
||||
})
|
||||
}
|
||||
|
||||
fexec := &fakeexec.FakeExec{
|
||||
CommandScript: fakeCmds,
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
|
||||
fhost := nettest.NewFakeHost(nil)
|
||||
kubenet := newFakeKubenetPlugin(map[kubecontainer.ContainerID]string{}, fexec, fhost)
|
||||
kubenet.iptables = ipttest.NewFake()
|
||||
|
||||
sysctl := sysctltest.NewFake()
|
||||
sysctl.Settings["net/bridge/bridge-nf-call-iptables"] = 0
|
||||
kubenet.sysctl = sysctl
|
||||
|
||||
if err := kubenet.Init(nettest.NewFakeHost(nil), kubeletconfig.HairpinNone, "10.0.0.0/8", 1234); err != nil {
|
||||
t.Fatalf("Unexpected error in Init: %v", err)
|
||||
}
|
||||
assert.Equal(t, 1234, kubenet.mtu, "kubenet.mtu should have been set")
|
||||
assert.Equal(t, 1, sysctl.Settings["net/bridge/bridge-nf-call-iptables"], "net/bridge/bridge-nf-call-iptables sysctl should have been set")
|
||||
}
|
||||
|
||||
// TestInvocationWithoutRuntime invokes the plugin without a runtime.
|
||||
// This is how kubenet is invoked from the cri.
|
||||
func TestTearDownWithoutRuntime(t *testing.T) {
|
||||
testCases := []struct {
|
||||
podCIDR string
|
||||
ip string
|
||||
expectedGateway string
|
||||
}{
|
||||
{
|
||||
podCIDR: "10.0.0.1/24",
|
||||
ip: "10.0.0.1",
|
||||
expectedGateway: "10.0.0.1",
|
||||
},
|
||||
{
|
||||
podCIDR: "2001:beef::1/48",
|
||||
ip: "2001:beef::1",
|
||||
expectedGateway: "2001:beef::1",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
fhost := nettest.NewFakeHost(nil)
|
||||
fhost.Legacy = false
|
||||
fhost.Runtime = nil
|
||||
mockcni := &mock_cni.MockCNI{}
|
||||
|
||||
fexec := &fakeexec.FakeExec{
|
||||
CommandScript: []fakeexec.FakeCommandAction{},
|
||||
LookPathFunc: func(file string) (string, error) {
|
||||
return fmt.Sprintf("/fake-bin/%s", file), nil
|
||||
},
|
||||
}
|
||||
|
||||
kubenet := newFakeKubenetPlugin(map[kubecontainer.ContainerID]string{}, fexec, fhost)
|
||||
kubenet.cniConfig = mockcni
|
||||
kubenet.iptables = ipttest.NewFake()
|
||||
|
||||
details := make(map[string]interface{})
|
||||
details[network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR] = tc.podCIDR
|
||||
kubenet.Event(network.NET_PLUGIN_EVENT_POD_CIDR_CHANGE, details)
|
||||
|
||||
if kubenet.gateway.String() != tc.expectedGateway {
|
||||
t.Errorf("generated gateway: %q, expecting: %q", kubenet.gateway.String(), tc.expectedGateway)
|
||||
}
|
||||
if kubenet.podCidr != tc.podCIDR {
|
||||
t.Errorf("generated podCidr: %q, expecting: %q", kubenet.podCidr, tc.podCIDR)
|
||||
}
|
||||
existingContainerID := kubecontainer.BuildContainerID("docker", "123")
|
||||
kubenet.podIPs[existingContainerID] = tc.ip
|
||||
|
||||
mockcni.On("DelNetwork", mock.AnythingOfType("*libcni.NetworkConfig"), mock.AnythingOfType("*libcni.RuntimeConf")).Return(nil)
|
||||
|
||||
if err := kubenet.TearDownPod("namespace", "name", existingContainerID); err != nil {
|
||||
t.Fatalf("Unexpected error in TearDownPod: %v", err)
|
||||
}
|
||||
// Assert that the CNI DelNetwork made it through and we didn't crash
|
||||
// without a runtime.
|
||||
mockcni.AssertExpectations(t)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: add unit test for each implementation of network plugin interface
|
55
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_unsupported.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/kubenet/kubenet_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
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 kubenet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
)
|
||||
|
||||
type kubenetNetworkPlugin struct {
|
||||
network.NoopNetworkPlugin
|
||||
}
|
||||
|
||||
func NewPlugin(networkPluginDirs []string) network.NetworkPlugin {
|
||||
return &kubenetNetworkPlugin{}
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Init(host network.Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
return fmt.Errorf("Kubenet is not supported in this build")
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) Name() string {
|
||||
return "kubenet"
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
return fmt.Errorf("Kubenet is not supported in this build")
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) TearDownPod(namespace string, name string, id kubecontainer.ContainerID) error {
|
||||
return fmt.Errorf("Kubenet is not supported in this build")
|
||||
}
|
||||
|
||||
func (plugin *kubenetNetworkPlugin) GetPodNetworkStatus(namespace string, name string, id kubecontainer.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
return nil, fmt.Errorf("Kubenet is not supported in this build")
|
||||
}
|
23
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics/BUILD
generated
vendored
Normal file
23
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics/BUILD
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["metrics.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/github.com/prometheus/client_golang/prometheus: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"],
|
||||
)
|
61
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics/metrics.go
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics/metrics.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
// NetworkPluginOperationsKey is the key for operation count metrics.
|
||||
NetworkPluginOperationsKey = "network_plugin_operations"
|
||||
// NetworkPluginOperationsLatencyKey is the key for the operation latency metrics.
|
||||
NetworkPluginOperationsLatencyKey = "network_plugin_operations_latency_microseconds"
|
||||
|
||||
// Keep the "kubelet" subsystem for backward compatibility.
|
||||
kubeletSubsystem = "kubelet"
|
||||
)
|
||||
|
||||
var (
|
||||
// NetworkPluginOperationsLatency collects operation latency numbers by operation
|
||||
// type.
|
||||
NetworkPluginOperationsLatency = prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
Subsystem: kubeletSubsystem,
|
||||
Name: NetworkPluginOperationsLatencyKey,
|
||||
Help: "Latency in microseconds of network plugin operations. Broken down by operation type.",
|
||||
},
|
||||
[]string{"operation_type"},
|
||||
)
|
||||
)
|
||||
|
||||
var registerMetrics sync.Once
|
||||
|
||||
// Register all metrics.
|
||||
func Register() {
|
||||
registerMetrics.Do(func() {
|
||||
prometheus.MustRegister(NetworkPluginOperationsLatency)
|
||||
})
|
||||
}
|
||||
|
||||
// SinceInMicroseconds gets the time since the specified start in microseconds.
|
||||
func SinceInMicroseconds(start time.Time) float64 {
|
||||
return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
|
||||
}
|
24
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/network.go
generated
vendored
Normal file
24
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/network.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
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 network
|
||||
|
||||
// TODO: Consider making this value configurable.
|
||||
const DefaultInterfaceName = "eth0"
|
||||
|
||||
// UseDefaultMTU is a marker value that indicates the plugin should determine its own MTU
|
||||
// It is the zero value, so a non-initialized value will mean "UseDefault"
|
||||
const UseDefaultMTU = 0
|
397
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/plugins.go
generated
vendored
Normal file
397
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/plugins.go
generated
vendored
Normal file
@ -0,0 +1,397 @@
|
||||
/*
|
||||
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 network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
utilsets "k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/metrics"
|
||||
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const DefaultPluginName = "kubernetes.io/no-op"
|
||||
|
||||
// Called when the node's Pod CIDR is known when using the
|
||||
// controller manager's --allocate-node-cidrs=true option
|
||||
const NET_PLUGIN_EVENT_POD_CIDR_CHANGE = "pod-cidr-change"
|
||||
const NET_PLUGIN_EVENT_POD_CIDR_CHANGE_DETAIL_CIDR = "pod-cidr"
|
||||
|
||||
// Plugin is an interface to network plugins for the kubelet
|
||||
type NetworkPlugin interface {
|
||||
// Init initializes the plugin. This will be called exactly once
|
||||
// before any other methods are called.
|
||||
Init(host Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error
|
||||
|
||||
// Called on various events like:
|
||||
// NET_PLUGIN_EVENT_POD_CIDR_CHANGE
|
||||
Event(name string, details map[string]interface{})
|
||||
|
||||
// Name returns the plugin's name. This will be used when searching
|
||||
// for a plugin by name, e.g.
|
||||
Name() string
|
||||
|
||||
// Returns a set of NET_PLUGIN_CAPABILITY_*
|
||||
Capabilities() utilsets.Int
|
||||
|
||||
// SetUpPod is the method called after the infra container of
|
||||
// the pod has been created but before the other containers of the
|
||||
// pod are launched.
|
||||
SetUpPod(namespace string, name string, podSandboxID kubecontainer.ContainerID, annotations map[string]string) error
|
||||
|
||||
// TearDownPod is the method called before a pod's infra container will be deleted
|
||||
TearDownPod(namespace string, name string, podSandboxID kubecontainer.ContainerID) error
|
||||
|
||||
// GetPodNetworkStatus is the method called to obtain the ipv4 or ipv6 addresses of the container
|
||||
GetPodNetworkStatus(namespace string, name string, podSandboxID kubecontainer.ContainerID) (*PodNetworkStatus, error)
|
||||
|
||||
// Status returns error if the network plugin is in error state
|
||||
Status() error
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// PodNetworkStatus stores the network status of a pod (currently just the primary IP address)
|
||||
// This struct represents version "v1beta1"
|
||||
type PodNetworkStatus struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// IP is the primary ipv4/ipv6 address of the pod. Among other things it is the address that -
|
||||
// - kube expects to be reachable across the cluster
|
||||
// - service endpoints are constructed with
|
||||
// - will be reported in the PodStatus.PodIP field (will override the IP reported by docker)
|
||||
IP net.IP `json:"ip" description:"Primary IP address of the pod"`
|
||||
}
|
||||
|
||||
// Host is an interface that plugins can use to access the kubelet.
|
||||
// TODO(#35457): get rid of this backchannel to the kubelet. The scope of
|
||||
// the back channel is restricted to host-ports/testing, and restricted
|
||||
// to kubenet. No other network plugin wrapper needs it. Other plugins
|
||||
// only require a way to access namespace information and port mapping
|
||||
// information , which they can do directly through the embedded interfaces.
|
||||
type Host interface {
|
||||
// NamespaceGetter is a getter for sandbox namespace information.
|
||||
NamespaceGetter
|
||||
|
||||
// PortMappingGetter is a getter for sandbox port mapping information.
|
||||
PortMappingGetter
|
||||
}
|
||||
|
||||
// NamespaceGetter is an interface to retrieve namespace information for a given
|
||||
// podSandboxID. Typically implemented by runtime shims that are closely coupled to
|
||||
// CNI plugin wrappers like kubenet.
|
||||
type NamespaceGetter interface {
|
||||
// GetNetNS returns network namespace information for the given containerID.
|
||||
// Runtimes should *never* return an empty namespace and nil error for
|
||||
// a container; if error is nil then the namespace string must be valid.
|
||||
GetNetNS(containerID string) (string, error)
|
||||
}
|
||||
|
||||
// PortMappingGetter is an interface to retrieve port mapping information for a given
|
||||
// podSandboxID. Typically implemented by runtime shims that are closely coupled to
|
||||
// CNI plugin wrappers like kubenet.
|
||||
type PortMappingGetter interface {
|
||||
// GetPodPortMappings returns sandbox port mappings information.
|
||||
GetPodPortMappings(containerID string) ([]*hostport.PortMapping, error)
|
||||
}
|
||||
|
||||
// InitNetworkPlugin inits the plugin that matches networkPluginName. Plugins must have unique names.
|
||||
func InitNetworkPlugin(plugins []NetworkPlugin, networkPluginName string, host Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) (NetworkPlugin, error) {
|
||||
if networkPluginName == "" {
|
||||
// default to the no_op plugin
|
||||
plug := &NoopNetworkPlugin{}
|
||||
plug.Sysctl = utilsysctl.New()
|
||||
if err := plug.Init(host, hairpinMode, nonMasqueradeCIDR, mtu); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plug, nil
|
||||
}
|
||||
|
||||
pluginMap := map[string]NetworkPlugin{}
|
||||
|
||||
allErrs := []error{}
|
||||
for _, plugin := range plugins {
|
||||
name := plugin.Name()
|
||||
if errs := validation.IsQualifiedName(name); len(errs) != 0 {
|
||||
allErrs = append(allErrs, fmt.Errorf("network plugin has invalid name: %q: %s", name, strings.Join(errs, ";")))
|
||||
continue
|
||||
}
|
||||
|
||||
if _, found := pluginMap[name]; found {
|
||||
allErrs = append(allErrs, fmt.Errorf("network plugin %q was registered more than once", name))
|
||||
continue
|
||||
}
|
||||
pluginMap[name] = plugin
|
||||
}
|
||||
|
||||
chosenPlugin := pluginMap[networkPluginName]
|
||||
if chosenPlugin != nil {
|
||||
err := chosenPlugin.Init(host, hairpinMode, nonMasqueradeCIDR, mtu)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, fmt.Errorf("Network plugin %q failed init: %v", networkPluginName, err))
|
||||
} else {
|
||||
glog.V(1).Infof("Loaded network plugin %q", networkPluginName)
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, fmt.Errorf("Network plugin %q not found.", networkPluginName))
|
||||
}
|
||||
|
||||
return chosenPlugin, utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
type NoopNetworkPlugin struct {
|
||||
Sysctl utilsysctl.Interface
|
||||
}
|
||||
|
||||
const sysctlBridgeCallIPTables = "net/bridge/bridge-nf-call-iptables"
|
||||
const sysctlBridgeCallIP6Tables = "net/bridge/bridge-nf-call-ip6tables"
|
||||
|
||||
func (plugin *NoopNetworkPlugin) Init(host Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
// Set bridge-nf-call-iptables=1 to maintain compatibility with older
|
||||
// kubernetes versions to ensure the iptables-based kube proxy functions
|
||||
// correctly. Other plugins are responsible for setting this correctly
|
||||
// depending on whether or not they connect containers to Linux bridges
|
||||
// or use some other mechanism (ie, SDN vswitch).
|
||||
|
||||
// Ensure the netfilter module is loaded on kernel >= 3.18; previously
|
||||
// it was built-in.
|
||||
utilexec.New().Command("modprobe", "br-netfilter").CombinedOutput()
|
||||
if err := plugin.Sysctl.SetSysctl(sysctlBridgeCallIPTables, 1); err != nil {
|
||||
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIPTables, err)
|
||||
}
|
||||
if val, err := plugin.Sysctl.GetSysctl(sysctlBridgeCallIP6Tables); err == nil {
|
||||
if val != 1 {
|
||||
if err = plugin.Sysctl.SetSysctl(sysctlBridgeCallIP6Tables, 1); err != nil {
|
||||
glog.Warningf("can't set sysctl %s: %v", sysctlBridgeCallIP6Tables, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) Event(name string, details map[string]interface{}) {
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) Name() string {
|
||||
return DefaultPluginName
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) Capabilities() utilsets.Int {
|
||||
return utilsets.NewInt()
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) TearDownPod(namespace string, name string, id kubecontainer.ContainerID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) GetPodNetworkStatus(namespace string, name string, id kubecontainer.ContainerID) (*PodNetworkStatus, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (plugin *NoopNetworkPlugin) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOnePodIP(execer utilexec.Interface, nsenterPath, netnsPath, interfaceName, addrType string) (net.IP, error) {
|
||||
// Try to retrieve ip inside container network namespace
|
||||
output, err := execer.Command(nsenterPath, fmt.Sprintf("--net=%s", netnsPath), "-F", "--",
|
||||
"ip", "-o", addrType, "addr", "show", "dev", interfaceName, "scope", "global").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unexpected command output %s with error: %v", output, err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
if len(lines) < 1 {
|
||||
return nil, fmt.Errorf("Unexpected command output %s", output)
|
||||
}
|
||||
fields := strings.Fields(lines[0])
|
||||
if len(fields) < 4 {
|
||||
return nil, fmt.Errorf("Unexpected address output %s ", lines[0])
|
||||
}
|
||||
ip, _, err := net.ParseCIDR(fields[3])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CNI failed to parse ip from output %s due to %v", output, err)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// GetPodIP gets the IP of the pod by inspecting the network info inside the pod's network namespace.
|
||||
func GetPodIP(execer utilexec.Interface, nsenterPath, netnsPath, interfaceName string) (net.IP, error) {
|
||||
ip, err := getOnePodIP(execer, nsenterPath, netnsPath, interfaceName, "-4")
|
||||
if err != nil {
|
||||
// Fall back to IPv6 address if no IPv4 address is present
|
||||
ip, err = getOnePodIP(execer, nsenterPath, netnsPath, interfaceName, "-6")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
type NoopPortMappingGetter struct{}
|
||||
|
||||
func (*NoopPortMappingGetter) GetPodPortMappings(containerID string) ([]*hostport.PortMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// The PluginManager wraps a kubelet network plugin and provides synchronization
|
||||
// for a given pod's network operations. Each pod's setup/teardown/status operations
|
||||
// are synchronized against each other, but network operations of other pods can
|
||||
// proceed in parallel.
|
||||
type PluginManager struct {
|
||||
// Network plugin being wrapped
|
||||
plugin NetworkPlugin
|
||||
|
||||
// Pod list and lock
|
||||
podsLock sync.Mutex
|
||||
pods map[string]*podLock
|
||||
}
|
||||
|
||||
func NewPluginManager(plugin NetworkPlugin) *PluginManager {
|
||||
metrics.Register()
|
||||
return &PluginManager{
|
||||
plugin: plugin,
|
||||
pods: make(map[string]*podLock),
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *PluginManager) PluginName() string {
|
||||
return pm.plugin.Name()
|
||||
}
|
||||
|
||||
func (pm *PluginManager) Event(name string, details map[string]interface{}) {
|
||||
pm.plugin.Event(name, details)
|
||||
}
|
||||
|
||||
func (pm *PluginManager) Status() error {
|
||||
return pm.plugin.Status()
|
||||
}
|
||||
|
||||
type podLock struct {
|
||||
// Count of in-flight operations for this pod; when this reaches zero
|
||||
// the lock can be removed from the pod map
|
||||
refcount uint
|
||||
|
||||
// Lock to synchronize operations for this specific pod
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Lock network operations for a specific pod. If that pod is not yet in
|
||||
// the pod map, it will be added. The reference count for the pod will
|
||||
// be increased.
|
||||
func (pm *PluginManager) podLock(fullPodName string) *sync.Mutex {
|
||||
pm.podsLock.Lock()
|
||||
defer pm.podsLock.Unlock()
|
||||
|
||||
lock, ok := pm.pods[fullPodName]
|
||||
if !ok {
|
||||
lock = &podLock{}
|
||||
pm.pods[fullPodName] = lock
|
||||
}
|
||||
lock.refcount++
|
||||
return &lock.mu
|
||||
}
|
||||
|
||||
// Unlock network operations for a specific pod. The reference count for the
|
||||
// pod will be decreased. If the reference count reaches zero, the pod will be
|
||||
// removed from the pod map.
|
||||
func (pm *PluginManager) podUnlock(fullPodName string) {
|
||||
pm.podsLock.Lock()
|
||||
defer pm.podsLock.Unlock()
|
||||
|
||||
lock, ok := pm.pods[fullPodName]
|
||||
if !ok {
|
||||
glog.Warningf("Unbalanced pod lock unref for %s", fullPodName)
|
||||
return
|
||||
} else if lock.refcount == 0 {
|
||||
// This should never ever happen, but handle it anyway
|
||||
delete(pm.pods, fullPodName)
|
||||
glog.Warningf("Pod lock for %s still in map with zero refcount", fullPodName)
|
||||
return
|
||||
}
|
||||
lock.refcount--
|
||||
lock.mu.Unlock()
|
||||
if lock.refcount == 0 {
|
||||
delete(pm.pods, fullPodName)
|
||||
}
|
||||
}
|
||||
|
||||
// recordOperation records operation and duration
|
||||
func recordOperation(operation string, start time.Time) {
|
||||
metrics.NetworkPluginOperationsLatency.WithLabelValues(operation).Observe(metrics.SinceInMicroseconds(start))
|
||||
}
|
||||
|
||||
func (pm *PluginManager) GetPodNetworkStatus(podNamespace, podName string, id kubecontainer.ContainerID) (*PodNetworkStatus, error) {
|
||||
defer recordOperation("get_pod_network_status", time.Now())
|
||||
fullPodName := kubecontainer.BuildPodFullName(podName, podNamespace)
|
||||
pm.podLock(fullPodName).Lock()
|
||||
defer pm.podUnlock(fullPodName)
|
||||
|
||||
netStatus, err := pm.plugin.GetPodNetworkStatus(podNamespace, podName, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NetworkPlugin %s failed on the status hook for pod %q: %v", pm.plugin.Name(), fullPodName, err)
|
||||
}
|
||||
|
||||
return netStatus, nil
|
||||
}
|
||||
|
||||
func (pm *PluginManager) SetUpPod(podNamespace, podName string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
defer recordOperation("set_up_pod", time.Now())
|
||||
fullPodName := kubecontainer.BuildPodFullName(podName, podNamespace)
|
||||
pm.podLock(fullPodName).Lock()
|
||||
defer pm.podUnlock(fullPodName)
|
||||
|
||||
glog.V(3).Infof("Calling network plugin %s to set up pod %q", pm.plugin.Name(), fullPodName)
|
||||
if err := pm.plugin.SetUpPod(podNamespace, podName, id, annotations); err != nil {
|
||||
return fmt.Errorf("NetworkPlugin %s failed to set up pod %q network: %v", pm.plugin.Name(), fullPodName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *PluginManager) TearDownPod(podNamespace, podName string, id kubecontainer.ContainerID) error {
|
||||
defer recordOperation("tear_down_pod", time.Now())
|
||||
fullPodName := kubecontainer.BuildPodFullName(podName, podNamespace)
|
||||
pm.podLock(fullPodName).Lock()
|
||||
defer pm.podUnlock(fullPodName)
|
||||
|
||||
glog.V(3).Infof("Calling network plugin %s to tear down pod %q", pm.plugin.Name(), fullPodName)
|
||||
if err := pm.plugin.TearDownPod(podNamespace, podName, id); err != nil {
|
||||
return fmt.Errorf("NetworkPlugin %s failed to teardown pod %q network: %v", pm.plugin.Name(), fullPodName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/BUILD
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/BUILD
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"fake_host.go",
|
||||
"mock_network_plugin.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing",
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/container/testing:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network/hostport:go_default_library",
|
||||
"//vendor/github.com/golang/mock/gomock:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["plugins_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/container:go_default_library",
|
||||
"//pkg/kubelet/dockershim/network:go_default_library",
|
||||
"//pkg/util/sysctl/testing:go_default_library",
|
||||
"//vendor/github.com/golang/mock/gomock:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/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"],
|
||||
)
|
73
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/fake_host.go
generated
vendored
Normal file
73
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/fake_host.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 testing
|
||||
|
||||
// helper for testing plugins
|
||||
// a fake host is created here that can be used by plugins for testing
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network/hostport"
|
||||
)
|
||||
|
||||
type fakeNetworkHost struct {
|
||||
fakeNamespaceGetter
|
||||
FakePortMappingGetter
|
||||
kubeClient clientset.Interface
|
||||
Legacy bool
|
||||
Runtime *containertest.FakeRuntime
|
||||
}
|
||||
|
||||
func NewFakeHost(kubeClient clientset.Interface) *fakeNetworkHost {
|
||||
host := &fakeNetworkHost{kubeClient: kubeClient, Legacy: true, Runtime: &containertest.FakeRuntime{}}
|
||||
return host
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetPodByName(name, namespace string) (*v1.Pod, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetKubeClient() clientset.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nh *fakeNetworkHost) GetRuntime() kubecontainer.Runtime {
|
||||
return nh.Runtime
|
||||
}
|
||||
|
||||
func (nh *fakeNetworkHost) SupportsLegacyFeatures() bool {
|
||||
return nh.Legacy
|
||||
}
|
||||
|
||||
type fakeNamespaceGetter struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
func (nh *fakeNamespaceGetter) GetNetNS(containerID string) (string, error) {
|
||||
return nh.ns, nil
|
||||
}
|
||||
|
||||
type FakePortMappingGetter struct {
|
||||
PortMaps map[string][]*hostport.PortMapping
|
||||
}
|
||||
|
||||
func (pm *FakePortMappingGetter) GetPodPortMappings(containerID string) ([]*hostport.PortMapping, error) {
|
||||
return pm.PortMaps[containerID], nil
|
||||
}
|
133
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/mock_network_plugin.go
generated
vendored
Normal file
133
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/mock_network_plugin.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// Generated code, generated via: `mockgen k8s.io/kubernetes/pkg/kubelet/network NetworkPlugin > $GOPATH/src/k8s.io/kubernetes/pkg/kubelet/network/testing/mock_network_plugin.go`
|
||||
// Edited by hand for boilerplate and gofmt.
|
||||
// TODO, this should be autogenerated/autoupdated by scripts.
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
sets "k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
container "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
)
|
||||
|
||||
// Mock of NetworkPlugin interface
|
||||
type MockNetworkPlugin struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *_MockNetworkPluginRecorder
|
||||
}
|
||||
|
||||
// Recorder for MockNetworkPlugin (not exported)
|
||||
type _MockNetworkPluginRecorder struct {
|
||||
mock *MockNetworkPlugin
|
||||
}
|
||||
|
||||
func NewMockNetworkPlugin(ctrl *gomock.Controller) *MockNetworkPlugin {
|
||||
mock := &MockNetworkPlugin{ctrl: ctrl}
|
||||
mock.recorder = &_MockNetworkPluginRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) EXPECT() *_MockNetworkPluginRecorder {
|
||||
return _m.recorder
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Capabilities() sets.Int {
|
||||
ret := _m.ctrl.Call(_m, "Capabilities")
|
||||
ret0, _ := ret[0].(sets.Int)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Finish() {
|
||||
_m.ctrl.Finish()
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) Capabilities() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Capabilities")
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Event(_param0 string, _param1 map[string]interface{}) {
|
||||
_m.ctrl.Call(_m, "Event", _param0, _param1)
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) Event(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Event", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) GetPodNetworkStatus(_param0 string, _param1 string, _param2 container.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
ret := _m.ctrl.Call(_m, "GetPodNetworkStatus", _param0, _param1, _param2)
|
||||
ret0, _ := ret[0].(*network.PodNetworkStatus)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) GetPodNetworkStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetPodNetworkStatus", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Init(_param0 network.Host, _param1 kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
ret := _m.ctrl.Call(_m, "Init", _param0, _param1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) Init(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Init", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Name() string {
|
||||
ret := _m.ctrl.Call(_m, "Name")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) Name() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Name")
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) SetUpPod(_param0 string, _param1 string, _param2 container.ContainerID, annotations map[string]string) error {
|
||||
ret := _m.ctrl.Call(_m, "SetUpPod", _param0, _param1, _param2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) SetUpPod(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SetUpPod", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) Status() error {
|
||||
ret := _m.ctrl.Call(_m, "Status")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) Status() *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Status")
|
||||
}
|
||||
|
||||
func (_m *MockNetworkPlugin) TearDownPod(_param0 string, _param1 string, _param2 container.ContainerID) error {
|
||||
ret := _m.ctrl.Call(_m, "TearDownPod", _param0, _param1, _param2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockNetworkPluginRecorder) TearDownPod(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "TearDownPod", arg0, arg1, arg2)
|
||||
}
|
249
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/plugins_test.go
generated
vendored
Normal file
249
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/network/testing/plugins_test.go
generated
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
utilsets "k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
sysctltest "k8s.io/kubernetes/pkg/util/sysctl/testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSelectDefaultPlugin(t *testing.T) {
|
||||
all_plugins := []network.NetworkPlugin{}
|
||||
plug, err := network.InitNetworkPlugin(all_plugins, "", NewFakeHost(nil), kubeletconfig.HairpinNone, "10.0.0.0/8", network.UseDefaultMTU)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error in selecting default plugin: %v", err)
|
||||
}
|
||||
if plug == nil {
|
||||
t.Fatalf("Failed to select the default plugin.")
|
||||
}
|
||||
if plug.Name() != network.DefaultPluginName {
|
||||
t.Errorf("Failed to select the default plugin. Expected %s. Got %s", network.DefaultPluginName, plug.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
tests := []struct {
|
||||
setting string
|
||||
expectedLen int
|
||||
}{
|
||||
{
|
||||
setting: "net/bridge/bridge-nf-call-iptables",
|
||||
expectedLen: 1,
|
||||
},
|
||||
{
|
||||
setting: "net/bridge/bridge-nf-call-ip6tables",
|
||||
expectedLen: 2,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
sysctl := sysctltest.NewFake()
|
||||
sysctl.Settings[tt.setting] = 0
|
||||
plug := &network.NoopNetworkPlugin{}
|
||||
plug.Sysctl = sysctl
|
||||
plug.Init(NewFakeHost(nil), kubeletconfig.HairpinNone, "10.0.0.0/8", network.UseDefaultMTU)
|
||||
// Verify the sysctl specified is set
|
||||
assert.Equal(t, 1, sysctl.Settings[tt.setting], tt.setting+" sysctl should have been set")
|
||||
// Verify iptables is always set
|
||||
assert.Equal(t, 1, sysctl.Settings["net/bridge/bridge-nf-call-iptables"], "net/bridge/bridge-nf-call-iptables sysctl should have been set")
|
||||
// Verify ip6tables is only set if it existed
|
||||
assert.Len(t, sysctl.Settings, tt.expectedLen, "length wrong for "+tt.setting)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginManager(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
fnp := NewMockNetworkPlugin(ctrl)
|
||||
defer fnp.Finish()
|
||||
pm := network.NewPluginManager(fnp)
|
||||
|
||||
fnp.EXPECT().Name().Return("someNetworkPlugin").AnyTimes()
|
||||
|
||||
allCreatedWg := sync.WaitGroup{}
|
||||
allCreatedWg.Add(1)
|
||||
allDoneWg := sync.WaitGroup{}
|
||||
|
||||
// 10 pods, 4 setup/status/teardown runs each. Ensure that network locking
|
||||
// works and the pod map isn't concurrently accessed
|
||||
for i := 0; i < 10; i++ {
|
||||
podName := fmt.Sprintf("pod%d", i)
|
||||
containerID := kubecontainer.ContainerID{ID: podName}
|
||||
|
||||
fnp.EXPECT().SetUpPod("", podName, containerID).Return(nil).Times(4)
|
||||
fnp.EXPECT().GetPodNetworkStatus("", podName, containerID).Return(&network.PodNetworkStatus{IP: net.ParseIP("1.2.3.4")}, nil).Times(4)
|
||||
fnp.EXPECT().TearDownPod("", podName, containerID).Return(nil).Times(4)
|
||||
|
||||
for x := 0; x < 4; x++ {
|
||||
allDoneWg.Add(1)
|
||||
go func(name string, id kubecontainer.ContainerID, num int) {
|
||||
defer allDoneWg.Done()
|
||||
|
||||
// Block all goroutines from running until all have
|
||||
// been created and are ready. This ensures we
|
||||
// have more pod network operations running
|
||||
// concurrently.
|
||||
allCreatedWg.Wait()
|
||||
|
||||
if err := pm.SetUpPod("", name, id, nil); err != nil {
|
||||
t.Errorf("Failed to set up pod %q: %v", name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := pm.GetPodNetworkStatus("", name, id); err != nil {
|
||||
t.Errorf("Failed to inspect pod %q: %v", name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pm.TearDownPod("", name, id); err != nil {
|
||||
t.Errorf("Failed to tear down pod %q: %v", name, err)
|
||||
return
|
||||
}
|
||||
}(podName, containerID, x)
|
||||
}
|
||||
}
|
||||
// Block all goroutines from running until all have been created and started
|
||||
allCreatedWg.Done()
|
||||
|
||||
// Wait for them all to finish
|
||||
allDoneWg.Wait()
|
||||
}
|
||||
|
||||
type hookableFakeNetworkPluginSetupHook func(namespace, name string, id kubecontainer.ContainerID)
|
||||
|
||||
type hookableFakeNetworkPlugin struct {
|
||||
setupHook hookableFakeNetworkPluginSetupHook
|
||||
}
|
||||
|
||||
func newHookableFakeNetworkPlugin(setupHook hookableFakeNetworkPluginSetupHook) *hookableFakeNetworkPlugin {
|
||||
return &hookableFakeNetworkPlugin{
|
||||
setupHook: setupHook,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) Init(host network.Host, hairpinMode kubeletconfig.HairpinMode, nonMasqueradeCIDR string, mtu int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) Event(name string, details map[string]interface{}) {
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) Name() string {
|
||||
return "fakeplugin"
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) Capabilities() utilsets.Int {
|
||||
return utilsets.NewInt()
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) SetUpPod(namespace string, name string, id kubecontainer.ContainerID, annotations map[string]string) error {
|
||||
if p.setupHook != nil {
|
||||
p.setupHook(namespace, name, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) TearDownPod(string, string, kubecontainer.ContainerID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) GetPodNetworkStatus(string, string, kubecontainer.ContainerID) (*network.PodNetworkStatus, error) {
|
||||
return &network.PodNetworkStatus{IP: net.ParseIP("10.1.2.3")}, nil
|
||||
}
|
||||
|
||||
func (p *hookableFakeNetworkPlugin) Status() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure that one pod's network operations don't block another's. If the
|
||||
// test is successful (eg, first pod doesn't block on second) the test
|
||||
// will complete. If unsuccessful, it will hang and get killed.
|
||||
func TestMultiPodParallelNetworkOps(t *testing.T) {
|
||||
podWg := sync.WaitGroup{}
|
||||
podWg.Add(1)
|
||||
|
||||
// Can't do this with MockNetworkPlugin because the gomock controller
|
||||
// has its own locks which don't allow the parallel network operation
|
||||
// to proceed.
|
||||
didWait := false
|
||||
fakePlugin := newHookableFakeNetworkPlugin(func(podNamespace, podName string, id kubecontainer.ContainerID) {
|
||||
if podName == "waiter" {
|
||||
podWg.Wait()
|
||||
didWait = true
|
||||
}
|
||||
})
|
||||
pm := network.NewPluginManager(fakePlugin)
|
||||
|
||||
opsWg := sync.WaitGroup{}
|
||||
|
||||
// Start the pod that will wait for the other to complete
|
||||
opsWg.Add(1)
|
||||
go func() {
|
||||
defer opsWg.Done()
|
||||
|
||||
podName := "waiter"
|
||||
containerID := kubecontainer.ContainerID{ID: podName}
|
||||
|
||||
// Setup will block on the runner pod completing. If network
|
||||
// operations locking isn't correct (eg pod network operations
|
||||
// block other pods) setUpPod() will never return.
|
||||
if err := pm.SetUpPod("", podName, containerID, nil); err != nil {
|
||||
t.Errorf("Failed to set up waiter pod: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pm.TearDownPod("", podName, containerID); err != nil {
|
||||
t.Errorf("Failed to tear down waiter pod: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
opsWg.Add(1)
|
||||
go func() {
|
||||
defer opsWg.Done()
|
||||
// Let other pod proceed
|
||||
defer podWg.Done()
|
||||
|
||||
podName := "runner"
|
||||
containerID := kubecontainer.ContainerID{ID: podName}
|
||||
|
||||
if err := pm.SetUpPod("", podName, containerID, nil); err != nil {
|
||||
t.Errorf("Failed to set up runner pod: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pm.TearDownPod("", podName, containerID); err != nil {
|
||||
t.Errorf("Failed to tear down runner pod: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
opsWg.Wait()
|
||||
|
||||
if !didWait {
|
||||
t.Errorf("waiter pod didn't wait for runner pod!")
|
||||
}
|
||||
}
|
1
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/remote/BUILD
generated
vendored
1
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/remote/BUILD
generated
vendored
@ -13,7 +13,6 @@ go_library(
|
||||
"//pkg/kubelet/apis/cri/runtime/v1alpha2:go_default_library",
|
||||
"//pkg/kubelet/dockershim:go_default_library",
|
||||
"//pkg/kubelet/util:go_default_library",
|
||||
"//pkg/util/interrupt:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
],
|
||||
|
24
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/remote/docker_server.go
generated
vendored
24
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/remote/docker_server.go
generated
vendored
@ -21,13 +21,15 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockershim"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util"
|
||||
"k8s.io/kubernetes/pkg/util/interrupt"
|
||||
)
|
||||
|
||||
// maxMsgSize use 8MB as the default message size limit.
|
||||
// grpc library default is 4MB
|
||||
const maxMsgSize = 1024 * 1024 * 8
|
||||
|
||||
// DockerServer is the grpc server of dockershim.
|
||||
type DockerServer struct {
|
||||
// endpoint is the endpoint to serve on.
|
||||
@ -60,22 +62,16 @@ func (s *DockerServer) Start() error {
|
||||
return fmt.Errorf("failed to listen on %q: %v", s.endpoint, err)
|
||||
}
|
||||
// Create the grpc server and register runtime and image services.
|
||||
s.server = grpc.NewServer()
|
||||
s.server = grpc.NewServer(
|
||||
grpc.MaxRecvMsgSize(maxMsgSize),
|
||||
grpc.MaxSendMsgSize(maxMsgSize),
|
||||
)
|
||||
runtimeapi.RegisterRuntimeServiceServer(s.server, s.service)
|
||||
runtimeapi.RegisterImageServiceServer(s.server, s.service)
|
||||
go func() {
|
||||
// Use interrupt handler to make sure the server to be stopped properly.
|
||||
h := interrupt.New(nil, s.Stop)
|
||||
err := h.Run(func() error { return s.server.Serve(l) })
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to serve connections: %v", err)
|
||||
if err := s.server.Serve(l); err != nil {
|
||||
glog.Fatalf("Failed to serve connections: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the dockershim grpc server.
|
||||
func (s *DockerServer) Stop() {
|
||||
glog.V(2).Infof("Stop docker server")
|
||||
s.server.Stop()
|
||||
}
|
||||
|
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/security_context.go
generated
vendored
30
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/security_context.go
generated
vendored
@ -25,7 +25,7 @@ import (
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
|
||||
knetwork "k8s.io/kubernetes/pkg/kubelet/network"
|
||||
knetwork "k8s.io/kubernetes/pkg/kubelet/dockershim/network"
|
||||
)
|
||||
|
||||
// applySandboxSecurityContext updates docker sandbox options according to security context.
|
||||
@ -39,13 +39,18 @@ func applySandboxSecurityContext(lc *runtimeapi.LinuxPodSandboxConfig, config *d
|
||||
sc = &runtimeapi.LinuxContainerSecurityContext{
|
||||
SupplementalGroups: lc.SecurityContext.SupplementalGroups,
|
||||
RunAsUser: lc.SecurityContext.RunAsUser,
|
||||
RunAsGroup: lc.SecurityContext.RunAsGroup,
|
||||
ReadonlyRootfs: lc.SecurityContext.ReadonlyRootfs,
|
||||
SelinuxOptions: lc.SecurityContext.SelinuxOptions,
|
||||
NamespaceOptions: lc.SecurityContext.NamespaceOptions,
|
||||
}
|
||||
}
|
||||
|
||||
modifyContainerConfig(sc, config)
|
||||
err := modifyContainerConfig(sc, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := modifyHostConfig(sc, hc, separator); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -59,7 +64,10 @@ func applyContainerSecurityContext(lc *runtimeapi.LinuxContainerConfig, podSandb
|
||||
return nil
|
||||
}
|
||||
|
||||
modifyContainerConfig(lc.SecurityContext, config)
|
||||
err := modifyContainerConfig(lc.SecurityContext, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := modifyHostConfig(lc.SecurityContext, hc, separator); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -68,9 +76,9 @@ func applyContainerSecurityContext(lc *runtimeapi.LinuxContainerConfig, podSandb
|
||||
}
|
||||
|
||||
// modifyContainerConfig applies container security context config to dockercontainer.Config.
|
||||
func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) {
|
||||
func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config *dockercontainer.Config) error {
|
||||
if sc == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if sc.RunAsUser != nil {
|
||||
config.User = strconv.FormatInt(sc.GetRunAsUser().Value, 10)
|
||||
@ -78,6 +86,18 @@ func modifyContainerConfig(sc *runtimeapi.LinuxContainerSecurityContext, config
|
||||
if sc.RunAsUsername != "" {
|
||||
config.User = sc.RunAsUsername
|
||||
}
|
||||
|
||||
user := config.User
|
||||
if sc.RunAsGroup != nil {
|
||||
if user == "" {
|
||||
return fmt.Errorf("runAsGroup is specified without a runAsUser.")
|
||||
}
|
||||
user = fmt.Sprintf("%s:%d", config.User, sc.GetRunAsGroup().Value)
|
||||
}
|
||||
|
||||
config.User = user
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// modifyHostConfig applies security context config to dockercontainer.HostConfig.
|
||||
|
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/security_context_test.go
generated
vendored
43
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/security_context_test.go
generated
vendored
@ -31,11 +31,13 @@ import (
|
||||
func TestModifyContainerConfig(t *testing.T) {
|
||||
var uid int64 = 123
|
||||
var username = "testuser"
|
||||
var gid int64 = 423
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
sc *runtimeapi.LinuxContainerSecurityContext
|
||||
expected *dockercontainer.Config
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "container.SecurityContext.RunAsUser set",
|
||||
@ -45,6 +47,7 @@ func TestModifyContainerConfig(t *testing.T) {
|
||||
expected: &dockercontainer.Config{
|
||||
User: strconv.FormatInt(uid, 10),
|
||||
},
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "container.SecurityContext.RunAsUsername set",
|
||||
@ -54,18 +57,54 @@ func TestModifyContainerConfig(t *testing.T) {
|
||||
expected: &dockercontainer.Config{
|
||||
User: username,
|
||||
},
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "no RunAsUser value set",
|
||||
sc: &runtimeapi.LinuxContainerSecurityContext{},
|
||||
expected: &dockercontainer.Config{},
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "RunAsUser value set, RunAsGroup set",
|
||||
sc: &runtimeapi.LinuxContainerSecurityContext{
|
||||
RunAsUser: &runtimeapi.Int64Value{Value: uid},
|
||||
RunAsGroup: &runtimeapi.Int64Value{Value: gid},
|
||||
},
|
||||
expected: &dockercontainer.Config{
|
||||
User: "123:423",
|
||||
},
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "RunAsUsername value set, RunAsGroup set",
|
||||
sc: &runtimeapi.LinuxContainerSecurityContext{
|
||||
RunAsUsername: username,
|
||||
RunAsGroup: &runtimeapi.Int64Value{Value: gid},
|
||||
},
|
||||
expected: &dockercontainer.Config{
|
||||
User: "testuser:423",
|
||||
},
|
||||
isErr: false,
|
||||
},
|
||||
{
|
||||
name: "RunAsUser/RunAsUsername not set, RunAsGroup set",
|
||||
sc: &runtimeapi.LinuxContainerSecurityContext{
|
||||
RunAsGroup: &runtimeapi.Int64Value{Value: gid},
|
||||
},
|
||||
isErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
dockerCfg := &dockercontainer.Config{}
|
||||
modifyContainerConfig(tc.sc, dockerCfg)
|
||||
assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
|
||||
err := modifyContainerConfig(tc.sc, dockerCfg)
|
||||
if tc.isErr {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.expected, dockerCfg, "[Test case %q]", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
6
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/selinux_util.go
generated
vendored
6
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/selinux_util.go
generated
vendored
@ -54,12 +54,6 @@ func selinuxLabelLevel(separator rune) string {
|
||||
return fmt.Sprintf("label%clevel", separator)
|
||||
}
|
||||
|
||||
// dockerLaelDisable returns the Docker security opt that disables SELinux for
|
||||
// the container.
|
||||
func selinuxLabelDisable(separator rune) string {
|
||||
return fmt.Sprintf("label%cdisable", separator)
|
||||
}
|
||||
|
||||
// addSELinuxOptions adds SELinux options to config using the given
|
||||
// separator.
|
||||
func addSELinuxOptions(config []string, selinuxOpts *runtimeapi.SELinuxOption, separator rune) []string {
|
||||
|
66
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/testing/util.go
generated
vendored
66
vendor/k8s.io/kubernetes/pkg/kubelet/dockershim/testing/util.go
generated
vendored
@ -1,66 +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"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MemStore is an implementation of CheckpointStore interface which stores checkpoint in memory.
|
||||
type MemStore struct {
|
||||
mem map[string][]byte
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewMemStore() *MemStore {
|
||||
return &MemStore{mem: make(map[string][]byte)}
|
||||
}
|
||||
|
||||
func (mstore *MemStore) Write(key string, data []byte) error {
|
||||
mstore.Lock()
|
||||
defer mstore.Unlock()
|
||||
mstore.mem[key] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mstore *MemStore) Read(key string) ([]byte, error) {
|
||||
mstore.Lock()
|
||||
defer mstore.Unlock()
|
||||
data, ok := mstore.mem[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("checkpoint is not found")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (mstore *MemStore) Delete(key string) error {
|
||||
mstore.Lock()
|
||||
defer mstore.Unlock()
|
||||
delete(mstore.mem, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mstore *MemStore) List() ([]string, error) {
|
||||
mstore.Lock()
|
||||
defer mstore.Unlock()
|
||||
keys := make([]string, 0)
|
||||
for key := range mstore.mem {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
Reference in New Issue
Block a user