Merge pull request #36 from coreos/gc
api/database: Add the ability to delete layers
This commit is contained in:
commit
15bc682f60
@ -51,6 +51,18 @@ func POSTLayers(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
jsonhttp.Render(w, http.StatusCreated, struct{ Version string }{Version: strconv.Itoa(worker.Version)})
|
||||
}
|
||||
|
||||
// DeleteLayer deletes the specified layer and any child layers that are
|
||||
// dependent on the specified layer.
|
||||
func DELETELayers(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
err := database.DeleteLayer(p.ByName("id"))
|
||||
if err != nil {
|
||||
jsonhttp.RenderError(w, 0, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonhttp.Render(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// GETLayersOS returns the operating system of a layer if it exists.
|
||||
// It uses not only the specified layer but also its parent layers if necessary.
|
||||
// An empty OS string is returned if no OS has been detected.
|
||||
|
@ -68,6 +68,7 @@ func NewRouterV1(to time.Duration) *httprouter.Router {
|
||||
|
||||
// Layers
|
||||
router.POST("/layers", wrap(logic.POSTLayers))
|
||||
router.DELETE("/layers/:id", wrap(logic.DELETELayers))
|
||||
router.GET("/layers/:id/os", wrap(logic.GETLayersOS))
|
||||
router.GET("/layers/:id/parent", wrap(logic.GETLayersParent))
|
||||
router.GET("/layers/:id/packages", wrap(logic.GETLayersPackages))
|
||||
|
@ -131,6 +131,60 @@ func InsertLayer(layer *Layer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteLayer deletes the specified layer and any child layers that are
|
||||
// dependent on the specified layer.
|
||||
func DeleteLayer(ID string) error {
|
||||
layer, err := FindOneLayerByID(ID, []string{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return deleteLayerTreeFrom(layer.Node, nil)
|
||||
}
|
||||
|
||||
func deleteLayerTreeFrom(node string, t *graph.Transaction) error {
|
||||
// Determine if that function call is the root call of the recursivity
|
||||
// And create transaction if its the case.
|
||||
root := (t == nil)
|
||||
if root {
|
||||
t = cayley.NewTransaction()
|
||||
}
|
||||
|
||||
// Find layer.
|
||||
layer, err := FindOneLayerByNode(node, FieldLayerAll)
|
||||
if err != nil {
|
||||
// Ignore missing layer.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove all successor layers.
|
||||
for _, succNode := range layer.SuccessorsNodes {
|
||||
deleteLayerTreeFrom(succNode, t)
|
||||
}
|
||||
|
||||
// Remove layer.
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldIs, FieldLayerIsValue, ""))
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerID, layer.ID, ""))
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerParent, layer.ParentNode, ""))
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerOS, layer.OS, ""))
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion), ""))
|
||||
for _, pkg := range layer.InstalledPackagesNodes {
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
|
||||
}
|
||||
for _, pkg := range layer.RemovedPackagesNodes {
|
||||
t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
|
||||
}
|
||||
|
||||
// Apply transaction if root call.
|
||||
if root {
|
||||
if err = store.ApplyTransaction(t); err != nil {
|
||||
log.Errorf("failed transaction (deleteLayerTreeFrom): %s", err)
|
||||
return ErrTransaction
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindOneLayerByID finds and returns a single layer having the given ID,
|
||||
// selecting the specified fields and hardcoding its ID
|
||||
func FindOneLayerByID(ID string, selectedFields []string) (*Layer, error) {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/utils"
|
||||
cerrors "github.com/coreos/clair/utils/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -38,7 +39,7 @@ func TestLayerSimple(t *testing.T) {
|
||||
// Insert a layer and find it back
|
||||
l1 := &Layer{ID: "l1", OS: "os1", InstalledPackagesNodes: []string{"p1", "p2"}, EngineVersion: 1}
|
||||
if assert.Nil(t, InsertLayer(l1)) {
|
||||
fl1, err := FindOneLayerByID("l1", FieldLayerAll)
|
||||
fl1, err := FindOneLayerByID(l1.ID, FieldLayerAll)
|
||||
if assert.Nil(t, err) && assert.NotNil(t, fl1) {
|
||||
// Saved = found
|
||||
assert.True(t, layerEqual(l1, fl1), "layers are not equal, expected %v, have %s", l1, fl1)
|
||||
@ -66,6 +67,12 @@ func TestLayerSimple(t *testing.T) {
|
||||
if assert.Nil(t, err) && assert.Len(t, al1, 1) {
|
||||
assert.Equal(t, al1[0].Node, l1.Node)
|
||||
}
|
||||
|
||||
// Delete
|
||||
if assert.Nil(t, DeleteLayer(l1.ID)) {
|
||||
_, err := FindOneLayerByID(l1.ID, FieldLayerAll)
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +126,14 @@ func TestLayerTree(t *testing.T) {
|
||||
fl4bpkg, err := flayers[4].AllPackages()
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, fl4bpkg, 0)
|
||||
|
||||
// Delete a layer in the middle of the tree.
|
||||
if assert.Nil(t, DeleteLayer(flayers[1].ID)) {
|
||||
for _, l := range layers[1:] {
|
||||
_, err := FindOneLayerByID(l.ID, FieldLayerAll)
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
36
docs/API.md
36
docs/API.md
@ -150,6 +150,41 @@ HTTP/1.1 400 Bad Request
|
||||
|
||||
It could also return a `415 Unsupported Media Type` response with a `Message` if the request content is not valid JSON.
|
||||
|
||||
## Delete a Layer
|
||||
|
||||
It deletes a layer from the database and any child layers that are dependent on the specified layer.
|
||||
|
||||
DELETE /v1/layers/{ID}
|
||||
|
||||
### Parameters
|
||||
|
||||
|Name|Type|Description|
|
||||
|------|-----|-------------|
|
||||
|ID|String|Unique ID of the Layer|
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
curl -s -X DELETE 127.0.0.1:6060/v1/layers/39bb80489af75406073b5364c9c326134015140e1f7976a370a8bd446889e6f8
|
||||
```
|
||||
|
||||
### Success Response
|
||||
|
||||
```
|
||||
HTTP/1.1 204 No Content
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
```
|
||||
HTTP/1.1 404 Not Found
|
||||
{
|
||||
"Message": "the resource cannot be found"
|
||||
}
|
||||
```
|
||||
|
||||
//////////
|
||||
|
||||
## Get a Layer's operating system
|
||||
|
||||
It returns the operating system a given Layer.
|
||||
@ -210,6 +245,7 @@ HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
```
|
||||
HTTP/1.1 404 Not Found
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user