Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backup): extends backup manifest with info needed for 1-to-1 restore. #4177

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/scylladb/scylla-manager/v3
go 1.23.2

require (
cloud.google.com/go/compute/metadata v0.3.0
github.com/aws/aws-sdk-go v1.35.17
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cespare/xxhash/v2 v2.3.0
Expand Down Expand Up @@ -32,7 +33,7 @@ require (
github.com/scylladb/gocqlx/v2 v2.8.0
github.com/scylladb/scylla-manager/v3/pkg/managerclient v0.0.0-20241104134613-aba35605c28b
github.com/scylladb/scylla-manager/v3/pkg/util v0.0.0-20241104134613-aba35605c28b
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241112131737-4fc93b5355fd
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241217161122-cafa851a39fc
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stoewer/go-strcase v1.3.0
Expand All @@ -48,7 +49,6 @@ require (
)

require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-storage-blob-go v0.13.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
Expand Down Expand Up @@ -112,12 +112,12 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/config v1.4.0 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.27.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/api v0.114.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
Expand Down
14 changes: 8 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1066,8 +1066,10 @@ github.com/scylladb/scylla-manager/v3/pkg/managerclient v0.0.0-20241104134613-ab
github.com/scylladb/scylla-manager/v3/pkg/managerclient v0.0.0-20241104134613-aba35605c28b/go.mod h1:Tss7a99vrgds+B70w8ZFG3Skxfr9Br3kAzrKP2b3CmQ=
github.com/scylladb/scylla-manager/v3/pkg/util v0.0.0-20241104134613-aba35605c28b h1:7CHNmPrQqSdApaEh5nkRL+D52KFHaOHVBBVDvytHEOY=
github.com/scylladb/scylla-manager/v3/pkg/util v0.0.0-20241104134613-aba35605c28b/go.mod h1:+sPCx2oaOXmMpy/ODNNEDGJ7vCghBeKP4S7xEfMI+eA=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241112131737-4fc93b5355fd h1:NNkXlN5SnutcpictWx3sc4jaOz2QMeGmzxES2XdC9RQ=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241112131737-4fc93b5355fd/go.mod h1:Oxfuz1XcXi9iV4ggSGfQdn+p6gPz6djPOegRMMe/6/s=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241217153220-2fde88815361 h1:JFDmfDNdCaKNfJfrikWNOOEnt8upW9Ah1lRwoJ+bgbY=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241217153220-2fde88815361/go.mod h1:nCN5P0jiWL0W7jbcZ9p0ndtZAPoyEWXefddx/nbyFes=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241217161122-cafa851a39fc h1:0Ous1SELrmSuO44z+3XcAGZnDNafjd4IQxoQ2yuFn4k=
github.com/scylladb/scylla-manager/v3/swagger v0.0.0-20241217161122-cafa851a39fc/go.mod h1:nCN5P0jiWL0W7jbcZ9p0ndtZAPoyEWXefddx/nbyFes=
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4 h1:8qmTC5ByIXO3GP/IzBkxcZ/99VITvnIETDhdFz/om7A=
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
Expand Down Expand Up @@ -1296,8 +1298,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1528,8 +1530,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
14 changes: 9 additions & 5 deletions pkg/cloudmeta/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,16 @@ func (cloud *CloudMeta) GetInstanceMetadata(ctx context.Context) (InstanceMetada
// Return the first non error result or wait until all providers return err.
var mErr error
for range len(cloud.providers) {
res := <-results
if res.err != nil {
mErr = multierr.Append(mErr, res.err)
continue
select {
case <-ctx.Done():
return InstanceMetadata{}, ctx.Err()
case res := <-results:
if res.err != nil {
mErr = multierr.Append(mErr, res.err)
continue
}
return res.meta, nil
}
return res.meta, nil
}
return InstanceMetadata{}, mErr
}
Expand Down
30 changes: 28 additions & 2 deletions pkg/cloudmeta/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func TestGetInstanceMetadata(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cloudmeta := &CloudMeta{
providers: tc.providers,
providers: tc.providers,
providerTimeout: 1 * time.Second,
}

meta, err := cloudmeta.GetInstanceMetadata(context.Background())
Expand All @@ -101,6 +102,31 @@ func TestGetInstanceMetadata(t *testing.T) {
}
}

func TestGetInstanceMetadataWithCancelledContext(t *testing.T) {
cloudmeta := &CloudMeta{
providers: []CloudMetadataProvider{
newTestProvider(t, "test_provider_1", "x-test-1", 1*time.Second, nil),
},
providerTimeout: 100 * time.Millisecond,
}

ctx, cancel := context.WithCancel(context.Background())
cancel()

meta, err := cloudmeta.GetInstanceMetadata(ctx)
if !errors.Is(err, context.Canceled) {
t.Fatalf("expected context.Canceled, got %v", err)
}

if meta.CloudProvider != "" {
t.Fatalf("meta.CloudProvider should be empty, got %s", meta.CloudProvider)
}

if meta.InstanceType != "" {
t.Fatalf("meta.InstanceType should be empty, got %s", meta.InstanceType)
}
}

func newTestProvider(t *testing.T, providerName, instanceType string, latency time.Duration, err error) *testProvider {
t.Helper()

Expand Down Expand Up @@ -128,5 +154,5 @@ func (tp testProvider) Metadata(ctx context.Context) (InstanceMetadata, error) {
return InstanceMetadata{
CloudProvider: tp.name,
InstanceType: tp.instanceType,
}, nil
}, ctx.Err()
}
8 changes: 8 additions & 0 deletions pkg/cmd/agent/nodeinfo_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package main
import (
"runtime"

"github.com/pkg/errors"
"github.com/scylladb/scylla-manager/v3/pkg/scyllaclient"
"golang.org/x/sys/unix"
)
Expand All @@ -17,9 +18,16 @@ func (h *nodeInfoHandler) sysInfo(info *scyllaclient.NodeInfo) error {
return err
}

var statfs unix.Statfs_t
if err := unix.Statfs(info.DataDirectory, &statfs); err != nil {
return errors.Wrap(err, "statfs")
}
total := statfs.Blocks * uint64(statfs.Bsize)

info.MemoryTotal = int64(si.Totalram)
info.CPUCount = int64(runtime.NumCPU())
info.Uptime = si.Uptime
info.StorageSize = total

return nil
}
1 change: 1 addition & 0 deletions pkg/cmd/agent/nodeinfo_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ func (h *nodeInfoHandler) sysInfo(info *scyllaclient.NodeInfo) error {
info.MemoryTotal = 0
info.CPUCount = 0
info.Uptime = 0
info.StorageSize = 0
return nil
}
1 change: 1 addition & 0 deletions pkg/scyllaclient/config_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ func (c *ConfigClient) NodeInfo(ctx context.Context) (*NodeInfo, error) {
{Field: &ni.AlternatorAddress, Fetcher: c.AlternatorAddress},
{Field: &ni.AlternatorPort, Fetcher: c.AlternatorPort},
{Field: &ni.AlternatorHTTPSPort, Fetcher: c.AlternatorHTTPSPort},
{Field: &ni.DataDirectory, Fetcher: c.DataDirectory},
}

for i, ff := range ffs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"rpc_port":"9160",
"sstable_uuid_format":true,
"consistent_cluster_management":true,
"enable_tablets":true
}
"enable_tablets":true,
"data_directory": "/var/lib/scylla/data"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
"rpc_address":"192.168.100.101",
"rpc_port":"9160",
"sstable_uuid_format":false,
"consistent_cluster_management":false
}
"consistent_cluster_management":false,
"data_directory": "/var/lib/scylla/data"
}
27 changes: 21 additions & 6 deletions pkg/service/backup/backupspec/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,27 @@ func (m *ManifestInfo) fileNameParser(v string) error {

// ManifestContent is structure containing information about the backup.
type ManifestContent struct {
Version string `json:"version"`
ClusterName string `json:"cluster_name"`
IP string `json:"ip"`
Size int64 `json:"size"`
Tokens []int64 `json:"tokens"`
Schema string `json:"schema"`
Version string `json:"version"`
ClusterID uuid.UUID `json:"cluster_id"`
ClusterName string `json:"cluster_name"`
NodeID string `json:"node_id"`
DC string `json:"dc"`
IP string `json:"ip"`
Size int64 `json:"size"`
Tokens []int64 `json:"tokens"`
Schema string `json:"schema"`
Rack string `json:"rack"`
InstanceDetails InstanceDetails `json:"instance_details"`
}

// InstanceDetails extends backup manifest with additional instance details.
// Mainly needed for 1-to-1 restore.
type InstanceDetails struct {
CloudProvider string `json:"cloud_provider,omitempty"`
InstanceType string `json:"instance_type,omitempty"`

ShardCount int `json:"shard_count"`
StorageSize uint64 `json:"storage_size"`
}

// ManifestContentWithIndex is structure containing information about the backup
Expand Down
58 changes: 56 additions & 2 deletions pkg/service/backup/worker_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"

"github.com/pkg/errors"
"github.com/scylladb/scylla-manager/v3/pkg/cloudmeta"
"github.com/scylladb/scylla-manager/v3/pkg/scyllaclient"
. "github.com/scylladb/scylla-manager/v3/pkg/service/backup/backupspec"
"github.com/scylladb/scylla-manager/v3/pkg/util/parallel"
Expand Down Expand Up @@ -45,11 +46,14 @@ func (w *worker) createAndUploadHostManifest(ctx context.Context, h hostInfo) er
return err
}

