From 7fed3d210bb17d00292957e21e6dad20c96a6f58 Mon Sep 17 00:00:00 2001 From: mozhulee <21621232@zju.edu.cn> Date: Fri, 25 Aug 2017 20:54:39 +0800 Subject: [PATCH] Add fake CRD client Change-Id: I4748ce465b2886098cfab37277a18cc0b571c22b Implements: blueprint enhance-unit-testing Signed-off-by: mozhuli <21621232@zju.edu.cn> --- .../rbacmanager/rbac_controller.go | 4 +- .../tenant/tenant_controller.go | 14 +- pkg/kubecrd/crdclient.go | 94 ++-- pkg/kubecrd/crdclient_fake.go | 245 +++++++++++ pkg/network-controller/network_controller.go | 14 +- pkg/openstack/client.go | 11 +- pkg/openstack/openstack_fake.go | 400 +++++++++++++----- pkg/proxy/iptables_fake.go | 4 + pkg/proxy/proxier_test.go | 37 +- 9 files changed, 647 insertions(+), 176 deletions(-) create mode 100644 pkg/kubecrd/crdclient_fake.go diff --git a/pkg/auth-controller/rbacmanager/rbac_controller.go b/pkg/auth-controller/rbacmanager/rbac_controller.go index 2161a4a..31f3889 100644 --- a/pkg/auth-controller/rbacmanager/rbac_controller.go +++ b/pkg/auth-controller/rbacmanager/rbac_controller.go @@ -41,13 +41,13 @@ const ( // Controller manages life cycle of namespace's rbac. type Controller struct { k8sclient *kubernetes.Clientset - kubeCRDClient *crdClient.CRDClient + kubeCRDClient crdClient.Interface userCIDR string userGateway string } // NewRBACController creates a new RBAC controller. -func NewRBACController(kubeClient *kubernetes.Clientset, kubeCRDClient *crdClient.CRDClient, userCIDR string, +func NewRBACController(kubeClient *kubernetes.Clientset, kubeCRDClient crdClient.Interface, userCIDR string, userGateway string) (*Controller, error) { c := &Controller{ k8sclient: kubeClient, diff --git a/pkg/auth-controller/tenant/tenant_controller.go b/pkg/auth-controller/tenant/tenant_controller.go index 9602d00..fcaf447 100644 --- a/pkg/auth-controller/tenant/tenant_controller.go +++ b/pkg/auth-controller/tenant/tenant_controller.go @@ -34,10 +34,10 @@ import ( "k8s.io/client-go/tools/cache" ) -// TenantController manages life cycle of Tenant. +// TenantController manages the life cycle of Tenant. type TenantController struct { k8sClient *kubernetes.Clientset - kubeCRDClient *crdClient.CRDClient + kubeCRDClient crdClient.Interface openstackClient openstack.Interface } @@ -64,16 +64,12 @@ func NewTenantController(kubeClient *kubernetes.Clientset, return c, nil } -func (c *TenantController) GetKubeCRDClient() *crdClient.CRDClient { - return c.kubeCRDClient -} - // Run the controller. func (c *TenantController) Run(stopCh <-chan struct{}) error { defer utilruntime.HandleCrash() source := cache.NewListWatchFromClient( - c.kubeCRDClient.Client, + c.kubeCRDClient.Client(), crv1.TenantResourcePlural, apiv1.NamespaceAll, fields.Everything()) @@ -97,7 +93,7 @@ func (c *TenantController) onAdd(obj interface{}) { tenant := obj.(*crv1.Tenant) glog.V(3).Infof("Tenant controller received new object %#v\n", tenant) - copyObj, err := c.kubeCRDClient.Scheme.Copy(tenant) + copyObj, err := c.kubeCRDClient.Scheme().Copy(tenant) if err != nil { glog.Errorf("ERROR creating a deep copy of tenant object: %#v\n", err) return @@ -140,7 +136,7 @@ func (c *TenantController) onDelete(obj interface{}) { glog.Errorf("failed to delete network for tenant: %v", tenantName) } - //Delete namespace + // Delete namespace err = c.deleteNamespace(tenantName) if err != nil { glog.Errorf("Delete namespace %s failed: %v", tenantName, err) diff --git a/pkg/kubecrd/crdclient.go b/pkg/kubecrd/crdclient.go index 5847df3..f76a757 100644 --- a/pkg/kubecrd/crdclient.go +++ b/pkg/kubecrd/crdclient.go @@ -30,12 +30,34 @@ import ( "github.com/golang/glog" ) -type CRDClient struct { - Client *rest.RESTClient - Scheme *runtime.Scheme +// Interface should be implemented by a CRD client. +type Interface interface { + // AddTenant adds Tenant CRD object by given object. + AddTenant(tenant *crv1.Tenant) error + // GetTenant returns Tenant CRD object by tenantName. + GetTenant(tenantName string) (*crv1.Tenant, error) + // UpdateTenant updates Tenant CRD object by given object. + UpdateTenant(tenant *crv1.Tenant) error + // AddNetwork adds Network CRD object by given object. + AddNetwork(network *crv1.Network) error + // UpdateNetwork updates Network CRD object by given object. + UpdateNetwork(network *crv1.Network) error + // DeleteNetwork deletes Network CRD object by networkName. + DeleteNetwork(networkName string) error + // Client returns the RESTClient. + Client() *rest.RESTClient + // Scheme returns runtime scheme. + Scheme() *runtime.Scheme } -func NewCRDClient(cfg *rest.Config) (*CRDClient, error) { +// CRDClient implements the Interface. +type CRDClient struct { + client *rest.RESTClient + scheme *runtime.Scheme +} + +// NewCRDClient returns a new CRD client. +func NewCRDClient(cfg *rest.Config) (Interface, error) { scheme := runtime.NewScheme() if err := crv1.AddToScheme(scheme); err != nil { return nil, err @@ -53,14 +75,24 @@ func NewCRDClient(cfg *rest.Config) (*CRDClient, error) { } return &CRDClient{ - Client: client, - Scheme: scheme, + client: client, + scheme: scheme, }, nil } -// UpdateNetwork updates Network CRD object by given object -func (c *CRDClient) UpdateNetwork(network *crv1.Network) { - err := c.Client.Put(). +// Client returns the RESTClient. +func (c *CRDClient) Client() *rest.RESTClient { + return c.client +} + +// Scheme returns runtime scheme. +func (c *CRDClient) Scheme() *runtime.Scheme { + return c.scheme +} + +// UpdateNetwork updates Network CRD object by given object. +func (c *CRDClient) UpdateNetwork(network *crv1.Network) error { + err := c.client.Put(). Name(network.Name). Namespace(network.Namespace). Resource(crv1.NetworkResourcePlural). @@ -70,14 +102,15 @@ func (c *CRDClient) UpdateNetwork(network *crv1.Network) { if err != nil { glog.Errorf("ERROR updating network: %v\n", err) - } else { - glog.V(3).Infof("UPDATED network: %#v\n", network) + return err } + glog.V(3).Infof("UPDATED network: %#v\n", network) + return nil } -// UpdateTenant updates Network CRD object by given object -func (c *CRDClient) UpdateTenant(tenant *crv1.Tenant) { - err := c.Client.Put(). +// UpdateTenant updates Network CRD object by given object. +func (c *CRDClient) UpdateTenant(tenant *crv1.Tenant) error { + err := c.client.Put(). Name(tenant.Name). Namespace(util.SystemTenant). Resource(crv1.TenantResourcePlural). @@ -87,17 +120,18 @@ func (c *CRDClient) UpdateTenant(tenant *crv1.Tenant) { if err != nil { glog.Errorf("ERROR updating tenant: %v\n", err) - } else { - glog.V(3).Infof("UPDATED tenant: %#v\n", tenant) + return err } + glog.V(3).Infof("UPDATED tenant: %#v\n", tenant) + return nil } -// GetTenant returns tenant from CRD -// NOTE: all tenant are stored under system namespace +// GetTenant returns Tenant CRD object by tenantName. +// NOTE: all tenant are stored under system namespace. func (c *CRDClient) GetTenant(tenantName string) (*crv1.Tenant, error) { tenant := crv1.Tenant{} - // tenant always has same name and namespace - err := c.Client.Get(). + // tenant always has the same name with namespace + err := c.client.Get(). Resource(crv1.TenantResourcePlural). Namespace(util.SystemTenant). Name(tenantName). @@ -108,10 +142,10 @@ func (c *CRDClient) GetTenant(tenantName string) (*crv1.Tenant, error) { return &tenant, nil } -// AddTenant adds tenant to CRD -// NOTE: all tenant are added to system namespace +// AddTenant adds Tenant CRD object by given object. +// NOTE: all tenant are added to system namespace. func (c *CRDClient) AddTenant(tenant *crv1.Tenant) error { - err := c.Client.Post(). + err := c.client.Post(). Namespace(util.SystemTenant). Resource(crv1.TenantResourcePlural). Body(tenant). @@ -122,8 +156,9 @@ func (c *CRDClient) AddTenant(tenant *crv1.Tenant) error { return nil } +// AddNetwork adds Network CRD object by given object. func (c *CRDClient) AddNetwork(network *crv1.Network) error { - err := c.Client.Post(). + err := c.client.Post(). Resource(crv1.NetworkResourcePlural). Namespace(network.GetNamespace()). Body(network). @@ -134,12 +169,13 @@ func (c *CRDClient) AddNetwork(network *crv1.Network) error { return nil } -func (c *CRDClient) DeleteNetwork(namespace string) error { - // NOTE: the automatically created network for tenant use namespace as name - err := c.Client.Delete(). +// DeleteNetwork deletes Network CRD object by networkName. +// NOTE: the automatically created network for tenant use namespace as name. +func (c *CRDClient) DeleteNetwork(networkName string) error { + err := c.client.Delete(). Resource(crv1.NetworkResourcePlural). - Namespace(namespace). - Name(namespace). + Namespace(networkName). + Name(networkName). Do().Error() if err != nil { return fmt.Errorf("failed to delete Network: %v", err) diff --git a/pkg/kubecrd/crdclient_fake.go b/pkg/kubecrd/crdclient_fake.go new file mode 100644 index 0000000..3b539fd --- /dev/null +++ b/pkg/kubecrd/crdclient_fake.go @@ -0,0 +1,245 @@ +/* +Copyright (c) 2017 OpenStack Foundation. + +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 kubecrd + +import ( + "fmt" + "sync" + + crv1 "git.openstack.org/openstack/stackube/pkg/apis/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" +) + +// CalledDetail is the struct contains called function name and arguments. +type CalledDetail struct { + // Name of the function called. + Name string + // Argument of the function called. + Argument interface{} +} + +// FakeCRDClient is a simple fake CRD client, so that stackube +// can be run for testing without requiring a real kubernetes setup. +type FakeCRDClient struct { + sync.Mutex + called []CalledDetail + errors map[string]error + Tenants map[string]*crv1.Tenant + Networks map[string]*crv1.Network + scheme *runtime.Scheme +} + +var _ = Interface(&FakeCRDClient{}) + +// NewFake creates a new FakeCRDClient. +func NewFake() (*FakeCRDClient, error) { + scheme := runtime.NewScheme() + if err := crv1.AddToScheme(scheme); err != nil { + return nil, err + } + + return &FakeCRDClient{ + errors: make(map[string]error), + Tenants: make(map[string]*crv1.Tenant), + Networks: make(map[string]*crv1.Network), + scheme: scheme, + }, nil +} + +func (f *FakeCRDClient) getError(op string) error { + err, ok := f.errors[op] + if ok { + delete(f.errors, op) + return err + } + return nil +} + +// InjectError inject error for call +func (f *FakeCRDClient) InjectError(fn string, err error) { + f.Lock() + defer f.Unlock() + f.errors[fn] = err +} + +// InjectErrors inject errors for calls +func (f *FakeCRDClient) InjectErrors(errs map[string]error) { + f.Lock() + defer f.Unlock() + for fn, err := range errs { + f.errors[fn] = err + } +} + +// ClearErrors clear errors for call +func (f *FakeCRDClient) ClearErrors() { + f.Lock() + defer f.Unlock() + f.errors = make(map[string]error) +} + +func (f *FakeCRDClient) appendCalled(name string, argument interface{}) { + call := CalledDetail{Name: name, Argument: argument} + f.called = append(f.called, call) +} + +// GetCalledNames get names of call +func (f *FakeCRDClient) GetCalledNames() []string { + f.Lock() + defer f.Unlock() + names := []string{} + for _, detail := range f.called { + names = append(names, detail.Name) + } + return names +} + +// GetCalledDetails get detail of each call. +func (f *FakeCRDClient) GetCalledDetails() []CalledDetail { + f.Lock() + defer f.Unlock() + // Copy the list and return. + return append([]CalledDetail{}, f.called...) +} + +// SetTenants injects fake tenant. +func (f *FakeCRDClient) SetTenants(tenants []*crv1.Tenant) { + f.Lock() + defer f.Unlock() + for _, tenant := range tenants { + f.Tenants[tenant.Name] = tenant + } +} + +// SetNetworks injects fake network. +func (f *FakeCRDClient) SetNetworks(networks []*crv1.Network) { + f.Lock() + defer f.Unlock() + for _, network := range networks { + f.Networks[network.Name] = network + } +} + +// Client is a test implementation of Interface.Client. +func (f *FakeCRDClient) Client() *rest.RESTClient { + return nil +} + +// Scheme is a test implementation of Interface.Scheme. +func (f *FakeCRDClient) Scheme() *runtime.Scheme { + return f.scheme +} + +// AddTenant is a test implementation of Interface.AddTenant. +func (f *FakeCRDClient) AddTenant(tenant *crv1.Tenant) error { + f.Lock() + defer f.Unlock() + f.appendCalled("AddTenant", tenant) + if err := f.getError("AddTenant"); err != nil { + return err + } + + if _, ok := f.Tenants[tenant.Name]; ok { + return nil + } + + f.Tenants[tenant.Name] = tenant + return nil +} + +// GetTenant is a test implementation of Interface.GetTenant. +func (f *FakeCRDClient) GetTenant(tenantName string) (*crv1.Tenant, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("GetTenant", tenantName) + if err := f.getError("GetTenant"); err != nil { + return nil, err + } + + tenant, ok := f.Tenants[tenantName] + if !ok { + return nil, fmt.Errorf("Tenant %s not found", tenantName) + } + + return tenant, nil +} + +// AddNetwork is a test implementation of Interface.AddNetwork. +func (f *FakeCRDClient) AddNetwork(network *crv1.Network) error { + f.Lock() + defer f.Unlock() + f.appendCalled("AddNetwork", network) + if err := f.getError("AddNetwork"); err != nil { + return err + } + + if _, ok := f.Networks[network.Name]; ok { + return nil + } + + f.Networks[network.Name] = network + return nil +} + +// UpdateTenant is a test implementation of Interface.UpdateTenant. +func (f *FakeCRDClient) UpdateTenant(tenant *crv1.Tenant) error { + f.Lock() + defer f.Unlock() + f.appendCalled("UpdateTenant", tenant) + if err := f.getError("UpdateTenant"); err != nil { + return err + } + + if _, ok := f.Tenants[tenant.Name]; !ok { + return fmt.Errorf("Tenant %s not exist", tenant.Name) + } + + f.Tenants[tenant.Name] = tenant + return nil +} + +// UpdateNetwork is a test implementation of Interface.UpdateNetwork. +func (f *FakeCRDClient) UpdateNetwork(network *crv1.Network) error { + f.Lock() + defer f.Unlock() + f.appendCalled("UpdateNetwork", network) + if err := f.getError("UpdateNetwork"); err != nil { + return err + } + + if _, ok := f.Networks[network.Name]; !ok { + return fmt.Errorf("Network %s not exist", network.Name) + } + + f.Networks[network.Name] = network + return nil +} + +// DeleteNetwork is a test implementation of Interface.DeleteNetwork. +func (f *FakeCRDClient) DeleteNetwork(networkName string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("DeleteNetwork", networkName) + if err := f.getError("DeleteNetwork"); err != nil { + return err + } + + delete(f.Networks, networkName) + + return nil +} diff --git a/pkg/network-controller/network_controller.go b/pkg/network-controller/network_controller.go index 85d2a4c..4cab055 100644 --- a/pkg/network-controller/network_controller.go +++ b/pkg/network-controller/network_controller.go @@ -48,18 +48,15 @@ const ( defaultSideCarImage = "stackube/k8s-dns-sidecar-amd64:1.14.4" ) -// Watcher is an network of watching on resource create/update/delete events +// NetworkController manages the life cycle of Network. type NetworkController struct { k8sclient *kubernetes.Clientset - kubeCRDClient *kubecrd.CRDClient + kubeCRDClient kubecrd.Interface driver openstack.Interface networkInformer cache.Controller } -func (c *NetworkController) GetKubeCRDClient() *kubecrd.CRDClient { - return c.kubeCRDClient -} - +// Run the network controller. func (c *NetworkController) Run(stopCh <-chan struct{}) error { defer utilruntime.HandleCrash() @@ -69,6 +66,7 @@ func (c *NetworkController) Run(stopCh <-chan struct{}) error { return nil } +// NewNetworkController creates a new NetworkController. func NewNetworkController(kubeClient *kubernetes.Clientset, osClient openstack.Interface, kubeExtClient *apiextensionsclient.Clientset) (*NetworkController, error) { // initialize CRD if it does not exist _, err := kubecrd.CreateNetworkCRD(kubeExtClient) @@ -77,7 +75,7 @@ func NewNetworkController(kubeClient *kubernetes.Clientset, osClient openstack.I } source := cache.NewListWatchFromClient( - osClient.GetCRDClient().Client, + osClient.GetCRDClient().Client(), crv1.NetworkResourcePlural, apiv1.NamespaceAll, fields.Everything()) @@ -108,7 +106,7 @@ func (c *NetworkController) onAdd(obj interface{}) { // NEVER modify objects from the store. It's a read-only, local cache. // You can use networkScheme.Copy() to make a deep copy of original object and modify this copy // Or create a copy manually for better performance - copyObj, err := c.GetKubeCRDClient().Scheme.Copy(network) + copyObj, err := c.kubeCRDClient.Scheme().Copy(network) if err != nil { glog.Errorf("ERROR creating a deep copy of network object: %v\n", err) return diff --git a/pkg/openstack/client.go b/pkg/openstack/client.go index 072cda4..6b441d4 100644 --- a/pkg/openstack/client.go +++ b/pkg/openstack/client.go @@ -106,13 +106,14 @@ type Interface interface { // EnsureLoadBalancerDeleted ensures a load balancer is deleted. EnsureLoadBalancerDeleted(name string) error // GetCRDClient returns the CRDClient. - GetCRDClient() *crdClient.CRDClient + GetCRDClient() crdClient.Interface // GetPluginName returns the plugin name. GetPluginName() string // GetIntegrationBridge returns the integration bridge name. GetIntegrationBridge() string } +// Client implements the openstack client Interface. type Client struct { Identity *gophercloud.ServiceClient Provider *gophercloud.ProviderClient @@ -121,7 +122,7 @@ type Client struct { ExtNetID string PluginName string IntegrationBridge string - CRDClient *crdClient.CRDClient + CRDClient crdClient.Interface } type PluginOpts struct { @@ -129,6 +130,7 @@ type PluginOpts struct { IntegrationBridge string `gcfg:"integration-bridge"` } +// Config used to configure the openstack client. type Config struct { Global struct { AuthUrl string `gcfg:"auth-url"` @@ -151,6 +153,7 @@ func toAuthOptions(cfg Config) gophercloud.AuthOptions { } } +// NewClient returns a new openstack client. func NewClient(config string, kubeConfig string) (Interface, error) { var opts gophercloud.AuthOptions cfg, err := readConfig(config) @@ -221,7 +224,7 @@ func readConfig(config string) (Config, error) { } // GetCRDClient returns the CRDClient. -func (os *Client) GetCRDClient() *crdClient.CRDClient { +func (os *Client) GetCRDClient() crdClient.Interface { return os.CRDClient } @@ -466,6 +469,7 @@ func (os *Client) GetNetworkByName(networkName string) (*drivertypes.Network, er return os.OSNetworktoProviderNetwork(osNetwork) } +// OSNetworktoProviderNetwork transfers networks.Network to drivertypes.Network. func (os *Client) OSNetworktoProviderNetwork(osNetwork *networks.Network) (*drivertypes.Network, error) { var providerNetwork drivertypes.Network var providerSubnets []*drivertypes.Subnet @@ -487,6 +491,7 @@ func (os *Client) OSNetworktoProviderNetwork(osNetwork *networks.Network) (*driv return &providerNetwork, nil } +// ToProviderStatus transfers networks.Network's status to drivertypes.Network's status. func (os *Client) ToProviderStatus(status string) string { switch status { case "ACTIVE": diff --git a/pkg/openstack/openstack_fake.go b/pkg/openstack/openstack_fake.go index 0057c9b..f2ee097 100644 --- a/pkg/openstack/openstack_fake.go +++ b/pkg/openstack/openstack_fake.go @@ -18,11 +18,10 @@ package openstack import ( "crypto/sha1" - "errors" "fmt" "io" + "sync" - crv1 "git.openstack.org/openstack/stackube/pkg/apis/v1" crdClient "git.openstack.org/openstack/stackube/pkg/kubecrd" drivertypes "git.openstack.org/openstack/stackube/pkg/openstack/types" "git.openstack.org/openstack/stackube/pkg/util" @@ -34,19 +33,27 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) -var ErrAlreadyExist = errors.New("AlreadyExist") +// CalledDetail is the struct contains called function name and arguments. +type CalledDetail struct { + // Name of the function called. + Name string + // Argument of the function called. + Argument []interface{} +} // FakeOSClient is a simple fake openstack client, so that stackube // can be run for testing without requiring a real openstack setup. type FakeOSClient struct { - Tenants map[string]*tenants.Tenant - Users map[string]*users.User - Networks map[string]*drivertypes.Network - Subnets map[string]*subnets.Subnet - Routers map[string]*routers.Router - Ports map[string][]ports.Port - // TODO(mozhuli): Add fakeCRDClient. - CRDClient *crdClient.CRDClient + sync.Mutex + called []CalledDetail + errors map[string]error + Tenants map[string]*tenants.Tenant + Users map[string]*users.User + Networks map[string]*drivertypes.Network + Subnets map[string]*subnets.Subnet + Routers map[string]*routers.Router + Ports map[string][]ports.Port + CRDClient crdClient.Interface PluginName string IntegrationBridge string } @@ -54,50 +61,116 @@ type FakeOSClient struct { var _ = Interface(&FakeOSClient{}) // NewFake creates a new FakeOSClient. -func NewFake() *FakeOSClient { +func NewFake(crdClient crdClient.Interface) *FakeOSClient { return &FakeOSClient{ + errors: make(map[string]error), Tenants: make(map[string]*tenants.Tenant), Users: make(map[string]*users.User), Networks: make(map[string]*drivertypes.Network), Subnets: make(map[string]*subnets.Subnet), Routers: make(map[string]*routers.Router), Ports: make(map[string][]ports.Port), + CRDClient: crdClient, PluginName: "ovs", IntegrationBridge: "bi-int", } } +func (f *FakeOSClient) getError(op string) error { + err, ok := f.errors[op] + if ok { + delete(f.errors, op) + return err + } + return nil +} + +// InjectError inject error for call +func (f *FakeOSClient) InjectError(fn string, err error) { + f.Lock() + defer f.Unlock() + f.errors[fn] = err +} + +// InjectErrors inject errors for calls +func (f *FakeOSClient) InjectErrors(errs map[string]error) { + f.Lock() + defer f.Unlock() + for fn, err := range errs { + f.errors[fn] = err + } +} + +// ClearErrors clear errors for call +func (f *FakeOSClient) ClearErrors() { + f.Lock() + defer f.Unlock() + f.errors = make(map[string]error) +} + +func (f *FakeOSClient) appendCalled(name string, argument ...interface{}) { + call := CalledDetail{Name: name, Argument: argument} + f.called = append(f.called, call) +} + +// GetCalledNames get names of call +func (f *FakeOSClient) GetCalledNames() []string { + f.Lock() + defer f.Unlock() + names := []string{} + for _, detail := range f.called { + names = append(names, detail.Name) + } + return names +} + +// GetCalledDetails get detail of each call. +func (f *FakeOSClient) GetCalledDetails() []CalledDetail { + f.Lock() + defer f.Unlock() + // Copy the list and return. + return append([]CalledDetail{}, f.called...) +} + // SetTenant injects fake tenant. -func (os *FakeOSClient) SetTenant(tenantName, tenantID string) { +func (f *FakeOSClient) SetTenant(tenantName, tenantID string) { + f.Lock() + defer f.Unlock() tenant := &tenants.Tenant{ Name: tenantName, ID: tenantID, } - os.Tenants[tenantName] = tenant + f.Tenants[tenantName] = tenant } // SetUser injects fake user. -func (os *FakeOSClient) SetUser(userName, userID, tenantID string) { +func (f *FakeOSClient) SetUser(userName, userID, tenantID string) { + f.Lock() + defer f.Unlock() user := &users.User{ Username: userName, ID: userID, TenantID: tenantID, } - os.Users[tenantID] = user + f.Users[tenantID] = user } // SetNetwork injects fake network. -func (os *FakeOSClient) SetNetwork(networkName, networkID string) { +func (f *FakeOSClient) SetNetwork(networkName, networkID string) { + f.Lock() + defer f.Unlock() network := &drivertypes.Network{ Name: networkName, Uid: networkID, } - os.Networks[networkName] = network + f.Networks[networkName] = network } // SetPort injects fake port. -func (os *FakeOSClient) SetPort(networkID, deviceOwner, deviceID string) { - netPorts, ok := os.Ports[networkID] +func (f *FakeOSClient) SetPort(networkID, deviceOwner, deviceID string) { + f.Lock() + defer f.Unlock() + netPorts, ok := f.Ports[networkID] p := ports.Port{ NetworkID: networkID, DeviceOwner: deviceOwner, @@ -106,10 +179,10 @@ func (os *FakeOSClient) SetPort(networkID, deviceOwner, deviceID string) { if !ok { var ps []ports.Port ps = append(ps, p) - os.Ports[networkID] = ps + f.Ports[networkID] = ps } netPorts = append(netPorts, p) - os.Ports[networkID] = netPorts + f.Ports[networkID] = netPorts } func tenantIDHash(tenantName string) string { @@ -146,84 +219,129 @@ func idHash(data ...string) string { return fmt.Sprintf("%x", h.Sum(nil))[:16] } -// CreateTenant creates tenant by tenantname. -func (os *FakeOSClient) CreateTenant(tenantName string) (string, error) { - if t, ok := os.Tenants[tenantName]; ok { +// CreateTenant is a test implementation of Interface.CreateTenant. +func (f *FakeOSClient) CreateTenant(tenantName string) (string, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("CreateTenant", tenantName) + if err := f.getError("CreateTenant"); err != nil { + return "", err + } + + if t, ok := f.Tenants[tenantName]; ok { return t.ID, nil } tenant := &tenants.Tenant{ Name: tenantName, ID: tenantIDHash(tenantName), } - os.Tenants[tenantName] = tenant + f.Tenants[tenantName] = tenant return tenant.ID, nil } -// DeleteTenant deletes tenant by tenantName. -func (os *FakeOSClient) DeleteTenant(tenantName string) error { - delete(os.Tenants, tenantName) +// DeleteTenant is a test implementation of Interface.DeleteTenant. +func (f *FakeOSClient) DeleteTenant(tenantName string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("DeleteTenant", tenantName) + if err := f.getError("DeleteTenant"); err != nil { + return err + } + + delete(f.Tenants, tenantName) return nil } -// GetTenantIDFromName gets tenantID by tenantName. -func (os *FakeOSClient) GetTenantIDFromName(tenantName string) (string, error) { +// GetTenantIDFromName is a test implementation of Interface.GetTenantIDFromName. +func (f *FakeOSClient) GetTenantIDFromName(tenantName string) (string, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("GetTenantIDFromName", tenantName) + if err := f.getError("GetTenantIDFromName"); err != nil { + return "", err + } + if util.IsSystemNamespace(tenantName) { tenantName = util.SystemTenant } // If tenantID is specified, return it directly - var ( - tenant *crv1.Tenant - err error - ) - // TODO(mozhuli): use fakeCRDClient. - if tenant, err = os.CRDClient.GetTenant(tenantName); err != nil { + tenant, err := f.CRDClient.GetTenant(tenantName) + if err != nil { return "", err } if tenant.Spec.TenantID != "" { return tenant.Spec.TenantID, nil } - t, ok := os.Tenants[tenantName] + t, ok := f.Tenants[tenantName] if !ok { - return "", nil + return "", fmt.Errorf("Tenant %s not exist", tenantName) } return t.ID, nil } -// CheckTenantByID checks tenant exist or not by tenantID. -func (os *FakeOSClient) CheckTenantByID(tenantID string) (bool, error) { - for _, tenent := range os.Tenants { +// CheckTenantByID is a test implementation of Interface.CheckTenantByID. +func (f *FakeOSClient) CheckTenantByID(tenantID string) (bool, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("CheckTenantByID", tenantID) + if err := f.getError("CheckTenantByID"); err != nil { + return false, err + } + + for _, tenent := range f.Tenants { if tenent.ID == tenantID { return true, nil } } - return false, ErrNotFound + return false, fmt.Errorf("Tenant %s not exist", tenantID) } -// CreateUser creates user with username, password in the tenant. -func (os *FakeOSClient) CreateUser(username, password, tenantID string) error { +// CreateUser is a test implementation of Interface.CreateUser. +func (f *FakeOSClient) CreateUser(username, password, tenantID string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("CreateUser", username, password, tenantID) + if err := f.getError("CreateUser"); err != nil { + return err + } + user := &users.User{ Name: username, TenantID: tenantID, ID: userIDHash(username, tenantID), } - os.Users[tenantID] = user + f.Users[tenantID] = user return nil } -// DeleteAllUsersOnTenant deletes all users on the tenant. -func (os *FakeOSClient) DeleteAllUsersOnTenant(tenantName string) error { - tenant := os.Tenants[tenantName] +// DeleteAllUsersOnTenant is a test implementation of Interface.DeleteAllUsersOnTenant. +func (f *FakeOSClient) DeleteAllUsersOnTenant(tenantName string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("DeleteAllUsersOnTenant", tenantName) + if err := f.getError("DeleteAllUsersOnTenant"); err != nil { + return err + } - delete(os.Users, tenant.ID) + tenant := f.Tenants[tenantName] + + delete(f.Users, tenant.ID) return nil } -func (os *FakeOSClient) createNetwork(networkName, tenantID string) error { - if _, ok := os.Networks[networkName]; ok { - return ErrAlreadyExist +func (f *FakeOSClient) createNetwork(networkName, tenantID string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("createNetwork", networkName, tenantID) + if err := f.getError("createNetwork"); err != nil { + return err + } + + if _, ok := f.Networks[networkName]; ok { + return fmt.Errorf("Network %s already exist", networkName) } network := &drivertypes.Network{ @@ -231,18 +349,32 @@ func (os *FakeOSClient) createNetwork(networkName, tenantID string) error { Uid: networkIDHash(networkName), TenantID: tenantID, } - os.Networks[networkName] = network + f.Networks[networkName] = network return nil } -func (os *FakeOSClient) deleteNetwork(networkName string) error { - delete(os.Networks, networkName) +func (f *FakeOSClient) deleteNetwork(networkName string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("deleteNetwork", networkName) + if err := f.getError("deleteNetwork"); err != nil { + return err + } + + delete(f.Networks, networkName) return nil } -func (os *FakeOSClient) createRouter(routerName, tenantID string) error { - if _, ok := os.Routers[routerName]; ok { - return ErrAlreadyExist +func (f *FakeOSClient) createRouter(routerName, tenantID string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("createRouter", routerName, tenantID) + if err := f.getError("createRouter"); err != nil { + return err + } + + if _, ok := f.Routers[routerName]; ok { + return fmt.Errorf("Router %s already exist", routerName) } router := &routers.Router{ @@ -250,18 +382,32 @@ func (os *FakeOSClient) createRouter(routerName, tenantID string) error { TenantID: tenantID, ID: routerIDHash(routerName), } - os.Routers[routerName] = router + f.Routers[routerName] = router return nil } -func (os *FakeOSClient) deleteRouter(routerName string) error { - delete(os.Routers, routerName) +func (f *FakeOSClient) deleteRouter(routerName string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("deleteRouter", routerName) + if err := f.getError("deleteRouter"); err != nil { + return err + } + + delete(f.Routers, routerName) return nil } -func (os *FakeOSClient) createSubnet(subnetName, networkID, tenantID string) error { - if _, ok := os.Subnets[subnetName]; ok { - return ErrAlreadyExist +func (f *FakeOSClient) createSubnet(subnetName, networkID, tenantID string) error { + f.Lock() + defer f.Unlock() + f.appendCalled("createSubnet", subnetName, networkID, tenantID) + if err := f.getError("createSubnet"); err != nil { + return err + } + + if _, ok := f.Subnets[subnetName]; ok { + return fmt.Errorf("Subnet %s already exist", subnetName) } subnet := &subnets.Subnet{ @@ -270,77 +416,98 @@ func (os *FakeOSClient) createSubnet(subnetName, networkID, tenantID string) err NetworkID: networkID, ID: subnetIDHash(subnetName), } - os.Subnets[subnetName] = subnet + f.Subnets[subnetName] = subnet return nil } -// CreateNetwork creates network. -// TODO(mozhuli): make it more general. -func (os *FakeOSClient) CreateNetwork(network *drivertypes.Network) error { +// CreateNetwork is a test implementation of Interface.CreateNetwork. +func (f *FakeOSClient) CreateNetwork(network *drivertypes.Network) error { + f.Lock() + defer f.Unlock() + f.appendCalled("CreateNetwork", network) + if err := f.getError("CreateNetwork"); err != nil { + return err + } + if len(network.Subnets) == 0 { - return errors.New("Subnets is null") + return fmt.Errorf("Subnets is null") } // create network - err := os.createNetwork(network.Name, network.TenantID) + err := f.createNetwork(network.Name, network.TenantID) if err != nil { - return errors.New("Create network failed") + return err } // create router, and use network name as router name for convenience. - err = os.createRouter(network.Name, network.TenantID) + err = f.createRouter(network.Name, network.TenantID) if err != nil { - os.deleteNetwork(network.Name) - return errors.New("Create router failed") + f.deleteNetwork(network.Name) + return err } // create subnets and connect them to router - err = os.createSubnet(network.Subnets[0].Name, network.Uid, network.TenantID) + err = f.createSubnet(network.Subnets[0].Name, network.Uid, network.TenantID) if err != nil { - os.deleteRouter(network.Name) - os.deleteNetwork(network.Name) - return errors.New("Create subnet failed") + f.deleteRouter(network.Name) + f.deleteNetwork(network.Name) + return err } return nil } // GetNetworkByID gets network by networkID. -func (os *FakeOSClient) GetNetworkByID(networkID string) (*drivertypes.Network, error) { +// CreateTenant is a test implementation of Interface.CreateTenant. +func (f *FakeOSClient) GetNetworkByID(networkID string) (*drivertypes.Network, error) { return nil, nil } -// GetNetworkByName get network by networkName -func (os *FakeOSClient) GetNetworkByName(networkName string) (*drivertypes.Network, error) { - network, ok := os.Networks[networkName] +// GetNetworkByName is a test implementation of Interface.GetNetworkByName. +func (f *FakeOSClient) GetNetworkByName(networkName string) (*drivertypes.Network, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("GetNetworkByName", networkName) + if err := f.getError("GetNetworkByName"); err != nil { + return nil, err + } + + network, ok := f.Networks[networkName] if !ok { - return nil, ErrNotFound + return nil, fmt.Errorf("Network %s not exist", networkName) } return network, nil } -// DeleteNetwork deletes network by networkName. -func (os *FakeOSClient) DeleteNetwork(networkName string) error { +// DeleteNetwork is a test implementation of Interface.DeleteNetwork. +func (f *FakeOSClient) DeleteNetwork(networkName string) error { return nil } -// GetProviderSubnet gets provider subnet by id -func (os *FakeOSClient) GetProviderSubnet(osSubnetID string) (*drivertypes.Subnet, error) { +// GetProviderSubnet is a test implementation of Interface.GetProviderSubnet. +func (f *FakeOSClient) GetProviderSubnet(osSubnetID string) (*drivertypes.Subnet, error) { return nil, nil } -// CreatePort creates port by neworkID, tenantID and portName. -func (os *FakeOSClient) CreatePort(networkID, tenantID, portName string) (*portsbinding.Port, error) { +// CreatePort is a test implementation of Interface.CreatePort. +func (f *FakeOSClient) CreatePort(networkID, tenantID, portName string) (*portsbinding.Port, error) { return nil, nil } -// GetPort gets port by portName. -func (os *FakeOSClient) GetPort(name string) (*ports.Port, error) { +// GetPort is a test implementation of Interface.GetPort. +func (f *FakeOSClient) GetPort(name string) (*ports.Port, error) { return nil, nil } -// ListPorts list all ports which have the deviceOwner in the network. -func (os *FakeOSClient) ListPorts(networkID, deviceOwner string) ([]ports.Port, error) { +// ListPorts is a test implementation of Interface.ListPorts. +func (f *FakeOSClient) ListPorts(networkID, deviceOwner string) ([]ports.Port, error) { + f.Lock() + defer f.Unlock() + f.appendCalled("ListPorts", networkID, deviceOwner) + if err := f.getError("ListPorts"); err != nil { + return nil, err + } + var results []ports.Port - portList, ok := os.Ports[networkID] + portList, ok := f.Ports[networkID] if !ok { return results, nil } @@ -353,48 +520,47 @@ func (os *FakeOSClient) ListPorts(networkID, deviceOwner string) ([]ports.Port, return results, nil } -// DeletePortByName deletes port by portName. -func (os *FakeOSClient) DeletePortByName(portName string) error { +// DeletePortByName is a test implementation of Interface.DeletePortByName. +func (f *FakeOSClient) DeletePortByName(portName string) error { return nil } -// DeletePortByID deletes port by portID. -func (os *FakeOSClient) DeletePortByID(portID string) error { +// DeletePortByID is a test implementation of Interface.DeletePortByID. +func (f *FakeOSClient) DeletePortByID(portID string) error { return nil } -// UpdatePortsBinding updates port binding. -func (os *FakeOSClient) UpdatePortsBinding(portID, deviceOwner string) error { +// UpdatePortsBinding is a test implementation of Interface.UpdatePortsBinding. +func (f *FakeOSClient) UpdatePortsBinding(portID, deviceOwner string) error { return nil } -// LoadBalancerExist returns whether a load balancer has already been exist. -func (os *FakeOSClient) LoadBalancerExist(name string) (bool, error) { +// LoadBalancerExist is a test implementation of Interface.LoadBalancerExist. +func (f *FakeOSClient) LoadBalancerExist(name string) (bool, error) { return true, nil } -// EnsureLoadBalancer ensures a load balancer is created. -func (os *FakeOSClient) EnsureLoadBalancer(lb *LoadBalancer) (*LoadBalancerStatus, error) { +// EnsureLoadBalancer is a test implementation of Interface.EnsureLoadBalancer. +func (f *FakeOSClient) EnsureLoadBalancer(lb *LoadBalancer) (*LoadBalancerStatus, error) { return nil, nil } -// EnsureLoadBalancerDeleted ensures a load balancer is deleted. -func (os *FakeOSClient) EnsureLoadBalancerDeleted(name string) error { +// EnsureLoadBalancerDeleted is a test implementation of Interface.EnsureLoadBalancerDeleted. +func (f *FakeOSClient) EnsureLoadBalancerDeleted(name string) error { return nil } -// GetCRDClient returns the CRDClient. -// TODO(mozhuli): use fakeCRDClient. -func (os *FakeOSClient) GetCRDClient() *crdClient.CRDClient { - return os.CRDClient +// GetCRDClient is a test implementation of Interface.GetCRDClient. +func (f *FakeOSClient) GetCRDClient() crdClient.Interface { + return f.CRDClient } -// GetPluginName returns the plugin name. -func (os *FakeOSClient) GetPluginName() string { - return os.PluginName +// GetPluginName is a test implementation of Interface.GetPluginName. +func (f *FakeOSClient) GetPluginName() string { + return f.PluginName } -// GetIntegrationBridge returns the integration bridge name. -func (os *FakeOSClient) GetIntegrationBridge() string { - return os.IntegrationBridge +// GetIntegrationBridge is a test implementation of Interface.GetIntegrationBridge. +func (f *FakeOSClient) GetIntegrationBridge() string { + return f.IntegrationBridge } diff --git a/pkg/proxy/iptables_fake.go b/pkg/proxy/iptables_fake.go index ef12b22..859af76 100644 --- a/pkg/proxy/iptables_fake.go +++ b/pkg/proxy/iptables_fake.go @@ -19,6 +19,7 @@ package proxy import ( "fmt" "strings" + "sync" ) const ( @@ -35,6 +36,7 @@ type Rule map[string]string // FakeIPTables have noop implementation of fake iptables function. type FakeIPTables struct { + sync.Mutex namespace string NSLines map[string][]byte } @@ -55,6 +57,8 @@ func (f *FakeIPTables) ensureRule(op, chain string, args []string) error { } func (f *FakeIPTables) restoreAll(data []byte) error { + f.Lock() + defer f.Unlock() d := make([]byte, len(data)) copy(d, data) f.NSLines[f.namespace] = d diff --git a/pkg/proxy/proxier_test.go b/pkg/proxy/proxier_test.go index e292f32..8201018 100644 --- a/pkg/proxy/proxier_test.go +++ b/pkg/proxy/proxier_test.go @@ -24,6 +24,7 @@ import ( "github.com/davecgh/go-spew/spew" + crdClient "git.openstack.org/openstack/stackube/pkg/kubecrd" "git.openstack.org/openstack/stackube/pkg/openstack" "git.openstack.org/openstack/stackube/pkg/util" @@ -190,10 +191,15 @@ func TestClusterNoEndpoint(t *testing.T) { Port: "80", } - //Creates fake iptables. + // Creates fake iptables. ipt := NewFake() - //Create a fake openstack client. - osClient := openstack.NewFake() + // Creates fake CRD client. + crdClient, err := crdClient.NewFake() + if err != nil { + t.Fatal("Failed init fake CRD client") + } + // Create a fake openstack client. + osClient := openstack.NewFake(crdClient) // Injects fake network. networkName := util.BuildNetworkName(testNamespace, testNamespace) osClient.SetNetwork(networkName, "123") @@ -226,7 +232,7 @@ func TestClusterNoEndpoint(t *testing.T) { } } -func noClusterIPType(svcType v1.ServiceType) []Rule { +func noClusterIPType(t *testing.T, svcType v1.ServiceType) []Rule { testNamespace := "test" svcIP := "1.2.3.4" svcPort := 80 @@ -237,8 +243,13 @@ func noClusterIPType(svcType v1.ServiceType) []Rule { // Creates fake iptables. ipt := NewFake() + // Creates fake CRD client. + crdClient, err := crdClient.NewFake() + if err != nil { + t.Fatal("Failed init fake CRD client") + } // Create a fake openstack client. - osClient := openstack.NewFake() + osClient := openstack.NewFake(crdClient) // Injects fake network. networkName := util.BuildNetworkName(testNamespace, testNamespace) osClient.SetNetwork(networkName, "123") @@ -277,7 +288,7 @@ func TestNoClusterIPType(t *testing.T) { } for k, tc := range testCases { - got := noClusterIPType(tc) + got := noClusterIPType(t, tc) if len(got) != 0 { errorf(fmt.Sprintf("%v: unexpected rule for chain %v without ClusterIP service type", k, ChainSKPrerouting), got, t) } @@ -295,8 +306,13 @@ func TestClusterIPEndpointsJump(t *testing.T) { // Creates fake iptables. ipt := NewFake() + // Creates fake CRD client. + crdClient, err := crdClient.NewFake() + if err != nil { + t.Fatal("Failed init fake CRD client") + } // Create a fake openstack client. - osClient := openstack.NewFake() + osClient := openstack.NewFake(crdClient) // Injects fake network. networkName := util.BuildNetworkName(testNamespace, testNamespace) osClient.SetNetwork(networkName, "123") @@ -365,8 +381,13 @@ func TestMultiNamespacesService(t *testing.T) { // Creates fake iptables. ipt := NewFake() + // Creates fake CRD client. + crdClient, err := crdClient.NewFake() + if err != nil { + t.Fatal("Failed init fake CRD client") + } // Create a fake openstack client. - osClient := openstack.NewFake() + osClient := openstack.NewFake(crdClient) // Injects fake network. networkName1 := util.BuildNetworkName(ns1, ns1) osClient.SetNetwork(networkName1, "123")