Implement server deletion
This commit is contained in:
parent
038436c781
commit
0866b84d8e
48
http.go
48
http.go
|
@ -474,6 +474,53 @@ func (rt *Router) routeSystemInformation(w http.ResponseWriter, r *http.Request,
|
||||||
json.NewEncoder(w).Encode(s)
|
json.NewEncoder(w).Encode(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rt *Router) routeServerDelete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
s := rt.GetServer(ps.ByName("server"))
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
// Immediately suspend the server to prevent a user from attempting
|
||||||
|
// to start it while this process is running.
|
||||||
|
s.Suspended = true
|
||||||
|
|
||||||
|
// Destroy the environment; in Docker this will handle a running container and
|
||||||
|
// forcibly terminate it before removing the container, so we do not need to handle
|
||||||
|
// that here.
|
||||||
|
if err := s.Environment.Destroy(); err != nil {
|
||||||
|
zap.S().Errorw("failed to destroy server environment", zap.Error(errors.WithStack(err)))
|
||||||
|
|
||||||
|
http.Error(w, "failed to destroy server environment", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the environment is terminated, remove the server files from the system. This is
|
||||||
|
// done in a seperate process since failure is not the end of the world and can be
|
||||||
|
// manually cleaned up after the fact.
|
||||||
|
//
|
||||||
|
// In addition, servers with large amounts of files can take some time to finish deleting
|
||||||
|
// so we don't want to block the HTTP call while waiting on this.
|
||||||
|
go func(p string) {
|
||||||
|
if err := os.RemoveAll(p); err != nil {
|
||||||
|
zap.S().Warnw("failed to remove server files on deletion", zap.String("path", p), zap.Error(errors.WithStack(err)))
|
||||||
|
}
|
||||||
|
}(s.Filesystem.Path())
|
||||||
|
|
||||||
|
var uuid = s.Uuid
|
||||||
|
server.GetServers().Remove(func(s2 *server.Server) bool {
|
||||||
|
return s2.Uuid == uuid
|
||||||
|
})
|
||||||
|
|
||||||
|
s = nil
|
||||||
|
|
||||||
|
// Remove the configuration file stored on the Daemon for this server.
|
||||||
|
go func(u string) {
|
||||||
|
if err := os.Remove("data/servers/" + u + ".yml"); err != nil {
|
||||||
|
zap.S().Warnw("failed to delete server configuration file on deletion", zap.String("server", u), zap.Error(errors.WithStack(err)))
|
||||||
|
}
|
||||||
|
}(uuid)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
}
|
||||||
|
|
||||||
func (rt *Router) ReaderToBytes(r io.Reader) []byte {
|
func (rt *Router) ReaderToBytes(r io.Reader) []byte {
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
buf.ReadFrom(r)
|
buf.ReadFrom(r)
|
||||||
|
@ -506,6 +553,7 @@ func (rt *Router) ConfigureRouter() *httprouter.Router {
|
||||||
router.POST("/api/servers/:server/power", rt.AuthenticateRequest(rt.routeServerPower))
|
router.POST("/api/servers/:server/power", rt.AuthenticateRequest(rt.routeServerPower))
|
||||||
router.POST("/api/servers/:server/commands", rt.AuthenticateRequest(rt.routeServerSendCommand))
|
router.POST("/api/servers/:server/commands", rt.AuthenticateRequest(rt.routeServerSendCommand))
|
||||||
router.PATCH("/api/servers/:server", rt.AuthenticateRequest(rt.routeServerUpdate))
|
router.PATCH("/api/servers/:server", rt.AuthenticateRequest(rt.routeServerUpdate))
|
||||||
|
router.DELETE("/api/servers/:server", rt.AuthenticateRequest(rt.routeServerDelete))
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
items []*Server
|
items []*Server
|
||||||
|
mutex *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new collection from a slice of servers.
|
||||||
|
func NewCollection(servers []*Server) *Collection {
|
||||||
|
return &Collection{
|
||||||
|
items: servers,
|
||||||
|
mutex: &sync.Mutex{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return all of the items in the collection.
|
// Return all of the items in the collection.
|
||||||
|
@ -11,11 +22,17 @@ func (c *Collection) All() []*Server {
|
||||||
|
|
||||||
// Adds an item to the collection store.
|
// Adds an item to the collection store.
|
||||||
func (c *Collection) Add(s *Server) {
|
func (c *Collection) Add(s *Server) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
c.items = append(c.items, s)
|
c.items = append(c.items, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns only those items matching the filter criteria.
|
// Returns only those items matching the filter criteria.
|
||||||
func (c *Collection) Filter(filter func(*Server) bool) []*Server {
|
func (c *Collection) Filter(filter func(*Server) bool) []*Server {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
r := make([]*Server, 0)
|
r := make([]*Server, 0)
|
||||||
for _, v := range c.items {
|
for _, v := range c.items {
|
||||||
if filter(v) {
|
if filter(v) {
|
||||||
|
@ -37,3 +54,18 @@ func (c *Collection) Find(filter func (*Server) bool) *Server {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes all items from the collection that match the filter function.
|
||||||
|
func (c *Collection) Remove(filter func(*Server) bool) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
r := make([]*Server, 0)
|
||||||
|
for _, v := range c.items {
|
||||||
|
if !filter(v) {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.items = r
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ type Environment interface {
|
||||||
// is not running no error should be returned.
|
// is not running no error should be returned.
|
||||||
Terminate(signal os.Signal) error
|
Terminate(signal os.Signal) error
|
||||||
|
|
||||||
|
// Destroys the environment removing any containers that were created (in Docker
|
||||||
|
// environments at least).
|
||||||
|
Destroy() error
|
||||||
|
|
||||||
// Returns the exit state of the process. The first result is the exit code, the second
|
// Returns the exit state of the process. The first result is the exit code, the second
|
||||||
// determines if the process was killed by the system OOM killer.
|
// determines if the process was killed by the system OOM killer.
|
||||||
ExitState() (uint32, bool, error)
|
ExitState() (uint32, bool, error)
|
||||||
|
|
|
@ -325,6 +325,18 @@ func (d *DockerEnvironment) Terminate(signal os.Signal) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the Docker container from the machine. If the container is currently running
|
||||||
|
// it will be forcibly stopped by Docker.
|
||||||
|
func (d *DockerEnvironment) Destroy() error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
return d.Client.ContainerRemove(ctx, d.Server.Uuid, types.ContainerRemoveOptions{
|
||||||
|
RemoveVolumes: true,
|
||||||
|
RemoveLinks: false,
|
||||||
|
Force: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the container exit state and return the exit code and wether or not
|
// Determine the container exit state and return the exit code and wether or not
|
||||||
// the container was killed by the OOM killer.
|
// the container was killed by the OOM killer.
|
||||||
func (d *DockerEnvironment) ExitState() (uint32, bool, error) {
|
func (d *DockerEnvironment) ExitState() (uint32, bool, error) {
|
||||||
|
|
|
@ -158,7 +158,7 @@ func LoadDirectory(dir string, cfg *config.SystemConfiguration) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
servers = new(Collection)
|
servers = NewCollection(nil)
|
||||||
|
|
||||||
for _, file := range f {
|
for _, file := range f {
|
||||||
if !strings.HasSuffix(file.Name(), ".yml") || file.IsDir() {
|
if !strings.HasSuffix(file.Name(), ".yml") || file.IsDir() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user