mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-54852] Add mmctl command to download Support Packet (#25419)
* Add mmctl command to download Support Packet
* Simplify file name
* Revert "Simplify file name"
This reverts commit 17084a3350
.
* Fix docs
This commit is contained in:
parent
88520e6740
commit
5e94af1302
@ -148,4 +148,5 @@ type Client interface {
|
|||||||
DownloadExport(ctx context.Context, name string, wr io.Writer, offset int64) (int64, *model.Response, error)
|
DownloadExport(ctx context.Context, name string, wr io.Writer, offset int64) (int64, *model.Response, error)
|
||||||
GeneratePresignedURL(ctx context.Context, name string) (*model.PresignURLResponse, *model.Response, error)
|
GeneratePresignedURL(ctx context.Context, name string) (*model.PresignURLResponse, *model.Response, error)
|
||||||
ResetSamlAuthDataToEmail(ctx context.Context, includeDeleted bool, dryRun bool, userIDs []string) (int64, *model.Response, error)
|
ResetSamlAuthDataToEmail(ctx context.Context, includeDeleted bool, dryRun bool, userIDs []string) (int64, *model.Response, error)
|
||||||
|
GenerateSupportPacket(ctx context.Context) ([]byte, *model.Response, error)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -65,15 +67,28 @@ var SystemStatusCmd = &cobra.Command{
|
|||||||
RunE: withClient(systemStatusCmdF),
|
RunE: withClient(systemStatusCmdF),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var SystemSupportPacketCmd = &cobra.Command{
|
||||||
|
Use: "supportpacket",
|
||||||
|
Short: "Download a Support Packet",
|
||||||
|
Long: "Generate and download a Support Packet of the server to share it with Mattermost Support",
|
||||||
|
Example: ` system supportpacket`,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
RunE: withClient(systemSupportPacketCmdF),
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
SystemSetBusyCmd.Flags().UintP("seconds", "s", 3600, "Number of seconds until server is automatically marked as not busy.")
|
SystemSetBusyCmd.Flags().UintP("seconds", "s", 3600, "Number of seconds until server is automatically marked as not busy.")
|
||||||
_ = SystemSetBusyCmd.MarkFlagRequired("seconds")
|
_ = SystemSetBusyCmd.MarkFlagRequired("seconds")
|
||||||
|
|
||||||
|
SystemSupportPacketCmd.Flags().StringP("output-file", "o", "", "Output file name (default \"mattermost_support_packet_YYYY-MM-DD-HH-MM.zip\")")
|
||||||
|
|
||||||
SystemCmd.AddCommand(
|
SystemCmd.AddCommand(
|
||||||
SystemGetBusyCmd,
|
SystemGetBusyCmd,
|
||||||
SystemSetBusyCmd,
|
SystemSetBusyCmd,
|
||||||
SystemClearBusyCmd,
|
SystemClearBusyCmd,
|
||||||
SystemVersionCmd,
|
SystemVersionCmd,
|
||||||
SystemStatusCmd,
|
SystemStatusCmd,
|
||||||
|
SystemSupportPacketCmd,
|
||||||
)
|
)
|
||||||
RootCmd.AddCommand(SystemCmd)
|
RootCmd.AddCommand(SystemCmd)
|
||||||
}
|
}
|
||||||
@ -152,3 +167,36 @@ Filestore Status: {{.filestore_status}}`, status)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemSupportPacketCmdF(c client.Client, cmd *cobra.Command, _ []string) error {
|
||||||
|
printer.SetSingle(true)
|
||||||
|
|
||||||
|
filename, err := cmd.Flags().GetString("output-file")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filename == "" {
|
||||||
|
filename = fmt.Sprintf("mattermost_support_packet_%s.zip", time.Now().Format("2006-01-02-03-04"))
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.Print("Downloading Support Packet")
|
||||||
|
|
||||||
|
data, _, err := c.GenerateSupportPacket(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to fetch Support Packet: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create zip file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write to zip file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.PrintT("Downloaded Support Packet to {{ .filename }}", map[string]string{"filename": filename})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
|
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
|
||||||
@ -103,3 +105,61 @@ func (s *MmctlE2ETestSuite) TestClearBusyCmd() {
|
|||||||
s.Require().False(s.th.App.Srv().Platform().Busy.IsBusy())
|
s.Require().False(s.th.App.Srv().Platform().Busy.IsBusy())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MmctlE2ETestSuite) TestSupportPacketCmdF() {
|
||||||
|
s.SetupEnterpriseTestHelper().InitBasic()
|
||||||
|
|
||||||
|
printer.SetFormat(printer.FormatPlain)
|
||||||
|
s.T().Cleanup(func() { printer.SetFormat(printer.FormatJSON) })
|
||||||
|
|
||||||
|
s.Run("Download support packet with default filename", func() {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
s.T().Cleanup(cleanupSupportPacket(s.T()))
|
||||||
|
|
||||||
|
err := systemSupportPacketCmdF(s.th.SystemAdminClient, SystemSupportPacketCmd, []string{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(printer.GetLines(), 2)
|
||||||
|
s.Require().Equal(printer.GetLines()[0], "Downloading Support Packet")
|
||||||
|
s.Require().Contains(printer.GetLines()[1], "Downloaded Support Packet to ")
|
||||||
|
s.Require().Len(printer.GetErrorLines(), 0)
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(".")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
for _, e := range entries {
|
||||||
|
if strings.HasPrefix(e.Name(), "mattermost_support_packet_") && strings.HasSuffix(e.Name(), ".zip") {
|
||||||
|
b, err := os.ReadFile(e.Name())
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
s.NotEmpty(b, b)
|
||||||
|
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.True(found)
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("Download support packet with custom filename", func() {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
err := SystemSupportPacketCmd.ParseFlags([]string{"-o", "foo.zip"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.T().Cleanup(func() {
|
||||||
|
s.Require().NoError(os.Remove("foo.zip"))
|
||||||
|
})
|
||||||
|
|
||||||
|
err = systemSupportPacketCmdF(s.th.SystemAdminClient, SystemSupportPacketCmd, []string{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(printer.GetErrorLines(), 0)
|
||||||
|
s.Require().Len(printer.GetLines(), 2)
|
||||||
|
s.Require().Equal(printer.GetLines()[0], "Downloading Support Packet")
|
||||||
|
s.Require().Equal(printer.GetLines()[1], "Downloaded Support Packet to foo.zip")
|
||||||
|
|
||||||
|
b, err := os.ReadFile("foo.zip")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.NotNil(b, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -6,15 +6,19 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *MmctlUnitTestSuite) TestGetBusyCmd() {
|
func (s *MmctlUnitTestSuite) TestGetBusyCmd() {
|
||||||
@ -210,3 +214,102 @@ func (s *MmctlUnitTestSuite) TestServerStatusCmd() {
|
|||||||
s.Require().Len(printer.GetLines(), 0)
|
s.Require().Len(printer.GetLines(), 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupSupportPacket(t *testing.T) func() {
|
||||||
|
return func() {
|
||||||
|
entries, err := os.ReadDir(".")
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, e := range entries {
|
||||||
|
if strings.HasPrefix(e.Name(), "mattermost_support_packet_") && strings.HasSuffix(e.Name(), ".zip") {
|
||||||
|
err = os.Remove(e.Name())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MmctlUnitTestSuite) TestSupportPacketCmdF() {
|
||||||
|
printer.SetFormat(printer.FormatPlain)
|
||||||
|
s.T().Cleanup(func() { printer.SetFormat(printer.FormatJSON) })
|
||||||
|
|
||||||
|
s.Run("Download support packet with default filename", func() {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
s.T().Cleanup(cleanupSupportPacket(s.T()))
|
||||||
|
|
||||||
|
data := []byte("some bytes")
|
||||||
|
s.client.
|
||||||
|
EXPECT().
|
||||||
|
GenerateSupportPacket(context.TODO()).
|
||||||
|
Return(data, &model.Response{}, nil).
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
err := systemSupportPacketCmdF(s.client, SystemSupportPacketCmd, []string{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(printer.GetErrorLines(), 0)
|
||||||
|
s.Require().Len(printer.GetLines(), 2)
|
||||||
|
s.Require().Equal(printer.GetLines()[0], "Downloading Support Packet")
|
||||||
|
s.Require().Contains(printer.GetLines()[1], "Downloaded Support Packet to ")
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(".")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
for _, e := range entries {
|
||||||
|
if strings.HasPrefix(e.Name(), "mattermost_support_packet_") && strings.HasSuffix(e.Name(), ".zip") {
|
||||||
|
b, err := os.ReadFile(e.Name())
|
||||||
|
s.NoError(err)
|
||||||
|
s.Equal(b, data)
|
||||||
|
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.True(found)
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("Download support packet with custom filename", func() {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
data := []byte("some bytes")
|
||||||
|
s.client.
|
||||||
|
EXPECT().
|
||||||
|
GenerateSupportPacket(context.TODO()).
|
||||||
|
Return(data, &model.Response{}, nil).
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
err := SystemSupportPacketCmd.ParseFlags([]string{"-o", "foo.zip"})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.T().Cleanup(func() {
|
||||||
|
s.Require().NoError(os.Remove("foo.zip"))
|
||||||
|
})
|
||||||
|
|
||||||
|
err = systemSupportPacketCmdF(s.client, SystemSupportPacketCmd, []string{})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(printer.GetErrorLines(), 0)
|
||||||
|
s.Require().Len(printer.GetLines(), 2)
|
||||||
|
s.Require().Equal(printer.GetLines()[0], "Downloading Support Packet")
|
||||||
|
s.Require().Equal(printer.GetLines()[1], "Downloaded Support Packet to foo.zip")
|
||||||
|
|
||||||
|
b, err := os.ReadFile("foo.zip")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Equal(b, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
s.Run("Request to the server fails", func() {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
s.client.
|
||||||
|
EXPECT().
|
||||||
|
GenerateSupportPacket(context.TODO()).
|
||||||
|
Return(nil, &model.Response{}, errors.New("mock error")).
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
err := systemSupportPacketCmdF(s.client, SystemSupportPacketCmd, []string{})
|
||||||
|
s.Require().Error(err)
|
||||||
|
s.Require().Len(printer.GetErrorLines(), 0)
|
||||||
|
s.Require().Len(printer.GetLines(), 1)
|
||||||
|
s.Require().Equal(printer.GetLines()[0], "Downloading Support Packet")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -41,5 +41,6 @@ SEE ALSO
|
|||||||
* `mmctl system getbusy <mmctl_system_getbusy.rst>`_ - Get the current busy state
|
* `mmctl system getbusy <mmctl_system_getbusy.rst>`_ - Get the current busy state
|
||||||
* `mmctl system setbusy <mmctl_system_setbusy.rst>`_ - Set the busy state to true
|
* `mmctl system setbusy <mmctl_system_setbusy.rst>`_ - Set the busy state to true
|
||||||
* `mmctl system status <mmctl_system_status.rst>`_ - Prints the status of the server
|
* `mmctl system status <mmctl_system_status.rst>`_ - Prints the status of the server
|
||||||
|
* `mmctl system supportpacket <mmctl_system_supportpacket.rst>`_ - Download a Support Packet
|
||||||
* `mmctl system version <mmctl_system_version.rst>`_ - Prints the remote server version
|
* `mmctl system version <mmctl_system_version.rst>`_ - Prints the remote server version
|
||||||
|
|
||||||
|
52
server/cmd/mmctl/docs/mmctl_system_supportpacket.rst
Normal file
52
server/cmd/mmctl/docs/mmctl_system_supportpacket.rst
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
.. _mmctl_system_supportpacket:
|
||||||
|
|
||||||
|
mmctl system supportpacket
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Download a Support Packet
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
Generate and download a Support Packet of the server to share it with Mattermost Support
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
mmctl system supportpacket [flags]
|
||||||
|
|
||||||
|
Examples
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
system supportpacket
|
||||||
|
|
||||||
|
Options
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
-h, --help help for supportpacket
|
||||||
|
-o, --output-file string Output file name (default "mattermost_support_packet_YYYY-MM-DD-HH-MM.zip")
|
||||||
|
|
||||||
|
Options inherited from parent commands
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
--config string path to the configuration file (default "$XDG_CONFIG_HOME/mmctl/config")
|
||||||
|
--disable-pager disables paged output
|
||||||
|
--insecure-sha1-intermediate allows to use insecure TLS protocols, such as SHA-1
|
||||||
|
--insecure-tls-version allows to use TLS versions 1.0 and 1.1
|
||||||
|
--json the output format will be in json format
|
||||||
|
--local allows communicating with the server through a unix socket
|
||||||
|
--quiet prevent mmctl to generate output for the commands
|
||||||
|
--strict will only run commands if the mmctl version matches the server one
|
||||||
|
--suppress-warnings disables printing warning messages
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
* `mmctl system <mmctl_system.rst>`_ - System management
|
||||||
|
|
@ -541,6 +541,22 @@ func (mr *MockClientMockRecorder) GeneratePresignedURL(arg0, arg1 interface{}) *
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GeneratePresignedURL", reflect.TypeOf((*MockClient)(nil).GeneratePresignedURL), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GeneratePresignedURL", reflect.TypeOf((*MockClient)(nil).GeneratePresignedURL), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateSupportPacket mocks base method.
|
||||||
|
func (m *MockClient) GenerateSupportPacket(arg0 context.Context) ([]byte, *model.Response, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GenerateSupportPacket", arg0)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(*model.Response)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateSupportPacket indicates an expected call of GenerateSupportPacket.
|
||||||
|
func (mr *MockClientMockRecorder) GenerateSupportPacket(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateSupportPacket", reflect.TypeOf((*MockClient)(nil).GenerateSupportPacket), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllTeams mocks base method.
|
// GetAllTeams mocks base method.
|
||||||
func (m *MockClient) GetAllTeams(arg0 context.Context, arg1 string, arg2, arg3 int) ([]*model.Team, *model.Response, error) {
|
func (m *MockClient) GetAllTeams(arg0 context.Context, arg1 string, arg2, arg3 int) ([]*model.Team, *model.Response, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
Loading…
Reference in New Issue
Block a user