m := w.createTemporaryManifest(h, tokens)
m, err := w.createTemporaryManifest(ctx, h, tokens)
if err != nil {
return errors.Wrap(err, "create temp manifest")
}
return w.uploadHostManifest(ctx, h, m)
}

func (w *worker) createTemporaryManifest(h hostInfo, tokens []int64) ManifestInfoWithContent {
func (w *worker) createTemporaryManifest(ctx context.Context, h hostInfo, tokens []int64) (ManifestInfoWithContent, error) {
m := &ManifestInfo{
Location: h.Location,
DC: h.DC,
Expand Down Expand Up @@ -88,10 +92,60 @@ func (w *worker) createTemporaryManifest(h hostInfo, tokens []int64) ManifestInf
c.Size += d.Progress.Size
}

c.ClusterID = w.ClusterID
c.NodeID = h.ID
c.DC = h.DC

rack, err := w.Client.HostRack(ctx, h.IP)
if err != nil {
return ManifestInfoWithContent{}, errors.Wrap(err, "client.HostRack")
}
c.Rack = rack

instanceDetails, err := w.manifestInstanceDetails(ctx, h)
if err != nil {
return ManifestInfoWithContent{}, errors.Wrap(err, "manifest instance details")
}
c.InstanceDetails = instanceDetails

return ManifestInfoWithContent{
ManifestInfo: m,
ManifestContentWithIndex: c,
}, nil
}

// manifestInstanceDetails collects node/instance specific information that's needed for 1-to-1 restore.
func (w *worker) manifestInstanceDetails(ctx context.Context, host hostInfo) (InstanceDetails, error) {
var result InstanceDetails

shardCound, err := w.Client.ShardCount(ctx, host.IP)
if err != nil {
return InstanceDetails{}, errors.Wrap(err, "client.ShardCount")
}
result.ShardCount = int(shardCound)

nodeInfo, err := w.Client.NodeInfo(ctx, host.IP)
if err != nil {
return InstanceDetails{}, errors.Wrap(err, "client.NodeInfo")
}
result.StorageSize = nodeInfo.StorageSize

metaSvc, err := cloudmeta.NewCloudMeta(w.Logger)
if err != nil {
return InstanceDetails{}, errors.Wrap(err, "new cloud meta svc")
}

instanceMeta, err := metaSvc.GetInstanceMetadata(ctx)
if err != nil {
// Metadata may not be available for several reasons:
// 1. running on-premise 2. disabled 3. smth went wrong with metadata server.
// As we cannot distiguish between this cases we can only log err and continue with backup.
w.Logger.Error(ctx, "Get instance metadata", "err", err)
}
result.CloudProvider = string(instanceMeta.CloudProvider)
result.InstanceType = instanceMeta.InstanceType

return result, nil
}

func (w *worker) uploadHostManifest(ctx context.Context, h hostInfo, m ManifestInfoWithContent) error {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading