From 8e5e3d4ea5ec89a291b577ebdb872f557fd6f431 Mon Sep 17 00:00:00 2001 From: Ole Markus With Date: Sat, 22 Aug 2020 21:51:11 +0200 Subject: [PATCH] Mock create server should associate with mock ports --- cloudmock/openstack/mockcompute/BUILD.bazel | 2 + cloudmock/openstack/mockcompute/api.go | 16 ++-- cloudmock/openstack/mockcompute/servers.go | 88 ++++++++++++++++++- .../openstack/mocknetworking/BUILD.bazel | 1 + cloudmock/openstack/mocknetworking/ports.go | 47 +++++++++- pkg/testutils/integrationtestharness.go | 2 +- 6 files changed, 147 insertions(+), 9 deletions(-) diff --git a/cloudmock/openstack/mockcompute/BUILD.bazel b/cloudmock/openstack/mockcompute/BUILD.bazel index 737c8a3482..a688171b70 100644 --- a/cloudmock/openstack/mockcompute/BUILD.bazel +++ b/cloudmock/openstack/mockcompute/BUILD.bazel @@ -16,10 +16,12 @@ go_library( "//pkg/pki:go_default_library", "//upup/pkg/fi:go_default_library", "//vendor/github.com/google/uuid:go_default_library", + "//vendor/github.com/gophercloud/gophercloud:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports:go_default_library", ], ) diff --git a/cloudmock/openstack/mockcompute/api.go b/cloudmock/openstack/mockcompute/api.go index 00d61702a2..32dbb68bdc 100644 --- a/cloudmock/openstack/mockcompute/api.go +++ b/cloudmock/openstack/mockcompute/api.go @@ -20,6 +20,8 @@ import ( "net/http/httptest" "sync" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" @@ -33,15 +35,16 @@ type MockClient struct { openstack.MockOpenstackServer mutex sync.Mutex - serverGroups map[string]servergroups.ServerGroup - servers map[string]servers.Server - keyPairs map[string]keypairs.KeyPair - images map[string]images.Image - flavors map[string]flavors.Flavor + serverGroups map[string]servergroups.ServerGroup + servers map[string]servers.Server + keyPairs map[string]keypairs.KeyPair + images map[string]images.Image + flavors map[string]flavors.Flavor + networkClient *gophercloud.ServiceClient } // CreateClient will create a new mock networking client -func CreateClient() *MockClient { +func CreateClient(networkClient *gophercloud.ServiceClient) *MockClient { m := &MockClient{} m.SetupMux() m.Reset() @@ -50,6 +53,7 @@ func CreateClient() *MockClient { m.mockKeyPairs() m.mockFlavors() m.Server = httptest.NewServer(m.Mux) + m.networkClient = networkClient return m } diff --git a/cloudmock/openstack/mockcompute/servers.go b/cloudmock/openstack/mockcompute/servers.go index d01e9db66e..f2127f7b92 100644 --- a/cloudmock/openstack/mockcompute/servers.go +++ b/cloudmock/openstack/mockcompute/servers.go @@ -24,7 +24,11 @@ import ( "regexp" "strings" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "k8s.io/kops/upup/pkg/fi" + "github.com/google/uuid" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) @@ -37,7 +41,84 @@ type serverListResponse struct { } type serverCreateRequest struct { - Server servers.CreateOpts `json:"server"` + Server Server `json:"server"` +} + +// CreateOpts specifies server creation parameters. +type Server struct { + // Name is the name to assign to the newly launched server. + Name string `json:"name" required:"true"` + + // ImageRef [optional; required if ImageName is not provided] is the ID or + // full URL to the image that contains the server's OS and initial state. + // Also optional if using the boot-from-volume extension. + ImageRef string `json:"imageRef"` + + // ImageName [optional; required if ImageRef is not provided] is the name of + // the image that contains the server's OS and initial state. + // Also optional if using the boot-from-volume extension. + ImageName string `json:"-"` + + // FlavorRef [optional; required if FlavorName is not provided] is the ID or + // full URL to the flavor that describes the server's specs. + FlavorRef string `json:"flavorRef"` + + // FlavorName [optional; required if FlavorRef is not provided] is the name of + // the flavor that describes the server's specs. + FlavorName string `json:"-"` + + // SecurityGroups lists the names of the security groups to which this server + // should belong. + SecurityGroups []string `json:"-"` + + // UserData contains configuration information or scripts to use upon launch. + // Create will base64-encode it for you, if it isn't already. + UserData []byte `json:"-"` + + // AvailabilityZone in which to launch the server. + AvailabilityZone string `json:"availability_zone,omitempty"` + + // Networks dictates how this server will be attached to available networks. + // By default, the server will be attached to all isolated networks for the + // tenant. + // Starting with microversion 2.37 networks can also be an "auto" or "none" + // string. + Networks []Networks `json:"networks"` + + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the + // server. + Metadata map[string]string `json:"metadata,omitempty"` + + // ConfigDrive enables metadata injection through a configuration drive. + ConfigDrive *bool `json:"config_drive,omitempty"` + + // AdminPass sets the root user password. If not set, a randomly-generated + // password will be created and returned in the response. + AdminPass string `json:"adminPass,omitempty"` + + // AccessIPv4 specifies an IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` + + // AccessIPv6 specifies an IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` + + // Min specifies Minimum number of servers to launch. + Min int `json:"min_count,omitempty"` + + // Max specifies Maximum number of servers to launch. + Max int `json:"max_count,omitempty"` + + // ServiceClient will allow calls to be made to retrieve an image or + // flavor ID by name. + ServiceClient *gophercloud.ServiceClient `json:"-"` + + // Tags allows a server to be tagged with single-word metadata. + // Requires microversion 2.52 or later. + Tags []string `json:"tags,omitempty"` +} + +type Networks struct { + Port string `json:"port,omitempty"` } func (m *MockClient) mockServers() { @@ -118,6 +199,11 @@ func (m *MockClient) createServer(w http.ResponseWriter, r *http.Request) { } server.SecurityGroups = securityGroups + portID := create.Server.Networks[0].Port + ports.Update(m.networkClient, portID, ports.UpdateOpts{ + DeviceID: fi.String(server.ID), + }) + // Assign an IP address private := make([]map[string]string, 1) private[0] = make(map[string]string) diff --git a/cloudmock/openstack/mocknetworking/BUILD.bazel b/cloudmock/openstack/mocknetworking/BUILD.bazel index 20bea73b99..fff570185c 100644 --- a/cloudmock/openstack/mocknetworking/BUILD.bazel +++ b/cloudmock/openstack/mocknetworking/BUILD.bazel @@ -16,6 +16,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//cloudmock/openstack:go_default_library", + "//upup/pkg/fi:go_default_library", "//vendor/github.com/google/uuid:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers:go_default_library", diff --git a/cloudmock/openstack/mocknetworking/ports.go b/cloudmock/openstack/mocknetworking/ports.go index 43fdf4e25a..9efec359a0 100644 --- a/cloudmock/openstack/mocknetworking/ports.go +++ b/cloudmock/openstack/mocknetworking/ports.go @@ -24,6 +24,8 @@ import ( "regexp" "strings" + "k8s.io/kops/upup/pkg/fi" + "github.com/google/uuid" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -40,6 +42,10 @@ type portCreateRequest struct { Port ports.CreateOpts `json:"port"` } +type portUpdateRequest struct { + Port ports.UpdateOpts `json:"port"` +} + func (m *MockClient) mockPorts() { re := regexp.MustCompile(`/ports/?`) @@ -59,7 +65,13 @@ func (m *MockClient) mockPorts() { m.getPort(w, portID) } case http.MethodPut: - m.tagPort(w, r) + parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + if len(parts) == 4 && parts[2] == "tags" { + + m.tagPort(w, r) + } else if len(parts) == 2 { + m.updatePort(w, r) + } case http.MethodPost: m.createPort(w, r) case http.MethodDelete: @@ -203,3 +215,36 @@ func (m *MockClient) tagPort(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) } + +func (m *MockClient) updatePort(w http.ResponseWriter, r *http.Request) { + var update portUpdateRequest + err := json.NewDecoder(r.Body).Decode(&update) + if err != nil { + panic("error decoding update port request") + } + parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + if len(parts) != 2 { + w.WriteHeader(http.StatusBadRequest) + return + } + portID := parts[1] + + if _, ok := m.ports[portID]; !ok { + w.WriteHeader(http.StatusNotFound) + return + } + if _, ok := m.ports[portID]; !ok { + w.WriteHeader(http.StatusNotFound) + return + } + port := m.ports[portID] + + deviceID := update.Port.DeviceID + if deviceID != nil { + port.DeviceID = fi.StringValue(deviceID) + } + m.ports[portID] = port + + w.WriteHeader(http.StatusOK) + +} diff --git a/pkg/testutils/integrationtestharness.go b/pkg/testutils/integrationtestharness.go index fcf1d5f6d4..f71515c06f 100644 --- a/pkg/testutils/integrationtestharness.go +++ b/pkg/testutils/integrationtestharness.go @@ -261,7 +261,7 @@ func (h *IntegrationTestHarness) SetupMockOpenstack() *openstack.MockCloud { c.MockLBClient = mockloadbalancer.CreateClient() - c.MockNovaClient = mockcompute.CreateClient() + c.MockNovaClient = mockcompute.CreateClient(c.MockNeutronClient.ServiceClient()) c.MockDNSClient = mockdns.CreateClient()