api: implement /compose/delete route

This commit is contained in:
Ondřej Budai 2019-12-06 16:56:22 +01:00 committed by Lars Karlitski
parent 6d15833e4e
commit 6bbc89d5f3
4 changed files with 136 additions and 4 deletions

View file

@ -99,10 +99,13 @@ func (job *Job) Run(d distro.Distro) (*store.Image, error, []error) {
for _, t := range job.Targets {
switch options := t.Options.(type) {
case *target.LocalTargetOptions:
cp := exec.Command("cp", "-a", "-L", "/var/cache/osbuild-composer/store/refs/"+result.OutputID+"/.", options.Location)
cp.Stderr = os.Stderr
cp.Stdout = os.Stdout
err = cp.Run()
err = runCommand("cp", "-a", "-L", "/var/cache/osbuild-composer/store/refs/"+result.OutputID+"/.", options.Location)
if err != nil {
r = append(r, err)
continue
}
err = runCommand("chown", "-R", "_osbuild-composer:_osbuild-composer", options.Location)
if err != nil {
r = append(r, err)
continue
@ -158,3 +161,10 @@ func (job *Job) Run(d distro.Distro) (*store.Image, error, []error) {
return &image, nil, r
}
func runCommand(command string, params ...string) error {
cp := exec.Command(command, params...)
cp.Stderr = os.Stderr
cp.Stdout = os.Stdout
return cp.Run()
}

View file

@ -454,6 +454,29 @@ func (s *Store) PushCompose(composeID uuid.UUID, bp *blueprint.Blueprint, compos
return nil
}
func (s *Store) DeleteCompose(id uuid.UUID) error {
return s.change(func() error {
compose, exists := s.Composes[id]
if !exists {
return &NotFoundError{}
}
if compose.QueueStatus != "FINISHED" && compose.QueueStatus != "FAILED" {
return &InvalidRequestError{fmt.Sprintf("Compose %s is not in FINISHED or FAILED.", id)}
}
delete(s.Composes, id)
var err error
if s.stateDir != nil {
err = os.RemoveAll(*s.stateDir + "/outputs/" + id.String())
}
return err
})
}
func (s *Store) PopCompose() Job {
job := <-s.pendingJobs
s.change(func() error {

View file

@ -86,6 +86,7 @@ func New(rpmmd rpmmd.RPMMD, distro distro.Distro, logger *log.Logger, store *sto
api.router.DELETE("/api/v:version/blueprints/workspace/:blueprint", api.blueprintDeleteWorkspaceHandler)
api.router.POST("/api/v:version/compose", api.composeHandler)
api.router.DELETE("/api/v:version/compose/delete/:uuids", api.composeDeleteHandler)
api.router.GET("/api/v:version/compose/types", api.composeTypesHandler)
api.router.GET("/api/v:version/compose/queue", api.composeQueueHandler)
api.router.GET("/api/v:version/compose/status/:uuids", api.composeStatusHandler)
@ -1267,6 +1268,68 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
json.NewEncoder(writer).Encode(reply)
}
func (api *API) composeDeleteHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
if !verifyRequestVersion(writer, params, 0) {
return
}
type composeDeleteStatus struct {
UUID uuid.UUID `json:"uuid"`
Status bool `json:"status"`
}
type composeDeleteError struct {
ID string `json:"id"`
Msg string `json:"msg"`
}
uuidsParam := params.ByName("uuids")
results := []composeDeleteStatus{}
errors := []composeDeleteError{}
uuidStrings := strings.Split(uuidsParam, ",")
for _, uuidString := range uuidStrings {
id, err := uuid.Parse(uuidString)
if err != nil {
errors = append(errors, composeDeleteError{
"UnknownUUID",
fmt.Sprintf("%s is not a valid uuid", uuidString),
})
}
err = api.store.DeleteCompose(id)
if err != nil {
switch err.(type) {
case *store.NotFoundError:
errors = append(errors, composeDeleteError{
"UnknownUUID",
fmt.Sprintf("compose %s doesn't exist", id),
})
case *store.InvalidRequestError:
errors = append(errors, composeDeleteError{
"BuildInWrongState",
err.Error(),
})
default:
errors = append(errors, composeDeleteError{
"ComposeError",
fmt.Sprintf("%s: %s", id, err.Error()),
})
}
}
results = append(results, composeDeleteStatus{id, true})
}
reply := struct {
UUIDs []composeDeleteStatus `json:"uuids"`
Errors []composeDeleteError `json:"errors"`
}{results, errors}
json.NewEncoder(writer).Encode(reply)
}
func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
if !verifyRequestVersion(writer, params, 0) {
return

View file

@ -23,6 +23,7 @@ import (
"github.com/BurntSushi/toml"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func createWeldrAPI(fixtureGenerator rpmmd_mock.FixtureGenerator) (*weldr.API, *store.Store) {
@ -330,6 +331,41 @@ func TestCompose(t *testing.T) {
}
}
func TestComposeDelete(t *testing.T) {
if len(os.Getenv("OSBUILD_COMPOSER_TEST_EXTERNAL")) > 0 {
t.Skip("This test is for internal testing only")
}
var cases = []struct {
Path string
ExpectedJSON string
ExpectedIDsInStore []string
}{
{"/api/v0/compose/delete/30000000-0000-0000-0000-000000000002", `{"uuids":[{"uuid":"30000000-0000-0000-0000-000000000002","status":true}],"errors":[]}`, []string{"30000000-0000-0000-0000-000000000000", "30000000-0000-0000-0000-000000000001", "30000000-0000-0000-0000-000000000003"}},
{"/api/v0/compose/delete/30000000-0000-0000-0000-000000000002,30000000-0000-0000-0000-000000000003", `{"uuids":[{"uuid":"30000000-0000-0000-0000-000000000002","status":true},{"uuid":"30000000-0000-0000-0000-000000000003","status":true}],"errors":[]}`, []string{"30000000-0000-0000-0000-000000000000", "30000000-0000-0000-0000-000000000001"}},
{"/api/v0/compose/delete/30000000-0000-0000-0000-000000000003,30000000-0000-0000-0000-000000000000", `{"uuids":[{"uuid":"30000000-0000-0000-0000-000000000003","status":true},{"uuid":"30000000-0000-0000-0000-000000000000","status":true}],"errors":[{"id":"BuildInWrongState","msg":"Compose 30000000-0000-0000-0000-000000000000 is not in FINISHED or FAILED."}]}`, []string{"30000000-0000-0000-0000-000000000000", "30000000-0000-0000-0000-000000000001", "30000000-0000-0000-0000-000000000002"}},
{"/api/v0/compose/delete/30000000-0000-0000-0000-000000000003,30000000-0000-0000-0000", `{"uuids":[{"uuid":"30000000-0000-0000-0000-000000000003","status":true},{"uuid":"00000000-0000-0000-0000-000000000000","status":true}],"errors":[{"id":"UnknownUUID","msg":"30000000-0000-0000-0000 is not a valid uuid"},{"id":"UnknownUUID","msg":"compose 00000000-0000-0000-0000-000000000000 doesn't exist"}]}`, []string{"30000000-0000-0000-0000-000000000000", "30000000-0000-0000-0000-000000000001", "30000000-0000-0000-0000-000000000002"}},
{"/api/v0/compose/delete/30000000-0000-0000-0000-000000000003,42000000-0000-0000-0000-000000000000", `{"uuids":[{"uuid":"30000000-0000-0000-0000-000000000003","status":true},{"uuid":"42000000-0000-0000-0000-000000000000","status":true}],"errors":[{"id":"UnknownUUID","msg":"compose 42000000-0000-0000-0000-000000000000 doesn't exist"}]}`, []string{"30000000-0000-0000-0000-000000000000", "30000000-0000-0000-0000-000000000001", "30000000-0000-0000-0000-000000000002"}},
}
for _, c := range cases {
api, s := createWeldrAPI(rpmmd_mock.BaseFixture)
test.TestRoute(t, api, false, "DELETE", c.Path, "", http.StatusOK, c.ExpectedJSON)
idsInStore := []string{}
for id, _ := range s.Composes {
idsInStore = append(idsInStore, id.String())
}
diff := cmp.Diff(idsInStore, c.ExpectedIDsInStore, cmpopts.SortSlices(func(a, b string) bool { return a < b }))
if diff != "" {
t.Errorf("%s: composes in store are different, expected: %v, got: %v, diff:\n%s", c.Path, c.ExpectedIDsInStore, idsInStore, diff)
}
}
}
func TestComposeStatus(t *testing.T) {
var cases = []struct {
Fixture rpmmd_mock.FixtureGenerator