github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/cni/client.go (about) 1 /* 2 Copyright 2016 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cni 18 19 import ( 20 "fmt" 21 22 "github.com/containernetworking/cni/libcni" 23 cnicurrent "github.com/containernetworking/cni/pkg/types/current" 24 "github.com/davecgh/go-spew/spew" 25 "github.com/golang/glog" 26 27 "github.com/Mirantis/virtlet/pkg/nsfix" 28 "github.com/Mirantis/virtlet/pkg/utils" 29 ) 30 31 // Client provides an interface to CNI plugins. 32 type Client interface { 33 // AddSandboxToNetwork adds a pod sandbox to the CNI network. 34 AddSandboxToNetwork(podID, podName, podNs string) (*cnicurrent.Result, error) 35 // RemoveSandboxFromNetwork removes a pod sandbox from the CNI network. 36 RemoveSandboxFromNetwork(podID, podName, podNs string) error 37 // GetDummyNetwork creates a dummy network using CNI plugin. 38 // It's used for making a dummy gateway for Calico CNI plugin. 39 // It returns a CNI result and a path to the network namespace. 40 GetDummyNetwork() (*cnicurrent.Result, string, error) 41 } 42 43 // client provides an implementation of Client interface. 44 type client struct { 45 pluginsDir string 46 configsDir string 47 } 48 49 var _ Client = &client{} 50 51 // NewClient returns a client perpared to call plugins in `pluginsDir` 52 // using configurations found in `configsDir`. 53 func NewClient(pluginsDir, configsDir string) (*client, error) { 54 return &client{ 55 pluginsDir: pluginsDir, 56 configsDir: configsDir, 57 }, nil 58 } 59 60 // GetDummyNetwork implements GetDummyNetwork method of Client interface. 61 func (c *client) GetDummyNetwork() (*cnicurrent.Result, string, error) { 62 // TODO: virtlet pod restarts should not grab another address for 63 // the gateway. That's not a big problem usually though 64 // as the IPs are not returned to Calico so both old 65 // IPs on existing VMs and new ones should work. 66 podID := utils.NewUUID() 67 if err := CreateNetNS(podID); err != nil { 68 return nil, "", fmt.Errorf("couldn't create netns for fake pod %q: %v", podID, err) 69 } 70 r, err := c.AddSandboxToNetwork(podID, "", "") 71 if err != nil { 72 return nil, "", fmt.Errorf("couldn't set up CNI for fake pod %q: %v", podID, err) 73 } 74 return r, PodNetNSPath(podID), nil 75 } 76 77 // AddSandboxToNetwork implements AddSandboxToNetwork method of Client interface. 78 func (c *client) AddSandboxToNetwork(podID, podName, podNs string) (*cnicurrent.Result, error) { 79 var r cnicurrent.Result 80 if err := nsfix.NewCall("cniAddSandboxToNetwork"). 81 Arg(cniRequest{ 82 PluginsDir: c.pluginsDir, 83 ConfigsDir: c.configsDir, 84 PodID: podID, 85 PodName: podName, 86 PodNs: podNs, 87 }). 88 SpawnInNamespaces(&r); err != nil { 89 return nil, err 90 } 91 return &r, nil 92 } 93 94 // RemoveSandboxFromNetwork implements RemoveSandboxFromNetwork method of Client interface. 95 func (c *client) RemoveSandboxFromNetwork(podID, podName, podNs string) error { 96 return nsfix.NewCall("cniRemoveSandboxFromNetwork"). 97 Arg(cniRequest{ 98 PluginsDir: c.pluginsDir, 99 ConfigsDir: c.configsDir, 100 PodID: podID, 101 PodName: podName, 102 PodNs: podNs, 103 }). 104 SpawnInNamespaces(nil) 105 } 106 107 type cniRequest struct { 108 PluginsDir string 109 ConfigsDir string 110 PodID string 111 PodName string 112 PodNs string 113 } 114 115 type realClient struct { 116 cniConfig *libcni.CNIConfig 117 netConfigList *libcni.NetworkConfigList 118 } 119 120 func newRealclient(pluginsDir, configsDir string) (*realClient, error) { 121 netConfigList, err := ReadConfiguration(configsDir) 122 if err != nil { 123 return nil, fmt.Errorf("failed to read CNI configuration %q: %v", configsDir, err) 124 } 125 glog.V(3).Infof("CNI config: name: %q type: %q", netConfigList.Plugins[0].Network.Name, netConfigList.Plugins[0].Network.Type) 126 127 return &realClient{ 128 cniConfig: &libcni.CNIConfig{Path: []string{pluginsDir}}, 129 netConfigList: netConfigList, 130 }, nil 131 } 132 133 func (c *realClient) cniRuntimeConf(podID, podName, podNs string) *libcni.RuntimeConf { 134 r := &libcni.RuntimeConf{ 135 ContainerID: podID, 136 NetNS: PodNetNSPath(podID), 137 IfName: "virtlet-eth0", 138 } 139 if podName != "" && podNs != "" { 140 r.Args = [][2]string{ 141 {"IgnoreUnknown", "1"}, 142 {"K8S_POD_NAMESPACE", podNs}, 143 {"K8S_POD_NAME", podName}, 144 {"K8S_POD_INFRA_CONTAINER_ID", podID}, 145 } 146 } else { 147 r.Args = [][2]string{ 148 {"IgnoreUnknown", "1"}, 149 } 150 } 151 return r 152 } 153 154 func handleAddSandboxToNetwork(arg interface{}) (interface{}, error) { 155 req := arg.(*cniRequest) 156 c, err := newRealclient(req.PluginsDir, req.ConfigsDir) 157 if err != nil { 158 return nil, err 159 } 160 161 rtConf := c.cniRuntimeConf(req.PodID, req.PodName, req.PodNs) 162 // NOTE: this annotation is only need by CNI Genie 163 rtConf.Args = append(rtConf.Args, [2]string{ 164 "K8S_ANNOT", `{"cni": "calico"}`, 165 }) 166 glog.V(3).Infof("AddSandboxToNetwork: PodID %q, PodName %q, PodNs %q, runtime config:\n%s", 167 req.PodID, req.PodName, req.PodNs, spew.Sdump(rtConf)) 168 result, err := c.cniConfig.AddNetworkList(c.netConfigList, rtConf) 169 if err == nil { 170 glog.V(3).Infof("AddSandboxToNetwork: PodID %q, PodName %q, PodNs %q: result:\n%s", 171 req.PodID, req.PodName, req.PodNs, spew.Sdump(result)) 172 } else { 173 glog.Errorf("AddSandboxToNetwork: PodID %q, PodName %q, PodNs %q: error: %v", 174 req.PodID, req.PodName, req.PodNs, err) 175 return nil, err 176 } 177 r, err := cnicurrent.NewResultFromResult(result) 178 if err != nil { 179 return nil, fmt.Errorf("error converting CNI result to the current version: %v", err) 180 } 181 return r, err 182 } 183 184 func handleRemoveSandboxFromNetwork(arg interface{}) (interface{}, error) { 185 req := arg.(*cniRequest) 186 c, err := newRealclient(req.PluginsDir, req.ConfigsDir) 187 if err != nil { 188 return nil, err 189 } 190 191 glog.V(3).Infof("RemoveSandboxFromNetwork: PodID %q, PodName %q, PodNs %q", req.PodID, req.PodName, req.PodNs) 192 err = c.cniConfig.DelNetworkList(c.netConfigList, c.cniRuntimeConf(req.PodID, req.PodName, req.PodNs)) 193 if err == nil { 194 glog.V(3).Infof("RemoveSandboxFromNetwork: PodID %q, PodName %q, PodNs %q: success", 195 req.PodID, req.PodName, req.PodNs) 196 } else { 197 glog.Errorf("RemoveSandboxFromNetwork: PodID %q, PodName %q, PodNs %q: error: %v", 198 req.PodID, req.PodName, req.PodNs, err) 199 } 200 return nil, err 201 } 202 203 func init() { 204 nsfix.RegisterReexec("cniAddSandboxToNetwork", handleAddSandboxToNetwork, cniRequest{}) 205 nsfix.RegisterReexec("cniRemoveSandboxFromNetwork", handleRemoveSandboxFromNetwork, cniRequest{}) 206 }