api: implement /compose/logs route

The implementation is just a stub returning always the same tar archive.
The ability to return actual logs will be implemented in the future - osbuild
isn't currently returning any logs.
This commit is contained in:
Ondřej Budai 2019-12-06 11:07:13 +01:00 committed by Lars Karlitski
parent d7f81b36e6
commit 4e62f181fa
2 changed files with 124 additions and 0 deletions

View file

@ -1,6 +1,7 @@
package weldr
import (
"archive/tar"
"encoding/json"
"errors"
"fmt"
@ -92,6 +93,7 @@ func New(rpmmd rpmmd.RPMMD, distro distro.Distro, logger *log.Logger, store *sto
api.router.GET("/api/v:version/compose/finished", api.composeFinishedHandler)
api.router.GET("/api/v:version/compose/failed", api.composeFailedHandler)
api.router.GET("/api/v:version/compose/image/:uuid", api.composeImageHandler)
api.router.GET("/api/v:version/compose/logs/:uuid", api.composeLogsHandler)
api.router.POST("/api/v:version/compose/uploads/schedule/:uuid", api.uploadsScheduleHandler)
api.router.DELETE("/api/v:version/upload/delete/:uuid", api.uploadsDeleteHandler)
@ -1463,6 +1465,59 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
io.Copy(writer, file)
}
func (api *API) composeLogsHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
if !verifyRequestVersion(writer, params, 0) {
return
}
uuidString := params.ByName("uuid")
id, err := uuid.Parse(uuidString)
if err != nil {
errors := responseError{
ID: "UnknownUUID",
Msg: fmt.Sprintf("%s is not a valid build uuid", uuidString),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
compose, exists := api.store.GetCompose(id)
if !exists {
errors := responseError{
ID: "UnknownUUID",
Msg: fmt.Sprintf("Compose %s doesn't exist", uuidString),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
if compose.QueueStatus != "FINISHED" && compose.QueueStatus != "FAILED" {
errors := responseError{
ID: "BuildInWrongState",
Msg: fmt.Sprintf("Build %s not in FINISHED or FAILED state.", uuidString),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return
}
writer.Header().Set("Content-Disposition", "attachment; filename="+id.String()+"-logs.tar")
writer.Header().Set("Content-Type", "application/x-tar")
tw := tar.NewWriter(writer)
// TODO: return real log from osbuild
fileContents := []byte("SUCCESS\n")
header := &tar.Header{
Name: "logs/osbuild.log",
Mode: 0644,
Size: int64(len(fileContents)),
}
tw.WriteHeader(header)
tw.Write(fileContents)
}
func (api *API) composeFinishedHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
if !verifyRequestVersion(writer, params, 0) {
return

View file

@ -1,7 +1,9 @@
package weldr_test
import (
"archive/tar"
"bytes"
"io"
"math/rand"
"net/http"
"net/http/httptest"
@ -389,6 +391,73 @@ func TestComposeInfo(t *testing.T) {
}
}
func TestComposeLogs(t *testing.T) {
if len(os.Getenv("OSBUILD_COMPOSER_TEST_EXTERNAL")) > 0 {
t.Skip("This test is for internal testing only")
}
var successCases = []struct {
Path string
ExpectedContentDisposition string
ExpectedContentType string
ExpectedFileName string
ExpectedFileContent string
}{
{"/api/v0/compose/logs/30000000-0000-0000-0000-000000000002", "attachment; filename=30000000-0000-0000-0000-000000000002-logs.tar", "application/x-tar", "logs/osbuild.log", "SUCCESS\n"},
{"/api/v1/compose/logs/30000000-0000-0000-0000-000000000002", "attachment; filename=30000000-0000-0000-0000-000000000002-logs.tar", "application/x-tar", "logs/osbuild.log", "SUCCESS\n"},
}
for _, c := range successCases {
api, _ := createWeldrAPI(rpmmd_mock.BaseFixture)
response := test.SendHTTP(api, false, "GET", c.Path, "")
if response.Header.Get("content-disposition") != c.ExpectedContentDisposition {
t.Errorf("%s: expected content-disposition: %s, but got: %s", c.Path, c.ExpectedContentDisposition, response.Header.Get("content-disposition"))
}
if response.Header.Get("content-type") != c.ExpectedContentType {
t.Errorf("%s: expected content-type: %s, but got: %s", c.Path, c.ExpectedContentType, response.Header.Get("content-type"))
}
tr := tar.NewReader(response.Body)
h, err := tr.Next()
if err != nil {
t.Errorf("untarring failed with error: %s", err.Error())
}
if h.Name != c.ExpectedFileName {
t.Errorf("%s: expected log content: %s, but got: %s", c.Path, c.ExpectedFileName, h.Name)
}
var buffer bytes.Buffer
io.Copy(&buffer, tr)
if buffer.String() != c.ExpectedFileContent {
t.Errorf("%s: expected log content: %s, but got: %s", c.Path, c.ExpectedFileContent, buffer.String())
}
}
var failureCases = []struct {
Path string
ExpectedJSON string
}{
{"/api/v1/compose/logs/30000000-0000-0000-0000", `{"status":false,"errors":[{"id":"UnknownUUID","msg":"30000000-0000-0000-0000 is not a valid build uuid"}]}`},
{"/api/v1/compose/logs/42000000-0000-0000-0000-000000000000", `{"status":false,"errors":[{"id":"UnknownUUID","msg":"Compose 42000000-0000-0000-0000-000000000000 doesn't exist"}]}`},
{"/api/v1/compose/logs/30000000-0000-0000-0000-000000000000", `{"status":false,"errors":[{"id":"BuildInWrongState","msg":"Build 30000000-0000-0000-0000-000000000000 not in FINISHED or FAILED state."}]}`},
}
if len(os.Getenv("OSBUILD_COMPOSER_TEST_EXTERNAL")) > 0 {
t.Skip("This test is for internal testing only")
}
for _, c := range failureCases {
api, _ := createWeldrAPI(rpmmd_mock.BaseFixture)
test.TestRoute(t, api, false, "GET", c.Path, "", http.StatusBadRequest, c.ExpectedJSON)
}
}
func TestComposeQueue(t *testing.T) {
var cases = []struct {
Fixture rpmmd_mock.FixtureGenerator