Skip to content

Commit a5f814f

Browse files
committed
add a test for validating access to a node known to ironic
Show that when the host has no Provisioning.ID set we can find the node by name and we link it to the host by setting the Provisioning.ID field. Change the CreateNodes() method of the test server to take a callback so the node "created" by the method can be passed back to the test for checking. Only allow POST calls to invoke the handler from CreateNodes(). Unmarshal the input to the CreateNodes() handler and set the UUID in the node we get instead of changing the JSON string. Signed-off-by: Doug Hellmann <dhellmann@redhat.com>
1 parent ab24288 commit a5f814f

File tree

3 files changed

+86
-30
lines changed

3 files changed

+86
-30
lines changed

pkg/provisioner/ironic/testserver/ironic.go

+29-27
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
package testserver
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io/ioutil"
67
"net/http"
7-
"strings"
88
"testing"
99

1010
"github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes"
1111
)
1212

13-
// CreatedNode holds the body of the request to create the node and
14-
// the details generated by the server and included in the response.
15-
type CreatedNode struct {
16-
Body string
17-
UUID string
18-
}
19-
2013
// IronicMock is a test server that implements Ironic's semantics
2114
type IronicMock struct {
2215
*MockServer
23-
CreatedNodes []CreatedNode
16+
CreatedNodes int
2417
}
2518

2619
// NewIronic builds an ironic mock server
2720
func NewIronic(t *testing.T) *IronicMock {
2821

2922
return &IronicMock{
3023
MockServer: New(t, "ironic"),
31-
CreatedNodes: nil,
24+
CreatedNodes: 0,
3225
}
3326
}
3427

@@ -131,9 +124,16 @@ func (m *IronicMock) NodeError(name string, errorCode int) *IronicMock {
131124
return m
132125
}
133126

