mirror of
https://github.com/kopia/kopia.git
synced 2026-04-04 06:22:59 -04:00
Support for content-level compression (#1076)
* cli: added a flag to create repository with v2 index features * content: plumb through compression.ID parameter to content.Manager.WriteContent() * content: expose content.Manager.SupportsContentCompression This allows object manager to decide whether to create compressed object or let the content manager do it. * object: if compression is requested and the repo supports it, pass compression ID to the content manager * cli: show compression status in 'repository status' * cli: output compression information in 'content list' and 'content stats' * content: compression and decompression support * content: unit tests for compression * object: compression tests * testing: added integration tests against v2 index * testing: run all e2e tests with and without content-level compression * htmlui: added UI for specifying index format on creation * cli: additional tests for 'content ls' and 'content stats' * applied pr suggestions
This commit is contained in:
8
Makefile
8
Makefile
@@ -153,7 +153,10 @@ endif
|
||||
|
||||
ci-tests: lint vet test
|
||||
|
||||
ci-integration-tests: integration-tests robustness-tool-tests
|
||||
ci-integration-tests:
|
||||
$(MAKE) integration-tests
|
||||
$(MAKE) integration-tests-index-v2
|
||||
$(MAKE) robustness-tool-tests
|
||||
$(MAKE) stress-test
|
||||
|
||||
ci-publish-coverage:
|
||||
@@ -220,6 +223,9 @@ integration-tests: build-integration-test-binary $(gotestsum) $(TESTING_ACTION_E
|
||||
$(GO_TEST) $(TEST_FLAGS) -count=$(REPEAT_TEST) -parallel $(PARALLEL) -timeout 3600s github.com/kopia/kopia/tests/end_to_end_test
|
||||
-$(gotestsum) tool slowest --jsonfile .tmp.integration-tests.json --threshold 1000ms
|
||||
|
||||
integration-tests-index-v2:
|
||||
KOPIA_CREATE_INDEX_VERSION=2 KOPIA_RUN_ALL_INTEGRATION_TESTS=true $(MAKE) integration-tests
|
||||
|
||||
endurance-tests: export KOPIA_EXE ?= $(KOPIA_INTEGRATION_EXE)
|
||||
endurance-tests: export KOPIA_LOGS_DIR=$(CURDIR)/.logs
|
||||
endurance-tests: build-integration-test-binary $(gotestsum)
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kopia/kopia/internal/stats"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
)
|
||||
|
||||
@@ -16,6 +18,7 @@ type commandContentList struct {
|
||||
deletedOnly bool
|
||||
summary bool
|
||||
human bool
|
||||
compression bool
|
||||
|
||||
contentRange contentRangeFlags
|
||||
jo jsonOutput
|
||||
@@ -25,6 +28,7 @@ type commandContentList struct {
|
||||
func (c *commandContentList) setup(svc appServices, parent commandParent) {
|
||||
cmd := parent.Command("list", "List contents").Alias("ls")
|
||||
cmd.Flag("long", "Long output").Short('l').BoolVar(&c.long)
|
||||
cmd.Flag("compression", "Compression").Short('c').BoolVar(&c.compression)
|
||||
cmd.Flag("deleted", "Include deleted content").BoolVar(&c.includeDeleted)
|
||||
cmd.Flag("deleted-only", "Only show deleted content").BoolVar(&c.deletedOnly)
|
||||
cmd.Flag("summary", "Summarize the list").Short('s').BoolVar(&c.summary)
|
||||
@@ -56,24 +60,14 @@ func(b content.Info) error {
|
||||
|
||||
totalSize.Add(int64(b.GetPackedLength()))
|
||||
|
||||
if c.jo.jsonOutput {
|
||||
switch {
|
||||
case c.jo.jsonOutput:
|
||||
jl.emit(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.long {
|
||||
optionalDeleted := ""
|
||||
if b.GetDeleted() {
|
||||
optionalDeleted = " (deleted)"
|
||||
}
|
||||
c.out.printStdout("%v %v %v %v+%v%v\n",
|
||||
b.GetContentID(),
|
||||
formatTimestamp(b.Timestamp()),
|
||||
b.GetPackBlobID(),
|
||||
b.GetPackOffset(),
|
||||
maybeHumanReadableBytes(c.human, int64(b.GetPackedLength())),
|
||||
optionalDeleted)
|
||||
} else {
|
||||
case c.compression:
|
||||
c.outputCompressed(b)
|
||||
case c.long:
|
||||
c.outputLong(b)
|
||||
default:
|
||||
c.out.printStdout("%v\n", b.GetContentID())
|
||||
}
|
||||
|
||||
@@ -92,3 +86,52 @@ func(b content.Info) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *commandContentList) outputLong(b content.Info) {
|
||||
c.out.printStdout("%v %v %v %v %v+%v%v %v\n",
|
||||
b.GetContentID(),
|
||||
b.GetOriginalLength(),
|
||||
formatTimestamp(b.Timestamp()),
|
||||
b.GetPackBlobID(),
|
||||
b.GetPackOffset(),
|
||||
maybeHumanReadableBytes(c.human, int64(b.GetPackedLength())),
|
||||
c.deletedInfoString(b),
|
||||
c.compressionInfoStringString(b),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *commandContentList) outputCompressed(b content.Info) {
|
||||
c.out.printStdout("%v length %v packed %v %v %v\n",
|
||||
b.GetContentID(),
|
||||
maybeHumanReadableBytes(c.human, int64(b.GetOriginalLength())),
|
||||
maybeHumanReadableBytes(c.human, int64(b.GetPackedLength())),
|
||||
c.compressionInfoStringString(b),
|
||||
c.deletedInfoString(b),
|
||||
)
|
||||
}
|
||||
|
||||
func (*commandContentList) deletedInfoString(b content.Info) string {
|
||||
if b.GetDeleted() {
|
||||
return " (deleted)"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (*commandContentList) compressionInfoStringString(b content.Info) string {
|
||||
h := b.GetCompressionHeaderID()
|
||||
if h == content.NoCompression {
|
||||
return "-"
|
||||
}
|
||||
|
||||
s := string(compression.HeaderIDToName[h])
|
||||
if s == "" {
|
||||
s = fmt.Sprintf("compression-%x", h)
|
||||
}
|
||||
|
||||
if b.GetOriginalLength() > 0 {
|
||||
s += " " + formatCompressionPercentage(int64(b.GetOriginalLength()), int64(b.GetPackedLength()))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
"github.com/kopia/kopia/internal/units"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
)
|
||||
|
||||
@@ -25,39 +26,24 @@ func (c *commandContentStats) setup(svc appServices, parent commandParent) {
|
||||
cmd.Action(svc.directRepositoryReadAction(c.run))
|
||||
}
|
||||
|
||||
type contentStatsTotals struct {
|
||||
originalSize, packedSize, count int64
|
||||
}
|
||||
|
||||
func (c *commandContentStats) run(ctx context.Context, rep repo.DirectRepository) error {
|
||||
var sizeThreshold uint32 = 10
|
||||
|
||||
countMap := map[uint32]int{}
|
||||
totalSizeOfContentsUnder := map[uint32]int64{}
|
||||
|
||||
var sizeThresholds []uint32
|
||||
var (
|
||||
sizeThreshold uint32 = 10
|
||||
sizeBuckets []uint32
|
||||
)
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
sizeThresholds = append(sizeThresholds, sizeThreshold)
|
||||
countMap[sizeThreshold] = 0
|
||||
sizeBuckets = append(sizeBuckets, sizeThreshold)
|
||||
sizeThreshold *= 10
|
||||
}
|
||||
|
||||
var totalSize, count int64
|
||||
|
||||
if err := rep.ContentReader().IterateContents(
|
||||
ctx,
|
||||
content.IterateOptions{
|
||||
Range: c.contentRange.contentIDRange(),
|
||||
},
|
||||
func(b content.Info) error {
|
||||
totalSize += int64(b.GetPackedLength())
|
||||
count++
|
||||
for s := range countMap {
|
||||
if b.GetPackedLength() < s {
|
||||
countMap[s]++
|
||||
totalSizeOfContentsUnder[s] += int64(b.GetPackedLength())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "error iterating contents")
|
||||
grandTotal, byCompressionTotal, countMap, totalSizeOfContentsUnder, err := c.calculateStats(ctx, rep, sizeBuckets)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error calculating totals")
|
||||
}
|
||||
|
||||
sizeToString := units.BytesStringBase10
|
||||
@@ -65,20 +51,47 @@ func(b content.Info) error {
|
||||
sizeToString = func(l int64) string { return strconv.FormatInt(l, 10) }
|
||||
}
|
||||
|
||||
c.out.printStdout("Count: %v\n", count)
|
||||
c.out.printStdout("Total: %v\n", sizeToString(totalSize))
|
||||
c.out.printStdout("Count: %v\n", grandTotal.count)
|
||||
c.out.printStdout("Total Bytes: %v\n", sizeToString(grandTotal.originalSize))
|
||||
|
||||
if count == 0 {
|
||||
if grandTotal.packedSize < grandTotal.originalSize {
|
||||
c.out.printStdout(
|
||||
"Total Packed: %v (compression %v)\n",
|
||||
sizeToString(grandTotal.packedSize),
|
||||
formatCompressionPercentage(grandTotal.originalSize, grandTotal.packedSize))
|
||||
}
|
||||
|
||||
if len(byCompressionTotal) > 1 {
|
||||
c.out.printStdout("By Method:\n")
|
||||
|
||||
if bct := byCompressionTotal[content.NoCompression]; bct != nil {
|
||||
c.out.printStdout(" %-22v count: %v size: %v\n", "(uncompressed)", bct.count, sizeToString(bct.originalSize))
|
||||
}
|
||||
|
||||
for hdrID, bct := range byCompressionTotal {
|
||||
cname := compression.HeaderIDToName[hdrID]
|
||||
if cname == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
c.out.printStdout(" %-22v count: %v size: %v packed: %v compression: %v\n",
|
||||
cname, bct.count,
|
||||
sizeToString(bct.originalSize),
|
||||
sizeToString(bct.packedSize),
|
||||
formatCompressionPercentage(bct.originalSize, bct.packedSize))
|
||||
}
|
||||
}
|
||||
|
||||
if grandTotal.count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.out.printStdout("Average: %v\n", sizeToString(totalSize/count))
|
||||
|
||||
c.out.printStdout("Average: %v\n", sizeToString(grandTotal.originalSize/grandTotal.count))
|
||||
c.out.printStdout("Histogram:\n\n")
|
||||
|
||||
var lastSize uint32
|
||||
|
||||
for _, size := range sizeThresholds {
|
||||
for _, size := range sizeBuckets {
|
||||
c.out.printStdout("%9v between %v and %v (total %v)\n",
|
||||
countMap[size]-countMap[lastSize],
|
||||
sizeToString(int64(lastSize)),
|
||||
@@ -91,3 +104,51 @@ func(b content.Info) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *commandContentStats) calculateStats(ctx context.Context, rep repo.DirectRepository, sizeBuckets []uint32) (
|
||||
grandTotal contentStatsTotals,
|
||||
byCompressionTotal map[compression.HeaderID]*contentStatsTotals,
|
||||
countMap map[uint32]int,
|
||||
totalSizeOfContentsUnder map[uint32]int64,
|
||||
err error,
|
||||
) {
|
||||
byCompressionTotal = make(map[compression.HeaderID]*contentStatsTotals)
|
||||
totalSizeOfContentsUnder = make(map[uint32]int64)
|
||||
countMap = make(map[uint32]int)
|
||||
|
||||
for _, s := range sizeBuckets {
|
||||
countMap[s] = 0
|
||||
}
|
||||
|
||||
err = rep.ContentReader().IterateContents(
|
||||
ctx,
|
||||
content.IterateOptions{
|
||||
Range: c.contentRange.contentIDRange(),
|
||||
},
|
||||
func(b content.Info) error {
|
||||
grandTotal.packedSize += int64(b.GetPackedLength())
|
||||
grandTotal.originalSize += int64(b.GetOriginalLength())
|
||||
grandTotal.count++
|
||||
|
||||
bct := byCompressionTotal[b.GetCompressionHeaderID()]
|
||||
if bct == nil {
|
||||
bct = &contentStatsTotals{}
|
||||
byCompressionTotal[b.GetCompressionHeaderID()] = bct
|
||||
}
|
||||
|
||||
bct.packedSize += int64(b.GetPackedLength())
|
||||
bct.originalSize += int64(b.GetOriginalLength())
|
||||
bct.count++
|
||||
|
||||
for s := range countMap {
|
||||
if b.GetPackedLength() < s {
|
||||
countMap[s]++
|
||||
totalSizeOfContentsUnder[s] += int64(b.GetPackedLength())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// nolint:wrapcheck
|
||||
return grandTotal, byCompressionTotal, countMap, totalSizeOfContentsUnder, err
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ type commandRepositoryCreate struct {
|
||||
createBlockEncryptionFormat string
|
||||
createSplitter string
|
||||
createOnly bool
|
||||
createIndexVersion int
|
||||
|
||||
co connectOptions
|
||||
svc advancedAppServices
|
||||
@@ -34,6 +35,7 @@ func (c *commandRepositoryCreate) setup(svc advancedAppServices, parent commandP
|
||||
cmd.Flag("encryption", "Content encryption algorithm.").PlaceHolder("ALGO").Default(encryption.DefaultAlgorithm).EnumVar(&c.createBlockEncryptionFormat, encryption.SupportedAlgorithms(false)...)
|
||||
cmd.Flag("object-splitter", "The splitter to use for new objects in the repository").Default(splitter.DefaultAlgorithm).EnumVar(&c.createSplitter, splitter.SupportedAlgorithms()...)
|
||||
cmd.Flag("create-only", "Create repository, but don't connect to it.").Short('c').BoolVar(&c.createOnly)
|
||||
cmd.Flag("index-version", "Force particular index version").Hidden().Envar("KOPIA_CREATE_INDEX_VERSION").IntVar(&c.createIndexVersion)
|
||||
|
||||
c.co.setup(cmd)
|
||||
c.svc = svc
|
||||
@@ -63,8 +65,9 @@ func (c *commandRepositoryCreate) setup(svc advancedAppServices, parent commandP
|
||||
func (c *commandRepositoryCreate) newRepositoryOptionsFromFlags() *repo.NewRepositoryOptions {
|
||||
return &repo.NewRepositoryOptions{
|
||||
BlockFormat: content.FormattingOptions{
|
||||
Hash: c.createBlockHashFormat,
|
||||
Encryption: c.createBlockEncryptionFormat,
|
||||
Hash: c.createBlockHashFormat,
|
||||
Encryption: c.createBlockEncryptionFormat,
|
||||
IndexVersion: c.createIndexVersion,
|
||||
},
|
||||
|
||||
ObjectFormat: object.Format{
|
||||
|
||||
@@ -60,6 +60,7 @@ func (c *commandRepositoryStatus) run(ctx context.Context, rep repo.Repository)
|
||||
c.out.printStdout("Encryption: %v\n", dr.ContentReader().ContentFormat().Encryption)
|
||||
c.out.printStdout("Splitter: %v\n", dr.ObjectFormat().Splitter)
|
||||
c.out.printStdout("Format version: %v\n", dr.ContentReader().ContentFormat().Version)
|
||||
c.out.printStdout("Content compression: %v\n", dr.ContentReader().SupportsContentCompression())
|
||||
c.out.printStdout("Max pack length: %v\n", units.BytesStringBase2(int64(dr.ContentReader().ContentFormat().MaxPackSize)))
|
||||
|
||||
if !c.statusReconnectToken {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"github.com/kopia/kopia/internal/units"
|
||||
)
|
||||
|
||||
const oneHundredPercent = 100.0
|
||||
|
||||
// TODO - remove this global.
|
||||
var timeZone = "local"
|
||||
|
||||
@@ -90,3 +92,15 @@ func convertTimezone(ts time.Time) time.Time {
|
||||
return ts
|
||||
}
|
||||
}
|
||||
|
||||
func formatCompressionPercentage(original, compressed int64) string {
|
||||
if compressed >= original {
|
||||
return "0%"
|
||||
}
|
||||
|
||||
if original == 0 {
|
||||
return "0%"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.1f%%", oneHundredPercent*(1-float64(compressed)/float64(original)))
|
||||
}
|
||||
|
||||
@@ -159,6 +159,10 @@ export class RepoStatus extends Component {
|
||||
<Form.Label>Splitter Algorithm</Form.Label>
|
||||
<Form.Control readOnly defaultValue={this.state.status.splitter} />
|
||||
</Form.Group>
|
||||
<Form.Group as={Col}>
|
||||
<Form.Label>Supports Content Compression</Form.Label>
|
||||
<Form.Control readOnly defaultValue={this.state.status.supportsContentCompression ? "yes" : "no"} />
|
||||
</Form.Group>
|
||||
</Form.Row>
|
||||
</>}
|
||||
<Form.Row>
|
||||
|
||||
@@ -61,6 +61,7 @@ export class SetupRepository extends Component {
|
||||
hash: result.data.defaultHash,
|
||||
encryption: result.data.defaultEncryption,
|
||||
splitter: result.data.defaultSplitter,
|
||||
indexVersion: "",
|
||||
});
|
||||
});
|
||||
axios.get('/api/v1/current-user').then(result => {
|
||||
@@ -107,7 +108,7 @@ export class SetupRepository extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = {
|
||||
let request = {
|
||||
storage: {
|
||||
type: this.state.provider,
|
||||
config: this.state.providerSettings,
|
||||
@@ -124,6 +125,10 @@ export class SetupRepository extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
if (this.state.indexVersion) {
|
||||
request.options.blockFormat.indexVersion = parseInt(this.state.indexVersion)
|
||||
}
|
||||
|
||||
request.clientOptions = this.clientOptions();
|
||||
|
||||
axios.post('/api/v1/repo/create', request).then(result => {
|
||||
@@ -360,6 +365,18 @@ export class SetupRepository extends Component {
|
||||
{this.state.algorithms.splitter.map(x => <option key={x} value={x}>{x}</option>)}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
<Form.Group as={Col}>
|
||||
<Form.Label className="required">Index Format</Form.Label>
|
||||
<Form.Control as="select"
|
||||
name="indexVersion"
|
||||
onChange={this.handleChange}
|
||||
data-testid="control-indexVersion"
|
||||
value={this.state.indexVersion}>
|
||||
<option value="">(default)</option>
|
||||
<option value={1}>v1</option>
|
||||
<option value={2}>v2 (experimental)</option>
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
</Form.Row>
|
||||
{this.overrideUsernameHostnameRow()}
|
||||
<Form.Row>
|
||||
|
||||
@@ -319,9 +319,10 @@ type RepositoryParameters struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
HashFunction string `protobuf:"bytes,1,opt,name=hash_function,json=hashFunction,proto3" json:"hash_function,omitempty"`
|
||||
HmacSecret []byte `protobuf:"bytes,2,opt,name=hmac_secret,json=hmacSecret,proto3" json:"hmac_secret,omitempty"`
|
||||
Splitter string `protobuf:"bytes,3,opt,name=splitter,proto3" json:"splitter,omitempty"`
|
||||
HashFunction string `protobuf:"bytes,1,opt,name=hash_function,json=hashFunction,proto3" json:"hash_function,omitempty"`
|
||||
HmacSecret []byte `protobuf:"bytes,2,opt,name=hmac_secret,json=hmacSecret,proto3" json:"hmac_secret,omitempty"`
|
||||
Splitter string `protobuf:"bytes,3,opt,name=splitter,proto3" json:"splitter,omitempty"`
|
||||
SupportsContentCompression bool `protobuf:"varint,4,opt,name=supports_content_compression,json=supportsContentCompression,proto3" json:"supports_content_compression,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RepositoryParameters) Reset() {
|
||||
@@ -377,6 +378,13 @@ func (x *RepositoryParameters) GetSplitter() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RepositoryParameters) GetSupportsContentCompression() bool {
|
||||
if x != nil {
|
||||
return x.SupportsContentCompression
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InitializeSessionRequest must be sent by the client as the first request in a session.
|
||||
type InitializeSessionRequest struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -750,8 +758,9 @@ type WriteContentRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||
Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||
Compression uint32 `protobuf:"varint,3,opt,name=compression,proto3" json:"compression,omitempty"`
|
||||
}
|
||||
|
||||
func (x *WriteContentRequest) Reset() {
|
||||
@@ -800,6 +809,13 @@ func (x *WriteContentRequest) GetData() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *WriteContentRequest) GetCompression() uint32 {
|
||||
if x != nil {
|
||||
return x.Compression
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type WriteContentResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1674,206 +1690,212 @@ func (*SessionResponse_DeleteManifest) isSessionResponse_Response() {}
|
||||
0x10, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e,
|
||||
0x44, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x44, 0x45,
|
||||
0x4e, 0x49, 0x45, 0x44, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d,
|
||||
0x5f, 0x42, 0x52, 0x4f, 0x4b, 0x45, 0x4e, 0x10, 0x06, 0x22, 0x78, 0x0a, 0x14, 0x52, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
|
||||
0x73, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x68, 0x46, 0x75,
|
||||
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x6d, 0x61, 0x63, 0x5f, 0x73,
|
||||
0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x68, 0x6d, 0x61,
|
||||
0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x6c, 0x69, 0x74,
|
||||
0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x6c, 0x69, 0x74,
|
||||
0x74, 0x65, 0x72, 0x22, 0x51, 0x0a, 0x18, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x07, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61,
|
||||
0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65,
|
||||
0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x63, 0x0a, 0x19, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
|
||||
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f,
|
||||
0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73,
|
||||
0x69, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52,
|
||||
0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x36, 0x0a, 0x15, 0x47,
|
||||
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x49, 0x64, 0x22, 0x4b, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a,
|
||||
0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x6f,
|
||||
0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f,
|
||||
0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x0e,
|
||||
0x0a, 0x0c, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0f,
|
||||
0x0a, 0x0d, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x41, 0x0a, 0x13, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x22, 0x35, 0x0a, 0x14, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x35, 0x0a, 0x12, 0x47, 0x65, 0x74,
|
||||
0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64,
|
||||
0x22, 0x77, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f,
|
||||
0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e,
|
||||
0x44, 0x61, 0x74, 0x61, 0x12, 0x43, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72,
|
||||
0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65,
|
||||
0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
|
||||
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb6, 0x01, 0x0a, 0x12, 0x50, 0x75,
|
||||
0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x48, 0x0a,
|
||||
0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e,
|
||||
0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
|
||||
0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
|
||||
0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c,
|
||||
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
|
||||
0x38, 0x01, 0x22, 0x36, 0x0a, 0x13, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||
0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x15, 0x44, 0x65,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65,
|
||||
0x73, 0x74, 0x49, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61,
|
||||
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9d,
|
||||
0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c,
|
||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f,
|
||||
0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d,
|
||||
0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
|
||||
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62,
|
||||
0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5c,
|
||||
0x0a, 0x15, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
|
||||
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70, 0x69,
|
||||
0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf5, 0x05, 0x0a,
|
||||
0x0e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b,
|
||||
0x0a, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x65, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6b, 0x6f, 0x70,
|
||||
0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x49, 0x6e,
|
||||
0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x53, 0x0a, 0x10, 0x67,
|
||||
0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
|
||||
0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00,
|
||||
0x52, 0x0e, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x12, 0x36, 0x0a, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1e, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
|
||||
0x72, 0x79, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48,
|
||||
0x00, 0x52, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x4c, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74,
|
||||
0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x25, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
|
||||
0x72, 0x79, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x0b, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x6f,
|
||||
0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x48, 0x00, 0x52, 0x0a, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x49,
|
||||
0x0a, 0x0c, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x0f,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x67, 0x65,
|
||||
0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x0c, 0x70, 0x75, 0x74,
|
||||
0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
|
||||
0x72, 0x79, 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b,
|
||||
0x5f, 0x42, 0x52, 0x4f, 0x4b, 0x45, 0x4e, 0x10, 0x06, 0x22, 0xba, 0x01, 0x0a, 0x14, 0x52, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
|
||||
0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x68, 0x46,
|
||||
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x6d, 0x61, 0x63, 0x5f,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x68, 0x6d,
|
||||
0x61, 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x6c, 0x69,
|
||||
0x74, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x6c, 0x69,
|
||||
0x74, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x1c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73,
|
||||
0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x73, 0x75, 0x70, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x72,
|
||||
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x51, 0x0a, 0x18, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09,
|
||||
0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x63, 0x0a, 0x19, 0x49, 0x6e, 0x69,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
|
||||
0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x6f, 0x70,
|
||||
0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x52, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
|
||||
0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x36,
|
||||
0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x4b, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x31, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
|
||||
0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
|
||||
0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69,
|
||||
0x6e, 0x66, 0x6f, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74,
|
||||
0x61, 0x22, 0x0e, 0x0a, 0x0c, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x22, 0x0f, 0x0a, 0x0d, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x63, 0x0a, 0x13, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65,
|
||||
0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
|
||||
0x78, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70,
|
||||
0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x35, 0x0a, 0x14, 0x57, 0x72, 0x69, 0x74, 0x65,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x35,
|
||||
0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x77, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09,
|
||||
0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x43, 0x0a, 0x08, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f,
|
||||
0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4d,
|
||||
0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb6,
|
||||
0x01, 0x0a, 0x12, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61,
|
||||
0x74, 0x61, 0x12, 0x48, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73,
|
||||
0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b,
|
||||
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x36, 0x0a, 0x13, 0x50, 0x75, 0x74, 0x4d, 0x61,
|
||||
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f,
|
||||
0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22,
|
||||
0x38, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
|
||||
0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x9d, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x06,
|
||||
0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6b,
|
||||
0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
|
||||
0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x66, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f,
|
||||
0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
|
||||
0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65,
|
||||
0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
|
||||
0x02, 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x15, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27,
|
||||
0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
|
||||
0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x22, 0xb9, 0x06, 0x0a, 0x0f, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72,
|
||||
0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x12, 0x5c, 0x0a, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x73,
|
||||
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6b,
|
||||
0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
|
||||
0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x11, 0x69, 0x6e, 0x69,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x54,
|
||||
0x0a, 0x10, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e,
|
||||
0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61,
|
||||
0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x18, 0x0c, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f,
|
||||
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x4d, 0x0a,
|
||||
0x79, 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||
0x61, 0x22, 0xf5, 0x05, 0x0a, 0x0e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a,
|
||||
0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2a, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
|
||||
0x72, 0x79, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x69,
|
||||
0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x53, 0x0a, 0x10, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f,
|
||||
0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70,
|
||||
0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x18, 0x0c,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x12, 0x4c, 0x0a,
|
||||
0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0d,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c,
|
||||
0x77, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0b,
|
||||
0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x67, 0x65, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x6f,
|
||||
0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x67, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x70, 0x75, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f,
|
||||
0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61,
|
||||
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00,
|
||||
0x52, 0x0b, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a,
|
||||
0x0e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x18,
|
||||
0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00,
|
||||
0x52, 0x0d, 0x66, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x12,
|
||||
0x53, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65,
|
||||
0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61,
|
||||
0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65,
|
||||
0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69,
|
||||
0x66, 0x65, 0x73, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x32, 0x65, 0x0a, 0x0f, 0x4b, 0x6f, 0x70, 0x69, 0x61, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x12, 0x52, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20,
|
||||
0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
|
||||
0x79, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x21, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x2f, 0x6b, 0x6f, 0x70, 0x69,
|
||||
0x61, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x61,
|
||||
0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x77,
|
||||
0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x46, 0x0a, 0x0b, 0x67,
|
||||
0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x23, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x6e, 0x74, 0x12, 0x49, 0x0a, 0x0c, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69,
|
||||
0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48,
|
||||
0x00, 0x52, 0x0b, 0x67, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x49,
|
||||
0x0a, 0x0c, 0x70, 0x75, 0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x10,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x75,
|
||||
0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0e, 0x66, 0x69, 0x6e,
|
||||
0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
|
||||
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x66, 0x69, 0x6e,
|
||||
0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x65,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f,
|
||||
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e,
|
||||
0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e,
|
||||
0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x42, 0x09,
|
||||
0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb9, 0x06, 0x0a, 0x0f, 0x53, 0x65,
|
||||
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x03, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x05,
|
||||
0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6b, 0x6f,
|
||||
0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x45,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x05,
|
||||
0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x5c, 0x0a, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c,
|
||||
0x69, 0x7a, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
|
||||
0x74, 0x6f, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53,
|
||||
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00,
|
||||
0x52, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x53, 0x65, 0x73, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x10, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e,
|
||||
0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
|
||||
0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x65, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x6c, 0x75,
|
||||
0x73, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61,
|
||||
0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x6c, 0x75, 0x73,
|
||||
0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x05, 0x66, 0x6c, 0x75,
|
||||
0x73, 0x68, 0x12, 0x4d, 0x0a, 0x0d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6b, 0x6f, 0x70, 0x69,
|
||||
0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x57, 0x72, 0x69,
|
||||
0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x12, 0x47, 0x0a, 0x0b, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||
0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72,
|
||||
0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0a,
|
||||
0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x67, 0x65,
|
||||
0x74, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x25, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
|
||||
0x6f, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x67, 0x65, 0x74, 0x4d, 0x61,
|
||||
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x70, 0x75, 0x74, 0x5f, 0x6d, 0x61,
|
||||
0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b,
|
||||
0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
|
||||
0x50, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x75, 0x74, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65,
|
||||
0x73, 0x74, 0x12, 0x50, 0x0a, 0x0e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6b, 0x6f, 0x70,
|
||||
0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x46, 0x69,
|
||||
0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x66, 0x69, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x69, 0x66,
|
||||
0x65, 0x73, 0x74, 0x73, 0x12, 0x53, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x6d,
|
||||
0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e,
|
||||
0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
|
||||
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x65, 0x0a, 0x0f, 0x4b, 0x6f, 0x70, 0x69, 0x61, 0x52, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x52, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65, 0x70, 0x6f,
|
||||
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x5f, 0x72, 0x65,
|
||||
0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x29, 0x5a, 0x27,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x6f, 0x70, 0x69, 0x61,
|
||||
0x2f, 0x6b, 0x6f, 0x70, 0x69, 0x61, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
|
||||
0x67, 0x72, 0x70, 0x63, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -45,6 +45,7 @@ message RepositoryParameters {
|
||||
string hash_function = 1;
|
||||
bytes hmac_secret = 2;
|
||||
string splitter = 3;
|
||||
bool supports_content_compression = 4;
|
||||
}
|
||||
|
||||
// InitializeSessionRequest must be sent by the client as the first request in a session.
|
||||
@@ -83,6 +84,7 @@ message FlushResponse {
|
||||
message WriteContentRequest {
|
||||
string prefix = 1;
|
||||
bytes data = 2;
|
||||
uint32 compression = 3;
|
||||
}
|
||||
|
||||
message WriteContentResponse {
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
// Parameters encapsulates all parameters for repository.
|
||||
// returned by /api/v1/repo/parameters.
|
||||
type Parameters struct {
|
||||
HashFunction string `json:"hash"`
|
||||
HMACSecret []byte `json:"hmacSecret"`
|
||||
HashFunction string `json:"hash"`
|
||||
HMACSecret []byte `json:"hmacSecret"`
|
||||
SupportsContentCompression bool `json:"supportsContentCompression"`
|
||||
|
||||
object.Format
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"github.com/kopia/kopia/internal/faketime"
|
||||
"github.com/kopia/kopia/internal/mockfs"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
"github.com/kopia/kopia/snapshot/policy"
|
||||
"github.com/kopia/kopia/snapshot/snapshotfs"
|
||||
@@ -42,7 +43,7 @@ func TestTimeFuncWiring(t *testing.T) {
|
||||
// verify wiring for the content layer
|
||||
nt := ft.Advance(20 * time.Second)
|
||||
|
||||
cid, err := env.RepositoryWriter.ContentManager().WriteContent(ctx, []byte("foo"), "")
|
||||
cid, err := env.RepositoryWriter.ContentManager().WriteContent(ctx, []byte("foo"), "", content.NoCompression)
|
||||
if err != nil {
|
||||
t.Fatal("failed to write content:", err)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/kopia/kopia/internal/serverapi"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/manifest"
|
||||
)
|
||||
@@ -66,7 +68,21 @@ func (s *Server) handleContentPut(ctx context.Context, r *http.Request, data []b
|
||||
return nil, accessDeniedError()
|
||||
}
|
||||
|
||||
actualCID, err := dr.ContentManager().WriteContent(ctx, data, prefix)
|
||||
var comp compression.HeaderID
|
||||
|
||||
if c := r.URL.Query().Get("compression"); c != "" {
|
||||
v, err := strconv.ParseInt(c, 16, 32)
|
||||
if err != nil {
|
||||
return nil, requestError(serverapi.ErrorMalformedRequest, "malformed compression ID")
|
||||
}
|
||||
|
||||
comp = compression.HeaderID(v)
|
||||
if _, ok := compression.ByHeaderID[comp]; !ok {
|
||||
return nil, requestError(serverapi.ErrorMalformedRequest, "invalid compression ID")
|
||||
}
|
||||
}
|
||||
|
||||
actualCID, err := dr.ContentManager().WriteContent(ctx, data, prefix, comp)
|
||||
if err != nil {
|
||||
return nil, internalServerError(err)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,10 @@ func (s *Server) handleRepoParameters(ctx context.Context, r *http.Request, body
|
||||
}
|
||||
|
||||
rp := &remoterepoapi.Parameters{
|
||||
HashFunction: dr.ContentReader().ContentFormat().Hash,
|
||||
HMACSecret: dr.ContentReader().ContentFormat().HMACSecret,
|
||||
Format: dr.ObjectFormat(),
|
||||
HashFunction: dr.ContentReader().ContentFormat().Hash,
|
||||
HMACSecret: dr.ContentReader().ContentFormat().HMACSecret,
|
||||
Format: dr.ObjectFormat(),
|
||||
SupportsContentCompression: dr.ContentReader().SupportsContentCompression(),
|
||||
}
|
||||
|
||||
return rp, nil
|
||||
@@ -48,19 +49,21 @@ func (s *Server) handleRepoStatus(ctx context.Context, r *http.Request, body []b
|
||||
dr, ok := s.rep.(repo.DirectRepository)
|
||||
if ok {
|
||||
return &serverapi.StatusResponse{
|
||||
Connected: true,
|
||||
ConfigFile: dr.ConfigFilename(),
|
||||
Hash: dr.ContentReader().ContentFormat().Hash,
|
||||
Encryption: dr.ContentReader().ContentFormat().Encryption,
|
||||
MaxPackSize: dr.ContentReader().ContentFormat().MaxPackSize,
|
||||
Splitter: dr.ObjectFormat().Splitter,
|
||||
Storage: dr.BlobReader().ConnectionInfo().Type,
|
||||
ClientOptions: dr.ClientOptions(),
|
||||
Connected: true,
|
||||
ConfigFile: dr.ConfigFilename(),
|
||||
Hash: dr.ContentReader().ContentFormat().Hash,
|
||||
Encryption: dr.ContentReader().ContentFormat().Encryption,
|
||||
MaxPackSize: dr.ContentReader().ContentFormat().MaxPackSize,
|
||||
Splitter: dr.ObjectFormat().Splitter,
|
||||
Storage: dr.BlobReader().ConnectionInfo().Type,
|
||||
ClientOptions: dr.ClientOptions(),
|
||||
SupportsContentCompression: dr.ContentReader().SupportsContentCompression(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type remoteRepository interface {
|
||||
APIServerURL() string
|
||||
SupportsContentCompression() bool
|
||||
}
|
||||
|
||||
result := &serverapi.StatusResponse{
|
||||
@@ -70,6 +73,7 @@ type remoteRepository interface {
|
||||
|
||||
if rr, ok := s.rep.(remoteRepository); ok {
|
||||
result.APIServerURL = rr.APIServerURL()
|
||||
result.SupportsContentCompression = rr.SupportsContentCompression()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"github.com/kopia/kopia/internal/auth"
|
||||
"github.com/kopia/kopia/internal/grpcapi"
|
||||
"github.com/kopia/kopia/repo"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/manifest"
|
||||
"github.com/kopia/kopia/repo/object"
|
||||
@@ -230,7 +231,7 @@ func handleWriteContentRequest(ctx context.Context, dw repo.DirectRepositoryWrit
|
||||
return accessDeniedResponse()
|
||||
}
|
||||
|
||||
contentID, err := dw.ContentManager().WriteContent(ctx, req.GetData(), content.ID(req.GetPrefix()))
|
||||
contentID, err := dw.ContentManager().WriteContent(ctx, req.GetData(), content.ID(req.GetPrefix()), compression.HeaderID(req.GetCompression()))
|
||||
if err != nil {
|
||||
return errorResponse(err)
|
||||
}
|
||||
@@ -420,9 +421,10 @@ func (s *Server) handleInitialSessionHandshake(srv grpcapi.KopiaRepository_Sessi
|
||||
Response: &grpcapi.SessionResponse_InitializeSession{
|
||||
InitializeSession: &grpcapi.InitializeSessionResponse{
|
||||
Parameters: &grpcapi.RepositoryParameters{
|
||||
HashFunction: dr.ContentReader().ContentFormat().Hash,
|
||||
HmacSecret: dr.ContentReader().ContentFormat().HMACSecret,
|
||||
Splitter: dr.ObjectFormat().Splitter,
|
||||
HashFunction: dr.ContentReader().ContentFormat().Hash,
|
||||
HmacSecret: dr.ContentReader().ContentFormat().HMACSecret,
|
||||
Splitter: dr.ObjectFormat().Splitter,
|
||||
SupportsContentCompression: dr.ContentReader().SupportsContentCompression(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
|
||||
// StatusResponse is the response of 'status' HTTP API command.
|
||||
type StatusResponse struct {
|
||||
Connected bool `json:"connected"`
|
||||
ConfigFile string `json:"configFile,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Encryption string `json:"encryption,omitempty"`
|
||||
Splitter string `json:"splitter,omitempty"`
|
||||
MaxPackSize int `json:"maxPackSize,omitempty"`
|
||||
Storage string `json:"storage,omitempty"`
|
||||
APIServerURL string `json:"apiServerURL,omitempty"`
|
||||
Connected bool `json:"connected"`
|
||||
ConfigFile string `json:"configFile,omitempty"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Encryption string `json:"encryption,omitempty"`
|
||||
Splitter string `json:"splitter,omitempty"`
|
||||
MaxPackSize int `json:"maxPackSize,omitempty"`
|
||||
Storage string `json:"storage,omitempty"`
|
||||
APIServerURL string `json:"apiServerURL,omitempty"`
|
||||
SupportsContentCompression bool `json:"supportsContentCompression"`
|
||||
|
||||
repo.ClientOptions
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"github.com/kopia/kopia/internal/cache"
|
||||
"github.com/kopia/kopia/internal/clock"
|
||||
"github.com/kopia/kopia/internal/remoterepoapi"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/hashing"
|
||||
"github.com/kopia/kopia/repo/manifest"
|
||||
@@ -30,12 +31,13 @@ type APIServerInfo struct {
|
||||
// remoteRepository is an implementation of Repository that connects to an instance of
|
||||
// API server hosted by `kopia server`, instead of directly manipulating files in the BLOB storage.
|
||||
type apiServerRepository struct {
|
||||
cli *apiclient.KopiaAPIClient
|
||||
h hashing.HashFunc
|
||||
objectFormat object.Format
|
||||
cliOpts ClientOptions
|
||||
omgr *object.Manager
|
||||
wso WriteSessionOptions
|
||||
cli *apiclient.KopiaAPIClient
|
||||
h hashing.HashFunc
|
||||
objectFormat object.Format
|
||||
serverSupportsContentCompression bool
|
||||
cliOpts ClientOptions
|
||||
omgr *object.Manager
|
||||
wso WriteSessionOptions
|
||||
|
||||
isSharedReadOnlySession bool
|
||||
contentCache *cache.PersistentCache
|
||||
@@ -136,6 +138,10 @@ func (r *apiServerRepository) Flush(ctx context.Context) error {
|
||||
return errors.Wrap(r.cli.Post(ctx, "flush", nil, nil), "Flush")
|
||||
}
|
||||
|
||||
func (r *apiServerRepository) SupportsContentCompression() bool {
|
||||
return r.serverSupportsContentCompression
|
||||
}
|
||||
|
||||
func (r *apiServerRepository) NewWriter(ctx context.Context, opt WriteSessionOptions) (RepositoryWriter, error) {
|
||||
// apiServerRepository is stateless except object manager.
|
||||
r2 := *r
|
||||
@@ -177,7 +183,7 @@ func (r *apiServerRepository) GetContent(ctx context.Context, contentID content.
|
||||
})
|
||||
}
|
||||
|
||||
func (r *apiServerRepository) WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error) {
|
||||
func (r *apiServerRepository) WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error) {
|
||||
if err := content.ValidatePrefix(prefix); err != nil {
|
||||
return "", errors.Wrap(err, "invalid prefix")
|
||||
}
|
||||
@@ -194,7 +200,12 @@ func (r *apiServerRepository) WriteContent(ctx context.Context, data []byte, pre
|
||||
|
||||
r.wso.OnUpload(int64(len(data)))
|
||||
|
||||
if err := r.cli.Put(ctx, "contents/"+string(contentID), data, nil); err != nil {
|
||||
maybeCompression := ""
|
||||
if comp != content.NoCompression {
|
||||
maybeCompression = fmt.Sprintf("?compression=%x", comp)
|
||||
}
|
||||
|
||||
if err := r.cli.Put(ctx, "contents/"+string(contentID)+maybeCompression, data, nil); err != nil {
|
||||
return "", errors.Wrapf(err, "error writing content %v", contentID)
|
||||
}
|
||||
|
||||
@@ -266,6 +277,7 @@ func openRestAPIRepository(ctx context.Context, si *APIServerInfo, cliOpts Clien
|
||||
|
||||
rr.h = hf
|
||||
rr.objectFormat = p.Format
|
||||
rr.serverSupportsContentCompression = p.SupportsContentCompression
|
||||
|
||||
// create object manager using rr as contentManager implementation.
|
||||
omgr, err := object.NewObjectManager(ctx, rr, rr.objectFormat)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -13,6 +14,7 @@
|
||||
"github.com/kopia/kopia/internal/cache"
|
||||
"github.com/kopia/kopia/internal/clock"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/encryption"
|
||||
"github.com/kopia/kopia/repo/hashing"
|
||||
)
|
||||
@@ -218,11 +220,31 @@ func (sm *SharedManager) decryptContentAndVerify(payload []byte, bi Info) ([]byt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// reserved for future use
|
||||
if k := bi.GetEncryptionKeyID(); k != 0 {
|
||||
return nil, errors.Errorf("unsupported encryption key ID: %v", k)
|
||||
}
|
||||
|
||||
decrypted, err := sm.decryptAndVerify(payload, iv)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid checksum at %v offset %v length %v", bi.GetPackBlobID(), bi.GetPackOffset(), len(payload))
|
||||
}
|
||||
|
||||
if h := bi.GetCompressionHeaderID(); h != 0 {
|
||||
c := compression.ByHeaderID[h]
|
||||
if c == nil {
|
||||
return nil, errors.Errorf("unsupported compressor %x", h)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
|
||||
if err := c.Decompress(out, decrypted); err != nil {
|
||||
return nil, errors.Wrap(err, "error decompressing")
|
||||
}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
return decrypted, nil
|
||||
}
|
||||
|
||||
@@ -361,6 +383,15 @@ func NewSharedManager(ctx context.Context, st blob.Storage, f *FormattingOptions
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actualIndexVersion := f.IndexVersion
|
||||
if actualIndexVersion == 0 {
|
||||
actualIndexVersion = DefaultIndexVersion
|
||||
}
|
||||
|
||||
if actualIndexVersion < v1IndexVersion || actualIndexVersion > v2IndexVersion {
|
||||
return nil, errors.Errorf("index version %v is not supported", actualIndexVersion)
|
||||
}
|
||||
|
||||
sm := &SharedManager{
|
||||
st: st,
|
||||
encryptor: encryptor,
|
||||
@@ -375,8 +406,8 @@ func NewSharedManager(ctx context.Context, st blob.Storage, f *FormattingOptions
|
||||
repositoryFormatBytes: opts.RepositoryFormatBytes,
|
||||
checkInvariantsOnUnlock: os.Getenv("KOPIA_VERIFY_INVARIANTS") != "",
|
||||
writeFormatVersion: int32(f.Version),
|
||||
encryptionBufferPool: buf.NewPool(ctx, defaultEncryptionBufferPoolSegmentSize+encryptor.Overhead(), "content-manager-encryption"),
|
||||
indexVersion: v1IndexVersion,
|
||||
encryptionBufferPool: buf.NewPool(ctx, defaultEncryptionBufferPoolSegmentSize+encryptor.Overhead()+maxCompressionOverheadPerContent, "content-manager-encryption"),
|
||||
indexVersion: actualIndexVersion,
|
||||
}
|
||||
|
||||
caching = caching.CloneOrDefault()
|
||||
|
||||
@@ -121,7 +121,7 @@ func verifyEndToEndFormatter(ctx context.Context, t *testing.T, hashAlgo, encryp
|
||||
}
|
||||
|
||||
for _, b := range cases {
|
||||
contentID, err := bm.WriteContent(ctx, b, "")
|
||||
contentID, err := bm.WriteContent(ctx, b, "", NoCompression)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
// FormattingOptions describes the rules for formatting contents in repository.
|
||||
type FormattingOptions struct {
|
||||
Version int `json:"version,omitempty"` // version number, must be "1"
|
||||
Hash string `json:"hash,omitempty"` // identifier of the hash algorithm used
|
||||
Encryption string `json:"encryption,omitempty"` // identifier of the encryption algorithm used
|
||||
HMACSecret []byte `json:"secret,omitempty"` // HMAC secret used to generate encryption keys
|
||||
MasterKey []byte `json:"masterKey,omitempty"` // master encryption key (SIV-mode encryption only)
|
||||
MaxPackSize int `json:"maxPackSize,omitempty"` // maximum size of a pack object
|
||||
Version int `json:"version,omitempty"` // version number, must be "1"
|
||||
Hash string `json:"hash,omitempty"` // identifier of the hash algorithm used
|
||||
Encryption string `json:"encryption,omitempty"` // identifier of the encryption algorithm used
|
||||
HMACSecret []byte `json:"secret,omitempty"` // HMAC secret used to generate encryption keys
|
||||
MasterKey []byte `json:"masterKey,omitempty"` // master encryption key (SIV-mode encryption only)
|
||||
MaxPackSize int `json:"maxPackSize,omitempty"` // maximum size of a pack object
|
||||
IndexVersion int `json:"indexVersion,omitempty"` // force particular index format version (1,2,..)
|
||||
}
|
||||
|
||||
// GetEncryptionAlgorithm implements encryption.Parameters.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"github.com/kopia/kopia/internal/clock"
|
||||
"github.com/kopia/kopia/internal/gather"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/logging"
|
||||
)
|
||||
|
||||
@@ -31,10 +32,14 @@
|
||||
PackBlobIDPrefixRegular blob.ID = "p"
|
||||
PackBlobIDPrefixSpecial blob.ID = "q"
|
||||
|
||||
NoCompression compression.HeaderID = 0
|
||||
|
||||
FormatLogModule = "kopia/format"
|
||||
|
||||
maxHashSize = 64
|
||||
defaultEncryptionBufferPoolSegmentSize = 8 << 20 // 8 MB
|
||||
|
||||
DefaultIndexVersion = 1
|
||||
)
|
||||
|
||||
// PackBlobIDPrefixes contains all possible prefixes for pack blobs.
|
||||
@@ -208,7 +213,7 @@ func (bm *WriteManager) maybeRetryWritingFailedPacksUnlocked(ctx context.Context
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bm *WriteManager) addToPackUnlocked(ctx context.Context, contentID ID, data []byte, isDeleted bool) error {
|
||||
func (bm *WriteManager) addToPackUnlocked(ctx context.Context, contentID ID, data []byte, isDeleted bool, comp compression.HeaderID) error {
|
||||
// see if the current index is old enough to cause automatic flush.
|
||||
if err := bm.maybeFlushBasedOnTimeUnlocked(ctx); err != nil {
|
||||
return errors.Wrap(err, "unable to flush old pending writes")
|
||||
@@ -257,10 +262,12 @@ func (bm *WriteManager) addToPackUnlocked(ctx context.Context, contentID ID, dat
|
||||
OriginalLength: uint32(len(data)),
|
||||
}
|
||||
|
||||
if err := bm.maybeEncryptContentDataForPacking(pp.currentPackData, data, contentID); err != nil {
|
||||
actualComp, err := bm.maybeCompressAndEncryptDataForPacking(pp.currentPackData, data, contentID, comp)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to encrypt %q", contentID)
|
||||
}
|
||||
|
||||
info.CompressionHeaderID = actualComp
|
||||
info.PackedLength = uint32(pp.currentPackData.Length()) - info.PackOffset
|
||||
|
||||
pp.currentPackItems[contentID] = info
|
||||
@@ -528,6 +535,8 @@ func (bm *WriteManager) Flush(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// RewriteContent causes reads and re-writes a given content using the most recent format.
|
||||
// TODO(jkowalski): this will currently always re-encrypt and re-compress data, perhaps consider a
|
||||
// pass-through mode that preserves encrypted/compressed bits.
|
||||
func (bm *WriteManager) RewriteContent(ctx context.Context, contentID ID) error {
|
||||
formatLog(ctx).Debugf("rewrite-content %v", contentID)
|
||||
|
||||
@@ -541,7 +550,7 @@ func (bm *WriteManager) RewriteContent(ctx context.Context, contentID ID) error
|
||||
return err
|
||||
}
|
||||
|
||||
return bm.addToPackUnlocked(ctx, contentID, data, bi.GetDeleted())
|
||||
return bm.addToPackUnlocked(ctx, contentID, data, bi.GetDeleted(), bi.GetCompressionHeaderID())
|
||||
}
|
||||
|
||||
// UndeleteContent rewrites the content with the given ID if the content exists
|
||||
@@ -564,7 +573,7 @@ func (bm *WriteManager) UndeleteContent(ctx context.Context, contentID ID) error
|
||||
return err
|
||||
}
|
||||
|
||||
return bm.addToPackUnlocked(ctx, contentID, data, false)
|
||||
return bm.addToPackUnlocked(ctx, contentID, data, false, bi.GetCompressionHeaderID())
|
||||
}
|
||||
|
||||
func packPrefixForContentID(contentID ID) blob.ID {
|
||||
@@ -609,9 +618,14 @@ func (bm *WriteManager) getOrCreatePendingPackInfoLocked(ctx context.Context, pr
|
||||
return bm.pendingPacks[prefix], nil
|
||||
}
|
||||
|
||||
// SupportsContentCompression returns true if content manager supports content-compression.
|
||||
func (bm *WriteManager) SupportsContentCompression() bool {
|
||||
return bm.format.IndexVersion >= v2IndexVersion
|
||||
}
|
||||
|
||||
// WriteContent saves a given content of data to a pack group with a provided name and returns a contentID
|
||||
// that's based on the contents of data written.
|
||||
func (bm *WriteManager) WriteContent(ctx context.Context, data []byte, prefix ID) (ID, error) {
|
||||
func (bm *WriteManager) WriteContent(ctx context.Context, data []byte, prefix ID, comp compression.HeaderID) (ID, error) {
|
||||
if err := bm.maybeRetryWritingFailedPacksUnlocked(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -639,7 +653,7 @@ func (bm *WriteManager) WriteContent(ctx context.Context, data []byte, prefix ID
|
||||
formatLog(ctx).Debugf("write-content %v new", contentID)
|
||||
}
|
||||
|
||||
err := bm.addToPackUnlocked(ctx, contentID, data, false)
|
||||
err := bm.addToPackUnlocked(ctx, contentID, data, false, comp)
|
||||
|
||||
return contentID, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
cryptorand "crypto/rand"
|
||||
@@ -11,18 +12,52 @@
|
||||
|
||||
"github.com/kopia/kopia/internal/gather"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/encryption"
|
||||
"github.com/kopia/kopia/repo/hashing"
|
||||
)
|
||||
|
||||
// maxCompressionOverheadPerContent is the maximum amount of overhead any compressor
|
||||
// would need for non-compressible data of maximum size.
|
||||
// The consequences of getting this wrong are not fatal - just an unnecessary memory allocation.
|
||||
const maxCompressionOverheadPerContent = 16384
|
||||
|
||||
const indexBlobCompactionWarningThreshold = 1000
|
||||
|
||||
func (sm *SharedManager) maybeEncryptContentDataForPacking(output *gather.WriteBuffer, data []byte, contentID ID) error {
|
||||
func (sm *SharedManager) maybeCompressAndEncryptDataForPacking(output *gather.WriteBuffer, data []byte, contentID ID, comp compression.HeaderID) (compression.HeaderID, error) {
|
||||
var hashOutput [maxHashSize]byte
|
||||
|
||||
iv, err := getPackedContentIV(hashOutput[:], contentID)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get packed content IV for %q", contentID)
|
||||
return NoCompression, errors.Wrapf(err, "unable to get packed content IV for %q", contentID)
|
||||
}
|
||||
|
||||
// nolint:nestif
|
||||
if comp != NoCompression {
|
||||
if sm.format.IndexVersion < v2IndexVersion {
|
||||
return NoCompression, errors.Errorf("compression is not enabled for this repository.")
|
||||
}
|
||||
|
||||
// allocate temporary buffer to hold the compressed bytes.
|
||||
tmp := sm.encryptionBufferPool.Allocate(len(data) + maxCompressionOverheadPerContent)
|
||||
defer tmp.Release()
|
||||
|
||||
c := compression.ByHeaderID[comp]
|
||||
if c == nil {
|
||||
return NoCompression, errors.Errorf("unsupported compressor %x", comp)
|
||||
}
|
||||
|
||||
cbuf := bytes.NewBuffer(tmp.Data[:0])
|
||||
if err = c.Compress(cbuf, data); err != nil {
|
||||
return NoCompression, errors.Wrap(err, "compression error")
|
||||
}
|
||||
|
||||
if cd := cbuf.Bytes(); len(cd) >= len(data) {
|
||||
// data was not compressible enough.
|
||||
comp = NoCompression
|
||||
} else {
|
||||
data = cd
|
||||
}
|
||||
}
|
||||
|
||||
b := sm.encryptionBufferPool.Allocate(len(data) + sm.encryptor.Overhead())
|
||||
@@ -30,14 +65,14 @@ func (sm *SharedManager) maybeEncryptContentDataForPacking(output *gather.WriteB
|
||||
|
||||
cipherText, err := sm.encryptor.Encrypt(b.Data[:0], data, iv)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to encrypt")
|
||||
return NoCompression, errors.Wrap(err, "unable to encrypt")
|
||||
}
|
||||
|
||||
sm.Stats.encrypted(len(data))
|
||||
|
||||
output.Append(cipherText)
|
||||
|
||||
return nil
|
||||
return comp, nil
|
||||
}
|
||||
|
||||
func writeRandomBytesToBuffer(b *gather.WriteBuffer, count int) error {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
"github.com/kopia/kopia/repo/blob/logging"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -253,7 +254,7 @@ func TestContentManagerWriteMultiple(t *testing.T) {
|
||||
for i := 0; i < repeatCount; i++ {
|
||||
b := seededRandomData(i, i%113)
|
||||
|
||||
blkID, err := bm.WriteContent(ctx, b, "")
|
||||
blkID, err := bm.WriteContent(ctx, b, "", NoCompression)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
}
|
||||
@@ -324,12 +325,12 @@ func TestContentManagerFailedToWritePack(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
_, err = bm.WriteContent(ctx, seededRandomData(1, 10), "")
|
||||
_, err = bm.WriteContent(ctx, seededRandomData(1, 10), "", NoCompression)
|
||||
if !errors.Is(err, sessionPutErr) {
|
||||
t.Fatalf("can't create first content: %v", err)
|
||||
}
|
||||
|
||||
b1, err := bm.WriteContent(ctx, seededRandomData(1, 10), "")
|
||||
b1, err := bm.WriteContent(ctx, seededRandomData(1, 10), "", NoCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("can't create content: %v", err)
|
||||
}
|
||||
@@ -337,7 +338,7 @@ func TestContentManagerFailedToWritePack(t *testing.T) {
|
||||
// advance time enough to cause auto-flush, which will fail (firstPutErr)
|
||||
ta.Advance(1 * time.Hour)
|
||||
|
||||
if _, err := bm.WriteContent(ctx, seededRandomData(2, 10), ""); !errors.Is(err, firstPutErr) {
|
||||
if _, err := bm.WriteContent(ctx, seededRandomData(2, 10), "", NoCompression); !errors.Is(err, firstPutErr) {
|
||||
t.Fatalf("can't create 2nd content: %v", err)
|
||||
}
|
||||
|
||||
@@ -1771,7 +1772,7 @@ func verifyVersionCompat(t *testing.T, writeVersion int) {
|
||||
data := make([]byte, i)
|
||||
cryptorand.Read(data)
|
||||
|
||||
cid, err := mgr.WriteContent(ctx, data, "")
|
||||
cid, err := mgr.WriteContent(ctx, data, "", NoCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to write %v bytes: %v", len(data), err)
|
||||
}
|
||||
@@ -1921,6 +1922,94 @@ func verifyContentManagerDataSet(ctx context.Context, t *testing.T, mgr *WriteMa
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompression_Disabled(t *testing.T) {
|
||||
data := blobtesting.DataMap{}
|
||||
st := blobtesting.NewMapStorage(data, nil, nil)
|
||||
bm := newTestContentManagerWithTweaks(t, st, &contentManagerTestTweaks{
|
||||
indexVersion: v1IndexVersion,
|
||||
})
|
||||
|
||||
require.False(t, bm.SupportsContentCompression())
|
||||
ctx := testlogging.Context(t)
|
||||
compressibleData := bytes.Repeat([]byte{1, 2, 3, 4}, 1000)
|
||||
|
||||
// with index v1 the compression is disabled
|
||||
_, err := bm.WriteContent(ctx, compressibleData, "", compression.ByName["pgzip"].HeaderID())
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCompression_CompressibleData(t *testing.T) {
|
||||
data := blobtesting.DataMap{}
|
||||
st := blobtesting.NewMapStorage(data, nil, nil)
|
||||
bm := newTestContentManagerWithTweaks(t, st, &contentManagerTestTweaks{
|
||||
indexVersion: v2IndexVersion,
|
||||
})
|
||||
|
||||
require.True(t, bm.SupportsContentCompression())
|
||||
|
||||
ctx := testlogging.Context(t)
|
||||
compressibleData := bytes.Repeat([]byte{1, 2, 3, 4}, 1000)
|
||||
headerID := compression.ByName["gzip"].HeaderID()
|
||||
|
||||
cid, err := bm.WriteContent(ctx, compressibleData, "", headerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
ci, err := bm.ContentInfo(ctx, cid)
|
||||
require.NoError(t, err)
|
||||
|
||||
// gzip-compressed length
|
||||
require.Equal(t, uint32(79), ci.GetPackedLength())
|
||||
require.Equal(t, uint32(len(compressibleData)), ci.GetOriginalLength())
|
||||
require.Equal(t, headerID, ci.GetCompressionHeaderID())
|
||||
|
||||
verifyContent(ctx, t, bm, cid, compressibleData)
|
||||
|
||||
require.NoError(t, bm.Flush(ctx))
|
||||
verifyContent(ctx, t, bm, cid, compressibleData)
|
||||
|
||||
bm2 := newTestContentManagerWithTweaks(t, st, &contentManagerTestTweaks{
|
||||
indexVersion: v2IndexVersion,
|
||||
})
|
||||
verifyContent(ctx, t, bm2, cid, compressibleData)
|
||||
}
|
||||
|
||||
func TestCompression_NonCompressibleData(t *testing.T) {
|
||||
data := blobtesting.DataMap{}
|
||||
st := blobtesting.NewMapStorage(data, nil, nil)
|
||||
bm := newTestContentManagerWithTweaks(t, st, &contentManagerTestTweaks{
|
||||
indexVersion: v2IndexVersion,
|
||||
})
|
||||
|
||||
require.True(t, bm.SupportsContentCompression())
|
||||
|
||||
ctx := testlogging.Context(t)
|
||||
nonCompressibleData := make([]byte, 65000)
|
||||
headerID := compression.ByName["pgzip"].HeaderID()
|
||||
|
||||
rand.Read(nonCompressibleData)
|
||||
|
||||
cid, err := bm.WriteContent(ctx, nonCompressibleData, "", headerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
verifyContent(ctx, t, bm, cid, nonCompressibleData)
|
||||
|
||||
ci, err := bm.ContentInfo(ctx, cid)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify compression did not occur
|
||||
require.True(t, ci.GetPackedLength() > ci.GetOriginalLength())
|
||||
require.Equal(t, uint32(len(nonCompressibleData)), ci.GetOriginalLength())
|
||||
require.Equal(t, NoCompression, ci.GetCompressionHeaderID())
|
||||
|
||||
require.NoError(t, bm.Flush(ctx))
|
||||
verifyContent(ctx, t, bm, cid, nonCompressibleData)
|
||||
|
||||
bm2 := newTestContentManagerWithTweaks(t, st, &contentManagerTestTweaks{
|
||||
indexVersion: v2IndexVersion,
|
||||
})
|
||||
verifyContent(ctx, t, bm2, cid, nonCompressibleData)
|
||||
}
|
||||
|
||||
func newTestContentManager(t *testing.T, data blobtesting.DataMap) *WriteManager {
|
||||
t.Helper()
|
||||
|
||||
@@ -1944,6 +2033,8 @@ func newTestContentManagerWithCustomTime(t *testing.T, data blobtesting.DataMap,
|
||||
type contentManagerTestTweaks struct {
|
||||
CachingOptions
|
||||
ManagerOptions
|
||||
|
||||
indexVersion int
|
||||
}
|
||||
|
||||
func newTestContentManagerWithTweaks(t *testing.T, st blob.Storage, tweaks *contentManagerTestTweaks) *WriteManager {
|
||||
@@ -1964,6 +2055,8 @@ func newTestContentManagerWithTweaks(t *testing.T, st blob.Storage, tweaks *cont
|
||||
HMACSecret: hmacSecret,
|
||||
MaxPackSize: maxPackSize,
|
||||
Version: 1,
|
||||
|
||||
IndexVersion: tweaks.indexVersion,
|
||||
}
|
||||
|
||||
bm, err := NewManager(ctx, st, fo, &tweaks.CachingOptions, &tweaks.ManagerOptions)
|
||||
@@ -2023,7 +2116,7 @@ func verifyContent(ctx context.Context, t *testing.T, bm *WriteManager, contentI
|
||||
func writeContentAndVerify(ctx context.Context, t *testing.T, bm *WriteManager, b []byte) ID {
|
||||
t.Helper()
|
||||
|
||||
contentID, err := bm.WriteContent(ctx, b, "")
|
||||
contentID, err := bm.WriteContent(ctx, b, "", NoCompression)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
}
|
||||
@@ -2061,13 +2154,13 @@ func writeContentWithRetriesAndVerify(ctx context.Context, t *testing.T, bm *Wri
|
||||
|
||||
log(ctx).Infof("*** starting writeContentWithRetriesAndVerify")
|
||||
|
||||
contentID, err := bm.WriteContent(ctx, b, "")
|
||||
contentID, err := bm.WriteContent(ctx, b, "", NoCompression)
|
||||
for i := 0; err != nil && i < maxRetries; i++ {
|
||||
retryCount++
|
||||
|
||||
log(ctx).Infof("*** try %v", retryCount)
|
||||
|
||||
contentID, err = bm.WriteContent(ctx, b, "")
|
||||
contentID, err = bm.WriteContent(ctx, b, "", NoCompression)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
// Reader defines content read API.
|
||||
type Reader interface {
|
||||
SupportsContentCompression() bool
|
||||
ContentFormat() FormattingOptions
|
||||
GetContent(ctx context.Context, id ID) ([]byte, error)
|
||||
ContentInfo(ctx context.Context, id ID) (Info, error)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"github.com/kopia/kopia/internal/retry"
|
||||
"github.com/kopia/kopia/internal/tlsutil"
|
||||
"github.com/kopia/kopia/repo/blob"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/hashing"
|
||||
"github.com/kopia/kopia/repo/manifest"
|
||||
@@ -55,10 +56,11 @@ type grpcRepositoryClient struct {
|
||||
// how many times we tried to establish inner session
|
||||
innerSessionAttemptCount int
|
||||
|
||||
h hashing.HashFunc
|
||||
objectFormat object.Format
|
||||
cliOpts ClientOptions
|
||||
omgr *object.Manager
|
||||
h hashing.HashFunc
|
||||
objectFormat object.Format
|
||||
serverSupportsContentCompression bool
|
||||
cliOpts ClientOptions
|
||||
omgr *object.Manager
|
||||
|
||||
contentCache *cache.PersistentCache
|
||||
}
|
||||
@@ -533,7 +535,11 @@ func (r *grpcInnerSession) GetContent(ctx context.Context, contentID content.ID)
|
||||
return nil, errNoSessionResponse()
|
||||
}
|
||||
|
||||
func (r *grpcRepositoryClient) WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error) {
|
||||
func (r *grpcRepositoryClient) SupportsContentCompression() bool {
|
||||
return r.serverSupportsContentCompression
|
||||
}
|
||||
|
||||
func (r *grpcRepositoryClient) WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error) {
|
||||
if err := content.ValidatePrefix(prefix); err != nil {
|
||||
return "", errors.Wrap(err, "invalid prefix")
|
||||
}
|
||||
@@ -551,7 +557,7 @@ func (r *grpcRepositoryClient) WriteContent(ctx context.Context, data []byte, pr
|
||||
r.opt.OnUpload(int64(len(data)))
|
||||
|
||||
v, err := r.inSessionWithoutRetry(ctx, func(ctx context.Context, sess *grpcInnerSession) (interface{}, error) {
|
||||
return sess.WriteContent(ctx, data, prefix)
|
||||
return sess.WriteContent(ctx, data, prefix, comp)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -565,7 +571,7 @@ func (r *grpcRepositoryClient) WriteContent(ctx context.Context, data []byte, pr
|
||||
return v.(content.ID), nil
|
||||
}
|
||||
|
||||
func (r *grpcInnerSession) WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error) {
|
||||
func (r *grpcInnerSession) WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error) {
|
||||
if err := content.ValidatePrefix(prefix); err != nil {
|
||||
return "", errors.Wrap(err, "invalid prefix")
|
||||
}
|
||||
@@ -573,8 +579,9 @@ func (r *grpcInnerSession) WriteContent(ctx context.Context, data []byte, prefix
|
||||
for resp := range r.sendRequest(ctx, &apipb.SessionRequest{
|
||||
Request: &apipb.SessionRequest_WriteContent{
|
||||
WriteContent: &apipb.WriteContentRequest{
|
||||
Data: data,
|
||||
Prefix: string(prefix),
|
||||
Data: data,
|
||||
Prefix: string(prefix),
|
||||
Compression: uint32(comp),
|
||||
},
|
||||
},
|
||||
}) {
|
||||
@@ -773,6 +780,8 @@ func newGRPCAPIRepositoryForConnection(ctx context.Context, conn *grpc.ClientCon
|
||||
Splitter: p.Splitter,
|
||||
}
|
||||
|
||||
rr.serverSupportsContentCompression = p.SupportsContentCompression
|
||||
|
||||
rr.omgr, err = object.NewObjectManager(ctx, rr, rr.objectFormat)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to initialize object manager")
|
||||
|
||||
@@ -89,12 +89,13 @@ func formatBlobFromOptions(opt *NewRepositoryOptions) *formatBlob {
|
||||
func repositoryObjectFormatFromOptions(opt *NewRepositoryOptions) *repositoryObjectFormat {
|
||||
f := &repositoryObjectFormat{
|
||||
FormattingOptions: content.FormattingOptions{
|
||||
Version: 1,
|
||||
Hash: applyDefaultString(opt.BlockFormat.Hash, hashing.DefaultAlgorithm),
|
||||
Encryption: applyDefaultString(opt.BlockFormat.Encryption, encryption.DefaultAlgorithm),
|
||||
HMACSecret: applyDefaultRandomBytes(opt.BlockFormat.HMACSecret, hmacSecretLength),
|
||||
MasterKey: applyDefaultRandomBytes(opt.BlockFormat.MasterKey, masterKeyLength),
|
||||
MaxPackSize: applyDefaultInt(opt.BlockFormat.MaxPackSize, 20<<20), //nolint:gomnd
|
||||
Version: 1,
|
||||
Hash: applyDefaultString(opt.BlockFormat.Hash, hashing.DefaultAlgorithm),
|
||||
Encryption: applyDefaultString(opt.BlockFormat.Encryption, encryption.DefaultAlgorithm),
|
||||
HMACSecret: applyDefaultRandomBytes(opt.BlockFormat.HMACSecret, hmacSecretLength),
|
||||
MasterKey: applyDefaultRandomBytes(opt.BlockFormat.MasterKey, masterKeyLength),
|
||||
MaxPackSize: applyDefaultInt(opt.BlockFormat.MaxPackSize, 20<<20), //nolint:gomnd
|
||||
IndexVersion: applyDefaultInt(opt.BlockFormat.IndexVersion, content.DefaultIndexVersion),
|
||||
},
|
||||
Format: object.Format{
|
||||
Splitter: applyDefaultString(opt.ObjectFormat.Splitter, splitter.DefaultAlgorithm),
|
||||
|
||||
@@ -76,7 +76,7 @@ func (m *committedManifestManager) writeEntriesLocked(ctx context.Context, entri
|
||||
mustSucceed(gz.Flush())
|
||||
mustSucceed(gz.Close())
|
||||
|
||||
contentID, err := m.b.WriteContent(ctx, buf.Bytes(), ContentPrefix)
|
||||
contentID, err := m.b.WriteContent(ctx, buf.Bytes(), ContentPrefix, content.NoCompression)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to write content")
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kopia/kopia/internal/clock"
|
||||
"github.com/kopia/kopia/repo/compression"
|
||||
"github.com/kopia/kopia/repo/content"
|
||||
"github.com/kopia/kopia/repo/logging"
|
||||
)
|
||||
@@ -36,7 +37,7 @@
|
||||
type contentManager interface {
|
||||
Revision() int64
|
||||
GetContent(ctx context.Context, contentID content.ID) ([]byte, error)
|
||||
WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error)
|
||||
WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error)
|
||||
DeleteContent(ctx context.Context, contentID content.ID) error
|
||||
IterateContents(ctx context.Context, options content.IterateOptions, callback content.IterateCallback) error
|
||||
DisableIndexFlush(ctx context.Context)
|
||||
|
||||
@@ -34,7 +34,8 @@ type contentReader interface {
|
||||
|
||||
type contentManager interface {
|
||||
contentReader
|
||||
WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error)
|
||||
SupportsContentCompression() bool
|
||||
WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error)
|
||||
}
|
||||
|
||||
// Format describes the format of objects in a repository.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/kopia/kopia/internal/clock"
|
||||
@@ -29,8 +30,10 @@
|
||||
)
|
||||
|
||||
type fakeContentManager struct {
|
||||
mu sync.Mutex
|
||||
data map[content.ID][]byte
|
||||
mu sync.Mutex
|
||||
data map[content.ID][]byte
|
||||
compresionIDs map[content.ID]compression.HeaderID
|
||||
supportsContentCompression bool
|
||||
}
|
||||
|
||||
func (f *fakeContentManager) GetContent(ctx context.Context, contentID content.ID) ([]byte, error) {
|
||||
@@ -44,7 +47,7 @@ func (f *fakeContentManager) GetContent(ctx context.Context, contentID content.I
|
||||
return nil, content.ErrContentNotFound
|
||||
}
|
||||
|
||||
func (f *fakeContentManager) WriteContent(ctx context.Context, data []byte, prefix content.ID) (content.ID, error) {
|
||||
func (f *fakeContentManager) WriteContent(ctx context.Context, data []byte, prefix content.ID, comp compression.HeaderID) (content.ID, error) {
|
||||
h := sha256.New()
|
||||
h.Write(data)
|
||||
contentID := prefix + content.ID(hex.EncodeToString(h.Sum(nil)))
|
||||
@@ -53,10 +56,17 @@ func (f *fakeContentManager) WriteContent(ctx context.Context, data []byte, pref
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.data[contentID] = append([]byte(nil), data...)
|
||||
if f.compresionIDs != nil {
|
||||
f.compresionIDs[contentID] = comp
|
||||
}
|
||||
|
||||
return contentID, nil
|
||||
}
|
||||
|
||||
func (f *fakeContentManager) SupportsContentCompression() bool {
|
||||
return f.supportsContentCompression
|
||||
}
|
||||
|
||||
func (f *fakeContentManager) ContentInfo(ctx context.Context, contentID content.ID) (content.Info, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
@@ -72,16 +82,16 @@ func (f *fakeContentManager) Flush(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupTest(t *testing.T) (map[content.ID][]byte, *Manager) {
|
||||
func setupTest(t *testing.T, compressionHeaderID map[content.ID]compression.HeaderID) (map[content.ID][]byte, *Manager) {
|
||||
t.Helper()
|
||||
|
||||
return setupTestWithData(t, map[content.ID][]byte{})
|
||||
}
|
||||
data := map[content.ID][]byte{}
|
||||
|
||||
func setupTestWithData(t *testing.T, data map[content.ID][]byte) (map[content.ID][]byte, *Manager) {
|
||||
t.Helper()
|
||||
|
||||
r, err := NewObjectManager(testlogging.Context(t), &fakeContentManager{data: data}, Format{
|
||||
r, err := NewObjectManager(testlogging.Context(t), &fakeContentManager{
|
||||
data: data,
|
||||
supportsContentCompression: compressionHeaderID != nil,
|
||||
compresionIDs: compressionHeaderID,
|
||||
}, Format{
|
||||
Splitter: "FIXED-1M",
|
||||
})
|
||||
if err != nil {
|
||||
@@ -109,7 +119,7 @@ func TestWriters(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
data, om := setupTest(t)
|
||||
data, om := setupTest(t, nil)
|
||||
|
||||
writer := om.NewWriter(ctx, WriterOptions{})
|
||||
|
||||
@@ -144,9 +154,46 @@ func objectIDsEqual(o1, o2 ID) bool {
|
||||
return o1 == o2
|
||||
}
|
||||
|
||||
func TestCompression_ContentCompressionEnabled(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
|
||||
cmap := map[content.ID]compression.HeaderID{}
|
||||
_, om := setupTest(t, cmap)
|
||||
|
||||
w := om.NewWriter(ctx, WriterOptions{
|
||||
Compressor: "gzip",
|
||||
})
|
||||
w.Write(bytes.Repeat([]byte{1, 2, 3, 4}, 1000))
|
||||
oid, err := w.Result()
|
||||
require.NoError(t, err)
|
||||
|
||||
cid, isCompressed, ok := oid.ContentID()
|
||||
require.True(t, ok)
|
||||
require.False(t, isCompressed) // oid will not indicate compression
|
||||
require.Equal(t, compression.ByName["gzip"].HeaderID(), cmap[cid])
|
||||
}
|
||||
|
||||
func TestCompression_ContentCompressionDisabled(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
|
||||
// this disables content compression
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
w := om.NewWriter(ctx, WriterOptions{
|
||||
Compressor: "gzip",
|
||||
})
|
||||
w.Write(bytes.Repeat([]byte{1, 2, 3, 4}, 1000))
|
||||
oid, err := w.Result()
|
||||
require.NoError(t, err)
|
||||
|
||||
_, isCompressed, ok := oid.ContentID()
|
||||
require.True(t, ok)
|
||||
require.True(t, isCompressed) // oid will indicate compression
|
||||
}
|
||||
|
||||
func TestWriterCompleteChunkInTwoWrites(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
b := make([]byte, 100)
|
||||
writer := om.NewWriter(ctx, WriterOptions{})
|
||||
@@ -161,7 +208,7 @@ func TestWriterCompleteChunkInTwoWrites(t *testing.T) {
|
||||
|
||||
func TestCheckpointing(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
writer := om.NewWriter(ctx, WriterOptions{})
|
||||
|
||||
@@ -348,7 +395,7 @@ func TestIndirection(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
data, om := setupTest(t)
|
||||
data, om := setupTest(t, nil)
|
||||
|
||||
contentBytes := make([]byte, c.dataLength)
|
||||
|
||||
@@ -400,7 +447,7 @@ func TestHMAC(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
c := bytes.Repeat([]byte{0xcd}, 50)
|
||||
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
w := om.NewWriter(ctx, WriterOptions{})
|
||||
w.Write(c)
|
||||
@@ -414,7 +461,7 @@ func TestHMAC(t *testing.T) {
|
||||
// nolint:gocyclo
|
||||
func TestConcatenate(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
phrase := []byte("hello world\n")
|
||||
phraseLength := len(phrase)
|
||||
@@ -549,7 +596,7 @@ func mustWriteObject(t *testing.T, om *Manager, data []byte, compressor compress
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
data, om := setupTest(t)
|
||||
data, om := setupTest(t, nil)
|
||||
|
||||
storedPayload := []byte("foo\nbar")
|
||||
data["a76999788386641a3ec798554f1fe7e6"] = storedPayload
|
||||
@@ -589,7 +636,7 @@ func TestReader(t *testing.T) {
|
||||
|
||||
func TestReaderStoredBlockNotFound(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
objectID, err := ParseID("deadbeef")
|
||||
if err != nil {
|
||||
@@ -610,7 +657,7 @@ func TestEndToEndReadAndSeek(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
for _, size := range []int{1, 199, 200, 201, 9999, 512434, 5012434} {
|
||||
// Create some random data sample of the specified size.
|
||||
@@ -663,7 +710,7 @@ func TestEndToEndReadAndSeekWithCompression(t *testing.T) {
|
||||
|
||||
totalBytesWritten := 0
|
||||
|
||||
data, om := setupTest(t)
|
||||
data, om := setupTest(t, nil)
|
||||
|
||||
for _, size := range sizes {
|
||||
var inputData []byte
|
||||
@@ -762,7 +809,7 @@ func verify(ctx context.Context, t *testing.T, cr contentReader, objectID ID, ex
|
||||
// nolint:gocyclo
|
||||
func TestSeek(t *testing.T) {
|
||||
ctx := testlogging.Context(t)
|
||||
_, om := setupTest(t)
|
||||
_, om := setupTest(t, nil)
|
||||
|
||||
for _, size := range []int{0, 1, 500000, 15000000} {
|
||||
randomData := make([]byte, size)
|
||||
|
||||
@@ -189,13 +189,22 @@ func (w *objectWriter) prepareAndWriteContentChunk(chunkID int, data []byte) err
|
||||
b := w.om.bufferPool.Allocate(len(data) + maxCompressionOverheadPerSegment)
|
||||
defer b.Release()
|
||||
|
||||
comp := content.NoCompression
|
||||
objectComp := w.compressor
|
||||
|
||||
// do not compress in this layer, instead pass comp to the content manager.
|
||||
if w.om.contentMgr.SupportsContentCompression() && w.compressor != nil {
|
||||
comp = w.compressor.HeaderID()
|
||||
objectComp = nil
|
||||
}
|
||||
|
||||
// contentBytes is what we're going to write to the content manager, it potentially uses bytes from b
|
||||
contentBytes, isCompressed, err := maybeCompressedContentBytes(w.compressor, bytes.NewBuffer(b.Data[:0]), data)
|
||||
contentBytes, isCompressed, err := maybeCompressedContentBytes(objectComp, bytes.NewBuffer(b.Data[:0]), data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to prepare content bytes")
|
||||
}
|
||||
|
||||
contentID, err := w.om.contentMgr.WriteContent(w.ctx, contentBytes, w.prefix)
|
||||
contentID, err := w.om.contentMgr.WriteContent(w.ctx, contentBytes, w.prefix, comp)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to write content chunk %v of %v: %v", chunkID, w.description, err)
|
||||
}
|
||||
|
||||
@@ -56,11 +56,43 @@ func TestCompression(t *testing.T) {
|
||||
oid := sources[0].Snapshots[0].ObjectID
|
||||
entries := clitestutil.ListDirectory(t, e, oid)
|
||||
|
||||
if !strings.HasPrefix(entries[0].ObjectID, "Z") {
|
||||
t.Errorf("expected compressed object, got %v", entries[0].ObjectID)
|
||||
supportsContentLevelCompression := containsLineStartingWith(
|
||||
e.RunAndExpectSuccess(t, "repo", "status"),
|
||||
"Content compression: true",
|
||||
)
|
||||
|
||||
// without content-level compression, we'll do it at object level and object ID will be prefixed with 'Z'
|
||||
if !supportsContentLevelCompression {
|
||||
if !strings.HasPrefix(entries[0].ObjectID, "Z") {
|
||||
t.Errorf("expected compressed object, got %v", entries[0].ObjectID)
|
||||
}
|
||||
} else {
|
||||
// with content-level compression we're looking for a content with compression.
|
||||
lines := e.RunAndExpectSuccess(t, "content", "ls", "-c")
|
||||
found := false
|
||||
|
||||
for _, l := range lines {
|
||||
if strings.HasPrefix(l, entries[0].ObjectID) {
|
||||
require.Contains(t, l, "pgzip")
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
require.True(t, found)
|
||||
}
|
||||
|
||||
if lines := e.RunAndExpectSuccess(t, "show", entries[0].ObjectID); !reflect.DeepEqual(dataLines, lines) {
|
||||
t.Errorf("invalid object contents")
|
||||
}
|
||||
}
|
||||
|
||||
func containsLineStartingWith(lines []string, prefix string) bool {
|
||||
for _, l := range lines {
|
||||
if strings.HasPrefix(l, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
68
tests/end_to_end_test/content_info_test.go
Normal file
68
tests/end_to_end_test/content_info_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package endtoend_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kopia/kopia/internal/testutil"
|
||||
"github.com/kopia/kopia/snapshot"
|
||||
"github.com/kopia/kopia/tests/testenv"
|
||||
)
|
||||
|
||||
func TestContentListAndStats_Indexv1(t *testing.T) {
|
||||
t.Parallel()
|
||||
testContentListAndStats(t, "1")
|
||||
}
|
||||
|
||||
func TestContentListAndStats_Indexv2(t *testing.T) {
|
||||
t.Parallel()
|
||||
testContentListAndStats(t, "2")
|
||||
}
|
||||
|
||||
// nolint:thelper
|
||||
func testContentListAndStats(t *testing.T, indexVersion string) {
|
||||
runner := testenv.NewInProcRunner(t)
|
||||
e := testenv.NewCLITest(t, runner)
|
||||
|
||||
e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir, "--index-version", indexVersion)
|
||||
|
||||
require.Empty(t, e.RunAndExpectSuccess(t, "content", "list", "--deleted-only"))
|
||||
e.RunAndExpectSuccess(t, "policy", "set", "--global", "--compression", "pgzip")
|
||||
|
||||
srcDir := testutil.TempDirectory(t)
|
||||
ioutil.WriteFile(filepath.Join(srcDir, "compressible.txt"),
|
||||
bytes.Repeat([]byte{1, 2, 3, 4}, 1000),
|
||||
0o600,
|
||||
)
|
||||
|
||||
var man snapshot.Manifest
|
||||
|
||||
testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "snapshot", "create", srcDir, "--json"), &man)
|
||||
contentID := string(man.RootObjectID())
|
||||
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list"), contentID))
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "-l"), contentID))
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "-c"), contentID))
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "--summary"), "Total: "))
|
||||
|
||||
e.RunAndExpectSuccess(t, "content", "stats")
|
||||
|
||||
// sleep a bit to ensure at least one second passes, otherwise delete may end up happen on the same
|
||||
// second as create, in which case creation will prevail.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
e.RunAndExpectSuccess(t, "content", "delete", contentID)
|
||||
|
||||
require.False(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list"), contentID))
|
||||
require.False(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "-l"), contentID))
|
||||
require.False(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "-c"), contentID))
|
||||
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "--deleted"), contentID))
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "--deleted", "-l"), contentID))
|
||||
require.True(t, containsLineStartingWith(e.RunAndExpectSuccess(t, "content", "list", "--deleted", "-c"), contentID))
|
||||
}
|
||||
@@ -233,7 +233,7 @@ func writeRandomBlock(ctx context.Context, t *testing.T, r repo.DirectRepository
|
||||
data := make([]byte, 1000)
|
||||
cryptorand.Read(data)
|
||||
|
||||
contentID, err := r.ContentManager().WriteContent(ctx, data, "")
|
||||
contentID, err := r.ContentManager().WriteContent(ctx, data, "", content.NoCompression)
|
||||
if err == nil {
|
||||
knownBlocksMutex.Lock()
|
||||
if len(knownBlocks) >= 1000 {
|
||||
|
||||
@@ -95,7 +95,7 @@ type writtenBlock struct {
|
||||
|
||||
dataCopy := append([]byte{}, data...)
|
||||
|
||||
contentID, err := bm.WriteContent(ctx, data, "")
|
||||
contentID, err := bm.WriteContent(ctx, data, "", content.NoCompression)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
return
|
||||
|
||||
@@ -31,7 +31,7 @@ func (e *CLIInProcRunner) Start(t *testing.T, args []string) (stdout, stderr io.
|
||||
func NewInProcRunner(t *testing.T) *CLIInProcRunner {
|
||||
t.Helper()
|
||||
|
||||
if os.Getenv("KOPIA_EXE") != "" {
|
||||
if os.Getenv("KOPIA_EXE") != "" && os.Getenv("KOPIA_RUN_ALL_INTEGRATION_TESTS") == "" {
|
||||
t.Skip("not running test since it's also included in the unit tests")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user