diff --git a/pkg/client/client.go b/pkg/client/client.go index 229471d..739332a 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -13,38 +13,36 @@ import ( "github.com/OpenCHAMI/magellan/internal/util" ) -type Option func(*Client) +type Option[T Client] func(client T) // The 'Client' struct is a wrapper around the default http.Client // that provides an extended API to work with functional options. // It also provides functions that work with `collect` data. -type Client struct { - *http.Client +type Client interface { + Name() string + GetClient() *http.Client + RootEndpoint(endpoint string) string + + // functions needed to make request + Add(data util.HTTPBody, headers util.HTTPHeader) error + Update(data util.HTTPBody, headers util.HTTPHeader) error } // NewClient() creates a new client -func NewClient(opts ...Option) *Client { - client := &Client{ - Client: http.DefaultClient, - } +func NewClient[T Client](opts ...func(T)) T { + client := new(T) for _, opt := range opts { - opt(client) + opt(*client) } - return client + return *client } -func WithHttpClient(httpClient *http.Client) Option { - return func(c *Client) { - c.Client = httpClient - } -} - -func WithCertPool(certPool *x509.CertPool) Option { +func WithCertPool[T Client](certPool *x509.CertPool) func(T) { if certPool == nil { - return func(c *Client) {} + return func(client T) {} } - return func(c *Client) { - c.Client.Transport = &http.Transport{ + return func(client T) { + client.GetClient().Transport = &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certPool, InsecureSkipVerify: true, @@ -60,20 +58,20 @@ func WithCertPool(certPool *x509.CertPool) Option { } } -func WithSecureTLS(certPath string) Option { +func WithSecureTLS[T Client](certPath string) func(T) { cacert, err := os.ReadFile(certPath) if err != nil { - return func(c *Client) {} + return func(client T) {} } certPool := x509.NewCertPool() certPool.AppendCertsFromPEM(cacert) - return WithCertPool(certPool) + return WithCertPool[T](certPool) } // Post() is a simplified wrapper function that packages all of the // that marshals a mapper into a JSON-formatted byte array, and then performs // a request to the specified URL. -func (c *Client) Post(url string, data map[string]any, header util.HTTPHeader) (*http.Response, util.HTTPBody, error) { +func (c *MagellanClient) Post(url string, data map[string]any, header util.HTTPHeader) (*http.Response, util.HTTPBody, error) { // serialize data into byte array body, err := json.Marshal(data) if err != nil { @@ -81,7 +79,3 @@ func (c *Client) Post(url string, data map[string]any, header util.HTTPHeader) ( } return util.MakeRequest(c.Client, url, http.MethodPost, body, header) } - -func (c *Client) MakeRequest(url string, method string, body util.HTTPBody, header util.HTTPHeader) (*http.Response, util.HTTPBody, error) { - return util.MakeRequest(c.Client, url, method, body, header) -} diff --git a/pkg/client/default.go b/pkg/client/default.go new file mode 100644 index 0000000..e466125 --- /dev/null +++ b/pkg/client/default.go @@ -0,0 +1,55 @@ +package client + +import ( + "fmt" + "net/http" + + "github.com/OpenCHAMI/magellan/internal/util" +) + +type MagellanClient struct { + *http.Client +} + +func (c *MagellanClient) Name() string { + return "default" +} + +// Add() is the default function that is called with a client with no implementation. +// This function will simply make a HTTP request including all the data passed as +// the first argument with no data processing or manipulation. The function sends +// the data to a set callback URL (which may be changed to use a configurable value +// instead). +func (c *MagellanClient) Add(data util.HTTPBody, headers util.HTTPHeader) error { + if data == nil { + return fmt.Errorf("no data found") + } + + path := "/inventory/add" + res, body, err := util.MakeRequest(c.Client, path, http.MethodPost, data, headers) + if res != nil { + statusOk := res.StatusCode >= 200 && res.StatusCode < 300 + if !statusOk { + return fmt.Errorf("returned status code %d when POST'ing to endpoint", res.StatusCode) + } + fmt.Printf("%v (%v)\n%s\n", path, res.Status, string(body)) + } + return err +} + +func (c *MagellanClient) Update(data util.HTTPBody, headers util.HTTPHeader) error { + if data == nil { + return fmt.Errorf("no data found") + } + + path := "/inventory/update" + res, body, err := util.MakeRequest(c.Client, path, http.MethodPut, data, headers) + if res != nil { + statusOk := res.StatusCode >= 200 && res.StatusCode < 300 + if !statusOk { + return fmt.Errorf("returned status code %d when PUT'ing to endpoint", res.StatusCode) + } + fmt.Printf("%v (%v)\n%s\n", path, res.Status, string(body)) + } + return err +} diff --git a/pkg/client/smd.go b/pkg/client/smd.go index 726c5f6..2befba0 100644 --- a/pkg/client/smd.go +++ b/pkg/client/smd.go @@ -11,41 +11,39 @@ import ( ) var ( - Host = "http://localhost" + Host = "http://localhost:27779" BaseEndpoint = "/hsm/v2" - Port = 27779 ) -func (c *Client) GetRedfishEndpoints(header util.HTTPHeader) error { - url := makeEndpointUrl("/Inventory/RedfishEndpoints") - _, body, err := util.MakeRequest(c.Client, url, http.MethodGet, nil, header) - if err != nil { - return fmt.Errorf("failed to get endpoint: %v", err) - } - // fmt.Println(res) - fmt.Println(string(body)) - return nil +type SmdClient struct { + *http.Client + Host string + Xname string } -func (c *Client) GetComponentEndpoint(xname string) error { - url := makeEndpointUrl("/Inventory/ComponentsEndpoints/" + xname) - res, body, err := c.MakeRequest(url, "GET", nil, nil) - if err != nil { - return fmt.Errorf("failed to get endpoint: %v", err) - } - fmt.Println(res) - fmt.Println(string(body)) - return nil +func (c SmdClient) Name() string { + return "smd" } -func (c *Client) AddRedfishEndpoint(data map[string]any, headers util.HTTPHeader) error { +func (c SmdClient) RootEndpoint(endpoint string) string { + return fmt.Sprintf("/hsm/v2/%s%s", Host, endpoint) +} + +func (c SmdClient) GetClient() *http.Client { + return c.Client +} + +// Add() has a similar function definition to that of the default implementation, +// but also allows further customization and data/header manipulation that would +// be specific and/or unique to SMD's API. +func (c SmdClient) Add(data util.HTTPBody, headers util.HTTPHeader) error { if data == nil { return fmt.Errorf("failed to add redfish endpoint: no data found") } // Add redfish endpoint via POST `/hsm/v2/Inventory/RedfishEndpoints` endpoint - url := makeEndpointUrl("/Inventory/RedfishEndpoints") - res, body, err := c.Post(url, data, headers) + url := c.RootEndpoint("/Inventory/RedfishEndpoints") + res, body, err := util.MakeRequest(c.Client, url, http.MethodPost, data, headers) if res != nil { statusOk := res.StatusCode >= 200 && res.StatusCode < 300 if !statusOk { @@ -56,13 +54,13 @@ func (c *Client) AddRedfishEndpoint(data map[string]any, headers util.HTTPHeader return err } -func (c *Client) UpdateRedfishEndpoint(xname string, data []byte, headers map[string]string) error { +func (c SmdClient) Update(data util.HTTPBody, headers util.HTTPHeader) error { if data == nil { return fmt.Errorf("failed to add redfish endpoint: no data found") } // Update redfish endpoint via PUT `/hsm/v2/Inventory/RedfishEndpoints` endpoint - url := makeEndpointUrl("/Inventory/RedfishEndpoints/" + xname) - res, body, err := c.MakeRequest(url, "PUT", data, headers) + url := c.RootEndpoint("/Inventory/RedfishEndpoints/" + c.Xname) + res, body, err := util.MakeRequest(c.Client, url, http.MethodPut, data, headers) fmt.Printf("%v (%v)\n%s\n", url, res.Status, string(body)) if res != nil { statusOk := res.StatusCode >= 200 && res.StatusCode < 300 @@ -72,7 +70,3 @@ func (c *Client) UpdateRedfishEndpoint(xname string, data []byte, headers map[st } return err } - -func makeEndpointUrl(endpoint string) string { - return Host + ":" + fmt.Sprint(Port) + BaseEndpoint + endpoint -}