Skip to content

Commit 23dc838

Browse files
committed
Add an initial structure for openstack cloudmock
More info in the docs changes. This adds stubbed http handlers for every resource type used by Kops.
1 parent d1fe0a1 commit 23dc838

32 files changed

+1130
-6
lines changed

cloudmock/README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
cloudmock is a mock implementation of the AWS APIs.
1+
cloudmock is a mock implementation of the CloudProvider APIs.
22

3-
The goal is to let us test code that interacts with the AWS APIs, without creating actual AWS resources.
3+
The goal is to let us test code that interacts with the CloudProvider APIs, without creating actual resources.
44

55
While no resources are created, we maintain state so that (for example) after you call `CreateVpc`, a subsequent
6-
call to `DescribeVpcs` will return that VPC. The end-goal is that we simulate the AWS APIs accurately,
6+
call to `DescribeVpcs` will return that VPC. The end-goal is that we simulate the CloudProvider APIs accurately,
77
so that we can quickly run test-cases that might otherwise require a lot of time or money to run with real
8-
AWS resources.
8+
resources.
99

1010
In future, we can also do fault injection etc.
1111

12-
Note: The AWS API is very large, and most of it is not implemented. Functions that are implemented may
12+
Note: The APIs are very large, and most of them are not implemented. Functions that are implemented may
1313
not be implemented correctly, particularly around edge-cases (such as error handling).
1414

15-
Typical use: `c := &mockec2.MockEC2{}`. `MockEC2` implements the EC2 API interface `ec2iface.EC2API`,
15+
Typical AWS use: `c := &mockec2.MockEC2{}`. `MockEC2` implements the EC2 API interface `ec2iface.EC2API`,
1616
so can be used where otherwise you would use a real EC2 client.

cloudmock/openstack/BUILD.bazel

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["mock.go"],
6+
importpath = "k8s.io/kops/cloudmock/openstack",
7+
visibility = ["//visibility:public"],
8+
deps = ["//vendor/github.com/gophercloud/gophercloud:go_default_library"],
9+
)

