client: Use http.Client instead of a string for the socket

This converts the client and weldrcheck functions to use http.Client for
connections instead of passing around the socket path and opening it for
each test.
This commit is contained in:
Brian C. Lane 2020-03-06 18:22:37 -08:00 committed by Tom Gundersen
parent 2778efed6f
commit 46c3bed153
7 changed files with 73 additions and 57 deletions

View file

@ -3,9 +3,24 @@
package main
import (
"context"
"net"
"net/http"
"time"
"github.com/osbuild/osbuild-composer/internal/weldrcheck"
)
func main() {
weldrcheck.Run("/run/weldr/api.socket")
client := &http.Client{
// TODO This may be too short/simple for downloading images
Timeout: 60 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", "/run/weldr/api.socket")
},
},
}
weldrcheck.Run(client)
}

View file

@ -5,12 +5,13 @@ package client
import (
"encoding/json"
"fmt"
"net/http"
"strings"
)
// PostTOMLBlueprintV0 sends a TOML blueprint string to the API
// and returns an APIResponse
func PostTOMLBlueprintV0(socket, blueprint string) (*APIResponse, error) {
func PostTOMLBlueprintV0(socket *http.Client, blueprint string) (*APIResponse, error) {
body, resp, err := PostTOML(socket, "/api/v0/blueprints/new", blueprint)
if resp != nil || err != nil {
return resp, err
@ -20,7 +21,7 @@ func PostTOMLBlueprintV0(socket, blueprint string) (*APIResponse, error) {
// PostTOMLWorkspaceV0 sends a TOML blueprint string to the API
// and returns an APIResponse
func PostTOMLWorkspaceV0(socket, blueprint string) (*APIResponse, error) {
func PostTOMLWorkspaceV0(socket *http.Client, blueprint string) (*APIResponse, error) {
body, resp, err := PostTOML(socket, "/api/v0/blueprints/workspace", blueprint)
if resp != nil || err != nil {
return resp, err
@ -30,7 +31,7 @@ func PostTOMLWorkspaceV0(socket, blueprint string) (*APIResponse, error) {
// PostJSONBlueprintV0 sends a JSON blueprint string to the API
// and returns an APIResponse
func PostJSONBlueprintV0(socket, blueprint string) (*APIResponse, error) {
func PostJSONBlueprintV0(socket *http.Client, blueprint string) (*APIResponse, error) {
body, resp, err := PostJSON(socket, "/api/v0/blueprints/new", blueprint)
if resp != nil || err != nil {
return resp, err
@ -40,7 +41,7 @@ func PostJSONBlueprintV0(socket, blueprint string) (*APIResponse, error) {
// PostJSONWorkspaceV0 sends a JSON blueprint string to the API
// and returns an APIResponse
func PostJSONWorkspaceV0(socket, blueprint string) (*APIResponse, error) {
func PostJSONWorkspaceV0(socket *http.Client, blueprint string) (*APIResponse, error) {
body, resp, err := PostJSON(socket, "/api/v0/blueprints/workspace", blueprint)
if resp != nil || err != nil {
return resp, err
@ -49,7 +50,7 @@ func PostJSONWorkspaceV0(socket, blueprint string) (*APIResponse, error) {
}
// DeleteBlueprintV0 deletes the named blueprint and returns an APIResponse
func DeleteBlueprintV0(socket, bpName string) (*APIResponse, error) {
func DeleteBlueprintV0(socket *http.Client, bpName string) (*APIResponse, error) {
body, resp, err := DeleteRaw(socket, "/api/v0/blueprints/delete/"+bpName)
if resp != nil || err != nil {
return resp, err
@ -58,7 +59,7 @@ func DeleteBlueprintV0(socket, bpName string) (*APIResponse, error) {
}
// DeleteWorkspaceV0 deletes the named blueprint's workspace and returns an APIResponse
func DeleteWorkspaceV0(socket, bpName string) (*APIResponse, error) {
func DeleteWorkspaceV0(socket *http.Client, bpName string) (*APIResponse, error) {
body, resp, err := DeleteRaw(socket, "/api/v0/blueprints/workspace/"+bpName)
if resp != nil || err != nil {
return resp, err
@ -67,7 +68,7 @@ func DeleteWorkspaceV0(socket, bpName string) (*APIResponse, error) {
}
// ListBlueprintsV0 returns a list of blueprint names
func ListBlueprintsV0(socket string) ([]string, *APIResponse, error) {
func ListBlueprintsV0(socket *http.Client) ([]string, *APIResponse, error) {
body, resp, err := GetJSONAll(socket, "/api/v0/blueprints/list")
if resp != nil || err != nil {
return nil, resp, err
@ -81,7 +82,7 @@ func ListBlueprintsV0(socket string) ([]string, *APIResponse, error) {
}
// GetBlueprintInfoTOMLV0 returns the requested blueprint as a TOML string
func GetBlueprintInfoTOMLV0(socket, bpName string) (string, *APIResponse, error) {
func GetBlueprintInfoTOMLV0(socket *http.Client, bpName string) (string, *APIResponse, error) {
body, resp, err := GetRaw(socket, "GET", "/api/v0/blueprints/info/"+bpName+"?format=toml")
if resp != nil || err != nil {
return "", resp, err
@ -90,7 +91,7 @@ func GetBlueprintInfoTOMLV0(socket, bpName string) (string, *APIResponse, error)
}
// GetBlueprintsInfoJSONV0 returns the requested blueprints and their changed state
func GetBlueprintsInfoJSONV0(socket, bpName string) (BlueprintsInfoV0, *APIResponse, error) {
func GetBlueprintsInfoJSONV0(socket *http.Client, bpName string) (BlueprintsInfoV0, *APIResponse, error) {
body, resp, err := GetRaw(socket, "GET", "/api/v0/blueprints/info/"+bpName)
if resp != nil || err != nil {
return BlueprintsInfoV0{}, resp, err
@ -104,7 +105,7 @@ func GetBlueprintsInfoJSONV0(socket, bpName string) (BlueprintsInfoV0, *APIRespo
}
// GetBlueprintsChangesV0 returns the changes to the listed blueprints
func GetBlueprintsChangesV0(socket string, bpNames []string) (BlueprintsChangesV0, *APIResponse, error) {
func GetBlueprintsChangesV0(socket *http.Client, bpNames []string) (BlueprintsChangesV0, *APIResponse, error) {
names := strings.Join(bpNames, ",")
body, resp, err := GetRaw(socket, "GET", "/api/v0/blueprints/changes/"+names)
if resp != nil || err != nil {
@ -119,7 +120,7 @@ func GetBlueprintsChangesV0(socket string, bpNames []string) (BlueprintsChangesV
}
// UndoBlueprintChangeV0 reverts a blueprint to a previous commit
func UndoBlueprintChangeV0(socket, blueprint, commit string) (*APIResponse, error) {
func UndoBlueprintChangeV0(socket *http.Client, blueprint, commit string) (*APIResponse, error) {
request := fmt.Sprintf("/api/v0/blueprints/undo/%s/%s", blueprint, commit)
body, resp, err := PostRaw(socket, request, "", nil)
if resp != nil || err != nil {
@ -129,7 +130,7 @@ func UndoBlueprintChangeV0(socket, blueprint, commit string) (*APIResponse, erro
}
// TagBlueprintV0 tags the current blueprint commit as a new revision
func TagBlueprintV0(socket, blueprint string) (*APIResponse, error) {
func TagBlueprintV0(socket *http.Client, blueprint string) (*APIResponse, error) {
body, resp, err := PostRaw(socket, "/api/v0/blueprints/tag/"+blueprint, "", nil)
if resp != nil || err != nil {
return resp, err
@ -138,7 +139,7 @@ func TagBlueprintV0(socket, blueprint string) (*APIResponse, error) {
}
// DepsolveBlueprintV0 depsolves the listed blueprint
func DepsolveBlueprintV0(socket, blueprint string) (BlueprintsDepsolveV0, *APIResponse, error) {
func DepsolveBlueprintV0(socket *http.Client, blueprint string) (BlueprintsDepsolveV0, *APIResponse, error) {
body, resp, err := GetRaw(socket, "GET", "/api/v0/blueprints/depsolve/"+blueprint)
if resp != nil || err != nil {
return BlueprintsDepsolveV0{}, resp, err
@ -153,7 +154,7 @@ func DepsolveBlueprintV0(socket, blueprint string) (BlueprintsDepsolveV0, *APIRe
// FreezeBlueprintV0 depsolves the listed blueprint and returns the blueprint with frozen package
// versions
func FreezeBlueprintV0(socket, blueprint string) (BlueprintsFreezeV0, *APIResponse, error) {
func FreezeBlueprintV0(socket *http.Client, blueprint string) (BlueprintsFreezeV0, *APIResponse, error) {
body, resp, err := GetRaw(socket, "GET", "/api/v0/blueprints/freeze/"+blueprint)
if resp != nil || err != nil {
return BlueprintsFreezeV0{}, resp, err

View file

@ -4,14 +4,11 @@ package client
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"time"
)
// Request handles sending the request, handling errors, returning the response
@ -22,17 +19,7 @@ import (
//
// If it is successful a http.Response will be returned. If there is an error, the response will be
// nil and error will be returned.
func Request(socket, method, path, body string, headers map[string]string) (*http.Response, error) {
client := http.Client{
// TODO This may be too short/simple for downloading images
Timeout: 60 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", socket)
},
},
}
func Request(socket *http.Client, method, path, body string, headers map[string]string) (*http.Response, error) {
req, err := http.NewRequest(method, "http://localhost"+path, bytes.NewReader([]byte(body)))
if err != nil {
return nil, err
@ -42,7 +29,7 @@ func Request(socket, method, path, body string, headers map[string]string) (*htt
req.Header.Set(h, v)
}
resp, err := client.Do(req)
resp, err := socket.Do(req)
if err != nil {
return nil, err
}
@ -112,7 +99,7 @@ func apiError(resp *http.Response) (*APIResponse, error) {
// GetRaw returns raw data from a GET request
// Errors from the API are returned as an APIResponse, client errors are returned as error
func GetRaw(socket, method, path string) ([]byte, *APIResponse, error) {
func GetRaw(socket *http.Client, method, path string) ([]byte, *APIResponse, error) {
resp, err := Request(socket, method, path, "", map[string]string{})
if err != nil {
return nil, nil, err
@ -138,7 +125,7 @@ func GetRaw(socket, method, path string) ([]byte, *APIResponse, error) {
// and then with limit=TOTAL to fetch all of the results.
// The path passed to GetJSONAll should not include the limit or offset query parameters
// Errors from the API are returned as an APIResponse, client errors are returned as error
func GetJSONAll(socket, path string) ([]byte, *APIResponse, error) {
func GetJSONAll(socket *http.Client, path string) ([]byte, *APIResponse, error) {
body, api, err := GetRaw(socket, "GET", path+"?limit=0")
if api != nil || err != nil {
return nil, api, err
@ -167,7 +154,7 @@ func GetJSONAll(socket, path string) ([]byte, *APIResponse, error) {
// PostRaw sends a POST with raw data and returns the raw response body
// Errors from the API are returned as an APIResponse, client errors are returned as error
func PostRaw(socket, path, body string, headers map[string]string) ([]byte, *APIResponse, error) {
func PostRaw(socket *http.Client, path, body string, headers map[string]string) ([]byte, *APIResponse, error) {
resp, err := Request(socket, "POST", path, body, headers)
if err != nil {
return nil, nil, err
@ -190,21 +177,21 @@ func PostRaw(socket, path, body string, headers map[string]string) ([]byte, *API
// PostTOML sends a POST with TOML data and the Content-Type header set to "text/x-toml"
// Errors from the API are returned as an APIResponse, client errors are returned as error
func PostTOML(socket, path, body string) ([]byte, *APIResponse, error) {
func PostTOML(socket *http.Client, path, body string) ([]byte, *APIResponse, error) {
headers := map[string]string{"Content-Type": "text/x-toml"}
return PostRaw(socket, path, body, headers)
}
// PostJSON sends a POST with JSON data and the Content-Type header set to "application/json"
// Errors from the API are returned as an APIResponse, client errors are returned as error
func PostJSON(socket, path, body string) ([]byte, *APIResponse, error) {
func PostJSON(socket *http.Client, path, body string) ([]byte, *APIResponse, error) {
headers := map[string]string{"Content-Type": "application/json"}
return PostRaw(socket, path, body, headers)
}
// DeleteRaw sends a DELETE request
// Errors from the API are returned as an APIResponse, client errors are returned as error
func DeleteRaw(socket, path string) ([]byte, *APIResponse, error) {
func DeleteRaw(socket *http.Client, path string) ([]byte, *APIResponse, error) {
resp, err := Request(socket, "DELETE", path, "", nil)
if err != nil {
return nil, nil, err

View file

@ -3,6 +3,7 @@
package client
import (
"context"
"io/ioutil"
"log"
"net"
@ -10,6 +11,7 @@ import (
"os"
"strings"
"testing"
"time"
test_distro "github.com/osbuild/osbuild-composer/internal/distro/fedoratest"
rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
@ -21,9 +23,12 @@ import (
// socketPath is the path to the temporary unix domain socket
var socketPath string
// socket is the client connection to the server on socketPath
var socket *http.Client
func TestRequest(t *testing.T) {
// Make a request to the status route
resp, err := Request(socketPath, "GET", "/api/status", "", map[string]string{})
resp, err := Request(socket, "GET", "/api/status", "", map[string]string{})
if err != nil {
t.Fatalf("Request good route failed: %v", err)
}
@ -32,7 +37,7 @@ func TestRequest(t *testing.T) {
}
// Make a request to a bad route
resp, err = Request(socketPath, "GET", "/invalidroute", "", map[string]string{})
resp, err = Request(socket, "GET", "/invalidroute", "", map[string]string{})
if err != nil {
t.Fatalf("Request bad route failed: %v", err)
}
@ -47,7 +52,7 @@ func TestRequest(t *testing.T) {
}
// Make a request with a bad offset to trigger a JSON response with Status set to 400
resp, err = Request(socketPath, "GET", "/api/v0/blueprints/list?offset=bad", "", map[string]string{})
resp, err = Request(socket, "GET", "/api/v0/blueprints/list?offset=bad", "", map[string]string{})
if err != nil {
t.Fatalf("Request bad offset failed: %v", err)
}
@ -84,7 +89,7 @@ func TestAPIResponse(t *testing.T) {
func TestGetRaw(t *testing.T) {
// Get raw data
b, resp, err := GetRaw(socketPath, "GET", "/api/status")
b, resp, err := GetRaw(socket, "GET", "/api/status")
if err != nil {
t.Fatalf("GetRaw failed with a client error: %v", err)
}
@ -95,7 +100,7 @@ func TestGetRaw(t *testing.T) {
t.Fatal("GetRaw returned an empty string")
}
// Get an API error
b, resp, err = GetRaw(socketPath, "GET", "/api/v0/blueprints/list?offset=bad")
b, resp, err = GetRaw(socket, "GET", "/api/v0/blueprints/list?offset=bad")
if err != nil {
t.Fatalf("GetRaw bad request failed with a client error: %v", err)
}
@ -114,7 +119,7 @@ func TestGetRaw(t *testing.T) {
func TestGetJSONAll(t *testing.T) {
// Get all the projects
b, resp, err := GetJSONAll(socketPath, "/api/v0/projects/list")
b, resp, err := GetJSONAll(socket, "/api/v0/projects/list")
if err != nil {
t.Fatalf("GetJSONAll failed with a client error: %v", err)
}
@ -126,7 +131,7 @@ func TestGetJSONAll(t *testing.T) {
}
// Run it on a route that doesn't support offset/limit
b, resp, err = GetJSONAll(socketPath, "/api/status")
b, resp, err = GetJSONAll(socket, "/api/status")
if err == nil {
t.Fatalf("GetJSONAll bad route failed: %v", b)
}
@ -137,7 +142,7 @@ func TestGetJSONAll(t *testing.T) {
func TestPostRaw(t *testing.T) {
// There are no routes that accept raw POST w/o Content-Type so this ends up testing the error path
b, resp, err := PostRaw(socketPath, "/api/v0/blueprints/new", "nobody", nil)
b, resp, err := PostRaw(socket, "/api/v0/blueprints/new", "nobody", nil)
if err != nil {
t.Fatalf("PostRaw bad request failed with a client error: %v", err)
}
@ -158,7 +163,7 @@ func TestPostTOML(t *testing.T) {
blueprint := `name = "test-blueprint"
description = "TOML test blueprint"
version = "0.0.1"`
b, resp, err := PostTOML(socketPath, "/api/v0/blueprints/new", blueprint)
b, resp, err := PostTOML(socket, "/api/v0/blueprints/new", blueprint)
if err != nil {
t.Fatalf("PostTOML client failed: %v", err)
}
@ -174,7 +179,7 @@ func TestPostJSON(t *testing.T) {
blueprint := `{"name": "test-blueprint",
"description": "JSON test blueprint",
"version": "0.0.1"}`
b, resp, err := PostJSON(socketPath, "/api/v0/blueprints/new", blueprint)
b, resp, err := PostJSON(socket, "/api/v0/blueprints/new", blueprint)
if err != nil {
t.Fatalf("PostJSON client failed: %v", err)
}
@ -216,6 +221,16 @@ func TestMain(m *testing.M) {
}
}()
// Setup client connection to socketPath
socket = &http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", socketPath)
},
},
}
// Run the tests
os.Exit(m.Run())
}

View file

@ -4,10 +4,11 @@ package client
import (
"encoding/json"
"net/http"
)
// GetStatusV0 makes a GET request to /api/status and returns the v0 response as a StatusResponseV0
func GetStatusV0(socket string) (reply StatusV0, resp *APIResponse, err error) {
func GetStatusV0(socket *http.Client) (reply StatusV0, resp *APIResponse, err error) {
body, resp, err := GetRaw(socket, "GET", "/api/status")
if resp != nil || err != nil {
return reply, resp, err

View file

@ -4,6 +4,7 @@ package weldrcheck
import (
"log"
"net/http"
"reflect"
"sort"
"strings"
@ -14,7 +15,7 @@ import (
)
type checkBlueprintsV0 struct {
socket string
socket *http.Client
}
// Run will execute the API V0 Blueprint check functions

View file

@ -4,6 +4,7 @@ package weldrcheck
import (
"log"
"net/http"
"os"
"sort"
"strconv"
@ -32,7 +33,7 @@ func isStringInSlice(slice []string, s string) bool {
// Run the API V0 checks against the server
// Return true if all the checks pass
func runV0Checks(socket string) (pass bool) {
func runV0Checks(socket *http.Client) (pass bool) {
pass = true
bpv0 := checkBlueprintsV0{socket}
@ -47,7 +48,7 @@ func runV0Checks(socket string) (pass bool) {
}
// Run the V1 checks against the server
func runV1Checks(socket string) (pass bool) {
func runV1Checks(socket *http.Client) (pass bool) {
pass = true
if pass {
@ -60,14 +61,9 @@ func runV1Checks(socket string) (pass bool) {
// Run executes all of the weldr API checks against a running API server
// This is designed to run against any WELDR API server, not just osbuild-composer
func Run(socket string) {
log.Printf("Running API check on %s", socket)
func Run(socket *http.Client) {
log.Print("Running API check")
// Does the socket exist?
if _, err := os.Stat(socket); os.IsNotExist(err) {
log.Printf("ERROR: API socket %s is missing", socket)
os.Exit(1)
}
// Does the server respond to /api/status?
status, resp, err := client.GetStatusV0(socket)
if err != nil {