diff options
-rw-r--r-- | cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go | 6 | ||||
-rw-r--r-- | cmd/ovn4nfvk8s/ovn4nfvk8s.go | 4 | ||||
-rw-r--r-- | internal/pkg/config/config.go | 96 | ||||
-rw-r--r-- | internal/pkg/ovn/ovn_test.go | 82 | ||||
-rw-r--r-- | internal/pkg/ovn/pods.go | 53 |
5 files changed, 125 insertions, 116 deletions
diff --git a/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go index 2722eaa..ec37c02 100644 --- a/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go +++ b/cmd/ovn4nfvk8s-cni/ovn4nfvk8s-cni.go @@ -20,7 +20,6 @@ import ( "github.com/containernetworking/cni/pkg/version" "k8s.io/apimachinery/pkg/util/wait" - kexec "k8s.io/utils/exec" "ovn4nfv-k8s-plugin/internal/pkg/kube" "ovn4nfv-k8s-plugin/cmd/ovn4nfvk8s-cni/app" @@ -128,7 +127,7 @@ func addMultipleInterfaces(args *skel.CmdArgs, ovnAnnotation, namespace, podName gatewayIP := ovnNet["gateway_ip"] defaultGateway := ovnNet["defaultGateway"] - if ipAddress == "" || macAddress == "" || gatewayIP == "" { + if ipAddress == "" || macAddress == "" { logrus.Errorf("failed in pod annotation key extract") return nil } @@ -323,9 +322,8 @@ func main() { c.Version = "0.0.2" c.Flags = config.Flags - exec := kexec.New() c.Action = func(ctx *cli.Context) error { - if _, err := config.InitConfig(ctx, exec, nil); err != nil { + if _, err := config.InitConfig(ctx); err != nil { return err } diff --git a/cmd/ovn4nfvk8s/ovn4nfvk8s.go b/cmd/ovn4nfvk8s/ovn4nfvk8s.go index d097558..0c0cc2e 100644 --- a/cmd/ovn4nfvk8s/ovn4nfvk8s.go +++ b/cmd/ovn4nfvk8s/ovn4nfvk8s.go @@ -22,7 +22,6 @@ func main() { c := cli.NewApp() c.Name = "ovn4nfvk8s" c.Usage = "run ovn4nfvk8s to start pod watchers" - c.Version = config.Version c.Flags = append([]cli.Flag{ // Daemon file cli.StringFlag{ @@ -50,9 +49,8 @@ func delPidfile(pidfile string) { } func runOvnKube(ctx *cli.Context) error { - fmt.Println("ovn4nfvk8s started") exec := kexec.New() - _, err := config.InitConfig(ctx, exec, nil) + _, err := config.InitConfig(ctx) if err != nil { return err } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 045a0ac..b73dd9d 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -2,28 +2,21 @@ package config import ( "fmt" - "net/url" "os" "path/filepath" "reflect" - "strings" "github.com/sirupsen/logrus" "github.com/urfave/cli" gcfg "gopkg.in/gcfg.v1" - kexec "k8s.io/utils/exec" - - "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/cert" ) // The following are global config parameters that other modules may access directly var ( - // ovn-kubernetes version, to be changed with every release - Version = "0.3.0" // Default holds parsed config file parameters and command-line overrides Default = DefaultConfig{ @@ -42,10 +35,8 @@ var ( Plugin: "ovn4nfvk8s-cni", } - // Kubernetes holds Kubernetes-related parsed config file parameters and command-line overrides - Kubernetes = KubernetesConfig{ - APIServer: "http://localhost:8080", - } + // Kubernetes holds Kubernetes-related parsed config file parameters + Kubernetes = KubernetesConfig{} ) // DefaultConfig holds parsed config file parameters and command-line overrides @@ -73,9 +64,6 @@ type CNIConfig struct { // KubernetesConfig holds Kubernetes-related parsed config file parameters and command-line overrides type KubernetesConfig struct { Kubeconfig string `gcfg:"kubeconfig"` - CACert string `gcfg:"cacert"` - APIServer string `gcfg:"apiserver"` - Token string `gcfg:"token"` } // Config is used to read the structured config file and to cache config in testcases @@ -164,57 +152,21 @@ var Flags = []cli.Flag{ // Kubernetes-related options cli.StringFlag{ Name: "k8s-kubeconfig", - Usage: "absolute path to the Kubernetes kubeconfig file (not required if the --k8s-apiserver, --k8s-ca-cert, and --k8s-token are given)", + Usage: "absolute path to the Kubernetes kubeconfig file", Destination: &cliConfig.Kubernetes.Kubeconfig, }, - cli.StringFlag{ - Name: "k8s-apiserver", - Usage: "URL of the Kubernetes API server (not required if --k8s-kubeconfig is given) (default: http://localhost:8443)", - Destination: &cliConfig.Kubernetes.APIServer, - }, - cli.StringFlag{ - Name: "k8s-cacert", - Usage: "the absolute path to the Kubernetes API CA certificate (not required if --k8s-kubeconfig is given)", - Destination: &cliConfig.Kubernetes.CACert, - }, - cli.StringFlag{ - Name: "k8s-token", - Usage: "the Kubernetes API authentication token (not required if --k8s-kubeconfig is given)", - Destination: &cliConfig.Kubernetes.Token, - }, -} - -type Defaults struct { - K8sAPIServer bool - K8sToken bool - K8sCert bool } -const ( - ovsVsctlCommand = "ovs-vsctl" -) - -func buildKubernetesConfig(exec kexec.Interface, cli, file *config, defaults *Defaults) error { +func buildKubernetesConfig(cli, file *config) error { // Copy config file values over default values overrideFields(&Kubernetes, &file.Kubernetes) // And CLI overrides over config file and default values overrideFields(&Kubernetes, &cli.Kubernetes) - if Kubernetes.Kubeconfig != "" && !pathExists(Kubernetes.Kubeconfig) { + if Kubernetes.Kubeconfig == "" || !pathExists(Kubernetes.Kubeconfig) { return fmt.Errorf("kubernetes kubeconfig file %q not found", Kubernetes.Kubeconfig) } - if Kubernetes.CACert != "" && !pathExists(Kubernetes.CACert) { - return fmt.Errorf("kubernetes CA certificate file %q not found", Kubernetes.CACert) - } - - url, err := url.Parse(Kubernetes.APIServer) - if err != nil { - return fmt.Errorf("kubernetes API server address %q invalid: %v", Kubernetes.APIServer, err) - } else if url.Scheme != "https" && url.Scheme != "http" { - return fmt.Errorf("kubernetes API server URL scheme %q invalid", url.Scheme) - } - return nil } @@ -235,15 +187,15 @@ func getConfigFilePath(ctx *cli.Context) (string, bool) { // InitConfig reads the config file and common command-line options and // constructs the global config object from them. It returns the config file // path (if explicitly specified) or an error -func InitConfig(ctx *cli.Context, exec kexec.Interface, defaults *Defaults) (string, error) { - return InitConfigWithPath(ctx, exec, "", defaults) +func InitConfig(ctx *cli.Context) (string, error) { + return InitConfigWithPath(ctx, "") } // InitConfigWithPath reads the given config file (or if empty, reads the config file // specified by command-line arguments, or empty, the default config file) and // common command-line options and constructs the global config object from // them. It returns the config file path (if explicitly specified) or an error -func InitConfigWithPath(ctx *cli.Context, exec kexec.Interface, configFile string, defaults *Defaults) (string, error) { +func InitConfigWithPath(ctx *cli.Context, configFile string) (string, error) { var cfg config var retConfigFile string var configFileIsDefault bool @@ -277,10 +229,6 @@ func InitConfigWithPath(ctx *cli.Context, exec kexec.Interface, configFile strin logrus.Infof("Parsed config: %+v", cfg) } - if defaults == nil { - defaults = &Defaults{} - } - // Build config that needs no special processing overrideFields(&Default, &cfg.Default) overrideFields(&Default, &cliConfig.Default) @@ -301,7 +249,7 @@ func InitConfigWithPath(ctx *cli.Context, exec kexec.Interface, configFile strin } } - if err = buildKubernetesConfig(exec, &cliConfig, &cfg, defaults); err != nil { + if err = buildKubernetesConfig(&cliConfig, &cfg); err != nil { return "", err } logrus.Debugf("Default config: %+v", Default) @@ -320,8 +268,7 @@ func pathExists(path string) bool { return true } -// NewClientset creates a Kubernetes clientset from either a kubeconfig, -// TLS properties, or an apiserver URL +// NewClientset creates a Kubernetes clientset func NewClientset(conf *KubernetesConfig) (*kubernetes.Clientset, error) { var kconfig *rest.Config var err error @@ -329,27 +276,6 @@ func NewClientset(conf *KubernetesConfig) (*kubernetes.Clientset, error) { if conf.Kubeconfig != "" { // uses the current context in kubeconfig kconfig, err = clientcmd.BuildConfigFromFlags("", conf.Kubeconfig) - } else if strings.HasPrefix(conf.APIServer, "https") { - if conf.APIServer == "" || conf.Token == "" { - return nil, fmt.Errorf("TLS-secured apiservers require token and CA certificate") - } - kconfig = &rest.Config{ - Host: conf.APIServer, - BearerToken: conf.Token, - } - if conf.CACert != "" { - if _, err := cert.NewPool(conf.CACert); err != nil { - return nil, err - } - kconfig.TLSClientConfig = rest.TLSClientConfig{CAFile: conf.CACert} - } - } else if strings.HasPrefix(conf.APIServer, "http") { - kconfig, err = clientcmd.BuildConfigFromFlags(conf.APIServer, "") - } else { - // Assume we are running from a container managed by kubernetes - // and read the apiserver address and tokens from the - // container's environment. - kconfig, err = rest.InClusterConfig() } if err != nil { return nil, err diff --git a/internal/pkg/ovn/ovn_test.go b/internal/pkg/ovn/ovn_test.go index 2e558a6..f974c30 100644 --- a/internal/pkg/ovn/ovn_test.go +++ b/internal/pkg/ovn/ovn_test.go @@ -31,8 +31,6 @@ var _ = Describe("Add logical Port", func() { var app *cli.App BeforeEach(func() { - // Restore global default values before each testcase - //config.RestoreDefaultConfig() app = cli.NewApp() app.Name = "test" @@ -57,13 +55,13 @@ var _ = Describe("Add logical Port", func() { }) fakeCmds = ovntest.AddFakeCmd(fakeCmds, &ovntest.ExpectedCmd{ - Cmd: "ovn-nbctl --timeout=15 --if-exists get logical_switch " + netName + " external_ids:gateway_ip", - Output: gwCIDR, - }) - fakeCmds = ovntest.AddFakeCmd(fakeCmds, &ovntest.ExpectedCmd{ Cmd: "ovn-nbctl --timeout=15 get logical_switch_port " + portName + " dynamic_addresses", Output: macIPAddress, }) + fakeCmds = ovntest.AddFakeCmd(fakeCmds, &ovntest.ExpectedCmd{ + Cmd: "ovn-nbctl --timeout=15 --if-exists get logical_switch " + netName + " external_ids:gateway_ip", + Output: gwCIDR, + }) fexec := &fakeexec.FakeExec{ CommandScript: fakeCmds, @@ -75,9 +73,6 @@ var _ = Describe("Add logical Port", func() { err := util.SetExec(fexec) Expect(err).NotTo(HaveOccurred()) - _, err = config.InitConfig(ctx, fexec, nil) - Expect(err).NotTo(HaveOccurred()) - fakeClient := &fake.Clientset{} var fakeWatchFactory factory.WatchFactory @@ -105,7 +100,7 @@ var _ = Describe("Add logical Port", func() { ) ovnController.addLogicalPort(&okPod) - _, _ = ovnController.kube.GetAnnotationsOnPod("", "ok") + Expect(fexec.CommandCalls).To(Equal(len(fakeCmds))) return nil } @@ -113,4 +108,71 @@ var _ = Describe("Add logical Port", func() { err := app.Run([]string{app.Name}) Expect(err).NotTo(HaveOccurred()) }) + + It("tests Pod provider", func() { + app.Action = func(ctx *cli.Context) error { + const ( + gwIP string = "10.1.1.1" + gwCIDR string = gwIP + "/24" + netName string = "ovn-prot-net" + portName string = "_ok_net0" + macIPAddress string = "0a:00:00:00:00:01 192.168.1.3/24" + ) + fakeCmds := ovntest.AddFakeCmd(nil, &ovntest.ExpectedCmd{ + Cmd: "ovn-nbctl --timeout=15 --data=bare --no-heading --columns=name find logical_switch " + "name=" + netName, + Output: netName, + }) + + fakeCmds = ovntest.AddFakeCmdsNoOutputNoError(fakeCmds, []string{ + "ovn-nbctl --timeout=15 --may-exist lsp-add " + netName + " " + portName + " -- lsp-set-addresses " + portName + " " + macIPAddress + " -- --if-exists clear logical_switch_port " + portName + " dynamic_addresses" + " -- set logical_switch_port " + portName + " external-ids:namespace= external-ids:logical_switch=" + netName + " external-ids:pod=true", + }) + + fakeCmds = ovntest.AddFakeCmd(fakeCmds, &ovntest.ExpectedCmd{ + Cmd: "ovn-nbctl --timeout=15 get logical_switch_port " + portName + " addresses", + Output: macIPAddress, + }) + + fexec := &fakeexec.FakeExec{ + CommandScript: fakeCmds, + LookPathFunc: func(file string) (string, error) { + return fmt.Sprintf("/fake-bin/%s", file), nil + }, + } + err := util.SetExec(fexec) + Expect(err).NotTo(HaveOccurred()) + + fakeClient := &fake.Clientset{} + var fakeWatchFactory factory.WatchFactory + + ovnController := NewOvnController(fakeClient, &fakeWatchFactory) + var ( + okPod = v1.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "ok", + Annotations: map[string]string{"ovnNetwork": "[{ \"name\": \"ovn-prot-net\", \"interface\": \"net0\", \"netType\": \"provider\", \"ipAddress\": \"192.168.1.3/24\", \"macAddress\": \"0a:00:00:00:00:01\" }]"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "by-name", + }, + {}, + }, + }, + } + ) + ovnController.addLogicalPort(&okPod) + Expect(fexec.CommandCalls).To(Equal(len(fakeCmds))) + + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + }) diff --git a/internal/pkg/ovn/pods.go b/internal/pkg/ovn/pods.go index cc3d459..fcd258e 100644 --- a/internal/pkg/ovn/pods.go +++ b/internal/pkg/ovn/pods.go @@ -80,7 +80,7 @@ func (oc *Controller) deleteLogicalPort(pod *kapi.Pod) { return } -func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName string) (annotation string) { +func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType string) (annotation string) { var out, stderr string var err error var isStaticIP bool @@ -112,7 +112,11 @@ func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipA out, stderr, err = util.RunOVNNbctlUnix("--may-exist", "lsp-add", logicalSwitch, portName, "--", "lsp-set-addresses", portName, fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists", - "clear", "logical_switch_port", portName, "dynamic_addresses") + "clear", "logical_switch_port", portName, "dynamic_addresses", "--", "set", + "logical_switch_port", portName, + "external-ids:namespace="+pod.Namespace, + "external-ids:logical_switch="+logicalSwitch, + "external-ids:pod=true") if err != nil { logrus.Errorf("Failed to add logical port to switch "+ "stdout: %q, stderr: %q (%v)", @@ -136,11 +140,6 @@ func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipA } } oc.logicalPortCache[portName] = logicalSwitch - gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch) - if err != nil { - logrus.Errorf("Error obtaining gateway address for switch %s: %s", logicalSwitch, err) - return - } count := 30 for count > 0 { @@ -178,7 +177,18 @@ func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipA logrus.Errorf("Error while obtaining addresses for %s", portName) return } - annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP) + + if netType == "virtual" { + gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch) + if err != nil { + logrus.Errorf("Error obtaining gateway address for switch %s: %s", logicalSwitch, err) + return + } + annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP) + } else { + annotation = fmt.Sprintf(`{\"ip_address\":\"%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], addresses[0], "") + } + return annotation } @@ -203,7 +213,7 @@ func findLogicalSwitch(name string) bool { func (oc *Controller) addLogicalPort(pod *kapi.Pod) { var logicalSwitch string - var ipAddress, macAddress, interfaceName, defaultGateway string + var ipAddress, macAddress, interfaceName, defaultGateway, netType string annotation := pod.Annotations["ovnNetwork"] @@ -217,10 +227,15 @@ func (oc *Controller) addLogicalPort(pod *kapi.Pod) { ovnString = "[" for _, net := range ovnNetObjs { logicalSwitch = net["name"].(string) + if !findLogicalSwitch(logicalSwitch) { + logrus.Errorf("Logical Switch not found") + return + } if _, ok := net["interface"]; ok { interfaceName = net["interface"].(string) } else { - interfaceName = "" + logrus.Errorf("Interface name must be provided") + return } if _, ok := net["ipAddress"]; ok { ipAddress = net["ipAddress"].(string) @@ -237,14 +252,24 @@ func (oc *Controller) addLogicalPort(pod *kapi.Pod) { } else { defaultGateway = "false" } - if !findLogicalSwitch(logicalSwitch) { + if _, ok := net["netType"]; ok { + netType = net["netType"].(string) + } else { + netType = "virtual" + } + if netType != "provider" && netType != "virtual" { + logrus.Errorf("netType is not supported") return } - if interfaceName == "" { - logrus.Errorf("Interface name must be provided") + if netType == "provider" && ipAddress == "" { + logrus.Errorf("ipAddress must be provided for netType Provider") + return + } + if netType == "provider" && defaultGateway == "true" { + logrus.Errorf("defaultGateway not supported for provider network - Use ovnNetworkRoutes to add routes") return } - outStr = oc.addLogicalPortWithSwitch(pod, logicalSwitch, ipAddress, macAddress, interfaceName) + outStr = oc.addLogicalPortWithSwitch(pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType) if outStr == "" { return } |