diff --git a/host/VERSION b/host/VERSION index ad219194..d905a6d1 100644 --- a/host/VERSION +++ b/host/VERSION @@ -1 +1 @@ -1.25.0 +1.25.1 diff --git a/host/cmd/hostmgr/main.go b/host/cmd/hostmgr/main.go index 921720cb..0c3e9b9b 100644 --- a/host/cmd/hostmgr/main.go +++ b/host/cmd/hostmgr/main.go @@ -50,6 +50,11 @@ var ( hostmgr.AllowHostDiscoveryValue, hostmgr.AllowHostDiscoveryDescription, ) + disabledProvisioning = flag.Bool( + hostmgr.DisabledProvisioning, + hostmgr.DisabledProvisioningValue, + hostmgr.DisabledProvisioningDescription, + ) enableAuth = flag.Bool(rbac.EnableAuth, true, rbac.EnableAuthDescription) rbacRules = flag.String(rbac.RbacRules, "/rego/authz.rego", rbac.RbacRulesDescription) invCacheUUIDEnable = flag.Bool(client.InvCacheUUIDEnable, false, client.InvCacheUUIDEnableDescription) @@ -104,18 +109,19 @@ func main() { flag.Parse() conf := config.HostMgrConfig{ - EnableTracing: *enableTracing, - EnableMetrics: *enableMetrics, - TraceURL: *traceURL, - InventoryAddr: *invsvcaddr, - CACertPath: *caCertPath, - TLSKeyPath: *tlsKeyPath, - TLSCertPath: *tlsCertPath, - InsecureGRPC: *insecureGrpc, - EnableHostDiscovery: *allowHostDiscovery, - EnableUUIDCache: *invCacheUUIDEnable, - UUIDCacheTTL: *invCacheStaleTimeout, - UUIDCacheTTLOffset: int(*invCacheStaleTimeoutOffset), + EnableTracing: *enableTracing, + EnableMetrics: *enableMetrics, + TraceURL: *traceURL, + InventoryAddr: *invsvcaddr, + CACertPath: *caCertPath, + TLSKeyPath: *tlsKeyPath, + TLSCertPath: *tlsCertPath, + InsecureGRPC: *insecureGrpc, + EnableHostDiscovery: *allowHostDiscovery, + DisabledProvisioning: *disabledProvisioning, + EnableUUIDCache: *invCacheUUIDEnable, + UUIDCacheTTL: *invCacheStaleTimeout, + UUIDCacheTTLOffset: int(*invCacheStaleTimeoutOffset), } if err := conf.Validate(); err != nil { zlog.InfraSec().Fatal().Err(err).Msgf("Failed to start due to invalid configuration: %v", conf) diff --git a/host/pkg/config/config.go b/host/pkg/config/config.go index 0d458f62..d070e237 100644 --- a/host/pkg/config/config.go +++ b/host/pkg/config/config.go @@ -16,18 +16,19 @@ import ( // HostMgrConfig contains configuration for the host manager.. type HostMgrConfig struct { - EnableTracing bool - EnableMetrics bool - TraceURL string - InventoryAddr string - CACertPath string - TLSKeyPath string - TLSCertPath string - InsecureGRPC bool - EnableHostDiscovery bool - EnableUUIDCache bool - UUIDCacheTTL time.Duration - UUIDCacheTTLOffset int + EnableTracing bool + EnableMetrics bool + TraceURL string + InventoryAddr string + CACertPath string + TLSKeyPath string + TLSCertPath string + InsecureGRPC bool + EnableHostDiscovery bool + DisabledProvisioning bool + EnableUUIDCache bool + UUIDCacheTTL time.Duration + UUIDCacheTTLOffset int } // Validate checks if the configuration is valid. diff --git a/host/pkg/hostmgr/grpc_server.go b/host/pkg/hostmgr/grpc_server.go index ee730383..41d2151b 100644 --- a/host/pkg/hostmgr/grpc_server.go +++ b/host/pkg/hostmgr/grpc_server.go @@ -115,7 +115,7 @@ func (s *server) UpdateHostSystemInfoByGUID(ctx context.Context, "Host tID=%s, UUID=%s is not trusted, the message will not be handled", tenantID, guid) } - if hmgr_util.IsHostNotProvisioned(hostres) { + if !DisabledProvisioningValue && hmgr_util.IsHostNotProvisioned(hostres) { zlog.InfraSec(). InfraError("Host tID=%s, UUID=%s is not yet provisioned, skipping update", tenantID, hostres.GetUuid()). Msg("UpdateHostSystemInfoByGUID") @@ -190,9 +190,11 @@ func (s *server) UpdateInstanceStateStatusByHostGUID( return nil, err } - err = updateInstanceStateStatusByHostGUID(ctx, tenantID, host.GetInstance(), in) - if err != nil { - return nil, inv_errors.ErrorToSanitizedGrpcError(err) + if !DisabledProvisioningValue { + err = updateInstanceStateStatusByHostGUID(ctx, tenantID, host.GetInstance(), in) + if err != nil { + return nil, inv_errors.ErrorToSanitizedGrpcError(err) + } } return &pb.UpdateInstanceStateStatusByHostGUIDResponse{}, nil @@ -213,7 +215,7 @@ func (s *server) updateHostStatusIfNeeded( return nil } - if hmgr_util.IsHostNotProvisioned(host) { + if !DisabledProvisioningValue && hmgr_util.IsHostNotProvisioned(host) { zlog.InfraSec(). InfraError("Skip updating instance state for host tID=%s, UUID=%s (not provisioned)", tenantID, host.GetUuid()). Msg("UpdateInstanceStateStatusByHostGUID") diff --git a/host/pkg/hostmgr/grpc_server_test.go b/host/pkg/hostmgr/grpc_server_test.go index d141c003..e0ede901 100644 --- a/host/pkg/hostmgr/grpc_server_test.go +++ b/host/pkg/hostmgr/grpc_server_test.go @@ -18,6 +18,7 @@ import ( inv_v1 "github.com/open-edge-platform/infra-core/inventory/v2/pkg/api/inventory/v1" inv_testing "github.com/open-edge-platform/infra-core/inventory/v2/pkg/testing" pb "github.com/open-edge-platform/infra-managers/host/pkg/api/hostmgr/proto" + "github.com/open-edge-platform/infra-managers/host/pkg/hostmgr" hrm_status "github.com/open-edge-platform/infra-managers/host/pkg/status" hmgr_util "github.com/open-edge-platform/infra-managers/host/pkg/utils" om_status "github.com/open-edge-platform/infra-onboarding/onboarding-manager/pkg/status" @@ -662,6 +663,150 @@ func TestHostManagerClient_UpdateInstanceStateStatusByHostGUID(t *testing.T) { } } +func TestHostManagerClient_UpdateInstanceStateStatusByHostGUID_DisabledProvisioning(t *testing.T) { + testcases := map[string]struct { + disabledProvisioning bool + instanceUpdateError bool + expectedCode codes.Code + }{ + "DisabledProvisioningTrue": { + disabledProvisioning: true, + instanceUpdateError: false, + }, + "DisabledProvisioningFalse": { + disabledProvisioning: false, + instanceUpdateError: true, + expectedCode: codes.FailedPrecondition, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + prevDisabledProvisioning := hostmgr.DisabledProvisioningValue + hostmgr.DisabledProvisioningValue = tc.disabledProvisioning + t.Cleanup(func() { + hostmgr.DisabledProvisioningValue = prevDisabledProvisioning + }) + + dao := inv_testing.NewInvResourceDAOOrFail(t) + ctx, cancel := inv_testing.CreateContextWithENJWT(t, tenant1) + defer cancel() + + hostInv := dao.CreateHost(t, tenant1) + + instReq := &pb.UpdateInstanceStateStatusByHostGUIDRequest{ + HostGuid: hostInv.GetUuid(), + InstanceState: pb.InstanceState_INSTANCE_STATE_RUNNING, + InstanceStatus: pb.InstanceStatus_INSTANCE_STATUS_RUNNING, + ProviderStatusDetail: "5 of 5 components are running", + } + + instResp, err := HostManagerTestClient.UpdateInstanceStateStatusByHostGUID(ctx, instReq) + + if tc.instanceUpdateError { + require.Error(t, err) + assert.Equal(t, tc.expectedCode, status.Code(err)) + require.Nil(t, instResp) + return + } + require.NoError(t, err) + require.NotNil(t, instResp) + + updatedHost := GetHostbyUUID(t, hostInv.GetUuid()) + expectedHostStatus := hmgr_util.GetHostStatus(pb.HostStatus_RUNNING) + assert.Equal(t, expectedHostStatus.Status, updatedHost.GetHostStatus()) + assert.Equal(t, expectedHostStatus.StatusIndicator, updatedHost.GetHostStatusIndicator()) + }) + } +} + +func TestHostManagerClient_UpdateHostSystemInfoByGUID_DisabledProvisioning(t *testing.T) { + testcases := map[string]struct { + disabledProvisioning bool + provisioningDone bool // true if Instance is created or false if not + expectError bool + expectedCode codes.Code + }{ + "DisabledProvisioningTrue": { + disabledProvisioning: true, + provisioningDone: false, + expectError: false, + }, + "DisabledProvisioningFalse": { + disabledProvisioning: false, + provisioningDone: false, + expectError: true, + expectedCode: codes.FailedPrecondition, + }, + "DisabledProvisioningFalse_Provisioned": { + disabledProvisioning: false, + provisioningDone: true, + expectError: false, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + prevDisabledProvisioning := hostmgr.DisabledProvisioningValue + hostmgr.DisabledProvisioningValue = tc.disabledProvisioning + t.Cleanup(func() { + hostmgr.DisabledProvisioningValue = prevDisabledProvisioning + }) + + dao := inv_testing.NewInvResourceDAOOrFail(t) + ctx, cancel := inv_testing.CreateContextWithENJWT(t, tenant1) + defer cancel() + + hostInv := dao.CreateHost(t, tenant1) + osInv := dao.CreateOs(t, tenant1) + _ = dao.CreateInstanceWithOpts(t, tenant1, hostInv, osInv, true, func(inst *computev1.InstanceResource) { + if tc.provisioningDone { + inst.ProvisioningStatus = om_status.ProvisioningStatusDone.Status + inst.ProvisioningStatusIndicator = om_status.ProvisioningStatusDone.StatusIndicator + } else { + inst.ProvisioningStatus = om_status.ProvisioningStatusFailed.Status + inst.ProvisioningStatusIndicator = om_status.ProvisioningStatusFailed.StatusIndicator + } + }) + + req := &pb.UpdateHostSystemInfoByGUIDRequest{ + HostGuid: hostInv.GetUuid(), + SystemInfo: &pb.SystemInfo{ + HwInfo: &pb.HWInfo{ + SerialNum: "sn-disabled-prov", + Cpu: &pb.SystemCPU{ + Cores: 4, + Model: "Intel CPU", + Sockets: 1, + Threads: 8, + Vendor: "GenuineIntel", + Arch: "x86", + }, + Memory: &pb.SystemMemory{Size: 16}, + }, + }, + } + + resp, err := HostManagerTestClient.UpdateHostSystemInfoByGUID(ctx, req) + if tc.expectError { + require.Error(t, err) + assert.Equal(t, tc.expectedCode, status.Code(err)) + require.Nil(t, resp) + + updatedHost := GetHostbyUUID(t, hostInv.GetUuid()) + assert.Equal(t, hostInv.GetSerialNumber(), updatedHost.GetSerialNumber()) + return + } + require.NoError(t, err) + require.NotNil(t, resp) + + updatedHost := GetHostbyUUID(t, hostInv.GetUuid()) + assert.Equal(t, "sn-disabled-prov", updatedHost.GetSerialNumber()) + assert.Equal(t, uint32(4), updatedHost.GetCpuCores()) + }) + } +} + // TestHostManagerClient_TenantIsolation e2e test validating tenant isolation from SBI. func TestHostManagerClient_TenantIsolation(t *testing.T) { dao := inv_testing.NewInvResourceDAOOrFail(t) diff --git a/host/pkg/hostmgr/hostmgr.go b/host/pkg/hostmgr/hostmgr.go index da626cc2..59bb1439 100644 --- a/host/pkg/hostmgr/hostmgr.go +++ b/host/pkg/hostmgr/hostmgr.go @@ -32,8 +32,9 @@ var zlog = logging.GetLogger("HostManager") // TODO(max): remove global instances. var ( - invClientInstance inv_client.TenantAwareInventoryClient - AllowHostDiscoveryValue = true // Default value in flag + invClientInstance inv_client.TenantAwareInventoryClient + AllowHostDiscoveryValue = true // Default value in flag + DisabledProvisioningValue = false // Default value in flag ) const ( @@ -41,6 +42,10 @@ const ( AllowHostDiscovery = "allowHostDiscovery" // AllowHostDiscoveryDescription provides description of the AllowHostDiscovery flag. AllowHostDiscoveryDescription = "Flag to allow Host discovery automatically when it does not exist in the Inventory" + // DisabledProvisioning toggles provisioning-related checks in the host manager. + DisabledProvisioning = "disabledProvisioning" + // DisabledProvisioningDescription provides description of the DisabledProvisioning flag. + DisabledProvisioningDescription = "Flag to disable provisioning checks for host updates" // Backoff config for retrying the SetHostConnectionLost. backoffInterval = 5 * time.Second backoffRetries = uint64(5) @@ -113,7 +118,9 @@ func StartInvGrpcCli( ctx := context.Background() resourceKinds := []inv_v1.ResourceKind{ inv_v1.ResourceKind_RESOURCE_KIND_HOST, - inv_v1.ResourceKind_RESOURCE_KIND_INSTANCE, + } + if !conf.DisabledProvisioning { + resourceKinds = append(resourceKinds, inv_v1.ResourceKind_RESOURCE_KIND_INSTANCE) } zlog.InfraSec().Info().Msg("initial Inv Grpc Client start.") @@ -151,6 +158,7 @@ func StartInvGrpcCli( SetInvGrpcCli(gcli) zlog.InfraSec().Info().Msg("initial Grpc Client preparation is done.") AllowHostDiscoveryValue = conf.EnableHostDiscovery + DisabledProvisioningValue = conf.DisabledProvisioning return gcli, events, nil }