Skip to content

Commit

Permalink
feat: Support for datastore endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
dploeger committed Dec 22, 2023
1 parent 681eba7 commit 0758124
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 1 deletion.
3 changes: 3 additions & 0 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ type VSphereProxyApi interface {

// GetHosts retrieves a list of ESXi hosts from the vCenter
GetHosts(username string, password string) ([]Host, error)

// GetDatastores retrieves a list of datastores from the vCenter
GetDatastores(username string, password string) ([]Datastore, error)
}

// DefaultVSphereProxyApi is the default API implementation
Expand Down
30 changes: 30 additions & 0 deletions internal/api/datastores.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package api

import (
"fmt"
"github.com/sirupsen/logrus"
)

func (d DefaultVSphereProxyApi) GetDatastores(username string, password string) ([]Datastore, error) {
if s, err := d.GetSession(username, password); err != nil {
return []Datastore{}, err
} else {
logrus.Debugf("Fetching all datastores from %s for %s", d.Resty.BaseURL, username)
var datastores []Datastore
if r, err := d.Resty.
R().
SetHeader("vmware-api-session-id", s).
SetResult(&datastores).
Get("/api/vcenter/datastore"); err != nil {
logrus.Errorf("Error fetching datastores: %s", err)
return []Datastore{}, err
} else {
if r.IsError() {
err := fmt.Errorf("error getting datastores (%s): %s", r.Status(), r.Body())
logrus.Error(err)
return []Datastore{}, err
}
return datastores, nil
}
}
}
9 changes: 9 additions & 0 deletions internal/api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ type Host struct {
PowerState string `json:"power_state"`
ConnectionState string `json:"connection_state"`
}

// Datastore represents a host in the vCenter as described in https://developer.vmware.com/apis/vsphere-automation/v8.0U1/vcenter/data-structures/Datastore/Summary/
type Datastore struct {
Datastore string `json:"datastore"`
Name string `json:"name"`
Type string `json:"type"`
Capacity int `json:"capacity"`
FreeSpace int `json:"free_space"`
}
32 changes: 32 additions & 0 deletions internal/endpoints/datastores.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package endpoints

import (
"fmt"
"github.com/gin-gonic/gin"
"vmware-rest-proxy/internal/api"
)

type DataStoreEndpoint struct {
API api.VSphereProxyApi
}

func (d DataStoreEndpoint) Register(engine *gin.Engine) {
engine.GET("/datastores", d.getDatastores)
}

func (d DataStoreEndpoint) getDatastores(context *gin.Context) {
if r, ok := HandleRequest(context); ok {
if datastores, err := d.API.GetDatastores(r.Username, r.Password); err != nil {
context.AbortWithStatusJSON(500, gin.H{
"error": fmt.Sprintf("Error getting datastores: %s", err),
})
} else {
context.JSON(200, gin.H{
"datastores": gin.H{
"count": len(datastores),
"datastores": datastores,
},
})
}
}
}
56 changes: 56 additions & 0 deletions internal/endpoints_test/datastores_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package endpoints_test

import (
"encoding/json"
"github.com/dodevops/golang-handlerinspector/pkg/builder"
"github.com/dodevops/golang-handlerinspector/pkg/inspector"
"github.com/go-playground/assert/v2"
"net/http"
"testing"
"vmware-rest-proxy/internal/api"
"vmware-rest-proxy/test"
)

func TestDatastoresEndpoint_GetDatastores(t *testing.T) {
b := builder.NewBuilder().
WithRule(test.SessionRule).
WithRule(
builder.NewRule("datastores").
WithCondition(builder.HasPath("/api/vcenter/datastore")).
WithCondition(builder.HasMethod("GET")).
WithCondition(builder.HasHeader("Vmware-Api-Session-Id", test.AUTHTOKEN)).
ReturnBody(`[{"datastore": "1", "name": "test1", "type": "VMFS", "capacity": 100, "free_space": 0}, {"datastore": "2", "name": "test2", "type": "NFS", "capacity": 1000, "free_space": 10}]`).
ReturnHeader("Content-Type", "application/json").
Build(),
)
req, _ := http.NewRequest("GET", "/datastores", nil)
req.SetBasicAuth("test", "test")
w := test.TestRequests(b.Build(), []*http.Request{req})

type resp struct {
Datastores struct {
Count int `json:"count"`
Datastores []api.Datastore `json:"datastores"`
} `json:"datastores"`
}
var r resp
err := json.NewDecoder(w.Body).Decode(&r)
assert.Equal(t, err, nil)

i := inspector.NewInspector(b)
assert.Equal(t, i.Failed(), false)
assert.Equal(t, i.AllWereCalled(), true)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, r.Datastores.Count, 2)
assert.Equal(t, len(r.Datastores.Datastores), 2)
assert.Equal(t, r.Datastores.Datastores[0].Datastore, "1")
assert.Equal(t, r.Datastores.Datastores[0].Name, "test1")
assert.Equal(t, r.Datastores.Datastores[0].Type, "VMFS")
assert.Equal(t, r.Datastores.Datastores[0].Capacity, 100)
assert.Equal(t, r.Datastores.Datastores[0].FreeSpace, 0)
assert.Equal(t, r.Datastores.Datastores[1].Datastore, "2")
assert.Equal(t, r.Datastores.Datastores[1].Name, "test2")
assert.Equal(t, r.Datastores.Datastores[1].Type, "NFS")
assert.Equal(t, r.Datastores.Datastores[1].Capacity, 1000)
assert.Equal(t, r.Datastores.Datastores[1].FreeSpace, 10)
}
2 changes: 1 addition & 1 deletion test/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestRequests(handler http.Handler, requests []*http.Request) *httptest.Resp
r := resty.New().SetBaseURL(s.URL).SetBasicAuth("test", "test")
a := api.DefaultVSphereProxyApi{Resty: r}
g := gin.Default()
for _, endpoint := range []endpoints.Endpoint{&endpoints.VMSEndpoint{API: a}, &endpoints.HostsEndpoint{API: a}, &endpoints.StatusEndpoint{}} {
for _, endpoint := range []endpoints.Endpoint{&endpoints.VMSEndpoint{API: a}, &endpoints.HostsEndpoint{API: a}, &endpoints.StatusEndpoint{}, &endpoints.DataStoreEndpoint{API: a}} {
endpoint.Register(g)
}

Expand Down

0 comments on commit 0758124

Please sign in to comment.