From 06f4d756016ccc1be17191ef11104b7b7b9d0c39 Mon Sep 17 00:00:00 2001 From: CrystalChun Date: Mon, 28 Oct 2024 12:31:48 -0500 Subject: [PATCH] MGMT-19194: Save Agent inventory and status in annotation For Hypershift's backup and restore of hosted cluster from one hub to a new hub, the Agent requires these status fields to be restored in order for the NodePool to successfully re-adopt the Nodes for the hosted cluster. --- .../controllers/agent_controller.go | 39 +++++++++++-------- .../controllers/agent_controller_test.go | 13 +++---- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/internal/controller/controllers/agent_controller.go b/internal/controller/controllers/agent_controller.go index 8987c3620f4a..3f5216654c80 100644 --- a/internal/controller/controllers/agent_controller.go +++ b/internal/controller/controllers/agent_controller.go @@ -72,6 +72,8 @@ const ( AgentLabelHasNonrotationalDisk = InventoryLabelPrefix + "storage-hasnonrotationaldisk" AgentLabelCpuArchitecture = InventoryLabelPrefix + "cpu-architecture" AgentLabelCpuVirtEnabled = InventoryLabelPrefix + "cpu-virtenabled" + AgentInventoryAnnotation = "agent." + aiv1beta1.Group + "/inventory" + AgentRoleAnnotation = "agent." + aiv1beta1.Group + "/role" AgentLabelHostManufacturer = InventoryLabelPrefix + "host-manufacturer" AgentLabelHostProductName = InventoryLabelPrefix + "host-productname" AgentLabelHostIsVirtual = InventoryLabelPrefix + "host-isvirtual" @@ -173,10 +175,10 @@ func (r *AgentReconciler) Reconcile(origCtx context.Context, req ctrl.Request) ( log.WithError(err).Warnf("failed to set infraEnv name label on agent %s/%s", agent.Namespace, agent.Name) } - // Add/Update 'state' annotation (with host's status) - if setAgentAnnotation(log, agent, AgentStateAnnotation, swag.StringValue(h.Status)) { + // Add/Update Agent annotations + if updateAnnotations(log, agent, &h.Host) { if err = r.updateAndReplaceAgent(ctx, agent); err != nil { - log.WithError(err).Warnf("failed to set state annotation on agent %s/%s", agent.Namespace, agent.Name) + log.WithError(err).Warnf("failed to set annotations on agent %s/%s", agent.Namespace, agent.Name) } } @@ -250,6 +252,16 @@ func (r *AgentReconciler) Reconcile(origCtx context.Context, req ctrl.Request) ( return r.updateStatus(ctx, log, agent, origAgent, &h.Host, h.ClusterID, nil, false) } +func updateAnnotations(log logrus.FieldLogger, agent *v1beta1.Agent, h *models.Host) bool { + updated := false + updated = setAgentAnnotation(log, agent, AgentStateAnnotation, swag.StringValue(h.Status)) + updated = updated || setAgentAnnotation(log, agent, AgentRoleAnnotation, string(h.Role)) + if h.Inventory != "" { + updated = updated || setAgentAnnotation(log, agent, AgentInventoryAnnotation, h.Inventory) + } + return updated +} + // shouldCleanUnboundSpokeNode returns true if the agent has deprovision info set and the host is not in the process of unbinding func shouldCleanUnboundSpokeNode(agent *aiv1beta1.Agent, host *common.Host) bool { if host.Status == nil { @@ -1812,17 +1824,10 @@ func (r *AgentReconciler) restoreHostByAgent(ctx context.Context, agent *aiv1bet } func createNewHost(agent *v1beta1.Agent, clusterID *strfmt.UUID, infraEnvID strfmt.UUID) (*models.Host, error) { - // Create Intentory - hostInventory := models.Inventory{} - manufacturer, exist := agent.Labels[AgentLabelHostManufacturer] - if exist { - hostInventory.SystemVendor = &models.SystemVendor{} - hostInventory.SystemVendor.Manufacturer = manufacturer - } - inventory, err := json.Marshal(hostInventory) - if err != nil { - return nil, errors.New("Failed to marshal agent's inventory") - } + // Retrieve host inventory and role + hostrole := agent.Annotations[AgentRoleAnnotation] + role := models.HostRole(hostrole) + inventory := agent.Annotations[AgentInventoryAnnotation] // Fetch State var hostStatus string @@ -1843,12 +1848,12 @@ func createNewHost(agent *v1beta1.Agent, clusterID *strfmt.UUID, infraEnvID strf Kind: swag.String(models.HostKindHost), RegisteredAt: strfmt.DateTime(agent.CreationTimestamp.Time), CheckedInAt: strfmt.DateTime(agent.CreationTimestamp.Time), - Role: agent.Spec.Role, - SuggestedRole: agent.Spec.Role, + Role: role, + SuggestedRole: role, ClusterID: clusterID, InfraEnvID: infraEnvID, Status: &hostStatus, - Inventory: string(inventory), + Inventory: inventory, } return host, nil diff --git a/internal/controller/controllers/agent_controller_test.go b/internal/controller/controllers/agent_controller_test.go index 8f9ffcde204c..a4f7e7d6ad85 100644 --- a/internal/controller/controllers/agent_controller_test.go +++ b/internal/controller/controllers/agent_controller_test.go @@ -391,13 +391,13 @@ var _ = Describe("agent reconcile", func() { func(ctx context.Context, agent *v1beta1.Agent, opts ...client.UpdateOption) error { return c.Update(ctx, agent) }, - ).Times(3) + ).Times(5) mockClient.EXPECT().Status().Return(mockSubResourceWriter).AnyTimes() mockSubResourceWriter.EXPECT().Update(gomock.Any(), gomock.AssignableToTypeOf(&v1beta1.Agent{})).DoAndReturn( func(ctx context.Context, agent *v1beta1.Agent, opts ...client.UpdateOption) error { return c.Status().Update(ctx, agent) }, - ).Times(2) + ).Times(4) allowGetInfraEnvInternal(mockInstallerInternal, infraEnvId, "infraEnvName") // We test 4 times to verify that agent is only updated twice @@ -4536,7 +4536,8 @@ var _ = Describe("createNewHost", func() { It("all properties exist", func() { agent.ObjectMeta.Annotations = map[string]string{ - AgentStateAnnotation: models.HostStatusInstalled, + AgentStateAnnotation: models.HostStatusInstalled, + AgentInventoryAnnotation: "{\"disks\":null,\"gpus\":null,\"interfaces\":null,\"routes\":null,\"system_vendor\":{\"manufacturer\":\"RedHat\"}}", } agent.ObjectMeta.Labels = map[string]string{ AgentLabelHostManufacturer: "RedHat", @@ -4561,14 +4562,12 @@ var _ = Describe("createNewHost", func() { Expect(host.Inventory).To(Equal(string(inventory))) }) - It("missing host-manufacturer label", func() { + It("missing inventory annotation", func() { host, err := createNewHost(agent, &clusterID, infraEnvID) Expect(err).To(BeNil()) - hostInventory := models.Inventory{} - inventory, err := json.Marshal(hostInventory) Expect(err).To(BeNil()) - Expect(host.Inventory).To(Equal(string(inventory))) + Expect(host.Inventory).To(BeEmpty()) }) It("missing state annotation - bound", func() {