Skip to content

Commit

Permalink
Remove Server from registry once reached Available
Browse files Browse the repository at this point in the history
  • Loading branch information
afritzler committed Apr 30, 2024
1 parent 57f46ef commit abe4018
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 12 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"flag"
"fmt"
"os"
"time"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
Expand Down Expand Up @@ -67,7 +68,9 @@ func main() {
var registryPort int
var registryProtocol string
var registryURL string
var requeueInterval time.Duration

flag.DurationVar(&requeueInterval, "requeue-interval", 10*time.Second, "Reconciler requeue interval.")
flag.StringVar(&registryURL, "registry-url", "", "The URL of the registry.")
flag.StringVar(&registryProtocol, "registry-protocol", "http", "The protocol to use for the registry.")
flag.IntVar(&registryPort, "registry-port", 10000, "The port to use for the registry.")
Expand Down Expand Up @@ -203,6 +206,7 @@ func main() {
ProbeImage: probeImage,
ProbeOSImage: probeOSImage,
RegistryURL: registryURL,
RequeueInterval: requeueInterval,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Server")
os.Exit(1)
Expand Down
3 changes: 0 additions & 3 deletions config/crd/bases/metal.ironcore.dev_bmcs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions internal/controller/bmc_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
37 changes: 34 additions & 3 deletions internal/controller/server_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

Expand Down Expand Up @@ -61,6 +62,7 @@ type ServerReconciler struct {
ProbeImage string
RegistryURL string
ProbeOSImage string
RequeueInterval time.Duration
}

//+kubebuilder:rbac:groups=metal.ironcore.dev,resources=bmcs,verbs=get;list;watch
Expand Down Expand Up @@ -149,7 +151,7 @@ func (r *ServerReconciler) reconcile(ctx context.Context, log logr.Logger, serve

requeue, err := r.ensureServerStateTransition(ctx, log, server)
if requeue && err == nil {
return ctrl.Result{Requeue: requeue, RequeueAfter: 10 * time.Second}, nil
return ctrl.Result{Requeue: requeue, RequeueAfter: r.RequeueInterval}, nil
}
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to ensure server state transition: %w", err)
Expand Down Expand Up @@ -216,7 +218,6 @@ func (r *ServerReconciler) ensureServerStateTransition(ctx context.Context, log
}
if err != nil {
log.V(1).Info("Could not get server details from registry.")
// TODO: instead of requeue subscribe to registry events and requeue Server objects in SetupWithManager
return false, err
}
log.V(1).Info("Extracted Server details")
Expand All @@ -229,10 +230,15 @@ func (r *ServerReconciler) ensureServerStateTransition(ctx context.Context, log
// TODO: fix that by providing the power state to the ensure method
server.Spec.Power = metalv1alpha1.PowerOff
if err := r.ensureServerPowerState(ctx, log, server); err != nil {
return false, fmt.Errorf("failed to shutdown server: %w", err)
return false, fmt.Errorf("failed to ensure server power state: %w", err)
}
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
Expand Down Expand Up @@ -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).
Expand Down
7 changes: 7 additions & 0 deletions internal/controller/server_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controller

import (
"fmt"
"net/http"

apierrors "k8s.io/apimachinery/pkg/api/errors"

Expand Down Expand Up @@ -74,6 +75,7 @@ var _ = Describe("Server Controller", func() {
},
}
Eventually(Object(bootConfig)).Should(SatisfyAll(
HaveField("Finalizers", ContainElement(ServerBootConfigurationFinalizer)),
HaveField("Spec.ServerRef", v1.LocalObjectReference{Name: server.Name}),
HaveField("Spec.Image", "fooOS:latest"),
HaveField("Spec.IgnitionSecretRef", &v1.LocalObjectReference{Name: server.Name}),
Expand Down Expand Up @@ -157,6 +159,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
Expand Down
20 changes: 19 additions & 1 deletion internal/controller/serverbootconfiguration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"context"
"fmt"

"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/handler"

"github.com/ironcore-dev/controller-utils/clientutils"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -149,7 +152,7 @@ func (r *ServerBootConfigurationReconciler) removeServerBootConfigRef(ctx contex

serverBase := server.DeepCopy()
server.Spec.BootConfigurationRef = nil
if err := r.Patch(ctx, server, client.MergeFrom(serverBase)); !apierrors.IsNotFound(err) {
if err := r.Patch(ctx, server, client.MergeFrom(serverBase)); err != nil {
return err
}

Expand All @@ -160,5 +163,20 @@ func (r *ServerBootConfigurationReconciler) removeServerBootConfigRef(ctx contex
func (r *ServerBootConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&metalv1alpha1.ServerBootConfiguration{}).
Watches(&metalv1alpha1.Server{}, r.enqueueServerBootConfigByServerRef()).
Complete(r)
}

func (r *ServerBootConfigurationReconciler) enqueueServerBootConfigByServerRef() handler.EventHandler {
return handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []ctrl.Request {
server := obj.(*metalv1alpha1.Server)
if server.Spec.BootConfigurationRef != nil {
return []ctrl.Request{
{
NamespacedName: types.NamespacedName{Namespace: server.Spec.BootConfigurationRef.Namespace, Name: server.Spec.BootConfigurationRef.Name},
},
}
}
return nil
})
}
5 changes: 3 additions & 2 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ import (
)

const (
pollingInterval = 50 * time.Millisecond
eventuallyTimeout = 5 * time.Second
pollingInterval = 100 * time.Millisecond
eventuallyTimeout = 3 * time.Second
consistentlyDuration = 1 * time.Second
)

Expand Down Expand Up @@ -182,6 +182,7 @@ func SetupTest() *corev1.Namespace {
ProbeImage: "foo:latest",
ProbeOSImage: "fooOS:latest",
RegistryURL: registryURL,
RequeueInterval: 100 * time.Millisecond,
}).SetupWithManager(k8sManager)).To(Succeed())

Expect((&ServerClaimReconciler{
Expand Down
26 changes: 26 additions & 0 deletions internal/registry/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions internal/registry/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
})
})

0 comments on commit abe4018

Please sign in to comment.