127+
type CreateNodeCallback func(node nodes.Node)
128+
134129
// CreateNodes configures the server so POSTing to /v1/nodes saves the data
135-
func (m *IronicMock) CreateNodes() *IronicMock {
130+
func (m *IronicMock) CreateNodes(callback CreateNodeCallback) *IronicMock {
136131
m.Handler("/v1/nodes", func(w http.ResponseWriter, r *http.Request) {
132+
if r.Method != http.MethodPost {
133+
http.Error(w, fmt.Sprintf("%s not handled for %s", r.Method, r.URL),
134+
http.StatusNotImplemented)
135+
}
136+
137137
bodyRaw, err := ioutil.ReadAll(r.Body)
138138
if err != nil {
139139
m.logRequest(r, fmt.Sprintf("ERROR: %s", err))
@@ -144,25 +144,27 @@ func (m *IronicMock) CreateNodes() *IronicMock {
144144
body := string(bodyRaw)
145145
m.t.Logf("%s: create nodes request %v", m.name, body)
146146

147+
// Unpack the input so we can update it
148+
node := nodes.Node{}
149+
err = json.Unmarshal(bodyRaw, &node)
150+
if err != nil {
151+
m.logRequest(r, fmt.Sprintf("ERROR: %s", err))
152+
http.Error(w, fmt.Sprintf("%s", err), http.StatusInternalServerError)
153+
return
154+
}
155+
147156
// The UUID value doesn't actually have to be a UUID, so we
148157
// just make a new string based on the count of nodes already
149158
// created.
150-
uuid := fmt.Sprintf("node-%d", len(m.CreatedNodes))
151-
m.t.Logf("%s: uuid %s", m.name, uuid)
152-
153-
// Record what we have so the test can examine it later
154-
m.CreatedNodes = append(m.CreatedNodes, CreatedNode{
155-
Body: body,
156-
UUID: uuid,
157-
})
158-
159-
// hackily add uuid to the json response by inserting it to the front of the string
160-
response := fmt.Sprintf("{\"uuid\": \"%s\", %s", uuid, strings.TrimLeft(body, "{"))
161-
162-
w.Header().Set("Content-Type", "application/json")
163-
w.WriteHeader(http.StatusCreated)
164-
fmt.Fprint(w, response)
165-
m.logRequest(r, response)
159+
node.UUID = fmt.Sprintf("node-%d", m.CreatedNodes)
160+
m.t.Logf("%s: uuid %s", m.name, node.UUID)
161+
m.CreatedNodes++
162+
163+
// Pass the data to the test via the callback
164+
callback(node)
165+
166+
// Handle the response to this request
167+
m.SendJSONResponse(node, http.StatusCreated, w, r)
166168
})
167169
return m
168170
}

pkg/provisioner/ironic/testserver/server.go

+10
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,13 @@ func (m *MockServer) sendData(w http.ResponseWriter, r *http.Request, code int,
228228
w.WriteHeader(code)
229229
fmt.Fprint(w, payload)
230230
}
231+
232+
// SendJSONResponse marshalls the payload to a JSON object and sends
233+
// the response using the given writer
234+
func (m *MockServer) SendJSONResponse(payload interface{}, code int, w http.ResponseWriter, r *http.Request) {
235+
content, err := json.Marshal(payload)
236+
if err != nil {
237+
m.t.Error(err)
238+
}
239+
m.sendData(w, r, code, string(content))
240+
}

pkg/provisioner/ironic/validatemanagementaccess_test.go

+47-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,50 @@ func TestValidateManagementAccessCreateNode(t *testing.T) {
7575
host.Spec.BootMACAddress = ""
7676
host.Status.Provisioning.ID = "" // so we don't lookup by uuid
7777

78-
ironic := testserver.NewIronic(t).Ready().CreateNodes().NoNode(host.Name)
78+
var createdNode *nodes.Node
79+
80+
createCallback := func(node nodes.Node) {
81+
createdNode = &node
82+
}
83+
84+
ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Name)
85+
ironic.Start()
86+
defer ironic.Stop()
87+
88+
auth := clients.AuthConfig{Type: clients.NoAuth}
89+
prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nullEventPublisher,
90+
ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth,
91+
)
92+
if err != nil {
93+
t.Fatalf("could not create provisioner: %s", err)
94+
}
95+
96+
result, err := prov.ValidateManagementAccess(false)
97+
if err != nil {
98+
t.Fatalf("error from ValidateManagementAccess: %s", err)
99+
}
100+
assert.Equal(t, "", result.ErrorMessage)
101+
assert.NotEqual(t, "", createdNode.UUID)
102+
assert.Equal(t, createdNode.UUID, host.Status.Provisioning.ID)
103+
}
104+
105+
func TestValidateManagementAccessExistingNode(t *testing.T) {
106+
// Create a host without a bootMACAddress and with a BMC that
107+
// does not require one.
108+
host := makeHost()
109+
host.Spec.BootMACAddress = ""
110+
host.Status.Provisioning.ID = "" // so we don't lookup by uuid
111+
112+
var createdNode *nodes.Node
113+
114+
createCallback := func(node nodes.Node) {
115+
createdNode = &node
116+
}
117+
118+
ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{
119+
Name: host.Name,
120+
UUID: "uuid",
121+
})
79122
ironic.Start()
80123
defer ironic.Stop()
81124

@@ -92,6 +135,7 @@ func TestValidateManagementAccessCreateNode(t *testing.T) {
92135
t.Fatalf("error from ValidateManagementAccess: %s", err)
93136
}
94137
assert.Equal(t, "", result.ErrorMessage)
95-
assert.NotEqual(t, "", host.Status.Provisioning.ID)
96-
assert.Equal(t, ironic.CreatedNodes[0].UUID, host.Status.Provisioning.ID)
138+
assert.Equal(t, "uuid", host.Status.Provisioning.ID)
139+
// no node should have been created
140+
assert.Nil(t, createdNode)
97141
}

0 commit comments

Comments
 (0)