diff --git a/internal/kojiapi/server.go b/internal/kojiapi/server.go index e100fe046..30ac3f009 100644 --- a/internal/kojiapi/server.go +++ b/internal/kojiapi/server.go @@ -333,7 +333,7 @@ func (h *apiHandlers) GetComposeIdLogs(ctx echo.Context, idstr string) error { } var result worker.OSBuildJobResult - _, err = h.server.workers.JobStatus(id, &result) + _, _, err = h.server.workers.JobStatus(id, &result) if err != nil { return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Job %s not found: %s", idstr, err)) } diff --git a/internal/kojiapi/server_test.go b/internal/kojiapi/server_test.go index d27bbe27c..33b5fae9e 100644 --- a/internal/kojiapi/server_test.go +++ b/internal/kojiapi/server_test.go @@ -1,21 +1,28 @@ package kojiapi_test import ( + "context" "encoding/json" + "fmt" + "io/ioutil" + "log" "net/http" "net/http/httptest" + "os" + "sync" "testing" - "github.com/osbuild/osbuild-composer/internal/jobqueue/testjobqueue" + "github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue" "github.com/osbuild/osbuild-composer/internal/kojiapi" "github.com/osbuild/osbuild-composer/internal/kojiapi/api" distro_mock "github.com/osbuild/osbuild-composer/internal/mocks/distro" rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd" + "github.com/osbuild/osbuild-composer/internal/test" "github.com/osbuild/osbuild-composer/internal/worker" "github.com/stretchr/testify/require" ) -func newTestKojiServer(t *testing.T) *kojiapi.Server { +func newTestKojiServer(t *testing.T, dir string) (*kojiapi.Server, *worker.Server) { rpm_fixture := rpmmd_mock.BaseFixture() rpm := rpmmd_mock.NewRPMMDMock(rpm_fixture) require.NotNil(t, rpm) @@ -24,36 +31,182 @@ func newTestKojiServer(t *testing.T) *kojiapi.Server { require.NoError(t, err) require.NotNil(t, distros) - workers := worker.NewServer(nil, testjobqueue.New(), "") - require.NotNil(t, workers) + queue, err := fsjobqueue.New(dir, []string{"osbuild:x86_64", "koji-init", "osbuild-koji:x86_64", "koji-finalize"}) + require.NoError(t, err) - server := kojiapi.NewServer(nil, workers, rpm, distros) - require.NotNil(t, server) + workerServer := worker.NewServer(nil, queue, "") + require.NotNil(t, workerServer) - return server + kojiServer := kojiapi.NewServer(nil, workerServer, rpm, distros) + require.NotNil(t, kojiServer) + + return kojiServer, workerServer } func TestStatus(t *testing.T) { - server := newTestKojiServer(t) - handler := server.Handler("/api/composer-koji/v1") + dir, err := ioutil.TempDir("", "osbuild-composer-test-kojiapi-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) - req := httptest.NewRequest("GET", "/api/composer-koji/v1/status", nil) - req.Header.Set("Content-Type", "application/json") + kojiServer, _ := newTestKojiServer(t, dir) + handler := kojiServer.Handler("/api/composer-koji/v1") + test.TestRoute(t, handler, false, "GET", "/api/composer-koji/v1/status", ``, http.StatusOK, `{"status":"OK"}`, "message") +} - rec := httptest.NewRecorder() - handler.ServeHTTP(rec, req) - resp := rec.Result() +func TestCompose(t *testing.T) { + dir, err := ioutil.TempDir("", "osbuild-composer-test-kojiapi-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) - require.Equal(t, 200, resp.StatusCode) + kojiServer, workerServer := newTestKojiServer(t, dir) + handler := kojiServer.Handler("/api/composer-koji/v1") + var wg sync.WaitGroup + wg.Add(1) - var status api.Status - err := json.NewDecoder(resp.Body).Decode(&status) + go func() { + token, _, jobType, rawJob, _, err := workerServer.RequestJob(context.Background(), "x86_64", []string{"koji-init"}) + require.NoError(t, err) + require.Equal(t, "koji-init", jobType) + + var initJob worker.KojiInitJob + err = json.Unmarshal(rawJob, &initJob) + require.NoError(t, err) + require.Equal(t, "koji.example.com", initJob.Server) + require.Equal(t, "foo", initJob.Name) + require.Equal(t, "1", initJob.Version) + require.Equal(t, "2", initJob.Release) + + test.TestRoute(t, workerServer, false, "PATCH", fmt.Sprintf("/api/worker/v1/jobs/%v", token), `{ + "result": { + "build_id": 42, + "token": "foobar" + } + }`, http.StatusOK, `{}`) + wg.Done() + }() + + test.TestRoute(t, handler, false, "POST", "/api/composer-koji/v1/compose", ` + { + "name":"foo", + "version":"1", + "release":"2", + "distribution":"fedora-30", + "image_requests": [ + { + "architecture": "x86_64", + "image_type": "qcow2", + "repositories": [ + { + "baseurl": "https://repo.example.com/" + } + ] + }, + { + "architecture": "x86_64", + "image_type": "qcow2", + "repositories": [ + { + "baseurl": "https://repo.example.com/" + } + ] + } + ], + "koji": { + "server": "koji.example.com" + } + }`, http.StatusCreated, `{"koji_build_id":42}`, "id") + wg.Wait() + + token, _, jobType, rawJob, _, err := workerServer.RequestJob(context.Background(), "x86_64", []string{"osbuild-koji"}) require.NoError(t, err) - require.Equal(t, "OK", status.Status) + require.Equal(t, "osbuild-koji", jobType) + + var osbuildJob worker.OSBuildKojiJob + err = json.Unmarshal(rawJob, &osbuildJob) + require.NoError(t, err) + require.Equal(t, "koji.example.com", osbuildJob.KojiServer) + require.Equal(t, "test.img", osbuildJob.ImageName) + require.NotEmpty(t, osbuildJob.KojiDirectory) + + test.TestRoute(t, workerServer, false, "PATCH", fmt.Sprintf("/api/worker/v1/jobs/%v", token), `{ + "result": { + "arch": "x86_64", + "host_os": "fedora-30", + "image_hash": "browns", + "image_size": 42, + "osbuild_output": { + "success": true + } + } + }`, http.StatusOK, `{}`) + + token, _, jobType, rawJob, _, err = workerServer.RequestJob(context.Background(), "x86_64", []string{"osbuild-koji"}) + require.NoError(t, err) + require.Equal(t, "osbuild-koji", jobType) + + err = json.Unmarshal(rawJob, &osbuildJob) + require.NoError(t, err) + require.Equal(t, "koji.example.com", osbuildJob.KojiServer) + require.Equal(t, "test.img", osbuildJob.ImageName) + require.NotEmpty(t, osbuildJob.KojiDirectory) + + test.TestRoute(t, workerServer, false, "PATCH", fmt.Sprintf("/api/worker/v1/jobs/%v", token), `{ + "result": { + "arch": "x86_64", + "host_os": "fedora-30", + "image_hash": "browns", + "image_size": 42, + "osbuild_output": { + "success": true + } + } + }`, http.StatusOK, `{}`) + + token, finalizeID, jobType, rawJob, _, err := workerServer.RequestJob(context.Background(), "x86_64", []string{"koji-finalize"}) + require.NoError(t, err) + require.Equal(t, "koji-finalize", jobType) + + var kojiFinalizeJob worker.KojiFinalizeJob + err = json.Unmarshal(rawJob, &kojiFinalizeJob) + require.NoError(t, err) + require.Equal(t, "koji.example.com", kojiFinalizeJob.Server) + require.Equal(t, "1", kojiFinalizeJob.Version) + require.Equal(t, "2", kojiFinalizeJob.Release) + require.ElementsMatch(t, []string{"foo-1-2.x86_64.img", "foo-1-2.x86_64.img"}, kojiFinalizeJob.KojiFilenames) + require.NotEmpty(t, kojiFinalizeJob.KojiDirectory) + + test.TestRoute(t, workerServer, false, "PATCH", fmt.Sprintf("/api/worker/v1/jobs/%v", token), `{ + "result": { + } + }`, http.StatusOK, `{}`) + + test.TestRoute(t, handler, false, "GET", fmt.Sprintf("/api/composer-koji/v1/compose/%v", finalizeID), ``, http.StatusOK, `{ + "image_statuses": [ + { + "status": "success" + }, + { + "status": "success" + } + ], + "koji_build_id": 42, + "koji_task_id": 0, + "status": "success" + }`) } func TestRequest(t *testing.T) { - server := newTestKojiServer(t) + dir, err := ioutil.TempDir("", "osbuild-composer-test-kojiapi-") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + server, _ := newTestKojiServer(t, dir) handler := server.Handler("/api/composer-koji/v1") // Make request to an invalid route @@ -64,7 +217,7 @@ func TestRequest(t *testing.T) { resp := rec.Result() var status api.Status - err := json.NewDecoder(resp.Body).Decode(&status) + err = json.NewDecoder(resp.Body).Decode(&status) require.NoError(t, err) require.Equal(t, http.StatusNotFound, resp.StatusCode)