stackube/pkg/openstack/openstack_fake.go
mozhulee 068c895b27 Add network-controller unit tests
including
* fix creating kube-dns deployment and svc when
  getting network or creating network failed.
* add network-controller unit tests.
* enhance fake openstack client.

Change-Id: Ib527ff2a995301af343d34d3a7f948e7ba492516
Implements: blueprint auth-controller-test
Signed-off-by: mozhuli <21621232@zju.edu.cn>
2017-09-04 14:50:56 +08:00

627 lines
16 KiB
Go

/*
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 openstack
import (
"crypto/sha1"
"fmt"
"io"
"sync"
crdClient "git.openstack.org/openstack/stackube/pkg/kubecrd"
drivertypes "git.openstack.org/openstack/stackube/pkg/openstack/types"
"git.openstack.org/openstack/stackube/pkg/util"
"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants"
"github.com/gophercloud/gophercloud/openstack/identity/v2/users"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
)
// 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 {
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
LoadBalancers map[string]*LoadBalancer
CRDClient crdClient.Interface
PluginName string
IntegrationBridge string
}
var _ = Interface(&FakeOSClient{})
// NewFake creates a new 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),
LoadBalancers: make(map[string]*LoadBalancer),
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 (f *FakeOSClient) SetTenant(tenantName, tenantID string) {
f.Lock()
defer f.Unlock()
tenant := &tenants.Tenant{
Name: tenantName,
ID: tenantID,
}
f.Tenants[tenantName] = tenant
}
// SetUser injects fake user.
func (f *FakeOSClient) SetUser(userName, userID, tenantID string) {
f.Lock()
defer f.Unlock()
user := &users.User{
Username: userName,
ID: userID,
TenantID: tenantID,
}
f.Users[tenantID] = user
}
// SetNetwork injects fake network.
func (f *FakeOSClient) SetNetwork(network *drivertypes.Network) {
f.Lock()
defer f.Unlock()
f.Networks[network.Name] = network
}
// SetPort injects fake port.
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,
DeviceID: deviceID,
}
if !ok {
var ps []ports.Port
ps = append(ps, p)
f.Ports[networkID] = ps
}
netPorts = append(netPorts, p)
f.Ports[networkID] = netPorts
}
// SetLoadbalancer injects fake loadbalancer.
func (f *FakeOSClient) SetLoadbalancer(lb *LoadBalancer) {
f.Lock()
defer f.Unlock()
f.LoadBalancers[lb.Name] = lb
}
func tenantIDHash(tenantName string) string {
return idHash(tenantName)
}
func userIDHash(userName, tenantID string) string {
return idHash(userName)
}
func networkIDHash(networkName string) string {
return idHash(networkName)
}
func subnetIDHash(subnetName string) string {
return idHash(subnetName)
}
func routerIDHash(routerName string) string {
return idHash(routerName)
}
func portdeviceIDHash(networkID, deviceOwner string) string {
return idHash(networkID, deviceOwner)
}
func idHash(data ...string) string {
var s string
for _, d := range data {
s += d
}
h := sha1.New()
io.WriteString(h, s)
return fmt.Sprintf("%x", h.Sum(nil))[:16]
}
// 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),
}
f.Tenants[tenantName] = tenant
return tenant.ID, nil
}
// 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 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
tenant, err := f.CRDClient.GetTenant(tenantName)
if err != nil {
return "", err
}
if tenant.Spec.TenantID != "" {
return tenant.Spec.TenantID, nil
}
t, ok := f.Tenants[tenantName]
if !ok {
return "", ErrNotFound
}
return t.ID, nil
}
// 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, nil
}
// 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),
}
f.Users[tenantID] = user
return nil
}
// 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
}
tenant := f.Tenants[tenantName]
delete(f.Users, tenant.ID)
return nil
}
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{
Name: networkName,
Uid: networkIDHash(networkName),
TenantID: tenantID,
}
f.Networks[networkName] = network
return nil
}
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 (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{
Name: routerName,
TenantID: tenantID,
ID: routerIDHash(routerName),
}
f.Routers[routerName] = router
return nil
}
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 (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{
Name: subnetName,
TenantID: tenantID,
NetworkID: networkID,
ID: subnetIDHash(subnetName),
}
f.Subnets[subnetName] = subnet
return nil
}
func (f *FakeOSClient) deleteSubnet(subnetName string) error {
f.Lock()
defer f.Unlock()
f.appendCalled("deleteSubnet", subnetName)
if err := f.getError("deleteSubnet"); err != nil {
return err
}
delete(f.Subnets, subnetName)
return nil
}
// CreateNetwork is a test implementation of Interface.CreateNetwork.
func (f *FakeOSClient) CreateNetwork(network *drivertypes.Network) error {
f.appendCalled("CreateNetwork", network)
if err := f.getError("CreateNetwork"); err != nil {
return err
}
if len(network.Subnets) == 0 {
return fmt.Errorf("Subnets is null")
}
// create network
err := f.createNetwork(network.Name, network.TenantID)
if err != nil {
return err
}
// create router, and use network name as router name for convenience.
err = f.createRouter(network.Name, network.TenantID)
if err != nil {
f.deleteNetwork(network.Name)
return err
}
// create subnets and connect them to router
err = f.createSubnet(network.Subnets[0].Name, network.Uid, network.TenantID)
if err != nil {
f.deleteRouter(network.Name)
f.deleteNetwork(network.Name)
return err
}
return nil
}
// GetNetworkByID is a test implementation of Interface.GetNetworkByID.
func (f *FakeOSClient) GetNetworkByID(networkID string) (*drivertypes.Network, error) {
for _, network := range f.Networks {
if network.Uid == networkID {
return network, nil
}
}
return nil, ErrNotFound
}
// 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 network, nil
}
// DeleteNetwork is a test implementation of Interface.DeleteNetwork.
func (f *FakeOSClient) DeleteNetwork(networkName string) error {
f.appendCalled("DeleteNetwork", networkName)
if err := f.getError("DeleteNetwork"); err != nil {
return err
}
f.deleteRouter(networkName)
f.deleteSubnet(networkName + "-subnet")
f.deleteNetwork(networkName)
return nil
}
// GetProviderSubnet is a test implementation of Interface.GetProviderSubnet.
func (f *FakeOSClient) GetProviderSubnet(osSubnetID string) (*drivertypes.Subnet, error) {
return nil, fmt.Errorf("Not implemented")
}
// CreatePort is a test implementation of Interface.CreatePort.
func (f *FakeOSClient) CreatePort(networkID, tenantID, portName string) (*portsbinding.Port, error) {
return nil, fmt.Errorf("Not implemented")
}
// GetPort is a test implementation of Interface.GetPort.
func (f *FakeOSClient) GetPort(name string) (*ports.Port, error) {
return nil, fmt.Errorf("Not implemented")
}
// 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 := f.Ports[networkID]
if !ok {
return results, nil
}
for _, port := range portList {
if port.DeviceOwner == deviceOwner {
results = append(results, port)
}
}
return results, nil
}
// DeletePortByName is a test implementation of Interface.DeletePortByName.
func (f *FakeOSClient) DeletePortByName(portName string) error {
return fmt.Errorf("Not implemented")
}
// DeletePortByID is a test implementation of Interface.DeletePortByID.
func (f *FakeOSClient) DeletePortByID(portID string) error {
return fmt.Errorf("Not implemented")
}
// UpdatePortsBinding is a test implementation of Interface.UpdatePortsBinding.
func (f *FakeOSClient) UpdatePortsBinding(portID, deviceOwner string) error {
return fmt.Errorf("Not implemented")
}
// LoadBalancerExist is a test implementation of Interface.LoadBalancerExist.
func (f *FakeOSClient) LoadBalancerExist(name string) (bool, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("LoadBalancerExist", name)
if err := f.getError("LoadBalancerExist"); err != nil {
return false, err
}
if _, ok := f.LoadBalancers[name]; !ok {
return false, nil
}
return true, nil
}
// EnsureLoadBalancer is a test implementation of Interface.EnsureLoadBalancer.
func (f *FakeOSClient) EnsureLoadBalancer(lb *LoadBalancer) (*LoadBalancerStatus, error) {
f.Lock()
defer f.Unlock()
f.appendCalled("EnsureLoadBalancer", lb)
if err := f.getError("EnsureLoadBalancer"); err != nil {
return nil, err
}
f.LoadBalancers[lb.Name] = lb
return &LoadBalancerStatus{
InternalIP: lb.InternalIP,
ExternalIP: lb.ExternalIP,
}, nil
}
// EnsureLoadBalancerDeleted is a test implementation of Interface.EnsureLoadBalancerDeleted.
func (f *FakeOSClient) EnsureLoadBalancerDeleted(name string) error {
f.Lock()
defer f.Unlock()
f.appendCalled("EnsureLoadBalancerDeleted", name)
if err := f.getError("EnsureLoadBalancerDeleted"); err != nil {
return err
}
delete(f.LoadBalancers, name)
return nil
}
// GetCRDClient is a test implementation of Interface.GetCRDClient.
func (f *FakeOSClient) GetCRDClient() crdClient.Interface {
return f.CRDClient
}
// GetPluginName is a test implementation of Interface.GetPluginName.
func (f *FakeOSClient) GetPluginName() string {
return f.PluginName
}
// GetIntegrationBridge is a test implementation of Interface.GetIntegrationBridge.
func (f *FakeOSClient) GetIntegrationBridge() string {
return f.IntegrationBridge
}