Files
kopia/internal/serverapi/client_wrappers.go
Jarek Kowalski 7673753050 Merge retention tags in snapshot lists (#1567)
* cli: refactored snapshot list

* cli: show range tags in snapshot list

For example if N snapshots are coalesced together because they
have identical roots we may emit now:

```
  2021-03-31 23:09:27 PDT ked3400debc7dd61baffab070bafd59cd (monthly-10)
  2021-04-30 06:12:53 PDT kd0576d212e55a831b7ff1636f90a7233 (monthly-4..9)
  + 5 identical snapshots until 2021-09-30 23:00:19 PDT
  2021-10-31 23:22:25 PDT k846bf22aa2863d27f05e820f840b14f8 (monthly-3)
  2021-11-08 21:29:31 PST k5793ddcd61ef27b93c75ab74a5828176 (latest-1..3,hourly-1..13,daily-1..7,weekly-1..4,monthly-1..2,annual-1)
  + 18 identical snapshots until 2021-12-04 10:09:54 PST
```

* server: server-side coalescing of snapshot

* ui: added coalescing of retention tags
2021-12-05 20:49:41 -08:00

227 lines
7.0 KiB
Go

package serverapi
import (
"context"
"fmt"
"strings"
"github.com/pkg/errors"
"github.com/kopia/kopia/internal/apiclient"
"github.com/kopia/kopia/internal/uitask"
"github.com/kopia/kopia/repo/blob/throttling"
"github.com/kopia/kopia/repo/object"
"github.com/kopia/kopia/snapshot"
"github.com/kopia/kopia/snapshot/policy"
)
// CreateSnapshotSource creates snapshot source with a given path.
func CreateSnapshotSource(ctx context.Context, c *apiclient.KopiaAPIClient, req *CreateSnapshotSourceRequest) (*CreateSnapshotSourceResponse, error) {
resp := &CreateSnapshotSourceResponse{}
if err := c.Post(ctx, "sources", req, resp); err != nil {
return nil, errors.Wrap(err, "CreateSnapshotSource")
}
return resp, nil
}
// Estimate starts snapshot estimation task for a given directory.
func Estimate(ctx context.Context, c *apiclient.KopiaAPIClient, req *EstimateRequest) (*uitask.Info, error) {
resp := &uitask.Info{}
if err := c.Post(ctx, "estimate", req, resp); err != nil {
return nil, errors.Wrap(err, "Estimate")
}
return resp, nil
}
// GetTask starts snapshot estimation task for a given directory.
func GetTask(ctx context.Context, c *apiclient.KopiaAPIClient, taskID string) (*uitask.Info, error) {
resp := &uitask.Info{}
if err := c.Get(ctx, "tasks/"+taskID, nil, resp); err != nil {
return nil, errors.Wrap(err, "GetTask")
}
return resp, nil
}
// UploadSnapshots triggers snapshot upload on matching snapshots.
func UploadSnapshots(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*MultipleSourceActionResponse, error) {
resp := &MultipleSourceActionResponse{}
if err := c.Post(ctx, "sources/upload"+matchSourceParameters(match), &Empty{}, resp); err != nil {
return nil, errors.Wrap(err, "UploadSnapshots")
}
return resp, nil
}
// CancelUpload cancels snapshot upload on matching snapshots.
func CancelUpload(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*MultipleSourceActionResponse, error) {
resp := &MultipleSourceActionResponse{}
if err := c.Post(ctx, "sources/cancel"+matchSourceParameters(match), &Empty{}, resp); err != nil {
return nil, errors.Wrap(err, "CancelUpload")
}
return resp, nil
}
// CreateRepository invokes the 'repo/create' API.
func CreateRepository(ctx context.Context, c *apiclient.KopiaAPIClient, req *CreateRepositoryRequest) error {
// nolint:wrapcheck
return c.Post(ctx, "repo/create", req, &StatusResponse{})
}
// ConnectToRepository invokes the 'repo/connect' API.
func ConnectToRepository(ctx context.Context, c *apiclient.KopiaAPIClient, req *ConnectRepositoryRequest) error {
// nolint:wrapcheck
return c.Post(ctx, "repo/connect", req, &StatusResponse{})
}
// DisconnectFromRepository invokes the 'repo/disconnect' API.
func DisconnectFromRepository(ctx context.Context, c *apiclient.KopiaAPIClient) error {
// nolint:wrapcheck
return c.Post(ctx, "repo/disconnect", &Empty{}, &Empty{})
}
// Shutdown invokes the 'repo/shutdown' API.
func Shutdown(ctx context.Context, c *apiclient.KopiaAPIClient) error {
// nolint:wrapcheck
return c.Post(ctx, "shutdown", &Empty{}, &Empty{})
}
// Status invokes the 'repo/status' API.
func Status(ctx context.Context, c *apiclient.KopiaAPIClient) (*StatusResponse, error) {
resp := &StatusResponse{}
if err := c.Get(ctx, "repo/status", nil, resp); err != nil {
return nil, errors.Wrap(err, "Status")
}
return resp, nil
}
// GetThrottlingLimits gets the throttling limits.
func GetThrottlingLimits(ctx context.Context, c *apiclient.KopiaAPIClient) (throttling.Limits, error) {
resp := throttling.Limits{}
if err := c.Get(ctx, "repo/throttle", nil, &resp); err != nil {
return throttling.Limits{}, errors.Wrap(err, "throttling")
}
return resp, nil
}
// SetThrottlingLimits sets the throttling limits.
func SetThrottlingLimits(ctx context.Context, c *apiclient.KopiaAPIClient, l throttling.Limits) error {
if err := c.Put(ctx, "repo/throttle", &l, &Empty{}); err != nil {
return errors.Wrap(err, "throttling")
}
return nil
}
// ListSources lists the snapshot sources managed by the server.
func ListSources(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*SourcesResponse, error) {
resp := &SourcesResponse{}
if err := c.Get(ctx, "sources"+matchSourceParameters(match), nil, resp); err != nil {
return nil, errors.Wrap(err, "ListSources")
}
return resp, nil
}
// ListSnapshots lists the snapshots managed by the server for a given source source.
func ListSnapshots(ctx context.Context, c *apiclient.KopiaAPIClient, src snapshot.SourceInfo, all bool) (*SnapshotsResponse, error) {
resp := &SnapshotsResponse{}
u := "snapshots" + matchSourceParameters(&src)
if all {
u += "&all=1"
}
if err := c.Get(ctx, u, nil, resp); err != nil {
return nil, errors.Wrap(err, "ListSnapshots")
}
return resp, nil
}
// ListPolicies lists the policies managed by the server for a given target filter.
func ListPolicies(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*PoliciesResponse, error) {
resp := &PoliciesResponse{}
if err := c.Get(ctx, "policies"+matchSourceParameters(match), nil, resp); err != nil {
return nil, errors.Wrap(err, "ListPolicies")
}
return resp, nil
}
func policyTargetURLParamters(si snapshot.SourceInfo) string {
return fmt.Sprintf("userName=%v&host=%v&path=%v", si.UserName, si.Host, si.Path)
}
// SetPolicy sets the policy.
func SetPolicy(ctx context.Context, c *apiclient.KopiaAPIClient, si snapshot.SourceInfo, pol *policy.Policy) error {
resp := &Empty{}
if err := c.Put(ctx, "policy?"+policyTargetURLParamters(si), pol, resp); err != nil {
return errors.Wrap(err, "SetPolicy")
}
return nil
}
// ResolvePolicy resolves the policy.
func ResolvePolicy(ctx context.Context, c *apiclient.KopiaAPIClient, si snapshot.SourceInfo, req *ResolvePolicyRequest) (*ResolvePolicyResponse, error) {
resp := &ResolvePolicyResponse{}
if err := c.Post(ctx, "policy/resolve?"+policyTargetURLParamters(si), req, resp); err != nil {
return nil, errors.Wrap(err, "ResolvePolicy")
}
return resp, nil
}
// ListTasks lists the tasks.
func ListTasks(ctx context.Context, c *apiclient.KopiaAPIClient) (*TaskListResponse, error) {
resp := &TaskListResponse{}
if err := c.Get(ctx, "tasks", nil, resp); err != nil {
return nil, errors.Wrap(err, "ListTasks")
}
return resp, nil
}
// GetObject returns the object payload.
func GetObject(ctx context.Context, c *apiclient.KopiaAPIClient, objectID string) ([]byte, error) {
var b []byte
if err := c.Get(ctx, "objects/"+objectID, object.ErrObjectNotFound, &b); err != nil {
return nil, errors.Wrap(err, "GetObject")
}
return b, nil
}
func matchSourceParameters(match *snapshot.SourceInfo) string {
if match == nil {
return ""
}
var clauses []string
if v := match.Host; v != "" {
clauses = append(clauses, "host="+v)
}
if v := match.UserName; v != "" {
clauses = append(clauses, "username="+v)
}
if v := match.Path; v != "" {
clauses = append(clauses, "path="+v)
}
if len(clauses) == 0 {
return ""
}
return "?" + strings.Join(clauses, "&")
}