068c895b27
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>
627 lines
16 KiB
Go
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
|
|
}
|