diff --git a/api/descriptor.bin b/api/descriptor.bin index 2af5e75263c..e6442c35500 100644 Binary files a/api/descriptor.bin and b/api/descriptor.bin differ diff --git a/api/dump/v1beta1/dump.pb.go b/api/dump/v1beta1/dump.pb.go index 0edf763eb8c..e2c33114098 100644 --- a/api/dump/v1beta1/dump.pb.go +++ b/api/dump/v1beta1/dump.pb.go @@ -81,13 +81,15 @@ func (DumpStatus) EnumDescriptor() ([]byte, []int) { } type Dump struct { - state protoimpl.MessageState `protogen:"open.v1"` - DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` - Status DumpStatus `protobuf:"varint,2,opt,name=status,proto3,enum=dump.v1beta1.DumpStatus" json:"status,omitempty"` - ServiceNames []string `protobuf:"bytes,3,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` + Status DumpStatus `protobuf:"varint,2,opt,name=status,proto3,enum=dump.v1beta1.DumpStatus" json:"status,omitempty"` + ServiceNames []string `protobuf:"bytes,3,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // This field is set to true if the dump was created with encryption enabled, and false otherwise. + Encrypted bool `protobuf:"varint,8,opt,name=encrypted,proto3" json:"encrypted,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -164,15 +166,26 @@ func (x *Dump) GetCreatedAt() *timestamppb.Timestamp { return nil } +func (x *Dump) GetEncrypted() bool { + if x != nil { + return x.Encrypted + } + return false +} + type StartDumpRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - ServiceNames []string `protobuf:"bytes,1,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` - StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` - EndTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` - ExportQan bool `protobuf:"varint,4,opt,name=export_qan,json=exportQan,proto3" json:"export_qan,omitempty"` - IgnoreLoad bool `protobuf:"varint,5,opt,name=ignore_load,json=ignoreLoad,proto3" json:"ignore_load,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + ServiceNames []string `protobuf:"bytes,1,rep,name=service_names,json=serviceNames,proto3" json:"service_names,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + ExportQan bool `protobuf:"varint,4,opt,name=export_qan,json=exportQan,proto3" json:"export_qan,omitempty"` + IgnoreLoad bool `protobuf:"varint,5,opt,name=ignore_load,json=ignoreLoad,proto3" json:"ignore_load,omitempty"` + // If true, the dump will be encrypted. Note that enabling encryption may increase the time required to create the dump. + EnableEncryption bool `protobuf:"varint,6,opt,name=enable_encryption,json=enableEncryption,proto3" json:"enable_encryption,omitempty"` + // The password used for encryption. It must be at least 8 characters long. This field is required if enable_encryption is true. + EncryptionPassword string `protobuf:"bytes,7,opt,name=encryption_password,json=encryptionPassword,proto3" json:"encryption_password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StartDumpRequest) Reset() { @@ -240,6 +253,20 @@ func (x *StartDumpRequest) GetIgnoreLoad() bool { return false } +func (x *StartDumpRequest) GetEnableEncryption() bool { + if x != nil { + return x.EnableEncryption + } + return false +} + +func (x *StartDumpRequest) GetEncryptionPassword() string { + if x != nil { + return x.EncryptionPassword + } + return "" +} + type StartDumpResponse struct { state protoimpl.MessageState `protogen:"open.v1"` DumpId string `protobuf:"bytes,1,opt,name=dump_id,json=dumpId,proto3" json:"dump_id,omitempty"` @@ -770,7 +797,7 @@ var File_dump_v1beta1_dump_proto protoreflect.FileDescriptor const file_dump_v1beta1_dump_proto_rawDesc = "" + "\n" + - "\x17dump/v1beta1/dump.proto\x12\fdump.v1beta1\x1a\x1aextensions/v1/redact.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\x1a\x17validate/validate.proto\"\xa3\x02\n" + + "\x17dump/v1beta1/dump.proto\x12\fdump.v1beta1\x1a\x1aextensions/v1/redact.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\x1a\x17validate/validate.proto\"\xc1\x02\n" + "\x04Dump\x12\x17\n" + "\adump_id\x18\x01 \x01(\tR\x06dumpId\x120\n" + "\x06status\x18\x02 \x01(\x0e2\x18.dump.v1beta1.DumpStatusR\x06status\x12#\n" + @@ -779,7 +806,8 @@ const file_dump_v1beta1_dump_proto_rawDesc = "" + "start_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + "\bend_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\aendTime\x129\n" + "\n" + - "created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xe9\x01\n" + + "created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x1c\n" + + "\tencrypted\x18\b \x01(\bR\tencrypted\"\xc7\x02\n" + "\x10StartDumpRequest\x12#\n" + "\rservice_names\x18\x01 \x03(\tR\fserviceNames\x129\n" + "\n" + @@ -788,7 +816,9 @@ const file_dump_v1beta1_dump_proto_rawDesc = "" + "\n" + "export_qan\x18\x04 \x01(\bR\texportQan\x12\x1f\n" + "\vignore_load\x18\x05 \x01(\bR\n" + - "ignoreLoad\",\n" + + "ignoreLoad\x12+\n" + + "\x11enable_encryption\x18\x06 \x01(\bR\x10enableEncryption\x12/\n" + + "\x13encryption_password\x18\a \x01(\tR\x12encryptionPassword\",\n" + "\x11StartDumpResponse\x12\x17\n" + "\adump_id\x18\x01 \x01(\tR\x06dumpId\"\x12\n" + "\x10ListDumpsRequest\"=\n" + diff --git a/api/dump/v1beta1/dump.pb.validate.go b/api/dump/v1beta1/dump.pb.validate.go index 6ff806abc21..5b26407b171 100644 --- a/api/dump/v1beta1/dump.pb.validate.go +++ b/api/dump/v1beta1/dump.pb.validate.go @@ -147,6 +147,8 @@ func (m *Dump) validate(all bool) error { } } + // no validation rules for Encrypted + if len(errors) > 0 { return DumpMultiError(errors) } @@ -308,6 +310,10 @@ func (m *StartDumpRequest) validate(all bool) error { // no validation rules for IgnoreLoad + // no validation rules for EnableEncryption + + // no validation rules for EncryptionPassword + if len(errors) > 0 { return StartDumpRequestMultiError(errors) } diff --git a/api/dump/v1beta1/dump.proto b/api/dump/v1beta1/dump.proto index 69d46cf7a5f..2e5a366d6d0 100644 --- a/api/dump/v1beta1/dump.proto +++ b/api/dump/v1beta1/dump.proto @@ -22,6 +22,8 @@ message Dump { google.protobuf.Timestamp start_time = 4; google.protobuf.Timestamp end_time = 5; google.protobuf.Timestamp created_at = 7; + // This field is set to true if the dump was created with encryption enabled, and false otherwise. + bool encrypted = 8; } message StartDumpRequest { @@ -30,6 +32,10 @@ message StartDumpRequest { google.protobuf.Timestamp end_time = 3; bool export_qan = 4; bool ignore_load = 5; + // If true, the dump will be encrypted. Note that enabling encryption may increase the time required to create the dump. + bool enable_encryption = 6; + // The password used for encryption. It must be at least 8 characters long. This field is required if enable_encryption is true. + string encryption_password = 7; } message StartDumpResponse { diff --git a/api/dump/v1beta1/json/client/dump_service/list_dumps_responses.go b/api/dump/v1beta1/json/client/dump_service/list_dumps_responses.go index 6e37793be89..124301afd14 100644 --- a/api/dump/v1beta1/json/client/dump_service/list_dumps_responses.go +++ b/api/dump/v1beta1/json/client/dump_service/list_dumps_responses.go @@ -553,6 +553,9 @@ type ListDumpsOKBodyDumpsItems0 struct { // created at // Format: date-time CreatedAt strfmt.DateTime `json:"created_at,omitempty"` + + // This field is set to true if the dump was created with encryption enabled, and false otherwise. + Encrypted bool `json:"encrypted,omitempty"` } // Validate validates this list dumps OK body dumps items0 diff --git a/api/dump/v1beta1/json/client/dump_service/start_dump_responses.go b/api/dump/v1beta1/json/client/dump_service/start_dump_responses.go index 4e34e022214..936b90ac7ae 100644 --- a/api/dump/v1beta1/json/client/dump_service/start_dump_responses.go +++ b/api/dump/v1beta1/json/client/dump_service/start_dump_responses.go @@ -209,6 +209,12 @@ type StartDumpBody struct { // ignore load IgnoreLoad bool `json:"ignore_load,omitempty"` + + // If true, the dump will be encrypted. Note that enabling encryption may increase the time required to create the dump. + EnableEncryption bool `json:"enable_encryption,omitempty"` + + // The password used for encryption. It must be at least 8 characters long. This field is required if enable_encryption is true. + EncryptionPassword string `json:"encryption_password,omitempty"` } // Validate validates this start dump body diff --git a/api/dump/v1beta1/json/v1beta1.json b/api/dump/v1beta1/json/v1beta1.json index c36e0941f48..6b76cfcde19 100644 --- a/api/dump/v1beta1/json/v1beta1.json +++ b/api/dump/v1beta1/json/v1beta1.json @@ -70,6 +70,11 @@ "type": "string", "format": "date-time", "x-order": 5 + }, + "encrypted": { + "description": "This field is set to true if the dump was created with encryption enabled, and false otherwise.", + "type": "boolean", + "x-order": 6 } } }, @@ -314,6 +319,16 @@ "ignore_load": { "type": "boolean", "x-order": 4 + }, + "enable_encryption": { + "description": "If true, the dump will be encrypted. Note that enabling encryption may increase the time required to create the dump.", + "type": "boolean", + "x-order": 5 + }, + "encryption_password": { + "description": "The password used for encryption. It must be at least 8 characters long. This field is required if enable_encryption is true.", + "type": "string", + "x-order": 6 } } } diff --git a/api/swagger/swagger-dev.json b/api/swagger/swagger-dev.json index 1ca1471e02b..d42d18b815d 100644 --- a/api/swagger/swagger-dev.json +++ b/api/swagger/swagger-dev.json @@ -4763,6 +4763,11 @@ "type": "string", "format": "date-time", "x-order": 5 + }, + "encrypted": { + "description": "This field is set to true if the dump was created with encryption enabled, and false otherwise.", + "type": "boolean", + "x-order": 6 } } }, @@ -5007,6 +5012,16 @@ "ignore_load": { "type": "boolean", "x-order": 4 + }, + "enable_encryption": { + "description": "If true, the dump will be encrypted. Note that enabling encryption may increase the time required to create the dump.", + "type": "boolean", + "x-order": 5 + }, + "encryption_password": { + "description": "The password used for encryption. It must be at least 8 characters long. This field is required if enable_encryption is true.", + "type": "string", + "x-order": 6 } } } diff --git a/build/packages/rpm/server/SPECS/pmm-dump.spec b/build/packages/rpm/server/SPECS/pmm-dump.spec index c2fc0e41789..5d9719cdfae 100644 --- a/build/packages/rpm/server/SPECS/pmm-dump.spec +++ b/build/packages/rpm/server/SPECS/pmm-dump.spec @@ -2,14 +2,14 @@ %global repo pmm-dump %global provider github.com/percona/%{repo} -%global commit 4c38e9442fb2f6b0146cd5a581f18db4ebb034f7 +%global commit 8353b46afd09746c07a6d1c001dd1ef72e6c4761 %global shortcommit %(c=%{commit}; echo ${c:0:7}) %define build_timestamp %(date -u +"%y%m%d%H%M") %define release 1 %define rpm_release %{release}.%{build_timestamp}.%{shortcommit}%{?dist} Name: pmm-dump -Version: 0.7.1-ga +Version: 0.8.0-ga Release: %{rpm_release} Summary: Percona PMM Dump allows to export and import monitoring metrics and query analytics. @@ -37,6 +37,9 @@ install -p -m 0755 pmm-dump %{buildroot}%{_sbindir}/pmm-dump %changelog +* Wed Apr 22 2026 Maxim Kondratenko - 3.7.1 +- PMM-14441 Upgrade pmm-dump to support encryption + * Wed Sep 24 2025 Michael Okoko - 3.4.1 - PMM-14349 Update pmm-dump sources. diff --git a/managed/models/database.go b/managed/models/database.go index 6c74fb37cc7..60a5547bb37 100644 --- a/managed/models/database.go +++ b/managed/models/database.go @@ -1180,6 +1180,10 @@ var databaseSchema = [][]string{ 117: { `DROP TABLE IF EXISTS percona_sso_details`, }, + 118: { + `ALTER TABLE dumps ADD COLUMN encrypted boolean NOT NULL DEFAULT false`, + `UPDATE dumps SET encrypted = false`, + }, } // ^^^ Avoid default values in schema definition. ^^^ diff --git a/managed/models/dump.go b/managed/models/dump.go index c6fab03e2be..cc5ba6d1093 100644 --- a/managed/models/dump.go +++ b/managed/models/dump.go @@ -67,6 +67,7 @@ type Dump struct { IgnoreLoad bool `reform:"ignore_load"` CreatedAt time.Time `reform:"created_at"` UpdatedAt time.Time `reform:"updated_at"` + Encrypted bool `reform:"encrypted"` } // BeforeInsert implements reform.BeforeInserter interface. diff --git a/managed/models/dump_helpers.go b/managed/models/dump_helpers.go index dc4beb1072f..7f4e1feb97e 100644 --- a/managed/models/dump_helpers.go +++ b/managed/models/dump_helpers.go @@ -57,6 +57,7 @@ type CreateDumpParams struct { EndTime *time.Time ExportQAN bool IgnoreLoad bool + Encrypted bool } // Validate checks the validity of CreateDumpParams. @@ -87,6 +88,7 @@ func CreateDump(q *reform.Querier, params CreateDumpParams) (*Dump, error) { EndTime: params.EndTime, ExportQAN: params.ExportQAN, IgnoreLoad: params.IgnoreLoad, + Encrypted: params.Encrypted, } if err := q.Insert(dump); err != nil { return nil, errors.WithStack(err) diff --git a/managed/models/dump_helpers_test.go b/managed/models/dump_helpers_test.go index 0d53a2f179d..6f7449f6dcb 100644 --- a/managed/models/dump_helpers_test.go +++ b/managed/models/dump_helpers_test.go @@ -40,7 +40,7 @@ func TestDumps(t *testing.T) { }) t.Run("create", func(t *testing.T) { - t.Run("normal", func(t *testing.T) { + t.Run("normal non-encrypted", func(t *testing.T) { endTime := time.Now() startTime := endTime.Add(-10 * time.Minute) @@ -62,6 +62,30 @@ func TestDumps(t *testing.T) { assert.Equal(t, createDumpParams.IgnoreLoad, dump.IgnoreLoad) }) + t.Run("normal encrypted", func(t *testing.T) { + endTime := time.Now() + startTime := endTime.Add(-10 * time.Minute) + + createDumpParams := models.CreateDumpParams{ + ServiceNames: []string{"foo", "bar"}, + StartTime: &startTime, + EndTime: &endTime, + ExportQAN: false, + IgnoreLoad: true, + Encrypted: true, + } + dump, err := models.CreateDump(tx.Querier, createDumpParams) + require.NoError(t, err) + assert.NotEmpty(t, dump.ID) + assert.Equal(t, models.DumpStatusInProgress, dump.Status) + assert.ElementsMatch(t, createDumpParams.ServiceNames, dump.ServiceNames) + assert.Equal(t, createDumpParams.StartTime, dump.StartTime) + assert.Equal(t, createDumpParams.EndTime, dump.EndTime) + assert.Equal(t, createDumpParams.ExportQAN, dump.ExportQAN) + assert.Equal(t, createDumpParams.IgnoreLoad, dump.IgnoreLoad) + assert.Equal(t, createDumpParams.Encrypted, dump.Encrypted) + }) + t.Run("invalid start and end time", func(t *testing.T) { endTime := time.Now() startTime := endTime.Add(10 * time.Minute) diff --git a/managed/models/dump_reform.go b/managed/models/dump_reform.go index a78ca52e9a6..350fef63087 100644 --- a/managed/models/dump_reform.go +++ b/managed/models/dump_reform.go @@ -37,6 +37,7 @@ func (v *dumpTableType) Columns() []string { "ignore_load", "created_at", "updated_at", + "encrypted", } } @@ -70,6 +71,7 @@ var DumpTable = &dumpTableType{ {Name: "IgnoreLoad", Type: "bool", Column: "ignore_load"}, {Name: "CreatedAt", Type: "time.Time", Column: "created_at"}, {Name: "UpdatedAt", Type: "time.Time", Column: "updated_at"}, + {Name: "Encrypted", Type: "bool", Column: "encrypted"}, }, PKFieldIndex: 0, }, @@ -78,7 +80,7 @@ var DumpTable = &dumpTableType{ // String returns a string representation of this struct or record. func (s Dump) String() string { - res := make([]string, 9) + res := make([]string, 10) res[0] = "ID: " + reform.Inspect(s.ID, true) res[1] = "Status: " + reform.Inspect(s.Status, true) res[2] = "ServiceNames: " + reform.Inspect(s.ServiceNames, true) @@ -88,6 +90,7 @@ func (s Dump) String() string { res[6] = "IgnoreLoad: " + reform.Inspect(s.IgnoreLoad, true) res[7] = "CreatedAt: " + reform.Inspect(s.CreatedAt, true) res[8] = "UpdatedAt: " + reform.Inspect(s.UpdatedAt, true) + res[9] = "Encrypted: " + reform.Inspect(s.Encrypted, true) return strings.Join(res, ", ") } @@ -104,6 +107,7 @@ func (s *Dump) Values() []interface{} { s.IgnoreLoad, s.CreatedAt, s.UpdatedAt, + s.Encrypted, } } @@ -120,6 +124,7 @@ func (s *Dump) Pointers() []interface{} { &s.IgnoreLoad, &s.CreatedAt, &s.UpdatedAt, + &s.Encrypted, } } diff --git a/managed/services/dump/dump.go b/managed/services/dump/dump.go index 7916880bfaa..a60ec1894f4 100644 --- a/managed/services/dump/dump.go +++ b/managed/services/dump/dump.go @@ -75,15 +75,17 @@ func New(db *reform.DB, urls *URLs) *Service { // Params represents the parameters for configuring the dump service. type Params struct { - Token string - Cookie string - User string - Password string - ServiceNames []string - StartTime *time.Time - EndTime *time.Time - ExportQAN bool - IgnoreLoad bool + Token string + Cookie string + User string + Password string + ServiceNames []string + StartTime *time.Time + EndTime *time.Time + ExportQAN bool + IgnoreLoad bool + EnableEncryption bool + EncryptionPassword string } // StartDump initiates the process of creating and managing dumps in the dump service. @@ -99,6 +101,7 @@ func (s *Service) StartDump(params *Params) (string, error) { EndTime: params.EndTime, ExportQAN: params.ExportQAN, IgnoreLoad: params.IgnoreLoad, + Encrypted: params.EnableEncryption, }) if err != nil { s.running.Store(false) @@ -141,7 +144,7 @@ func (s *Service) StartDump(params *Params) (string, error) { "--pmm-url=http://127.0.0.1:8080", "--click-house-url="+s.urls.ClickhouseURL, "--victoria-metrics-url="+s.urls.VMURL, - fmt.Sprintf("--dump-path=%s", getDumpFilePath(dump.ID))) + "--dump-path="+getDumpFilePath(dump.ID, false)) if params.Token != "" { pmmDumpCmd.Args = append(pmmDumpCmd.Args, fmt.Sprintf(`--pmm-token=%s`, params.Token)) @@ -176,6 +179,13 @@ func (s *Service) StartDump(params *Params) (string, error) { pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--ignore-load") } + if params.EnableEncryption { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--encryption") + pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--pass="+params.EncryptionPassword) + } else { + pmmDumpCmd.Args = append(pmmDumpCmd.Args, "--no-encryption") + } + pReader, pWriter := io.Pipe() pmmDumpCmd.Stdout = pWriter pmmDumpCmd.Stderr = pWriter @@ -219,7 +229,7 @@ func (s *Service) DeleteDump(dumpID string) error { return errors.Wrap(err, "failed to find dump") } - filePath := getDumpFilePath(dump.ID) + filePath := getDumpFilePath(dump.ID, dump.Encrypted) err = validateFilePath(filePath) if err != nil && !errors.Is(err, os.ErrNotExist) { return errors.WithStack(err) @@ -250,7 +260,7 @@ func (s *Service) GetFilePathsForDumps(dumpIDs []string) (map[string]string, err s.l.Warnf("Dump with id %s is in %s state. Skiping it.", d.ID, d.Status) continue } - filePath := getDumpFilePath(d.ID) + filePath := getDumpFilePath(d.ID, d.Encrypted) if err = validateFilePath(filePath); err != nil { return nil, errors.WithStack(err) } @@ -319,8 +329,12 @@ func (s *Service) StopDump() { s.cancel() } -func getDumpFilePath(id string) string { - return fmt.Sprintf("%s/%s.tar.gz", dumpsDir, id) +func getDumpFilePath(id string, encrypted bool) string { + s := fmt.Sprintf("%s/%s.tar.gz", dumpsDir, id) + if encrypted { + s += ".enc" + } + return s } func validateFilePath(path string) error { diff --git a/managed/services/dump/dump_test.go b/managed/services/dump/dump_test.go new file mode 100644 index 00000000000..91c5bfdcdbe --- /dev/null +++ b/managed/services/dump/dump_test.go @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Percona LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package dump + +import ( + "testing" +) + +func Test_getDumpFilePath(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + id string + encrypted bool + want string + }{ + { + name: "Usual dump", + id: "123456789", + encrypted: false, + want: dumpsDir + "/123456789.tar.gz", + }, + { + name: "Encrypted dump", + id: "123456789", + encrypted: true, + want: dumpsDir + "/123456789.tar.gz.enc", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + if got := getDumpFilePath(tt.id, tt.encrypted); got != tt.want { + t.Errorf("getDumpFilePath() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/managed/services/management/dump/dump.go b/managed/services/management/dump/dump.go index bbb132d9023..8b5303f37ec 100644 --- a/managed/services/management/dump/dump.go +++ b/managed/services/management/dump/dump.go @@ -107,14 +107,20 @@ func (s *Service) StartDump(ctx context.Context, req *dumpv1beta1.StartDumpReque } } + if req.EnableEncryption && req.EncryptionPassword == "" { + return nil, status.Error(codes.InvalidArgument, "Encryption password must be provided when encryption is enabled") + } + params := &dump.Params{ - Token: token, - Cookie: cookie, - User: user, - Password: password, - ServiceNames: req.ServiceNames, - ExportQAN: req.ExportQan, - IgnoreLoad: req.IgnoreLoad, + Token: token, + Cookie: cookie, + User: user, + Password: password, + ServiceNames: req.ServiceNames, + ExportQAN: req.ExportQan, + IgnoreLoad: req.IgnoreLoad, + EnableEncryption: req.EnableEncryption, + EncryptionPassword: req.EncryptionPassword, } if req.StartTime != nil { @@ -289,6 +295,7 @@ func convertDump(dump *models.Dump) (*dumpv1beta1.Dump, error) { Status: ds, ServiceNames: dump.ServiceNames, CreatedAt: timestamppb.New(dump.CreatedAt), + Encrypted: dump.Encrypted, } if dump.StartTime != nil {