diff --git a/cmd/cluster/context.go b/cmd/cluster/context.go index c1c86cd0..abf3473a 100644 --- a/cmd/cluster/context.go +++ b/cmd/cluster/context.go @@ -3,6 +3,7 @@ package cluster import ( "encoding/json" "fmt" + v1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" "os" "os/exec" "sort" @@ -17,7 +18,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/cloudtrail" "github.com/aws/aws-sdk-go-v2/service/cloudtrail/types" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - sl "github.com/openshift/osdctl/internal/servicelog" "github.com/openshift/osdctl/pkg/osdCloud" "github.com/openshift/osdctl/pkg/osdctlConfig" "github.com/openshift/osdctl/pkg/printer" @@ -72,7 +72,7 @@ type contextData struct { // limited Support Status LimitedSupportReasons []*cmv1.LimitedSupportReason // Service Logs - ServiceLogs []sl.ServiceLogShort + ServiceLogs []*v1.LogEntry // Jira Cards JiraIssues []jira.Issue @@ -263,7 +263,7 @@ func (o *contextOptions) printShortOutput(data *contextData) { var numInternalServiceLogs int for _, serviceLog := range data.ServiceLogs { - if serviceLog.InternalOnly { + if serviceLog.InternalOnly() { numInternalServiceLogs++ } } @@ -360,7 +360,7 @@ func (o *contextOptions) generateContextData() (*contextData, []error) { if o.verbose { fmt.Fprintln(os.Stderr, "Getting Service Logs...") } - data.ServiceLogs, err = servicelog.GetServiceLogsSince(cluster.ID(), o.days) + data.ServiceLogs, err = servicelog.GetServiceLogsSince(cluster.ID(), o.days, false, false) if err != nil { errors = append(errors, fmt.Errorf("Error while getting the service logs: %v", err)) } diff --git a/cmd/org/context.go b/cmd/org/context.go index c54c418f..aad24317 100644 --- a/cmd/org/context.go +++ b/cmd/org/context.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + v1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" "os" "strconv" "sync" @@ -14,7 +15,6 @@ import ( sdk "github.com/openshift-online/ocm-sdk-go" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" "github.com/openshift/osdctl/cmd/servicelog" - sl "github.com/openshift/osdctl/internal/servicelog" "github.com/openshift/osdctl/pkg/printer" pdProvider "github.com/openshift/osdctl/pkg/provider/pagerduty" "github.com/openshift/osdctl/pkg/utils" @@ -34,7 +34,7 @@ type ClusterInfo struct { CloudProvider string Plan string NodeCount float64 - ServiceLogs []sl.ServiceLogShort + ServiceLogs []*v1.LogEntry PdAlerts map[string][]pd.Incident JiraIssues []jira.Issue LimitedSupportReasons []*cmv1.LimitedSupportReason @@ -285,7 +285,7 @@ func addLimitedSupportReasons(clusterInfo *ClusterInfo, ocmClient *sdk.Connectio func addServiceLogs(clusterInfo *ClusterInfo) error { var err error - clusterInfo.ServiceLogs, err = servicelog.GetServiceLogsSince(clusterInfo.ID, ServiceLogDaysSince) + clusterInfo.ServiceLogs, err = servicelog.GetServiceLogsSince(clusterInfo.ID, ServiceLogDaysSince, false, false) if err != nil { return fmt.Errorf("failed to fetch service logs for cluster %v: %w", clusterInfo.ID, err) } diff --git a/cmd/servicelog/common.go b/cmd/servicelog/common.go index 8195a250..8a858d91 100644 --- a/cmd/servicelog/common.go +++ b/cmd/servicelog/common.go @@ -3,8 +3,11 @@ package servicelog import ( "encoding/json" "fmt" + sdk "github.com/openshift-online/ocm-sdk-go" + cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" + v1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" "github.com/openshift/osdctl/internal/servicelog" - sl "github.com/openshift/osdctl/internal/servicelog" + "github.com/openshift/osdctl/pkg/utils" "time" ) @@ -61,30 +64,72 @@ func validateBadResponse(body []byte) (badReply *servicelog.BadReply, err error) // time.Now() and time.Now()-duration. the first parameter will contain a slice // of the service logs from the given time period, while the second return value // indicates if an error has happened. -func GetServiceLogsSince(clusterID string, days int) ([]sl.ServiceLogShort, error) { +func GetServiceLogsSince(clusterID string, days int, allMessages bool, internalOnly bool) ([]*v1.LogEntry, error) { // time.Now().Sub() returns the duration between two times, so we negate the duration in Add() earliestTime := time.Now().AddDate(0, 0, -days) - slResponse, err := FetchServiceLogs(clusterID) + slResponse, err := FetchServiceLogs(clusterID, allMessages, internalOnly) if err != nil { return nil, err } - var serviceLogs sl.ServiceLogShortList - err = json.Unmarshal(slResponse.Bytes(), &serviceLogs) + var errorServiceLogs []*v1.LogEntry + for _, serviceLog := range slResponse.Items().Slice() { + if serviceLog.CreatedAt().After(earliestTime) { + errorServiceLogs = append(errorServiceLogs, serviceLog) + } + } + + return errorServiceLogs, nil +} + +func FetchServiceLogs(clusterID string, allMessages bool, internalOnly bool) (*v1.ClustersClusterLogsListResponse, error) { + // Create OCM client to talk to cluster API + ocmClient, err := utils.CreateConnection() if err != nil { - return nil, fmt.Errorf("failed to unmarshal the SL response %w", err) + return nil, err + } + defer func() { + if err := ocmClient.Close(); err != nil { + fmt.Printf("Cannot close the ocmClient (possible memory leak): %q", err) + } + }() + // Use the OCM client to retrieve clusters + clusters := utils.GetClusters(ocmClient, []string{clusterID}) + if len(clusters) != 1 { + return nil, fmt.Errorf("GetClusters expected to return 1 cluster, got: %d", len(clusters)) } + cluster := clusters[0] - // Parsing the relevant service logs - // - We only care about SLs sent in the given duration - var errorServiceLogs []sl.ServiceLogShort - for _, serviceLog := range serviceLogs.Items { - if serviceLog.CreatedAt.After(earliestTime) { - errorServiceLogs = append(errorServiceLogs, serviceLog) + // Now get the SLs for the cluster + clusterLogsListResponse, err := sendClusterLogsListRequest(ocmClient, cluster, allMessages, internalOnly) + if err != nil { + return nil, fmt.Errorf("failed to fetch service logs for cluster %v: %w", clusterID, err) + } + return clusterLogsListResponse, nil +} + +func sendClusterLogsListRequest(ocmClient *sdk.Connection, cluster *cmv1.Cluster, allMessages bool, internalMessages bool) (*v1.ClustersClusterLogsListResponse, error) { + request := ocmClient.ServiceLogs().V1().Clusters().ClusterLogs().List(). + Parameter("cluster_id", cluster.ID()). + Parameter("cluster_uuid", cluster.ExternalID()) + + var searchQuery string + if !allMessages { + searchQuery = "service_name='SREManualAction'" + } + if internalMessages { + if searchQuery != "" { + searchQuery += " and " } + searchQuery += "internal_only='true'" } + request.Search(searchQuery) - return errorServiceLogs, nil + response, err := request.Send() + if err != nil { + return nil, fmt.Errorf("failed to fetch service logs: %w", err) + } + return response, nil } diff --git a/cmd/servicelog/list.go b/cmd/servicelog/list.go index 7c61955a..a06dc557 100644 --- a/cmd/servicelog/list.go +++ b/cmd/servicelog/list.go @@ -1,123 +1,129 @@ package servicelog import ( + "encoding/json" "fmt" + slv1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" "os" + "time" - "github.com/openshift-online/ocm-cli/pkg/arguments" "github.com/openshift-online/ocm-cli/pkg/dump" - sdk "github.com/openshift-online/ocm-sdk-go" - cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - "github.com/openshift/osdctl/pkg/utils" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +const ( + AllMessagesFlag = "all-messages" + AllMessagesShortFlag = "A" + InternalFlag = "internal" + InternalShortFlag = "i" ) // listCmd represents the list command var listCmd = &cobra.Command{ - Use: "list [flags] [options] cluster-identifier", - Short: "gets all servicelog messages for a given cluster", - Args: cobra.ArbitraryArgs, - SilenceErrors: true, - Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(complete(cmd, args)) - cmdutil.CheckErr(run(cmd, args[0])) - }, -} - -func complete(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - err := cmd.Help() + Use: "list [flags] [options] cluster-identifier", + Short: "gets all servicelog messages for a given cluster", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + allMessages, err := cmd.Flags().GetBool(AllMessagesFlag) if err != nil { - return fmt.Errorf("error calling cmd.Help(): %w", err) + return fmt.Errorf("failed to get flag `--%v`/`-%v`, %w", AllMessagesFlag, AllMessagesShortFlag, err) + } + internalOnly, err := cmd.Flags().GetBool(InternalFlag) + if err != nil { + return fmt.Errorf("failed to get flag `--%v`/`-%v`, %w", InternalFlag, InternalShortFlag, err) } - return fmt.Errorf("cluster-identifier was not provided. please provide a cluster id, UUID, or name") - } - if len(args) != 1 { - log.Infof("Too many arguments. Expected 1 got %d", len(args)) - } + return ListServiceLogs(args[0], allMessages, internalOnly) + }, +} - return nil +func init() { + // define flags + listCmd.Flags().BoolP(AllMessagesFlag, AllMessagesShortFlag, false, "Toggle if we should see all of the messages or only SRE-P specific ones") + listCmd.Flags().BoolP(InternalFlag, InternalShortFlag, false, "Toggle if we should see internal messages") } -func run(cmd *cobra.Command, clusterID string) error { - response, err := FetchServiceLogs(clusterID) +func ListServiceLogs(clusterID string, allMessages bool, internalOnly bool) error { + response, err := FetchServiceLogs(clusterID, allMessages, internalOnly) if err != nil { - // If the response has errored, likely the input was bad, so show usage - helpErr := cmd.Help() - if helpErr != nil { - return helpErr - } - return err + return fmt.Errorf("failed to fetch service logs: %w", err) } - err = dump.Pretty(os.Stdout, response.Bytes()) - if err != nil { - // If outputing the data errored, there's likely an internal error, so just return the error - return err + if err = printServiceLogResponse(response); err != nil { + return fmt.Errorf("failed to print service logs: %w", err) } + return nil } -var serviceLogListAllMessagesFlag = false -var serviceLogListInternalOnlyFlag = false - -func FetchServiceLogs(clusterID string) (*sdk.Response, error) { - // Create OCM client to talk to cluster API - ocmClient, err := utils.CreateConnection() - if err != nil { - return nil, err +func printServiceLogResponse(response *slv1.ClustersClusterLogsListResponse) error { + entryViews := logEntryToView(response.Items().Slice()) + view := LogEntryResponseView{ + Items: entryViews, + Kind: "ClusterLogList", + Page: response.Page(), + Size: response.Size(), + Total: response.Total(), } - defer func() { - if err := ocmClient.Close(); err != nil { - fmt.Printf("Cannot close the ocmClient (possible memory leak): %q", err) - } - }() - // Use the OCM client to retrieve clusters - clusters := utils.GetClusters(ocmClient, []string{clusterID}) - if len(clusters) != 1 { - return nil, fmt.Errorf("GetClusters expected to return 1 cluster, got: %d", len(clusters)) + viewBytes, err := json.Marshal(view) + if err != nil { + return fmt.Errorf("failed to marshal response for output: %w", err) } - cluster := clusters[0] - // Now get the SLs for the cluster - return utils.SendRequest(CreateListSLRequest(ocmClient, cluster, serviceLogListAllMessagesFlag, serviceLogListInternalOnlyFlag)) + return dump.Pretty(os.Stdout, viewBytes) } -func init() { - // define required flags - listCmd.Flags().BoolVarP(&serviceLogListAllMessagesFlag, "all-messages", "A", serviceLogListAllMessagesFlag, "Toggle if we should see all of the messages or only SRE-P specific ones") - listCmd.Flags().BoolVarP(&serviceLogListInternalOnlyFlag, "internal", "i", serviceLogListInternalOnlyFlag, "Toggle if we should see internal messages") +type LogEntryResponseView struct { + Items []*LogEntryView `json:"items"` + Kind string `json:"kind"` + Page int `json:"page"` + Size int `json:"size"` + Total int `json:"total"` } -func CreateListSLRequest(ocmClient *sdk.Connection, cluster *cmv1.Cluster, allMessages bool, internalMessages bool) *sdk.Request { - // Create and populate the request: - request := ocmClient.Get() - err := arguments.ApplyPathArg(request, targetAPIPath) - if err != nil { - log.Fatalf("Can't parse API path '%s': %v\n", targetAPIPath, err) - } - var empty []string - - // prefer cluster external over cluster internal ID - var formatMessage string - if cluster.ExternalID() != "" { - formatMessage = fmt.Sprintf(`search=cluster_uuid = '%s'`, cluster.ExternalID()) - } else { - formatMessage = fmt.Sprintf(`search=cluster_id = '%s'`, cluster.ID()) - } +type LogEntryView struct { + ClusterID string `json:"cluster_id"` + ClusterUUID string `json:"cluster_uuid"` + CreatedAt time.Time `json:"created_at"` + CreatedBy string `json:"created_by"` + Description string `json:"description"` + EventStreamID string `json:"event_stream_id"` + Href string `json:"href"` + ID string `json:"id"` + InternalOnly bool `json:"internal_only"` + Kind string `json:"kind"` + LogType string `json:"log_type"` + ServiceName string `json:"service_name"` + Severity string `json:"severity"` + Summary string `json:"summary"` + Timestamp time.Time `json:"timestamp"` + Username string `json:"username"` +} - if !allMessages { - formatMessage += ` and service_name = 'SREManualAction'` - } - if internalMessages { - formatMessage += ` and internal_only = 'true'` +func logEntryToView(entries []*slv1.LogEntry) []*LogEntryView { + entryViews := make([]*LogEntryView, 0, len(entries)) + for _, entry := range entries { + entryView := &LogEntryView{ + ClusterID: entry.ClusterID(), + ClusterUUID: entry.ClusterUUID(), + CreatedAt: entry.CreatedAt(), + CreatedBy: entry.CreatedBy(), + Description: entry.Description(), + EventStreamID: entry.EventStreamID(), + Href: entry.HREF(), + ID: entry.ID(), + InternalOnly: entry.InternalOnly(), + Kind: entry.Kind(), + LogType: string(entry.LogType()), + ServiceName: entry.ServiceName(), + Severity: string(entry.Severity()), + Summary: entry.Summary(), + Timestamp: entry.Timestamp(), + Username: entry.Username(), + } + entryViews = append(entryViews, entryView) } - arguments.ApplyParameterFlag(request, []string{formatMessage}) - arguments.ApplyHeaderFlag(request, empty) - return request + return entryViews } diff --git a/go.mod b/go.mod index b7737c39..2975bc68 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.27.7 github.com/openshift-online/ocm-cli v0.1.66 - github.com/openshift-online/ocm-sdk-go v0.1.344 + github.com/openshift-online/ocm-sdk-go v0.1.372 github.com/openshift/api v0.0.0-20230320211411-560b2fb170af github.com/openshift/aws-account-operator/api v0.0.0-20230322125717-5b5a00a3e99f github.com/openshift/backplane-cli v0.1.5 diff --git a/go.sum b/go.sum index c3b7880b..3f7d9bd5 100644 --- a/go.sum +++ b/go.sum @@ -586,8 +586,8 @@ github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/openshift-online/ocm-cli v0.1.66 h1:+zBcNLVF4zv5ru0tSelIlzWBb35/dxwXRTKwO9MYNgE= github.com/openshift-online/ocm-cli v0.1.66/go.mod h1:lTq3/GbsStzceXeIijnMHbe80w1kDWqNnTvOQV43V+w= -github.com/openshift-online/ocm-sdk-go v0.1.344 h1:Dv2IhoQzR5ONwCoK2j8TYa3e3AXLvrdUgZa90Gpvovk= -github.com/openshift-online/ocm-sdk-go v0.1.344/go.mod h1:KYOw8kAKAHyPrJcQoVR82CneQ4ofC02Na4cXXaTq4Nw= +github.com/openshift-online/ocm-sdk-go v0.1.372 h1:2IyhIJASYglplD+ljyB+AyN7CggxfWPuMnY1q5F19bM= +github.com/openshift-online/ocm-sdk-go v0.1.372/go.mod h1:KYOw8kAKAHyPrJcQoVR82CneQ4ofC02Na4cXXaTq4Nw= github.com/openshift/api v0.0.0-20230320211411-560b2fb170af h1:js8+NVUCTK7cz+IO6i9MnKfKU14QYSMgOiJYvqhAMM0= github.com/openshift/api v0.0.0-20230320211411-560b2fb170af/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/openshift/aws-account-operator/api v0.0.0-20230322125717-5b5a00a3e99f h1:sfv+SJc6S9r2m+c8/7N9RwzHIXQEVkXDl1odNcizwis= diff --git a/internal/servicelog/reply.go b/internal/servicelog/reply.go index f1fc9d87..3461b12f 100644 --- a/internal/servicelog/reply.go +++ b/internal/servicelog/reply.go @@ -16,14 +16,6 @@ type GoodReply struct { CreatedAt time.Time `json:"created_at"` } -type ServiceLogShort struct { - Summary string `json:"summary"` - Description string `json:"description"` - CreatedAt time.Time `json:"created_at"` - Severity string `json:"severity"` - InternalOnly bool `json:"internal_only"` -} - type ClusterListGoodReply struct { Kind string `json:"kind"` Page int `json:"page"` @@ -32,14 +24,6 @@ type ClusterListGoodReply struct { Items []GoodReply `json:"items"` } -type ServiceLogShortList struct { - Kind string `json:"kind"` - Page int `json:"page"` - Size int `json:"size"` - Total int `json:"total"` - Items []ServiceLogShort `json:"items"` -} - type BadReply struct { ID string `json:"id"` Kind string `json:"kind"` diff --git a/pkg/utils/print.go b/pkg/utils/print.go index 986ccdc4..af845919 100644 --- a/pkg/utils/print.go +++ b/pkg/utils/print.go @@ -7,7 +7,7 @@ import ( "github.com/andygrunwald/go-jira" "github.com/openshift-online/ocm-cli/pkg/dump" cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1" - sl "github.com/openshift/osdctl/internal/servicelog" + v1 "github.com/openshift-online/ocm-sdk-go/servicelogs/v1" "github.com/openshift/osdctl/pkg/printer" "math" "os" @@ -19,7 +19,7 @@ const ( delimiter = ">> " ) -func PrintServiceLogs(serviceLogs []sl.ServiceLogShort, verbose bool, sinceDays int) { +func PrintServiceLogs(serviceLogs []*v1.LogEntry, verbose bool, sinceDays int) { var name = fmt.Sprintf("Service Logs in the past %v days", sinceDays) fmt.Println(delimiter + name) @@ -35,19 +35,19 @@ func PrintServiceLogs(serviceLogs []sl.ServiceLogShort, verbose bool, sinceDays // Non-verbose only prints the summaries for i, errorServiceLog := range serviceLogs { var serviceLogSummary string - if errorServiceLog.InternalOnly { - internalServiceLogLines := strings.Split(errorServiceLog.Description, "\n") + if errorServiceLog.InternalOnly() { + internalServiceLogLines := strings.Split(errorServiceLog.Description(), "\n") if len(internalServiceLogLines) > 0 { // if the description is "", Split returns [] serviceLogSummary = fmt.Sprintf("INT %s", internalServiceLogLines[0]) } else { - serviceLogSummary = errorServiceLog.Summary + serviceLogSummary = errorServiceLog.Summary() } } else { - serviceLogSummary = errorServiceLog.Summary + serviceLogSummary = errorServiceLog.Summary() } serviceLogSummaryAbbreviated := serviceLogSummary[:int(math.Min(40, float64(len(serviceLogSummary))))] - fmt.Printf("%d. %s (%s)\n", i, serviceLogSummaryAbbreviated, errorServiceLog.CreatedAt.Format(time.RFC3339)) + fmt.Printf("%d. %s (%s)\n", i, serviceLogSummaryAbbreviated, errorServiceLog.CreatedAt().Format(time.RFC3339)) } } }