cloudmock/openstack/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Openstack Cloudmock
2+
3+
## Design
4+
5+
Because the gophercloud library does not provide client interfaces whose client-side functions could be mocked like aws-sdk-go, this cloudmock uses a local HTTP server and updates state based on incoming requests from the gophercloud clients.
6+
This is how the [gophercloud library tests](https://github.com/gophercloud/gophercloud/blob/51f8fa152459ae60d3b348023ad79f850db3a931/openstack/compute/v2/servers/testing/fixtures.go#L896-L914) themselves are implemented.
7+
8+
Each package represents one of the Openstack service clients and contains its own `net/http/httptest` server.
9+
Each package defines the endpoints for that client's resources.
10+
11+
## Troubleshooting
12+
13+
One recommended way to troubleshoot requests and responses is with Wireshark or an equivalent, monitoring the loopback interface.

cloudmock/openstack/mock.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package openstack
18+
19+
import (
20+
"fmt"
21+
"net/http"
22+
"net/http/httptest"
23+
24+
"github.com/gophercloud/gophercloud"
25+
)
26+
27+
type MockOpenstackServer struct {
28+
Mux *http.ServeMux
29+
30+
Server *httptest.Server
31+
}
32+
33+
// SetupMux prepares the Mux and Server.
34+
func (m *MockOpenstackServer) SetupMux() {
35+
m.Mux = http.NewServeMux()
36+
m.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
37+
w.WriteHeader(http.StatusNotImplemented)
38+
panic(fmt.Sprintf("Unhandled mock request: %+v\n", r))
39+
})
40+
}
41+
42+
// TeardownHTTP releases HTTP-related resources.
43+
func (m *MockOpenstackServer) TeardownHTTP() {
44+
m.Server.Close()
45+
}
46+
47+
func (m *MockOpenstackServer) ServiceClient() *gophercloud.ServiceClient {
48+
return &gophercloud.ServiceClient{
49+
ProviderClient: &gophercloud.ProviderClient{},
50+
Endpoint: m.Server.URL + "/",
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"api.go",
7+
"availabilityzones.go",
8+
"volumes.go",
9+
],
10+
importpath = "k8s.io/kops/cloudmock/openstack/mockblockstorage",
11+
visibility = ["//visibility:public"],
12+
deps = [
13+
"//cloudmock/openstack:go_default_library",
14+
"//vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes:go_default_library",
15+
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones:go_default_library",
16+
],
17+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mockblockstorage
18+
19+
import (
20+
"net/http/httptest"
21+
"sync"
22+
23+
cinderv3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
24+
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
25+
"k8s.io/kops/cloudmock/openstack"
26+
)
27+
28+
// MockClient represents a mocked blockstorage (cinderv3) client
29+
type MockClient struct {
30+
openstack.MockOpenstackServer
31+
mutex sync.Mutex
32+
33+
volumes map[string]cinderv3.Volume
34+
availabilityZones map[string]availabilityzones.AvailabilityZone
35+
}
36+
37+
// CreateClient will create a new mock blockstorage client
38+
func CreateClient() *MockClient {
39+
m := &MockClient{}
40+
m.Reset()
41+
m.SetupMux()
42+
m.mockVolumes()
43+
m.mockAvailabilityZones()
44+
m.Server = httptest.NewServer(m.Mux)
45+
return m
46+
}
47+
48+
// Reset will empty the state of the mock data
49+
func (m *MockClient) Reset() {
50+
m.volumes = make(map[string]cinderv3.Volume)
51+
m.availabilityZones = make(map[string]availabilityzones.AvailabilityZone)
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mockblockstorage
18+
19+
import (
20+
"net/http"
21+
)
22+
23+
func (m *MockClient) mockAvailabilityZones() {
24+
25+
handler := func(w http.ResponseWriter, r *http.Request) {
26+
m.mutex.Lock()
27+
defer m.mutex.Unlock()
28+
w.Header().Add("Content-Type", "application/json")
29+
w.WriteHeader(http.StatusOK)
30+
}
31+
m.Mux.HandleFunc("/os-availability-zone", handler)
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mockblockstorage
18+
19+
import (
20+
"net/http"
21+
)
22+
23+
func (m *MockClient) mockVolumes() {
24+
25+
handler := func(w http.ResponseWriter, r *http.Request) {
26+
m.mutex.Lock()
27+
defer m.mutex.Unlock()
28+
29+
w.Header().Add("Content-Type", "application/json")
30+
w.WriteHeader(http.StatusOK)
31+
}
32+
m.Mux.HandleFunc("/volumes/", handler)
33+
m.Mux.HandleFunc("/volumes", handler)
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"api.go",
7+
"flavors.go",
8+
"images.go",
9+
"keypairs.go",
10+
"servergroups.go",
11+
"servers.go",
12+
],
13+
importpath = "k8s.io/kops/cloudmock/openstack/mockcompute",
14+
visibility = ["//visibility:public"],
15+
deps = [
16+
"//cloudmock/openstack:go_default_library",
17+
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs:go_default_library",
18+
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library",
19+
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors:go_default_library",
20+
"//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library",
21+
"//vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images:go_default_library",
22+
],
23+
)
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mockcompute
18+
19+
import (
20+
"net/http/httptest"
21+
"sync"
22+
23+
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
24+
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
25+
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
26+
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
27+
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
28+
"k8s.io/kops/cloudmock/openstack"
29+
)
30+
31+
// MockClient represents a mocked networks (nebula) client
32+
type MockClient struct {
33+
openstack.MockOpenstackServer
34+
mutex sync.Mutex
35+
36+
serverGroups map[string]servergroups.ServerGroup
37+
servers map[string]servers.Server
38+
keyPairs map[string]keypairs.KeyPair
39+
images map[string]images.Image
40+
flavors map[string]flavors.Flavor
41+
}
42+
43+
// CreateClient will create a new mock networking client
44+
func CreateClient() *MockClient {
45+
m := &MockClient{}
46+
m.SetupMux()
47+
m.Reset()
48+
m.mockServerGroups()
49+
m.mockServers()
50+
m.mockKeyPairs()
51+
m.mockImages()
52+
m.mockFlavors()
53+
m.Server = httptest.NewServer(m.Mux)
54+
return m
55+
}
56+
57+
// Reset will empty the state of the mock data
58+
func (m *MockClient) Reset() {
59+
m.serverGroups = make(map[string]servergroups.ServerGroup)
60+
m.servers = make(map[string]servers.Server)
61+
m.keyPairs = make(map[string]keypairs.KeyPair)
62+
m.images = make(map[string]images.Image)
63+
m.flavors = make(map[string]flavors.Flavor)
64+
}
65+
66+
// All returns a map of all resource IDs to their resources
67+
func (m *MockClient) All() map[string]interface{} {
68+
all := make(map[string]interface{})
69+
for id, sg := range m.serverGroups {
70+
all[id] = sg
71+
}
72+
for id, kp := range m.keyPairs {
73+
all[id] = kp
74+
}
75+
for id, s := range m.servers {
76+
all[id] = s
77+
}
78+
return all
79+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mockcompute
18+
19+
import (
20+
"net/http"
21+
)
22+
23+
func (m *MockClient) mockFlavors() {
24+
25+
handler := func(w http.ResponseWriter, r *http.Request) {
26+
m.mutex.Lock()
27+
defer m.mutex.Unlock()
28+
29+
w.Header().Add("Content-Type", "application/json")
30+
w.WriteHeader(http.StatusOK)
31+
}
32+
m.Mux.HandleFunc("/flavors/", handler)
33+
m.Mux.HandleFunc("/flavors", handler)
34+
}

0 commit comments

Comments
 (0)