diff --git a/api/vendor/github.com/openshift/assisted-service/models/logs_type.go b/api/vendor/github.com/openshift/assisted-service/models/logs_type.go index b7d33d4401..713893b639 100644 --- a/api/vendor/github.com/openshift/assisted-service/models/logs_type.go +++ b/api/vendor/github.com/openshift/assisted-service/models/logs_type.go @@ -33,6 +33,9 @@ const ( // LogsTypeHost captures enum value "host" LogsTypeHost LogsType = "host" + // LogsTypeNodeBoot captures enum value "node-boot" + LogsTypeNodeBoot LogsType = "node-boot" + // LogsTypeController captures enum value "controller" LogsTypeController LogsType = "controller" @@ -48,7 +51,7 @@ var logsTypeEnum []interface{} func init() { var res []LogsType - if err := json.Unmarshal([]byte(`["host","controller","all",""]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["host","node-boot","controller","all",""]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/docs/events.yaml b/docs/events.yaml index 307329e25f..2b07e2c6ac 100644 --- a/docs/events.yaml +++ b/docs/events.yaml @@ -482,6 +482,16 @@ x-events: infra_env_id: UUID host_name: string +- name: host_boot_logs_uploaded + message: "Uploaded node boot logs for host {host_name} cluster {cluster_id}" + event_type: host + severity: "info" + properties: + host_id: UUID + infra_env_id: UUID + cluster_id: UUID_PTR + host_name: string + - name: host_logs_uploaded message: "Uploaded logs for host {host_name} cluster {cluster_id}" event_type: host diff --git a/internal/bminventory/inventory.go b/internal/bminventory/inventory.go index 15798402c5..65e2130e3d 100644 --- a/internal/bminventory/inventory.go +++ b/internal/bminventory/inventory.go @@ -3567,7 +3567,7 @@ func (b *bareMetalInventory) getLogFileForDownload(ctx context.Context, clusterI return "", "", err } switch logsType { - case string(models.LogsTypeHost): + case string(models.LogsTypeHost), string(models.LogsTypeNodeBoot): if hostId == nil { return "", "", common.NewApiError(http.StatusBadRequest, errors.Errorf("Host ID must be provided for downloading host logs")) } @@ -3580,17 +3580,21 @@ func (b *bareMetalInventory) getLogFileForDownload(ctx context.Context, clusterI if time.Time(hostObject.LogsCollectedAt).Equal(time.Time{}) { return "", "", common.NewApiError(http.StatusConflict, errors.Errorf("Logs for host %s were not found", hostId)) } - fileName = b.getLogsFullName(clusterId.String(), hostObject.ID.String()) + fileName = b.getLogsFullName(logsType, clusterId.String(), hostObject.ID.String()) role := string(hostObject.Role) if hostObject.Bootstrap { role = string(models.HostRoleBootstrap) } - downloadFileName = fmt.Sprintf("%s_%s_%s.tar.gz", sanitize.Name(c.Name), role, sanitize.Name(hostutil.GetHostnameForMsg(&hostObject.Host))) + name := sanitize.Name(hostutil.GetHostnameForMsg(&hostObject.Host)) + if logsType == string(models.LogsTypeNodeBoot) { + name = fmt.Sprintf("boot_%s", name) + } + downloadFileName = fmt.Sprintf("%s_%s_%s.tar.gz", sanitize.Name(c.Name), role, name) case string(models.LogsTypeController): if time.Time(c.Cluster.ControllerLogsCollectedAt).Equal(time.Time{}) { return "", "", common.NewApiError(http.StatusConflict, errors.Errorf("Controller Logs for cluster %s were not found", clusterId)) } - fileName = b.getLogsFullName(clusterId.String(), logsType) + fileName = b.getLogsFullName(logsType, clusterId.String(), logsType) downloadFileName = fmt.Sprintf("%s_%s_%s.tar.gz", sanitize.Name(c.Name), c.ID, logsType) default: fileName, err = b.prepareClusterLogs(ctx, c) @@ -3937,7 +3941,7 @@ func (b *bareMetalInventory) validateDNSDomain(cluster common.Cluster, params in return nil } -func (b *bareMetalInventory) uploadHostLogs(ctx context.Context, host *common.Host, upFile io.ReadCloser) error { +func (b *bareMetalInventory) uploadHostLogs(ctx context.Context, host *common.Host, logsType string, upFile io.ReadCloser) error { log := logutil.FromContext(ctx, b.log) var logPrefix string @@ -3946,7 +3950,7 @@ func (b *bareMetalInventory) uploadHostLogs(ctx context.Context, host *common.Ho } else { logPrefix = host.InfraEnvID.String() } - fileName := b.getLogsFullName(logPrefix, host.ID.String()) + fileName := b.getLogsFullName(logsType, logPrefix, host.ID.String()) log.Debugf("Start upload log file %s to bucket %s", fileName, b.S3Bucket) err := b.objectHandler.UploadStream(ctx, upFile, fileName) @@ -3960,6 +3964,9 @@ func (b *bareMetalInventory) uploadHostLogs(ctx context.Context, host *common.Ho log.WithError(err).Errorf("Failed update host %s logs_collected_at flag", host.ID.String()) return common.NewApiError(http.StatusInternalServerError, err) } + if logsType == string(models.LogsTypeNodeBoot) { + return nil + } err = b.hostApi.UpdateLogsProgress(ctx, &host.Host, string(models.LogsStateCollecting)) if err != nil { log.WithError(err).Errorf("Failed update host %s log progress %s", host.ID.String(), string(models.LogsStateCollecting)) @@ -3976,8 +3983,12 @@ func (b *bareMetalInventory) prepareClusterLogs(ctx context.Context, cluster *co return fileName, nil } -func (b *bareMetalInventory) getLogsFullName(clusterId string, logId string) string { - return fmt.Sprintf("%s/logs/%s/logs.tar.gz", clusterId, logId) +func (b *bareMetalInventory) getLogsFullName(logType string, clusterId string, logId string) string { + filename := "logs.tar.gz" + if logType == string(models.LogsTypeNodeBoot) { + filename = fmt.Sprintf("boot_%s", filename) + } + return fmt.Sprintf("%s/logs/%s/%s", clusterId, logId, filename) } func (b *bareMetalInventory) getHost(ctx context.Context, clusterId string, hostId string) (*common.Host, error) { diff --git a/internal/bminventory/inventory_test.go b/internal/bminventory/inventory_test.go index 452b58b463..1bea3558f1 100644 --- a/internal/bminventory/inventory_test.go +++ b/internal/bminventory/inventory_test.go @@ -9457,7 +9457,20 @@ var _ = Describe("Upload and Download logs test", func() { verifyApiError(bm.V2UploadLogs(ctx, params), http.StatusNotFound) }) - It("Upload S3 upload fails", func() { + It("Upload boot logs host not exits", func() { + hostId := strToUUID(uuid.New().String()) + params := installer.V2UploadLogsParams{ + ClusterID: clusterID, + HostID: hostId, + LogsType: string(models.LogsTypeNodeBoot), + InfraEnvID: &clusterID, + Upfile: kubeconfigFile, + HTTPRequest: request, + } + verifyApiError(bm.V2UploadLogs(ctx, params), http.StatusNotFound) + }) + + It("Upload host logs to S3 - upload fails", func() { newHostID := strfmt.UUID(uuid.New().String()) host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) params := installer.V2UploadLogsParams{ @@ -9469,10 +9482,28 @@ var _ = Describe("Upload and Download logs test", func() { HTTPRequest: request, } - fileName := bm.getLogsFullName(clusterID.String(), host.ID.String()) + fileName := bm.getLogsFullName(string(models.LogsTypeHost), clusterID.String(), host.ID.String()) + mockS3Client.EXPECT().UploadStream(gomock.Any(), gomock.Any(), fileName).Return(errors.Errorf("Dummy")).Times(1) + verifyApiError(bm.V2UploadLogs(ctx, params), http.StatusInternalServerError) + }) + + It("Upload host boot logs to S3 - upload fails", func() { + newHostID := strfmt.UUID(uuid.New().String()) + host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + params := installer.V2UploadLogsParams{ + ClusterID: clusterID, + HostID: host.ID, + LogsType: string(models.LogsTypeNodeBoot), + InfraEnvID: &clusterID, + Upfile: kubeconfigFile, + HTTPRequest: request, + } + + fileName := bm.getLogsFullName(string(models.LogsTypeNodeBoot), clusterID.String(), host.ID.String()) mockS3Client.EXPECT().UploadStream(gomock.Any(), gomock.Any(), fileName).Return(errors.Errorf("Dummy")).Times(1) verifyApiError(bm.V2UploadLogs(ctx, params), http.StatusInternalServerError) }) + It("Upload Hosts logs Happy flow", func() { newHostID := strfmt.UUID(uuid.New().String()) @@ -9485,7 +9516,7 @@ var _ = Describe("Upload and Download logs test", func() { Upfile: kubeconfigFile, HTTPRequest: request, } - fileName := bm.getLogsFullName(clusterID.String(), host.ID.String()) + fileName := bm.getLogsFullName(string(models.LogsTypeHost), clusterID.String(), host.ID.String()) mockEvents.EXPECT().SendHostEvent(gomock.Any(), eventstest.NewEventMatcher( eventstest.WithNameMatcher(eventgen.HostLogsUploadedEventName), eventstest.WithClusterIdMatcher(clusterID.String()), @@ -9497,7 +9528,30 @@ var _ = Describe("Upload and Download logs test", func() { Expect(reply).Should(BeAssignableToTypeOf(installer.NewV2UploadLogsNoContent())) }) - It(" V2 start collecting hosts logs indication", func() { + It("Upload Hosts boot logs Happy flow", func() { + + newHostID := strfmt.UUID(uuid.New().String()) + host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + params := installer.V2UploadLogsParams{ + ClusterID: clusterID, + HostID: host.ID, + LogsType: string(models.LogsTypeNodeBoot), + InfraEnvID: &clusterID, + Upfile: kubeconfigFile, + HTTPRequest: request, + } + fileName := bm.getLogsFullName(string(models.LogsTypeNodeBoot), clusterID.String(), host.ID.String()) + mockEvents.EXPECT().SendHostEvent(gomock.Any(), eventstest.NewEventMatcher( + eventstest.WithNameMatcher(eventgen.HostBootLogsUploadedEventName), + eventstest.WithClusterIdMatcher(clusterID.String()), + eventstest.WithHostIdMatcher(host.ID.String()))).Times(1) + mockS3Client.EXPECT().UploadStream(gomock.Any(), gomock.Any(), fileName).Return(nil).Times(1) + mockHostApi.EXPECT().SetUploadLogsAt(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1) + reply := bm.V2UploadLogs(ctx, params) + Expect(reply).Should(BeAssignableToTypeOf(installer.NewV2UploadLogsNoContent())) + }) + + It(" Start collecting hosts logs indication", func() { newHostID := strfmt.UUID(uuid.New().String()) host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, infraEnvID, clusterID, "{}", db) params := installer.V2UpdateHostLogsProgressParams{ @@ -9512,7 +9566,8 @@ var _ = Describe("Upload and Download logs test", func() { reply := bm.V2UpdateHostLogsProgress(ctx, params) Expect(reply).Should(BeAssignableToTypeOf(installer.NewV2UpdateHostLogsProgressNoContent())) }) - It("V2 complete collecting hosts logs indication", func() { + + It("Complete collecting hosts logs indication", func() { newHostID := strfmt.UUID(uuid.New().String()) host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, infraEnvID, clusterID, "{}", db) params := installer.V2UpdateHostLogsProgressParams{ @@ -9535,7 +9590,7 @@ var _ = Describe("Upload and Download logs test", func() { HTTPRequest: request, LogsType: string(models.LogsTypeController), } - fileName := bm.getLogsFullName(clusterID.String(), string(models.LogsTypeController)) + fileName := bm.getLogsFullName(string(models.LogsTypeController), clusterID.String(), string(models.LogsTypeController)) mockEvents.EXPECT().SendClusterEvent(gomock.Any(), eventstest.NewEventMatcher( eventstest.WithNameMatcher(eventgen.ClusterLogsUploadedEventName), eventstest.WithClusterIdMatcher(clusterID.String()))).Times(1) @@ -9560,7 +9615,18 @@ var _ = Describe("Upload and Download logs test", func() { verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusConflict) }) - It("Download S3 logs where not uploaded yet", func() { + It("Download S3 host boot logs where not uploaded yet", func() { + logsType := string(models.LogsTypeNodeBoot) + params := installer.V2DownloadClusterLogsParams{ + ClusterID: clusterID, + HostID: &hostID, + LogsType: &logsType, + HTTPRequest: request, + } + verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusConflict) + }) + + It("Download S3 host logs where not uploaded yet", func() { logsType := string(models.LogsTypeHost) params := installer.V2DownloadClusterLogsParams{ ClusterID: clusterID, @@ -9571,7 +9637,22 @@ var _ = Describe("Upload and Download logs test", func() { verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusConflict) }) - It("Download S3 object not found", func() { + It("Download S3 host boot logs - object not found", func() { + logsType := string(models.LogsTypeNodeBoot) + params := installer.V2DownloadClusterLogsParams{ + ClusterID: clusterID, + HostID: &hostID, + LogsType: &logsType, + HTTPRequest: request, + } + host1.LogsCollectedAt = strfmt.DateTime(time.Now()) + db.Save(&host1) + fileName := bm.getLogsFullName(logsType, clusterID.String(), hostID.String()) + mockS3Client.EXPECT().Download(ctx, fileName).Return(nil, int64(0), common.NotFound(fileName)) + verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusNotFound) + }) + + It("Download S3 host logs - object not found", func() { logsType := string(models.LogsTypeHost) params := installer.V2DownloadClusterLogsParams{ ClusterID: clusterID, @@ -9581,12 +9662,27 @@ var _ = Describe("Upload and Download logs test", func() { } host1.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host1) - fileName := bm.getLogsFullName(clusterID.String(), hostID.String()) + fileName := bm.getLogsFullName(logsType, clusterID.String(), hostID.String()) mockS3Client.EXPECT().Download(ctx, fileName).Return(nil, int64(0), common.NotFound(fileName)) verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusNotFound) }) - It("Download S3 object failed", func() { + It("Download S3 host boot logs - object failed", func() { + logsType := string(models.LogsTypeNodeBoot) + params := installer.V2DownloadClusterLogsParams{ + ClusterID: clusterID, + HostID: &hostID, + LogsType: &logsType, + HTTPRequest: request, + } + host1.LogsCollectedAt = strfmt.DateTime(time.Now()) + db.Save(&host1) + fileName := bm.getLogsFullName(logsType, clusterID.String(), hostID.String()) + mockS3Client.EXPECT().Download(ctx, fileName).Return(nil, int64(0), errors.Errorf("dummy")) + verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusInternalServerError) + }) + + It("Download S3 host logs -object failed", func() { logsType := string(models.LogsTypeHost) params := installer.V2DownloadClusterLogsParams{ ClusterID: clusterID, @@ -9596,7 +9692,7 @@ var _ = Describe("Upload and Download logs test", func() { } host1.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host1) - fileName := bm.getLogsFullName(clusterID.String(), hostID.String()) + fileName := bm.getLogsFullName(logsType, clusterID.String(), hostID.String()) mockS3Client.EXPECT().Download(ctx, fileName).Return(nil, int64(0), errors.Errorf("dummy")) verifyApiError(bm.V2DownloadClusterLogs(ctx, params), http.StatusInternalServerError) }) @@ -9612,7 +9708,7 @@ var _ = Describe("Upload and Download logs test", func() { HTTPRequest: request, } host.Bootstrap = true - fileName := bm.getLogsFullName(clusterID.String(), host.ID.String()) + fileName := bm.getLogsFullName(logsType, clusterID.String(), host.ID.String()) host.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host) r := io.NopCloser(bytes.NewReader([]byte("test"))) @@ -9621,13 +9717,35 @@ var _ = Describe("Upload and Download logs test", func() { downloadFileName := fmt.Sprintf("mycluster_bootstrap_%s.tar.gz", newHostID.String()) Expect(generateReply).Should(Equal(filemiddleware.NewResponder(installer.NewV2DownloadClusterLogsOK().WithPayload(r), downloadFileName, 4, nil))) }) + + It("Download Hosts boot logs happy flow", func() { + newHostID := strfmt.UUID(uuid.New().String()) + host := addHost(newHostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + logsType := string(models.LogsTypeNodeBoot) + params := installer.V2DownloadClusterLogsParams{ + ClusterID: clusterID, + HostID: host.ID, + LogsType: &logsType, + HTTPRequest: request, + } + host.Bootstrap = true + fileName := bm.getLogsFullName(logsType, clusterID.String(), host.ID.String()) + host.LogsCollectedAt = strfmt.DateTime(time.Now()) + db.Save(&host) + r := io.NopCloser(bytes.NewReader([]byte("test"))) + mockS3Client.EXPECT().Download(ctx, fileName).Return(r, int64(4), nil) + generateReply := bm.V2DownloadClusterLogs(ctx, params) + downloadFileName := fmt.Sprintf("mycluster_bootstrap_boot_%s.tar.gz", newHostID.String()) + Expect(generateReply).Should(Equal(filemiddleware.NewResponder(installer.NewV2DownloadClusterLogsOK().WithPayload(r), downloadFileName, 4, nil))) + }) + It("Download Controller logs happy flow", func() { logsType := string(models.LogsTypeController) params := installer.V2DownloadClusterLogsParams{ ClusterID: clusterID, LogsType: &logsType, } - fileName := bm.getLogsFullName(clusterID.String(), logsType) + fileName := bm.getLogsFullName(logsType, clusterID.String(), logsType) c.ControllerLogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&c) r := io.NopCloser(bytes.NewReader([]byte("test"))) @@ -9636,7 +9754,8 @@ var _ = Describe("Upload and Download logs test", func() { downloadFileName := fmt.Sprintf("mycluster_%s_%s.tar.gz", clusterID, logsType) Expect(generateReply).Should(Equal(filemiddleware.NewResponder(installer.NewV2DownloadClusterLogsOK().WithPayload(r), downloadFileName, 4, nil))) }) - It("Logs presigned host not found", func() { + + It("Host logs presigned host not found", func() { hostID := strfmt.UUID(uuid.New().String()) mockS3Client.EXPECT().IsAwsS3().Return(true) generateReply := bm.V2GetPresignedForClusterFiles(ctx, installer.V2GetPresignedForClusterFilesParams{ @@ -9647,7 +9766,21 @@ var _ = Describe("Upload and Download logs test", func() { }) verifyApiError(generateReply, http.StatusNotFound) }) - It("Logs presigned no logs found", func() { + + It("Host boot logs presigned host not found", func() { + logsType := string(models.LogsTypeNodeBoot) + hostID := strfmt.UUID(uuid.New().String()) + mockS3Client.EXPECT().IsAwsS3().Return(true) + generateReply := bm.V2GetPresignedForClusterFiles(ctx, installer.V2GetPresignedForClusterFilesParams{ + ClusterID: clusterID, + FileName: "logs", + HostID: &hostID, + LogsType: &logsType, + }) + verifyApiError(generateReply, http.StatusNotFound) + }) + + It("Host logs presigned no logs found", func() { hostID := strfmt.UUID(uuid.New().String()) _ = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) mockS3Client.EXPECT().IsAwsS3().Return(true) @@ -9659,11 +9792,26 @@ var _ = Describe("Upload and Download logs test", func() { }) verifyApiError(generateReply, http.StatusConflict) }) - It("Logs presigned s3 error", func() { + + It("Host boot logs presigned no logs found", func() { + logsType := string(models.LogsTypeNodeBoot) + hostID := strfmt.UUID(uuid.New().String()) + _ = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + mockS3Client.EXPECT().IsAwsS3().Return(true) + generateReply := bm.V2GetPresignedForClusterFiles(ctx, installer.V2GetPresignedForClusterFilesParams{ + ClusterID: clusterID, + FileName: "logs", + HostID: &hostID, + LogsType: &logsType, + }) + verifyApiError(generateReply, http.StatusConflict) + }) + + It("Host logs presigned s3 error", func() { hostID := strfmt.UUID(uuid.New().String()) host1 = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) mockS3Client.EXPECT().IsAwsS3().Return(true) - fileName := bm.getLogsFullName(clusterID.String(), hostID.String()) + fileName := bm.getLogsFullName(string(models.LogsTypeHost), clusterID.String(), hostID.String()) host1.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host1) mockS3Client.EXPECT().GeneratePresignedDownloadURL(ctx, fileName, fmt.Sprintf("mycluster_master_%s.tar.gz", hostID.String()), gomock.Any()).Return("", @@ -9676,11 +9824,31 @@ var _ = Describe("Upload and Download logs test", func() { }) verifyApiError(generateReply, http.StatusInternalServerError) }) + + It("Host boot logs presigned s3 error", func() { + logsType := string(models.LogsTypeNodeBoot) + hostID := strfmt.UUID(uuid.New().String()) + host1 = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + mockS3Client.EXPECT().IsAwsS3().Return(true) + fileName := bm.getLogsFullName(string(models.LogsTypeNodeBoot), clusterID.String(), hostID.String()) + host1.LogsCollectedAt = strfmt.DateTime(time.Now()) + db.Save(&host1) + mockS3Client.EXPECT().GeneratePresignedDownloadURL(ctx, fileName, fmt.Sprintf("mycluster_master_boot_%s.tar.gz", hostID.String()), gomock.Any()).Return("", + errors.Errorf("Dummy")) + generateReply := bm.V2GetPresignedForClusterFiles(ctx, installer.V2GetPresignedForClusterFilesParams{ + ClusterID: clusterID, + FileName: "logs", + HostID: &hostID, + LogsType: &logsType, + }) + verifyApiError(generateReply, http.StatusInternalServerError) + }) + It("host logs presigned happy flow", func() { hostID := strfmt.UUID(uuid.New().String()) host1 = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) mockS3Client.EXPECT().IsAwsS3().Return(true) - fileName := bm.getLogsFullName(clusterID.String(), hostID.String()) + fileName := bm.getLogsFullName(string(models.LogsTypeHost), clusterID.String(), hostID.String()) host1.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host1) mockS3Client.EXPECT().GeneratePresignedDownloadURL(ctx, fileName, fmt.Sprintf("mycluster_master_%s.tar.gz", hostID.String()), gomock.Any()).Return("url", nil) @@ -9694,11 +9862,32 @@ var _ = Describe("Upload and Download logs test", func() { replyPayload := generateReply.(*installer.V2GetPresignedForClusterFilesOK).Payload Expect(*replyPayload.URL).Should(Equal("url")) }) + + It("host boot logs presigned happy flow", func() { + logsType := string(models.LogsTypeNodeBoot) + hostID := strfmt.UUID(uuid.New().String()) + host1 = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) + mockS3Client.EXPECT().IsAwsS3().Return(true) + fileName := bm.getLogsFullName(string(models.LogsTypeNodeBoot), clusterID.String(), hostID.String()) + host1.LogsCollectedAt = strfmt.DateTime(time.Now()) + db.Save(&host1) + mockS3Client.EXPECT().GeneratePresignedDownloadURL(ctx, fileName, fmt.Sprintf("mycluster_master_boot_%s.tar.gz", hostID.String()), gomock.Any()).Return("url", nil) + generateReply := bm.V2GetPresignedForClusterFiles(ctx, installer.V2GetPresignedForClusterFilesParams{ + ClusterID: clusterID, + FileName: "logs", + HostID: &hostID, + LogsType: &logsType, + }) + Expect(generateReply).Should(BeAssignableToTypeOf(&installer.V2GetPresignedForClusterFilesOK{})) + replyPayload := generateReply.(*installer.V2GetPresignedForClusterFilesOK).Payload + Expect(*replyPayload.URL).Should(Equal("url")) + }) + It("host logs presigned happy flow without log type", func() { hostID := strfmt.UUID(uuid.New().String()) host1 = addHost(hostID, models.HostRoleMaster, "known", models.HostKindHost, clusterID, clusterID, "{}", db) mockS3Client.EXPECT().IsAwsS3().Return(true) - fileName := bm.getLogsFullName(clusterID.String(), hostID.String()) + fileName := bm.getLogsFullName(string(models.LogsTypeHost), clusterID.String(), hostID.String()) host1.LogsCollectedAt = strfmt.DateTime(time.Now()) db.Save(&host1) mockS3Client.EXPECT().GeneratePresignedDownloadURL(ctx, fileName, fmt.Sprintf("mycluster_master_%s.tar.gz", hostID.String()), gomock.Any()).Return("url", nil) @@ -9711,6 +9900,7 @@ var _ = Describe("Upload and Download logs test", func() { replyPayload := generateReply.(*installer.V2GetPresignedForClusterFilesOK).Payload Expect(*replyPayload.URL).Should(Equal("url")) }) + It("download cluster logs no cluster", func() { clusterId := strToUUID(uuid.New().String()) params := installer.V2DownloadClusterLogsParams{ @@ -9786,7 +9976,7 @@ var _ = Describe("Upload and Download logs test", func() { dbReply = db.Where("id = ?", clusterID).Delete(&common.Cluster{}) Expect(int(dbReply.RowsAffected)).Should(Equal(1)) r := io.NopCloser(bytes.NewReader([]byte("test"))) - fileName := bm.getLogsFullName(clusterID.String(), logsType) + fileName := bm.getLogsFullName(logsType, clusterID.String(), logsType) mockS3Client.EXPECT().Download(ctx, fileName).Return(r, int64(4), nil) generateReply := bm.V2DownloadClusterLogs(ctx, params) downloadFileName := fmt.Sprintf("mycluster_%s_%s.tar.gz", clusterID, logsType) diff --git a/internal/bminventory/inventory_v2_handlers.go b/internal/bminventory/inventory_v2_handlers.go index fa1ae394d3..0ef4b6cdea 100644 --- a/internal/bminventory/inventory_v2_handlers.go +++ b/internal/bminventory/inventory_v2_handlers.go @@ -426,9 +426,10 @@ func (b *bareMetalInventory) v2uploadLogs(ctx context.Context, params installer. } }() - if params.LogsType == string(models.LogsTypeHost) { + if params.LogsType == string(models.LogsTypeHost) || params.LogsType == string(models.LogsTypeNodeBoot) { if params.InfraEnvID == nil || params.HostID == nil { - return common.NewApiError(http.StatusInternalServerError, errors.New("infra_env_id and host_id are required for upload host logs")) + return common.NewApiError(http.StatusInternalServerError, + errors.Errorf("infra_env_id and host_id are required for upload %s logs", params.LogsType)) } dbHost, err := common.GetHostFromDB(b.db, params.InfraEnvID.String(), params.HostID.String()) @@ -436,13 +437,18 @@ func (b *bareMetalInventory) v2uploadLogs(ctx context.Context, params installer. return err } - err = b.uploadHostLogs(ctx, dbHost, params.Upfile) + err = b.uploadHostLogs(ctx, dbHost, params.LogsType, params.Upfile) if err != nil { return err } - eventgen.SendHostLogsUploadedEvent(ctx, b.eventsHandler, *params.HostID, dbHost.InfraEnvID, common.StrFmtUUIDPtr(params.ClusterID), - hostutil.GetHostnameForMsg(&dbHost.Host)) + if params.LogsType == string(models.LogsTypeHost) { + eventgen.SendHostLogsUploadedEvent(ctx, b.eventsHandler, *params.HostID, dbHost.InfraEnvID, common.StrFmtUUIDPtr(params.ClusterID), + hostutil.GetHostnameForMsg(&dbHost.Host)) + } else { + eventgen.SendHostBootLogsUploadedEvent(ctx, b.eventsHandler, *params.HostID, dbHost.InfraEnvID, common.StrFmtUUIDPtr(params.ClusterID), + hostutil.GetHostnameForMsg(&dbHost.Host)) + } return nil } @@ -450,7 +456,7 @@ func (b *bareMetalInventory) v2uploadLogs(ctx context.Context, params installer. if err != nil { return err } - fileName := b.getLogsFullName(params.ClusterID.String(), params.LogsType) + fileName := b.getLogsFullName(params.LogsType, params.ClusterID.String(), params.LogsType) log.Debugf("Start upload log file %s to bucket %s", fileName, b.S3Bucket) err = b.objectHandler.UploadStream(ctx, params.Upfile, fileName) if err != nil { diff --git a/internal/common/events/events.go b/internal/common/events/events.go index d2ccb556d4..0e49ef121d 100644 --- a/internal/common/events/events.go +++ b/internal/common/events/events.go @@ -4515,6 +4515,101 @@ func (e *HostResetFetchFailedEvent) FormatMessage() string { return e.format(&s) } +// +// Event host_boot_logs_uploaded +// +type HostBootLogsUploadedEvent struct { + eventName string + HostId strfmt.UUID + InfraEnvId strfmt.UUID + ClusterId *strfmt.UUID + HostName string +} + +var HostBootLogsUploadedEventName string = "host_boot_logs_uploaded" + +func NewHostBootLogsUploadedEvent( + hostId strfmt.UUID, + infraEnvId strfmt.UUID, + clusterId *strfmt.UUID, + hostName string, +) *HostBootLogsUploadedEvent { + return &HostBootLogsUploadedEvent{ + eventName: HostBootLogsUploadedEventName, + HostId: hostId, + InfraEnvId: infraEnvId, + ClusterId: clusterId, + HostName: hostName, + } +} + +func SendHostBootLogsUploadedEvent( + ctx context.Context, + eventsHandler eventsapi.Sender, + hostId strfmt.UUID, + infraEnvId strfmt.UUID, + clusterId *strfmt.UUID, + hostName string,) { + ev := NewHostBootLogsUploadedEvent( + hostId, + infraEnvId, + clusterId, + hostName, + ) + eventsHandler.SendHostEvent(ctx, ev) +} + +func SendHostBootLogsUploadedEventAtTime( + ctx context.Context, + eventsHandler eventsapi.Sender, + hostId strfmt.UUID, + infraEnvId strfmt.UUID, + clusterId *strfmt.UUID, + hostName string, + eventTime time.Time) { + ev := NewHostBootLogsUploadedEvent( + hostId, + infraEnvId, + clusterId, + hostName, + ) + eventsHandler.SendHostEventAtTime(ctx, ev, eventTime) +} + +func (e *HostBootLogsUploadedEvent) GetName() string { + return e.eventName +} + +func (e *HostBootLogsUploadedEvent) GetSeverity() string { + return "info" +} +func (e *HostBootLogsUploadedEvent) GetClusterId() *strfmt.UUID { + return e.ClusterId +} +func (e *HostBootLogsUploadedEvent) GetHostId() strfmt.UUID { + return e.HostId +} +func (e *HostBootLogsUploadedEvent) GetInfraEnvId() strfmt.UUID { + return e.InfraEnvId +} + + + +func (e *HostBootLogsUploadedEvent) format(message *string) string { + r := strings.NewReplacer( + "{host_id}", fmt.Sprint(e.HostId), + "{infra_env_id}", fmt.Sprint(e.InfraEnvId), + "{cluster_id}", fmt.Sprint(e.ClusterId), + "{host_name}", fmt.Sprint(e.HostName), + ) + return r.Replace(*message) +} + +func (e *HostBootLogsUploadedEvent) FormatMessage() string { + s := "Uploaded node boot logs for host {host_name} cluster {cluster_id}" + return e.format(&s) +} + // // Event host_logs_uploaded // diff --git a/models/logs_type.go b/models/logs_type.go index b7d33d4401..713893b639 100644 --- a/models/logs_type.go +++ b/models/logs_type.go @@ -33,6 +33,9 @@ const ( // LogsTypeHost captures enum value "host" LogsTypeHost LogsType = "host" + // LogsTypeNodeBoot captures enum value "node-boot" + LogsTypeNodeBoot LogsType = "node-boot" + // LogsTypeController captures enum value "controller" LogsTypeController LogsType = "controller" @@ -48,7 +51,7 @@ var logsTypeEnum []interface{} func init() { var res []LogsType - if err := json.Unmarshal([]byte(`["host","controller","all",""]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["host","node-boot","controller","all",""]`), &res); err != nil { panic(err) } for _, v := range res { diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 1fb1ac9209..7979680860 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -1669,6 +1669,7 @@ func init() { "enum": [ "host", "controller", + "node-boot", "all" ], "type": "string", @@ -1763,7 +1764,8 @@ func init() { { "enum": [ "host", - "controller" + "controller", + "node-boot" ], "type": "string", "description": "The type of log file to be uploaded.", @@ -8430,6 +8432,7 @@ func init() { "type": "string", "enum": [ "host", + "node-boot", "controller", "all", "" @@ -11266,6 +11269,7 @@ func init() { "enum": [ "host", "controller", + "node-boot", "all" ], "type": "string", @@ -11360,7 +11364,8 @@ func init() { { "enum": [ "host", - "controller" + "controller", + "node-boot" ], "type": "string", "description": "The type of log file to be uploaded.", @@ -18120,6 +18125,7 @@ func init() { "type": "string", "enum": [ "host", + "node-boot", "controller", "all", "" diff --git a/restapi/operations/installer/v2_download_cluster_logs_parameters.go b/restapi/operations/installer/v2_download_cluster_logs_parameters.go index d2fae22638..4dec3f5355 100644 --- a/restapi/operations/installer/v2_download_cluster_logs_parameters.go +++ b/restapi/operations/installer/v2_download_cluster_logs_parameters.go @@ -173,7 +173,7 @@ func (o *V2DownloadClusterLogsParams) bindLogsType(rawData []string, hasKey bool // validateLogsType carries on validations for parameter LogsType func (o *V2DownloadClusterLogsParams) validateLogsType(formats strfmt.Registry) error { - if err := validate.EnumCase("logs_type", "query", *o.LogsType, []interface{}{"host", "controller", "all"}, true); err != nil { + if err := validate.EnumCase("logs_type", "query", *o.LogsType, []interface{}{"host", "controller", "node-boot", "all"}, true); err != nil { return err } diff --git a/restapi/operations/installer/v2_upload_logs_parameters.go b/restapi/operations/installer/v2_upload_logs_parameters.go index fe88b75c2c..5290bfa9c8 100644 --- a/restapi/operations/installer/v2_upload_logs_parameters.go +++ b/restapi/operations/installer/v2_upload_logs_parameters.go @@ -256,7 +256,7 @@ func (o *V2UploadLogsParams) bindLogsType(rawData []string, hasKey bool, formats // validateLogsType carries on validations for parameter LogsType func (o *V2UploadLogsParams) validateLogsType(formats strfmt.Registry) error { - if err := validate.EnumCase("logs_type", "query", o.LogsType, []interface{}{"host", "controller"}, true); err != nil { + if err := validate.EnumCase("logs_type", "query", o.LogsType, []interface{}{"host", "controller", "node-boot"}, true); err != nil { return err } diff --git a/subsystem/cluster_test.go b/subsystem/cluster_test.go index 022db54852..afb3edc80c 100644 --- a/subsystem/cluster_test.go +++ b/subsystem/cluster_test.go @@ -2251,7 +2251,7 @@ var _ = Describe("cluster install", func() { Expect(s.Size()).ShouldNot(Equal(0)) } - By("Test happy flow host logs file") + By("Test happy flow node logs file") { kubeconfigFile, err := os.Open("test_kubeconfig") Expect(err).NotTo(HaveOccurred()) @@ -2278,6 +2278,33 @@ var _ = Describe("cluster install", func() { Expect(s.Size()).ShouldNot(Equal(0)) } + By("Test happy flow node logs file") + { + kubeconfigFile, err := os.Open("test_kubeconfig") + Expect(err).NotTo(HaveOccurred()) + logsType := string(models.LogsTypeNodeBoot) + hosts, _ := register3nodes(ctx, clusterID, *infraEnvID, defaultCIDRv4) + _, err = agentBMClient.Installer.V2UploadLogs(ctx, &installer.V2UploadLogsParams{ + ClusterID: clusterID, + HostID: hosts[0].ID, + InfraEnvID: infraEnvID, + LogsType: logsType, + Upfile: kubeconfigFile}) + Expect(err).NotTo(HaveOccurred()) + + file, err := os.CreateTemp("", "tmp") + Expect(err).NotTo(HaveOccurred()) + _, err = userBMClient.Installer.V2DownloadClusterLogs(ctx, &installer.V2DownloadClusterLogsParams{ + ClusterID: clusterID, + HostID: hosts[0].ID, + LogsType: &logsType, + }, file) + Expect(err).NotTo(HaveOccurred()) + s, err := file.Stat() + Expect(err).NotTo(HaveOccurred()) + Expect(s.Size()).ShouldNot(Equal(0)) + } + By("Test happy flow large file") { filePath := "../build/test_logs.txt" diff --git a/swagger.yaml b/swagger.yaml index b4e7de47b7..6cf69d6541 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -3103,7 +3103,7 @@ paths: name: logs_type description: The type of logs to be downloaded. type: string - enum: ['host', 'controller', 'all'] + enum: ['host', 'controller', 'node-boot', 'all'] required: false - in: query name: host_id @@ -3167,7 +3167,7 @@ paths: name: logs_type description: The type of log file to be uploaded. type: string - enum: ['host', 'controller'] + enum: ['host', 'controller', 'node-boot'] required: true - in: query name: infra_env_id @@ -6237,6 +6237,7 @@ definitions: type: string enum: - 'host' + - 'node-boot' - 'controller' - 'all' - '' diff --git a/vendor/github.com/openshift/assisted-service/models/logs_type.go b/vendor/github.com/openshift/assisted-service/models/logs_type.go index b7d33d4401..713893b639 100644 --- a/vendor/github.com/openshift/assisted-service/models/logs_type.go +++ b/vendor/github.com/openshift/assisted-service/models/logs_type.go @@ -33,6 +33,9 @@ const ( // LogsTypeHost captures enum value "host" LogsTypeHost LogsType = "host" + // LogsTypeNodeBoot captures enum value "node-boot" + LogsTypeNodeBoot LogsType = "node-boot" + // LogsTypeController captures enum value "controller" LogsTypeController LogsType = "controller" @@ -48,7 +51,7 @@ var logsTypeEnum []interface{} func init() { var res []LogsType - if err := json.Unmarshal([]byte(`["host","controller","all",""]`), &res); err != nil { + if err := json.Unmarshal([]byte(`["host","node-boot","controller","all",""]`), &res); err != nil { panic(err) } for _, v := range res {