snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
243
server/cmd/mmctl/commands/compliance_export.go
Normal file
243
server/cmd/mmctl/commands/compliance_export.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
||||
"github.com/mattermost/mattermost/server/v8/enterprise/message_export/shared"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var ComplianceExportCmd = &cobra.Command{
|
||||
Use: "compliance-export",
|
||||
Short: "Management of compliance exports",
|
||||
}
|
||||
|
||||
var ComplianceExportListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List compliance export jobs, sorted by creation date descending (newest first)",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: withClient(complianceExportListCmdF),
|
||||
}
|
||||
|
||||
var ComplianceExportShowCmd = &cobra.Command{
|
||||
Use: "show [complianceExportJobID]",
|
||||
Example: "compliance-export show o98rj3ur83dp5dppfyk5yk6osy",
|
||||
Short: "Show compliance export job",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: withClient(complianceExportShowCmdF),
|
||||
}
|
||||
|
||||
var ComplianceExportCancelCmd = &cobra.Command{
|
||||
Use: "cancel [complianceExportJobID]",
|
||||
Example: "compliance-export cancel o98rj3ur83dp5dppfyk5yk6osy",
|
||||
Short: "Cancel compliance export job",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: withClient(complianceExportCancelCmdF),
|
||||
}
|
||||
|
||||
var ComplianceExportDownloadCmd = &cobra.Command{
|
||||
Use: "download [complianceExportJobID] [output filepath (optional)]",
|
||||
Example: "compliance-export download o98rj3ur83dp5dppfyk5yk6osy",
|
||||
Short: "Download compliance export file",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: withClient(complianceExportDownloadCmdF),
|
||||
}
|
||||
|
||||
var ComplianceExportCreateCmd = &cobra.Command{
|
||||
Use: "create [complianceExportType] --date \"2025-03-27 -0400\"",
|
||||
Example: "compliance-export create csv --date \"2025-03-27 -0400\"",
|
||||
Long: "Create a compliance export job, of type 'csv' or 'actiance' or 'globalrelay'. If --date is set, the job will run for one day, from 12am to 12am (minus one millisecond) inclusively, in the format with timezone offset: `\"YYYY-MM-DD -0000\"`. E.g., \"2024-10-21 -0400\" for Oct 21, 2024 EDT timezone. \"2023-11-01 +0000\" for Nov 01, 2024 UTC. If set, the 'start' and 'end' flags will be ignored.\n\n" +
|
||||
"Important: Running a compliance export job from mmctl will NOT affect the next scheduled job's batch_start_time. This means that if you run a compliance export job from mmctl, the next scheduled job will run from the batch_end_time of the previous scheduled job, as usual.",
|
||||
Short: "Create a compliance export job, of type 'csv' or 'actiance' or 'globalrelay'",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: withClient(complianceExportCreateCmdF),
|
||||
}
|
||||
|
||||
func init() {
|
||||
ComplianceExportListCmd.Flags().Int("page", 0, "Page number to fetch for the list of compliance export jobs")
|
||||
ComplianceExportListCmd.Flags().Int("per-page", DefaultPageSize, "Number of compliance export jobs to be fetched")
|
||||
ComplianceExportListCmd.Flags().Bool("all", false, "Fetch all compliance export jobs. --page flag will be ignored if provided")
|
||||
|
||||
ComplianceExportDownloadCmd.Flags().Int("num-retries", 5, "Number of retries if the download fails")
|
||||
|
||||
ComplianceExportCreateCmd.Flags().String(
|
||||
"date",
|
||||
"",
|
||||
"Run the export for one day, from 12am to 12am (minus one millisecond) inclusively, in the format with timezone offset: `\"YYYY-MM-DD -0000\"`. E.g., `\"2024-10-21 -0400\"` for Oct 21, 2024 EDT timezone. `\"2023-11-01 +0000\"` for Nov 01, 2024 UTC. If set, the 'start' and 'end' flags will be ignored.",
|
||||
)
|
||||
ComplianceExportCreateCmd.Flags().Int(
|
||||
"start",
|
||||
0,
|
||||
"The start timestamp in unix milliseconds. Posts with updateAt >= start will be exported. If set, 'end' must be set as well. eg, `1743048000000` for 2025-03-27 EDT.",
|
||||
)
|
||||
ComplianceExportCreateCmd.Flags().Int(
|
||||
"end",
|
||||
0,
|
||||
"The end timestamp in unix milliseconds. Posts with updateAt <= end will be exported. If set, 'start' must be set as well. eg, `1743134400000` for 2025-03-28 EDT.",
|
||||
)
|
||||
|
||||
ComplianceExportCmd.AddCommand(
|
||||
ComplianceExportListCmd,
|
||||
ComplianceExportShowCmd,
|
||||
ComplianceExportCancelCmd,
|
||||
ComplianceExportDownloadCmd,
|
||||
ComplianceExportCreateCmd,
|
||||
)
|
||||
RootCmd.AddCommand(ComplianceExportCmd)
|
||||
}
|
||||
|
||||
func complianceExportListCmdF(c client.Client, command *cobra.Command, args []string) error {
|
||||
return jobListCmdF(c, command, "message_export", "")
|
||||
}
|
||||
|
||||
func complianceExportShowCmdF(c client.Client, command *cobra.Command, args []string) error {
|
||||
job, _, err := c.GetJob(context.TODO(), args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get compliance export job: %w", err)
|
||||
}
|
||||
|
||||
printJob(job)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func complianceExportCancelCmdF(c client.Client, command *cobra.Command, args []string) error {
|
||||
if _, err := c.CancelJob(context.TODO(), args[0]); err != nil {
|
||||
return fmt.Errorf("failed to cancel compliance export job: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func complianceExportDownloadCmdF(c client.Client, command *cobra.Command, args []string) error {
|
||||
jobID := args[0]
|
||||
var path string
|
||||
if len(args) > 1 {
|
||||
path = args[1]
|
||||
} else {
|
||||
path = jobID + ".zip"
|
||||
}
|
||||
|
||||
retries, _ := command.Flags().GetInt("num-retries")
|
||||
|
||||
downloadFn := func(outFile *os.File) (string, error) {
|
||||
return c.DownloadComplianceExport(context.TODO(), jobID, outFile)
|
||||
}
|
||||
|
||||
suggestedFilename, err := downloadFile(path, downloadFn, retries, "compliance export")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we didn't provide a path and got a suggested filename, rename the file
|
||||
if len(args) == 1 && suggestedFilename != "" && suggestedFilename != path {
|
||||
// If the suggested name already exists, don't overwrite
|
||||
if _, err := os.Stat(suggestedFilename); err == nil {
|
||||
printer.PrintWarning(fmt.Sprintf("File with the server's suggested name %q already exists, keeping %q", suggestedFilename, path))
|
||||
} else {
|
||||
if err := os.Rename(path, suggestedFilename); err != nil {
|
||||
printer.PrintWarning(fmt.Sprintf("Could not rename file to the server's suggested name %q: %v", suggestedFilename, err))
|
||||
} else {
|
||||
path = suggestedFilename
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printer.Print(fmt.Sprintf("Compliance export file downloaded to %q", path))
|
||||
return nil
|
||||
}
|
||||
|
||||
func complianceExportCreateCmdF(c client.Client, command *cobra.Command, args []string) error {
|
||||
exportType := args[0]
|
||||
if exportType != model.ComplianceExportTypeActiance &&
|
||||
exportType != model.ComplianceExportTypeCsv &&
|
||||
exportType != model.ComplianceExportTypeGlobalrelay {
|
||||
return fmt.Errorf("invalid export type: %s, must be one of: csv, actiance, globalrelay", exportType)
|
||||
}
|
||||
|
||||
dateStr, err := command.Flags().GetString("date")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
start, err := command.Flags().GetInt("start")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
end, err := command.Flags().GetInt("end")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startTimestamp, endTimestamp, err := getStartAndEnd(dateStr, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startTime := strconv.FormatInt(startTimestamp, 10)
|
||||
endTime := strconv.FormatInt(endTimestamp, 10)
|
||||
exportDir := path.Join(model.ComplianceExportPath, fmt.Sprintf("%s-%s-%s", time.Now().Format(model.ComplianceExportDirectoryFormat), startTime, endTime))
|
||||
|
||||
// If start and end are 0, we need to not set those keys in the job data.
|
||||
// This will make the job like a manual job (it will pick up where the previous job left off).
|
||||
data := model.StringMap{
|
||||
shared.JobDataInitiatedBy: "mmctl",
|
||||
shared.JobDataExportType: exportType,
|
||||
shared.JobDataBatchStartId: "",
|
||||
shared.JobDataJobStartId: "",
|
||||
}
|
||||
if startTimestamp != 0 && endTimestamp != 0 {
|
||||
data[shared.JobDataBatchStartTime] = startTime
|
||||
data[shared.JobDataJobStartTime] = startTime
|
||||
data[shared.JobDataJobEndTime] = endTime
|
||||
data[shared.JobDataExportDir] = exportDir
|
||||
}
|
||||
|
||||
job := &model.Job{
|
||||
Type: model.JobTypeMessageExport,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
if job, _, err = c.CreateJob(context.TODO(), job); err != nil {
|
||||
return fmt.Errorf("failed to create compliance export job: %w", err)
|
||||
}
|
||||
|
||||
printer.Print(fmt.Sprintf("Compliance export job created with ID: %s", job.Id))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getStartAndEnd returns the start and end timestamps in unix milliseconds
|
||||
func getStartAndEnd(dateStr string, start int, end int) (int64, int64, error) {
|
||||
if dateStr == "" && start == 0 && end == 0 {
|
||||
// return 0 so that the job will be like a manual job
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
if dateStr != "" && (start > 0 || end > 0) {
|
||||
return 0, 0, errors.New("if date is used, start and end must not be set")
|
||||
}
|
||||
|
||||
if dateStr != "" {
|
||||
t, err := time.Parse("2006-01-02 -0700", dateStr)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("could not parse date string: %s, use the format with time zone offset: YYYY-MM-DD -0700, eg for EDT: `2024-12-24 -0400`, error details: %w", dateStr, err)
|
||||
}
|
||||
endTimestamp := t.AddDate(0, 0, 1).UnixMilli() - 1
|
||||
return t.UnixMilli(), endTimestamp, nil
|
||||
}
|
||||
if start <= 0 || end <= 0 || start >= end {
|
||||
return 0, 0, fmt.Errorf("if date is not used, start: %d and end: %d must both be > 0, and start must be < end", start, end)
|
||||
}
|
||||
return int64(start), int64(end), nil
|
||||
}
|
||||
Reference in New Issue
Block a user