feat: add one pull plug and play feature
This commit is contained in:
@ -488,6 +488,77 @@ func TestListContainers_WithData(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── DeleteAgent ───────────────────────────────────────────────────────────────
|
||||
|
||||
func TestDeleteAgent_Success(t *testing.T) {
|
||||
h, s, reg, _ := newTestHandler(t)
|
||||
|
||||
_ = s.CreateAgentToken("a1", "t1", "host1")
|
||||
reg.Register("a1", "host1", "", "ip", "arch", "os")
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Delete("/api/v1/agents/{agentID}", h.DeleteAgent)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete, "/api/v1/agents/a1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Fatalf("expected 204, got %d — body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
// Agent must be gone from store
|
||||
_, err := s.GetAgent("a1")
|
||||
if err == nil {
|
||||
t.Error("expected store error after deletion, got nil")
|
||||
}
|
||||
|
||||
// Agent must be gone from registry
|
||||
_, ok := reg.Get("a1")
|
||||
if ok {
|
||||
t.Error("expected agent to be deregistered from registry")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgent_NotInRegistry(t *testing.T) {
|
||||
h, s, _, _ := newTestHandler(t)
|
||||
|
||||
_ = s.CreateAgentToken("a1", "t1", "host1")
|
||||
// Agent not registered in registry (offline agent)
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Delete("/api/v1/agents/{agentID}", h.DeleteAgent)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete, "/api/v1/agents/a1", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Fatalf("expected 204 even when not in registry, got %d — body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
_, err := s.GetAgent("a1")
|
||||
if err == nil {
|
||||
t.Error("expected store error after deletion, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgent_NonExistent(t *testing.T) {
|
||||
h, _, _, _ := newTestHandler(t)
|
||||
|
||||
router := chi.NewRouter()
|
||||
router.Delete("/api/v1/agents/{agentID}", h.DeleteAgent)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete, "/api/v1/agents/ghost", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
// DELETE on a non-existent ID is still 204 (idempotent)
|
||||
if w.Code != http.StatusNoContent {
|
||||
t.Fatalf("expected 204 for non-existent agent, got %d — body: %s", w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// ── ContainerAction ───────────────────────────────────────────────────────────
|
||||
|
||||
func TestContainerAction_AgentNotConnected(t *testing.T) {
|
||||
|
||||
@ -72,6 +72,16 @@ func (h *Handler) ListAgents(w http.ResponseWriter, r *http.Request) {
|
||||
jsonOK(w, out)
|
||||
}
|
||||
|
||||
func (h *Handler) DeleteAgent(w http.ResponseWriter, r *http.Request) {
|
||||
agentID := chi.URLParam(r, "agentID")
|
||||
if err := h.store.DeleteAgent(agentID); err != nil {
|
||||
http.Error(w, "store error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
h.registry.Deregister(agentID)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateAgent(w http.ResponseWriter, r *http.Request) {
|
||||
agentID := chi.URLParam(r, "agentID")
|
||||
var body struct {
|
||||
|
||||
@ -44,6 +44,7 @@ func NewRouter(h *Handler) http.Handler {
|
||||
r.Get("/agents", h.ListAgents)
|
||||
r.Post("/agents/token", h.CreateAgentToken)
|
||||
r.Patch("/agents/{agentID}", h.UpdateAgent)
|
||||
r.Delete("/agents/{agentID}", h.DeleteAgent)
|
||||
r.Get("/containers", h.ListContainers)
|
||||
r.Post("/agents/{agentID}/containers/{containerID}/action", h.ContainerAction)
|
||||
r.Get("/agents/{agentID}/containers/{containerID}/logs", h.LogsWS)
|
||||
|
||||
@ -138,6 +138,11 @@ func (s *Store) UpdateAgentAlias(id, alias string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) DeleteAgent(id string) error {
|
||||
_, err := s.db.Exec(`DELETE FROM agents WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// ── Users ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
func (s *Store) GetUserHash(username string) (string, error) {
|
||||
|
||||
@ -195,6 +195,54 @@ func TestUpdateAgentAlias(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgent(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
if err := s.CreateAgentToken("a1", "t1", "host1"); err != nil {
|
||||
t.Fatalf("CreateAgentToken: %v", err)
|
||||
}
|
||||
|
||||
if err := s.DeleteAgent("a1"); err != nil {
|
||||
t.Fatalf("DeleteAgent: %v", err)
|
||||
}
|
||||
|
||||
_, err := s.GetAgent("a1")
|
||||
if err == nil {
|
||||
t.Error("expected error after deletion, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgent_NotFound(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
// Deleting a non-existent agent should not error (DELETE is idempotent at SQL level)
|
||||
if err := s.DeleteAgent("nonexistent"); err != nil {
|
||||
t.Fatalf("DeleteAgent on missing id: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgent_RemovesFromList(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
_ = s.CreateAgentToken("a1", "t1", "host1")
|
||||
_ = s.CreateAgentToken("a2", "t2", "host2")
|
||||
|
||||
if err := s.DeleteAgent("a1"); err != nil {
|
||||
t.Fatalf("DeleteAgent: %v", err)
|
||||
}
|
||||
|
||||
agents, err := s.ListAgents()
|
||||
if err != nil {
|
||||
t.Fatalf("ListAgents: %v", err)
|
||||
}
|
||||
if len(agents) != 1 {
|
||||
t.Fatalf("expected 1 agent after deletion, got %d", len(agents))
|
||||
}
|
||||
if agents[0].ID != "a2" {
|
||||
t.Errorf("expected remaining agent to be a2, got %q", agents[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateAgentToken_IdempotentIgnore(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user