diff --git a/config/crd/bases/metal.ironcore.dev_bmcs.yaml b/config/crd/bases/metal.ironcore.dev_bmcs.yaml index b4e78c4..6eb31b7 100644 --- a/config/crd/bases/metal.ironcore.dev_bmcs.yaml +++ b/config/crd/bases/metal.ironcore.dev_bmcs.yaml @@ -21,9 +21,6 @@ spec: - jsonPath: .status.ip name: MACAddress type: string - - jsonPath: .status.manufacturer - name: Manufacturer - type: string - jsonPath: .status.model name: Model type: string diff --git a/internal/controller/bmc_controller.go b/internal/controller/bmc_controller.go index 65317cf..1ddc0d1 100644 --- a/internal/controller/bmc_controller.go +++ b/internal/controller/bmc_controller.go @@ -185,8 +185,7 @@ func (r *BMCReconciler) discoverServers(ctx context.Context, bmcObj *metalv1alph func (r *BMCReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&metalv1alpha1.BMC{}). - // TODO: don't recreate Server if deleted manually - //Owns(&metalv1alpha1.Server{}). + Owns(&metalv1alpha1.Server{}). // TODO: add watches for Endpoints and BMCSecrets Complete(r) } diff --git a/internal/controller/server_controller.go b/internal/controller/server_controller.go index 9f400b8..1368ebf 100644 --- a/internal/controller/server_controller.go +++ b/internal/controller/server_controller.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "time" @@ -233,6 +234,11 @@ func (r *ServerReconciler) ensureServerStateTransition(ctx context.Context, log } log.V(1).Info("Server state set to power off") + if err := r.invalidateRegistryEntryForServer(log, server); err != nil { + return false, fmt.Errorf("failed to invalidate registry entry for server: %w", err) + } + log.V(1).Info("Removed Server from Registry") + log.V(1).Info("Setting Server state set to available") if modified, err := r.patchServerState(ctx, server, metalv1alpha1.ServerStateAvailable); err != nil || modified { return false, err @@ -518,6 +524,31 @@ func (r *ServerReconciler) ensureInitialBootConfigurationIsDeleted(ctx context.C return nil } +func (r *ServerReconciler) invalidateRegistryEntryForServer(log logr.Logger, server *metalv1alpha1.Server) error { + url := fmt.Sprintf("%s/delete/%s", r.RegistryURL, server.Spec.UUID) + + c := &http.Client{} + + // Create the DELETE request + req, err := http.NewRequest(http.MethodDelete, url, nil) + if err != nil { + return err + } + + // Send the request + resp, err := c.Do(req) + if err != nil { + return err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + log.Error(err, "Failed to close response body") + } + }(resp.Body) + return nil +} + // SetupWithManager sets up the controller with the Manager. func (r *ServerReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/internal/controller/server_controller_test.go b/internal/controller/server_controller_test.go index 4d0f034..602e32f 100644 --- a/internal/controller/server_controller_test.go +++ b/internal/controller/server_controller_test.go @@ -18,6 +18,7 @@ package controller import ( "fmt" + "net/http" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -157,6 +158,11 @@ var _ = Describe("Server Controller", func() { }, } Consistently(Get(config)).Should(Satisfy(apierrors.IsNotFound)) + + By("Ensuring that the server is removed from the registry") + response, err := http.Get(registryURL + "/systems/" + server.Spec.UUID) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusNotFound)) }) // TODO: test server with manual BMC registration diff --git a/internal/registry/server.go b/internal/registry/server.go index bc0ef2b..b808259 100644 --- a/internal/registry/server.go +++ b/internal/registry/server.go @@ -56,6 +56,7 @@ func NewServer(addr string) *Server { // routes registers the server's routes. func (s *Server) routes() { s.mux.HandleFunc("/register", s.registerHandler) + s.mux.HandleFunc("/delete/", s.deleteHandler) s.mux.HandleFunc("/systems/", s.systemsHandler) } @@ -107,6 +108,31 @@ func (s *Server) systemsHandler(w http.ResponseWriter, r *http.Request) { } } +// deleteHandler handles the DELETE requests to remove a system by UUID. +func (s *Server) deleteHandler(w http.ResponseWriter, r *http.Request) { + log.Printf("Received method: %s", r.Method) // This will log the method of the request + log.Printf("Requested URI: %s", r.RequestURI) // This logs the full request URI + + if r.Method != http.MethodDelete { + http.Error(w, "Only DELETE method is allowed", http.StatusMethodNotAllowed) + return + } + + uuid := r.URL.Path[len("/delete/"):] // Assuming the URL is like /delete/{uuid} + + // Attempt to delete the entry from the store + if _, ok := s.systemsStore.Load(uuid); !ok { + http.NotFound(w, r) + return + } + + s.systemsStore.Delete(uuid) // Perform the deletion + + // Respond with success message + w.WriteHeader(http.StatusOK) + log.Printf("System with UUID %s deleted successfully", uuid) +} + // Start starts the server on the specified address and adds logging for key events. func (s *Server) Start(ctx context.Context) error { log.Printf("Starting registry server on port %s\n", s.addr) diff --git a/internal/registry/server_test.go b/internal/registry/server_test.go index 22a2e9f..b4bf306 100644 --- a/internal/registry/server_test.go +++ b/internal/registry/server_test.go @@ -67,5 +67,18 @@ var _ = Describe("RegistryServer", func() { }, }, })) + + By("Ensuring that the server is removed from the registry") + request, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/delete/%s", testServerURL, systemRegistrationPayload.SystemUUID), nil) + Expect(err).NotTo(HaveOccurred()) + c := &http.Client{} + response, err = c.Do(request) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusOK)) + + By("Ensuring that the server is removed from the registry") + response, err = http.Get(fmt.Sprintf("%s/systems/%s", testServerURL, systemRegistrationPayload.SystemUUID)) + Expect(err).NotTo(HaveOccurred()) + Expect(response.StatusCode).To(Equal(http.StatusNotFound)) }) })