Files
mostlymatter/cmd/mattermost/commands/export.go
Scott Bishel 0302e0f477 MM-27449: Compliance Jobs rerun after warning status (#15178)
* add warning as success result

* revert config changes

* add unit test, update unit test

* add a couple more tests

* update store layers

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2020-08-17 21:18:33 -06:00

234 строки
7.7 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"fmt"
"os"
"time"
"github.com/mattermost/mattermost-server/v5/audit"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var ExportCmd = &cobra.Command{
Use: "export",
Short: "Export data from Mattermost",
Long: "Export data from Mattermost in a format suitable for import into a third-party application or another Mattermost instance",
}
var ScheduleExportCmd = &cobra.Command{
Use: "schedule",
Short: "Schedule an export data job in Mattermost",
Long: "Schedule an export data job in Mattermost (this will run asynchronously via a background worker)",
Example: "export schedule --format=actiance --exportFrom=12345 --timeoutSeconds=12345",
RunE: scheduleExportCmdF,
}
var CsvExportCmd = &cobra.Command{
Use: "csv",
Short: "Export data from Mattermost in CSV format",
Long: "Export data from Mattermost in CSV format",
Example: "export csv --exportFrom=12345",
RunE: buildExportCmdF("csv"),
}
var ActianceExportCmd = &cobra.Command{
Use: "actiance",
Short: "Export data from Mattermost in Actiance format",
Long: "Export data from Mattermost in Actiance format",
Example: "export actiance --exportFrom=12345",
RunE: buildExportCmdF("actiance"),
}
var GlobalRelayZipExportCmd = &cobra.Command{
Use: "global-relay-zip",
Short: "Export data from Mattermost into a zip file containing emails to send to Global Relay for debug and testing purposes only.",
Long: "Export data from Mattermost into a zip file containing emails to send to Global Relay for debug and testing purposes only. This does not archive any information in Global Relay.",
Example: "export global-relay-zip --exportFrom=12345",
RunE: buildExportCmdF("globalrelay-zip"),
}
var BulkExportCmd = &cobra.Command{
Use: "bulk [file]",
Short: "Export bulk data.",
Long: "Export data to a file compatible with the Mattermost Bulk Import format.",
Example: "export bulk bulk_data.json",
RunE: bulkExportCmdF,
Args: cobra.ExactArgs(1),
}
func init() {
ScheduleExportCmd.Flags().String("format", "actiance", "The format to export data")
ScheduleExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
ScheduleExportCmd.Flags().Int("timeoutSeconds", -1, "The maximum number of seconds to wait for the job to complete before timing out.")
CsvExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
ActianceExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
GlobalRelayZipExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
BulkExportCmd.Flags().Bool("all-teams", true, "Export all teams from the server.")
ExportCmd.AddCommand(ScheduleExportCmd)
ExportCmd.AddCommand(CsvExportCmd)
ExportCmd.AddCommand(ActianceExportCmd)
ExportCmd.AddCommand(GlobalRelayZipExportCmd)
ExportCmd.AddCommand(BulkExportCmd)
RootCmd.AddCommand(ExportCmd)
}
func scheduleExportCmdF(command *cobra.Command, args []string) error {
a, err := InitDBCommandContextCobra(command)
if err != nil {
return err
}
defer a.Srv().Shutdown()
if !*a.Config().MessageExportSettings.EnableExport {
return errors.New("ERROR: The message export feature is not enabled")
}
// for now, format is hard-coded to actiance. In time, we'll have to support other formats and inject them into job data
format, err := command.Flags().GetString("format")
if err != nil {
return errors.New("format flag error")
}
if format != "actiance" {
return errors.New("unsupported export format")
}
startTime, err := command.Flags().GetInt64("exportFrom")
if err != nil {
return errors.New("exportFrom flag error")
}
if startTime < 0 {
return errors.New("exportFrom must be a positive integer")
}
timeoutSeconds, err := command.Flags().GetInt("timeoutSeconds")
if err != nil {
return errors.New("timeoutSeconds error")
}
if timeoutSeconds < 0 {
return errors.New("timeoutSeconds must be a positive integer")
}
if messageExportI := a.MessageExport(); messageExportI != nil {
ctx := context.Background()
if timeoutSeconds > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(timeoutSeconds))
defer cancel()
}
job, err := messageExportI.StartSynchronizeJob(ctx, startTime)
if err != nil || job.Status == model.JOB_STATUS_ERROR || job.Status == model.JOB_STATUS_CANCELED {
CommandPrintErrorln("ERROR: Message export job failed. Please check the server logs")
} else {
CommandPrettyPrintln("SUCCESS: Message export job complete")
auditRec := a.MakeAuditRecord("scheduleExport", audit.Success)
auditRec.AddMeta("format", format)
auditRec.AddMeta("start", startTime)
a.LogAuditRec(auditRec, nil)
}
}
return nil
}
func buildExportCmdF(format string) func(command *cobra.Command, args []string) error {
return func(command *cobra.Command, args []string) error {
a, err := InitDBCommandContextCobra(command)
license := a.Srv().License()
if err != nil {
return err
}
defer a.Srv().Shutdown()
startTime, err := command.Flags().GetInt64("exportFrom")
if err != nil {
return errors.New("exportFrom flag error")
}
if startTime < 0 {
return errors.New("exportFrom must be a positive integer")
}
if a.MessageExport() == nil || license == nil || !*license.Features.MessageExport {
return errors.New("message export feature not available")
}
warningsCount, appErr := a.MessageExport().RunExport(format, startTime)
if appErr != nil {
return appErr
}
if warningsCount == 0 {
CommandPrettyPrintln("SUCCESS: Your data was exported.")
} else {
if format == model.COMPLIANCE_EXPORT_TYPE_GLOBALRELAY || format == model.COMPLIANCE_EXPORT_TYPE_GLOBALRELAY_ZIP {
CommandPrettyPrintln(fmt.Sprintf("WARNING: %d warnings encountered, see logs for details.", warningsCount))
} else {
CommandPrettyPrintln(fmt.Sprintf("WARNING: %d warnings encountered, see warning.txt for details.", warningsCount))
}
}
auditRec := a.MakeAuditRecord("buildExport", audit.Success)
auditRec.AddMeta("format", format)
auditRec.AddMeta("start", startTime)
a.LogAuditRec(auditRec, nil)
return nil
}
}
func bulkExportCmdF(command *cobra.Command, args []string) error {
a, err := InitDBCommandContextCobra(command)
if err != nil {
return err
}
defer a.Srv().Shutdown()
allTeams, err := command.Flags().GetBool("all-teams")
if err != nil {
return errors.Wrap(err, "all-teams flag error")
}
if !allTeams {
return errors.New("Nothing to export. Please specify the --all-teams flag to export all teams.")
}
fileWriter, err := os.Create(args[0])
if err != nil {
return err
}
defer fileWriter.Close()
// Path to directory of custom emoji
pathToEmojiDir := "data/emoji/"
customDataDir := a.Config().FileSettings.Directory
if customDataDir != nil && *customDataDir != "" {
pathToEmojiDir = *customDataDir + "emoji/"
}
// Name of the directory to export custom emoji
dirNameToExportEmoji := "exported_emoji"
// args[0] points to the filename/filepath passed with export bulk command
if err := a.BulkExport(fileWriter, args[0], pathToEmojiDir, dirNameToExportEmoji); err != nil {
CommandPrintErrorln(err.Error())
return err
}
auditRec := a.MakeAuditRecord("bulkExport", audit.Success)
auditRec.AddMeta("all_teams", allTeams)
auditRec.AddMeta("file", args[0])
a.LogAuditRec(auditRec, nil)
return nil
}