mirror of https://github.com/k3s-io/k3s
Revert "Revert "[WIP] southbound networking hooks in kubelet""
parent
1dd4600eb6
commit
7ddcecfd1c
|
@ -20,6 +20,9 @@ package app
|
|||
import (
|
||||
// Credential providers
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/credentialprovider/gcp"
|
||||
// Network plugins
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network/exec"
|
||||
// Volume plugins
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/empty_dir"
|
||||
|
@ -44,3 +47,13 @@ func ProbeVolumePlugins() []volume.Plugin {
|
|||
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
// ProbeNetworkPlugins collects all compiled-in plugins
|
||||
func ProbeNetworkPlugins() []network.NetworkPlugin {
|
||||
allPlugins := []network.NetworkPlugin{}
|
||||
|
||||
// for each existing plugin, add to the list
|
||||
allPlugins = append(allPlugins, exec.ProbeNetworkPlugins()...)
|
||||
|
||||
return allPlugins
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
|
@ -79,6 +80,7 @@ type KubeletServer struct {
|
|||
StreamingConnectionIdleTimeout time.Duration
|
||||
ImageGCHighThresholdPercent int
|
||||
ImageGCLowThresholdPercent int
|
||||
NetworkPluginName string
|
||||
}
|
||||
|
||||
// NewKubeletServer will create a new KubeletServer with default values.
|
||||
|
@ -104,6 +106,7 @@ func NewKubeletServer() *KubeletServer {
|
|||
MasterServiceNamespace: api.NamespaceDefault,
|
||||
ImageGCHighThresholdPercent: 90,
|
||||
ImageGCLowThresholdPercent: 80,
|
||||
NetworkPluginName: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +145,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
|
|||
fs.DurationVar(&s.StreamingConnectionIdleTimeout, "streaming_connection_idle_timeout", 0, "Maximum time a streaming connection can be idle before the connection is automatically closed. Example: '5m'")
|
||||
fs.IntVar(&s.ImageGCHighThresholdPercent, "image_gc_high_threshold", s.ImageGCHighThresholdPercent, "The percent of disk usage after which image garbage collection is always run. Default: 90%%")
|
||||
fs.IntVar(&s.ImageGCLowThresholdPercent, "image_gc_low_threshold", s.ImageGCLowThresholdPercent, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Default: 80%%")
|
||||
fs.StringVar(&s.NetworkPluginName, "network_plugin", s.NetworkPluginName, "<Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle")
|
||||
}
|
||||
|
||||
// Run runs the specified KubeletServer. This should never exit.
|
||||
|
@ -200,6 +204,8 @@ func (s *KubeletServer) Run(_ []string) error {
|
|||
KubeClient: client,
|
||||
MasterServiceNamespace: s.MasterServiceNamespace,
|
||||
VolumePlugins: ProbeVolumePlugins(),
|
||||
NetworkPlugins: ProbeNetworkPlugins(),
|
||||
NetworkPluginName: s.NetworkPluginName,
|
||||
StreamingConnectionIdleTimeout: s.StreamingConnectionIdleTimeout,
|
||||
ImageGCPolicy: imageGCPolicy,
|
||||
}
|
||||
|
@ -397,6 +403,8 @@ type KubeletConfig struct {
|
|||
Runonce bool
|
||||
MasterServiceNamespace string
|
||||
VolumePlugins []volume.Plugin
|
||||
NetworkPlugins []network.NetworkPlugin
|
||||
NetworkPluginName string
|
||||
StreamingConnectionIdleTimeout time.Duration
|
||||
Recorder record.EventRecorder
|
||||
TLSOptions *kubelet.TLSOptions
|
||||
|
@ -437,6 +445,8 @@ func createAndInitKubelet(kc *KubeletConfig, pc *config.PodConfig) (*kubelet.Kub
|
|||
net.IP(kc.ClusterDNS),
|
||||
kc.MasterServiceNamespace,
|
||||
kc.VolumePlugins,
|
||||
kc.NetworkPlugins,
|
||||
kc.NetworkPluginName,
|
||||
kc.StreamingConnectionIdleTimeout,
|
||||
kc.Recorder,
|
||||
kc.CadvisorInterface,
|
||||
|
|
|
@ -42,6 +42,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/envvars"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/metrics"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||
|
@ -118,6 +119,8 @@ func NewMainKubelet(
|
|||
clusterDNS net.IP,
|
||||
masterServiceNamespace string,
|
||||
volumePlugins []volume.Plugin,
|
||||
networkPlugins []network.NetworkPlugin,
|
||||
networkPluginName string,
|
||||
streamingConnectionIdleTimeout time.Duration,
|
||||
recorder record.EventRecorder,
|
||||
cadvisorInterface cadvisor.Interface,
|
||||
|
@ -220,6 +223,12 @@ func NewMainKubelet(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if plug, err := network.InitNetworkPlugin(networkPlugins, networkPluginName, &networkHost{klet}); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
klet.networkPlugin = plug
|
||||
}
|
||||
|
||||
klet.podStatuses = make(map[string]api.PodStatus)
|
||||
|
||||
klet.mirrorManager = newBasicMirrorManager(klet.kubeClient)
|
||||
|
@ -301,6 +310,9 @@ type Kubelet struct {
|
|||
// Volume plugins.
|
||||
volumePluginMgr volume.PluginMgr
|
||||
|
||||
// Network plugin
|
||||
networkPlugin network.NetworkPlugin
|
||||
|
||||
// probe runner holder
|
||||
prober probeHolder
|
||||
// container readiness state holder
|
||||
|
@ -1357,6 +1369,11 @@ func (kl *Kubelet) syncPod(pod *api.Pod, hasMirrorPod bool, containersInPod dock
|
|||
}
|
||||
glog.Infof("Creating pod infra container for %q", podFullName)
|
||||
podInfraContainerID, err = kl.createPodInfraContainer(pod)
|
||||
|
||||
// Call the networking plugin
|
||||
if err == nil {
|
||||
err = kl.networkPlugin.SetUpPod(pod.Namespace, pod.Name, podInfraContainerID)
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to create pod infra container: %v; Skipping pod %q", err, podFullName)
|
||||
return err
|
||||
|
@ -1548,6 +1565,14 @@ func (kl *Kubelet) SyncPods(allPods []api.Pod, podSyncTypes map[types.UID]metric
|
|||
pc := podContainer{podFullName, uid, containerName}
|
||||
_, ok := desiredContainers[pc]
|
||||
if err != nil || !ok {
|
||||
// call the networking plugin for teardown
|
||||
if containerName == dockertools.PodInfraContainerName {
|
||||
name, namespace, _ := ParsePodFullName(podFullName)
|
||||
err := kl.networkPlugin.TearDownPod(namespace, name, dockertools.DockerID(dockerContainers[ix].ID))
|
||||
if err != nil {
|
||||
glog.Errorf("Network plugin pre-delete method returned an error: %v", err)
|
||||
}
|
||||
}
|
||||
glog.V(1).Infof("Killing unwanted container %+v", pc)
|
||||
err = kl.killContainer(dockerContainers[ix])
|
||||
if err != nil {
|
||||
|
|
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/metrics"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume"
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/volume/host_path"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||
|
@ -73,6 +74,7 @@ func newTestKubelet(t *testing.T) *TestKubelet {
|
|||
kubelet.kubeClient = fakeKubeClient
|
||||
kubelet.dockerPuller = &dockertools.FakeDockerPuller{}
|
||||
kubelet.hostname = "testnode"
|
||||
kubelet.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil))
|
||||
if tempDir, err := ioutil.TempDir("/tmp", "kubelet_test."); err != nil {
|
||||
t.Fatalf("can't make a temp rootdir: %v", err)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package exec scans and loads networking plugins that are installed
|
||||
// under /usr/libexec/kubernetes/kubelet-plugins/net/exec/
|
||||
// The layout convention for a plugin is:
|
||||
// plugin-name/ (plugins have to be directories first)
|
||||
// plugin-name/plugin-name (executable that will be called out, see Vendoring Note for more nuances)
|
||||
// plugin-name/<other-files>
|
||||
// where, 'executable' has the following requirements:
|
||||
// - should have exec permissions
|
||||
// - should give non-zero exit code on failure, and zero on sucess
|
||||
// - the arguments will be <action> <pod_namespace> <pod_name> <docker_id_of_infra_container>
|
||||
// whereupon, <action> will be one of:
|
||||
// - init, called when the kubelet loads the plugin
|
||||
// - setup, called after the infra container of a pod is
|
||||
// created, but before other containers of the pod are created
|
||||
// - teardown, called before the pod infra container is killed
|
||||
// As the executables are called, the file-descriptors stdin, stdout, stderr
|
||||
// remain open. The combined output of stdout/stderr is captured and logged.
|
||||
//
|
||||
// Note: If the pod infra container self-terminates (e.g. crashes or is killed),
|
||||
// the entire pod lifecycle will be restarted, but teardown will not be called.
|
||||
//
|
||||
// Vendoring Note:
|
||||
// Plugin Names can be vendored also. Use '~' as the escaped name for plugin directories.
|
||||
// And expect command line argument to call vendored plugins as 'vendor/pluginName'
|
||||
// e.g. pluginName = mysdn
|
||||
// vendorname = mycompany
|
||||
// then, plugin layout should be
|
||||
// mycompany~mysdn/
|
||||
// mycompany~mysdn/mysdn (this becomes the executable)
|
||||
// mycompany~mysdn/<other-files>
|
||||
// and, call the kubelet with '--network-plugin=mycompany/mysdn'
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
utilexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type execNetworkPlugin struct {
|
||||
execName string
|
||||
execPath string
|
||||
host network.Host
|
||||
}
|
||||
|
||||
const (
|
||||
initCmd = "init"
|
||||
setUpCmd = "setup"
|
||||
tearDownCmd = "teardown"
|
||||
execDir = "/usr/libexec/kubernetes/kubelet-plugins/net/exec/"
|
||||
X_OK = 0x1
|
||||
)
|
||||
|
||||
func ProbeNetworkPlugins() []network.NetworkPlugin {
|
||||
return probeNetworkPluginsWithExecDir(execDir)
|
||||
}
|
||||
|
||||
func probeNetworkPluginsWithExecDir(pluginDir string) []network.NetworkPlugin {
|
||||
execPlugins := []network.NetworkPlugin{}
|
||||
|
||||
files, _ := ioutil.ReadDir(pluginDir)
|
||||
for _, f := range files {
|
||||
// only directories are counted as plugins
|
||||
// and pluginDir/dirname/dirname should be an executable
|
||||
// unless dirname contains '~' for escaping namespace
|
||||
// e.g. dirname = vendor~ipvlan
|
||||
// then, executable will be pluginDir/dirname/ipvlan
|
||||
if f.IsDir() {
|
||||
execPath := path.Join(pluginDir, f.Name())
|
||||
execPlugins = append(execPlugins, &execNetworkPlugin{execName: network.UnescapePluginName(f.Name()), execPath: execPath})
|
||||
}
|
||||
}
|
||||
return execPlugins
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) Init(host network.Host) error {
|
||||
err := plugin.validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plugin.host = host
|
||||
// call the init script
|
||||
out, err := utilexec.New().Command(plugin.getExecutable(), initCmd).CombinedOutput()
|
||||
glog.V(5).Infof("Init 'exec' network plugin output: %s, %v", string(out), err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) getExecutable() string {
|
||||
parts := strings.Split(plugin.execName, "/")
|
||||
execName := parts[len(parts)-1]
|
||||
return path.Join(plugin.execPath, execName)
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) Name() string {
|
||||
return plugin.execName
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) validate() error {
|
||||
if syscall.Access(plugin.getExecutable(), X_OK) != nil {
|
||||
errStr := fmt.Sprintf("Invalid exec plugin. Executable '%s' does not have correct permissions.", plugin.execName)
|
||||
return errors.New(errStr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) SetUpPod(namespace string, name string, id dockertools.DockerID) error {
|
||||
out, err := utilexec.New().Command(plugin.getExecutable(), setUpCmd, namespace, name, string(id)).CombinedOutput()
|
||||
glog.V(5).Infof("SetUpPod 'exec' network plugin output: %s, %v", string(out), err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (plugin *execNetworkPlugin) TearDownPod(namespace string, name string, id dockertools.DockerID) error {
|
||||
out, err := utilexec.New().Command(plugin.getExecutable(), tearDownCmd, namespace, name, string(id)).CombinedOutput()
|
||||
glog.V(5).Infof("TearDownPod 'exec' network plugin output: %s, %v", string(out), err)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
)
|
||||
|
||||
// The temp dir where test plugins will be stored.
|
||||
const testPluginPath = "/tmp/fake/plugins/net"
|
||||
|
||||
func installPluginUnderTest(t *testing.T, vendorName string, plugName string) {
|
||||
vendoredName := plugName
|
||||
if vendorName != "" {
|
||||
vendoredName = fmt.Sprintf("%s~%s", vendorName, plugName)
|
||||
}
|
||||
pluginDir := path.Join(testPluginPath, vendoredName)
|
||||
err := os.MkdirAll(pluginDir, 0777)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create plugin: %v", err)
|
||||
}
|
||||
pluginExec := path.Join(pluginDir, plugName)
|
||||
f, err := os.Create(pluginExec)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to install plugin")
|
||||
}
|
||||
err = f.Chmod(0777)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to set exec perms on plugin")
|
||||
}
|
||||
writeStr := fmt.Sprintf("#!/bin/bash\necho -n $@ &> %s", path.Join(pluginDir, plugName+".out"))
|
||||
_, err = f.WriteString(writeStr)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to write plugin exec")
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
|
||||
func tearDownPlugin(plugName string) {
|
||||
err := os.RemoveAll(testPluginPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in cleaning up test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectPlugin(t *testing.T) {
|
||||
// install some random plugin under testPluginPath
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
installPluginUnderTest(t, "", pluginName)
|
||||
|
||||
plug, err := network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), pluginName, network.NewFakeHost(nil))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to select the desired plugin: %v", err)
|
||||
}
|
||||
if plug.Name() != pluginName {
|
||||
t.Errorf("Wrong plugin selected, chose %s, got %s\n", pluginName, plug.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectVendoredPlugin(t *testing.T) {
|
||||
// install some random plugin under testPluginPath
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
vendor := "mycompany"
|
||||
installPluginUnderTest(t, vendor, pluginName)
|
||||
|
||||
vendoredPluginName := fmt.Sprintf("%s/%s", vendor, pluginName)
|
||||
plug, err := network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), vendoredPluginName, network.NewFakeHost(nil))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to select the desired plugin: %v", err)
|
||||
}
|
||||
if plug.Name() != vendoredPluginName {
|
||||
t.Errorf("Wrong plugin selected, chose %s, got %s\n", vendoredPluginName, plug.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectWrongPlugin(t *testing.T) {
|
||||
// install some random plugin under testPluginPath
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
installPluginUnderTest(t, "", pluginName)
|
||||
|
||||
wrongPlugin := "abcd"
|
||||
plug, err := network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), wrongPlugin, network.NewFakeHost(nil))
|
||||
if plug != nil || err == nil {
|
||||
t.Errorf("Expected to see an error. Wrong plugin selected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginValidation(t *testing.T) {
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
installPluginUnderTest(t, "", pluginName)
|
||||
|
||||
// modify the perms of the pluginExecutable
|
||||
f, err := os.Open(path.Join(testPluginPath, pluginName, pluginName))
|
||||
if err != nil {
|
||||
t.Errorf("Nil value expected.")
|
||||
}
|
||||
err = f.Chmod(0444)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to set perms on plugin exec")
|
||||
}
|
||||
f.Close()
|
||||
|
||||
_, err = network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), pluginName, network.NewFakeHost(nil))
|
||||
if err == nil {
|
||||
// we expected an error here because validation would have failed
|
||||
t.Errorf("Expected non-nil value.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginSetupHook(t *testing.T) {
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
installPluginUnderTest(t, "", pluginName)
|
||||
|
||||
plug, err := network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), pluginName, network.NewFakeHost(nil))
|
||||
|
||||
err = plug.SetUpPod("podNamespace", "podName", "dockerid2345")
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil: %v", err)
|
||||
}
|
||||
// check output of setup hook
|
||||
output, err := ioutil.ReadFile(path.Join(testPluginPath, pluginName, pluginName+".out"))
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil")
|
||||
}
|
||||
expectedOutput := "setup podNamespace podName dockerid2345"
|
||||
if string(output) != expectedOutput {
|
||||
t.Errorf("Mismatch in expected output for setup hook. Expected '%s', got '%s'", expectedOutput, string(output))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginTearDownHook(t *testing.T) {
|
||||
pluginName := fmt.Sprintf("test%d", rand.Intn(1000))
|
||||
defer tearDownPlugin(pluginName)
|
||||
installPluginUnderTest(t, "", pluginName)
|
||||
|
||||
plug, err := network.InitNetworkPlugin(probeNetworkPluginsWithExecDir(testPluginPath), pluginName, network.NewFakeHost(nil))
|
||||
|
||||
err = plug.TearDownPod("podNamespace", "podName", "dockerid2345")
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil")
|
||||
}
|
||||
// check output of setup hook
|
||||
output, err := ioutil.ReadFile(path.Join(testPluginPath, pluginName, pluginName+".out"))
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil")
|
||||
}
|
||||
expectedOutput := "teardown podNamespace podName dockerid2345"
|
||||
if string(output) != expectedOutput {
|
||||
t.Errorf("Mismatch in expected output for teardown hook. Expected '%s', got '%s'", expectedOutput, string(output))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const DefaultPluginName = "kubernetes.io/no-op"
|
||||
|
||||
// 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) error
|
||||
|
||||
// Name returns the plugin's name. This will be used when searching
|
||||
// for a plugin by name, e.g.
|
||||
Name() string
|
||||
|
||||
// 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, podInfraContainerID dockertools.DockerID) error
|
||||
|
||||
// TearDownPod is the method called before a pod's infra container will be deleted
|
||||
TearDownPod(namespace string, name string, podInfraContainerID dockertools.DockerID) error
|
||||
}
|
||||
|
||||
// Host is an interface that plugins can use to access the kubelet.
|
||||
type Host interface {
|
||||
// Get the pod structure by its name, namespace
|
||||
GetPodByName(namespace, name string) (*api.Pod, bool)
|
||||
|
||||
// GetKubeClient returns a client interface
|
||||
GetKubeClient() client.Interface
|
||||
}
|
||||
|
||||
// InitNetworkPlugin inits the plugin that matches networkPluginName. Plugins must have unique names.
|
||||
func InitNetworkPlugin(plugins []NetworkPlugin, networkPluginName string, host Host) (NetworkPlugin, error) {
|
||||
if networkPluginName == "" {
|
||||
// default to the no_op plugin
|
||||
plug := &noopNetworkPlugin{}
|
||||
return plug, nil
|
||||
}
|
||||
|
||||
pluginMap := map[string]NetworkPlugin{}
|
||||
|
||||
allErrs := []error{}
|
||||
for _, plugin := range plugins {
|
||||
name := plugin.Name()
|
||||
if !util.IsQualifiedName(name) {
|
||||
allErrs = append(allErrs, fmt.Errorf("network plugin has invalid name: %#v", plugin))
|
||||
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)
|
||||
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, errors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func UnescapePluginName(in string) string {
|
||||
return strings.Replace(in, "~", "/", -1)
|
||||
}
|
||||
|
||||
type noopNetworkPlugin struct {
|
||||
}
|
||||
|
||||
func (plugin *noopNetworkPlugin) Init(host Host) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *noopNetworkPlugin) Name() string {
|
||||
return DefaultPluginName
|
||||
}
|
||||
|
||||
func (plugin *noopNetworkPlugin) SetUpPod(namespace string, name string, id dockertools.DockerID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *noopNetworkPlugin) TearDownPod(namespace string, name string, id dockertools.DockerID) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSelectDefaultPlugin(t *testing.T) {
|
||||
all_plugins := []NetworkPlugin{}
|
||||
plug, err := InitNetworkPlugin(all_plugins, "", NewFakeHost(nil))
|
||||
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() != DefaultPluginName {
|
||||
t.Errorf("Failed to select the default plugin. Expected %s. Got %s", DefaultPluginName, plug.Name())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
// helper for testing plugins
|
||||
// a fake host is created here that can be used by plugins for testing
|
||||
|
||||
import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
)
|
||||
|
||||
type fakeNetworkHost struct {
|
||||
kubeClient client.Interface
|
||||
}
|
||||
|
||||
func NewFakeHost(kubeClient client.Interface) *fakeNetworkHost {
|
||||
host := &fakeNetworkHost{kubeClient: kubeClient}
|
||||
return host
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetPodByName(name, namespace string) (*api.Pod, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (fnh *fakeNetworkHost) GetKubeClient() client.Interface {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
)
|
||||
|
||||
// This just exports required functions from kubelet proper, for use by network
|
||||
// plugins.
|
||||
type networkHost struct {
|
||||
kubelet *Kubelet
|
||||
}
|
||||
|
||||
func (nh *networkHost) GetPodByName(name, namespace string) (*api.Pod, bool) {
|
||||
return nh.kubelet.GetPodByName(name, namespace)
|
||||
}
|
||||
|
||||
func (nh *networkHost) GetKubeClient() client.Interface {
|
||||
return nh.kubelet.kubeClient
|
||||
}
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network"
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
cadvisorApi "github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
@ -78,6 +79,7 @@ func TestRunOnce(t *testing.T) {
|
|||
cadvisor: cadvisor,
|
||||
}
|
||||
|
||||
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", network.NewFakeHost(nil))
|
||||
if err := kb.setupDataDirs(); err != nil {
|
||||
t.Errorf("Failed to init data dirs: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue