diff --git a/pkg/termination/handler.go b/pkg/termination/handler.go index db5c64e03f..c16da1354e 100644 --- a/pkg/termination/handler.go +++ b/pkg/termination/handler.go @@ -8,6 +8,7 @@ import ( "time" "github.com/go-logr/logr" + machinev1 "github.com/openshift/machine-api-operator/pkg/apis/machine/v1beta1" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -35,6 +36,8 @@ func NewHandler(logger logr.Logger, cfg *rest.Config, pollInterval time.Duration panic(err) } + logger = logger.WithValues("node", nodeName) + return &handler{ client: c, pollURL: pollURL, @@ -80,5 +83,18 @@ func (h *handler) Run(stop <-chan struct{}) error { func (h *handler) run(ctx context.Context, wg *sync.WaitGroup) error { defer wg.Done() + machine, err := h.getMachineForNode(ctx) + if err != nil { + return fmt.Errorf("error fetching machine for node (%q): %v", h.nodeName, err) + } + + logger := h.log.WithValues("namespace", machine.Namespace, "machine", machine.Name) + logger.V(1).Info("Monitoring node for machine") + return fmt.Errorf("not implemented") } + +// getMachineForNodeName finds the Machine associated with the Node name given +func (h *handler) getMachineForNode(ctx context.Context) (*machinev1.Machine, error) { + return nil, fmt.Errorf("machine not found for node %q", h.nodeName) +} diff --git a/pkg/termination/handler_test.go b/pkg/termination/handler_test.go index d9acf55a49..9725dfa651 100644 --- a/pkg/termination/handler_test.go +++ b/pkg/termination/handler_test.go @@ -14,6 +14,7 @@ limitations under the License. package termination import ( + "fmt" "net/http" "net/http/httptest" "net/url" @@ -21,9 +22,18 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + machinev1 "github.com/openshift/machine-api-operator/pkg/apis/machine/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/klogr" + "sigs.k8s.io/controller-runtime/pkg/client" ) +var notFoundFunc = func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(404) +} + var _ = Describe("Handler Suite", func() { var terminationServer *httptest.Server var httpHandler http.Handler @@ -36,7 +46,8 @@ var _ = Describe("Handler Suite", func() { // Reset test vars terminationServer = nil httpHandler = nil - nodeName = "testNode" + nodeName = "test-node" + httpHandler = newMockHTTPHandler(notFoundFunc) h = &handler{ client: k8sClient, @@ -62,15 +73,11 @@ var _ = Describe("Handler Suite", func() { close(stop) } terminationServer.Close() + + Expect(deleteAllMachines(k8sClient)).To(Succeed()) }) Context("when the handler is stopped", func() { - BeforeEach(func() { - httpHandler = newMockHTTPHandler(func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(404) - }) - }) - JustBeforeEach(func() { close(stop) }) @@ -79,6 +86,65 @@ var _ = Describe("Handler Suite", func() { Eventually(errs).Should(Receive(BeNil())) }) }) + + Context("when no machine exists for the node", func() { + It("should return an error upon starting", func() { + Eventually(errs).Should(Receive(MatchError("error fetching machine for node (\"test-node\"): machine not found for node \"test-node\""))) + }) + }) + + Context("getMachineForNode", func() { + var machine *machinev1.Machine + var err error + + JustBeforeEach(func() { + machine, err = h.getMachineForNode(ctx) + }) + + Context("with a broken client", func() { + BeforeEach(func() { + brokenClient, err := client.New(cfg, client.Options{Scheme: runtime.NewScheme()}) + Expect(err).ToNot(HaveOccurred()) + h.client = brokenClient + }) + + It("should return an error", func() { + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(HavePrefix("error listing machines: no kind is registered for the type v1beta1.MachineList in scheme")) + }) + + It("should not return a machine", func() { + Expect(machine).To(BeNil()) + }) + }) + + Context("with no machine for the node name", func() { + It("should return an error", func() { + Expect(err).To(MatchError("machine not found for node \"test-node\"")) + }) + + It("should not return a machine", func() { + Expect(machine).To(BeNil()) + }) + }) + + Context("with a machine matching the node name", func() { + var testMachine *machinev1.Machine + + BeforeEach(func() { + testMachine = newTestMachine("test-machine", "test-namespace", nodeName) + createMachine(testMachine) + }) + + It("should not return an error", func() { + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return a machine", func() { + Expect(machine).To(Equal(testMachine)) + }) + }) + }) }) // mockHTTPHandler is used to mock the pollURL responses during tests @@ -106,3 +172,53 @@ func isClosed(ch <-chan struct{}) bool { return false } + +func deleteAllMachines(c client.Client) error { + machineList := &machinev1.MachineList{} + err := c.List(ctx, machineList) + if err != nil { + return fmt.Errorf("error listing machines: %v", err) + } + + // Delete all machines found + for _, machine := range machineList.Items { + m := machine + err := c.Delete(ctx, &m) + if err != nil { + return err + } + } + return nil +} + +func newTestMachine(name, namespace, nodeName string) *machinev1.Machine { + return &machinev1.Machine{ + TypeMeta: metav1.TypeMeta{ + Kind: "Machine", + APIVersion: machinev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Status: machinev1.MachineStatus{ + NodeRef: &corev1.ObjectReference{ + Name: nodeName, + }, + }, + } +} + +func createMachine(m *machinev1.Machine) { + typeMeta := m.TypeMeta + status := m.Status + Expect(k8sClient.Create(ctx, m)).To(Succeed()) + m.Status = status + Expect(k8sClient.Status().Update(ctx, m)).To(Succeed()) + + // Fetch object to sync back to latest changes + key := client.ObjectKey{Namespace: m.Namespace, Name: m.Name} + Expect(k8sClient.Get(ctx, key, m)).To(Succeed()) + // Restore TypeMeta as not restored by Get + m.TypeMeta = typeMeta +}