diff --git a/debugd/cmd/debugd/debugd.go b/debugd/cmd/debugd/debugd.go index 3b6b87457..2764c391d 100644 --- a/debugd/cmd/debugd/debugd.go +++ b/debugd/cmd/debugd/debugd.go @@ -14,7 +14,6 @@ import ( "os" "sync" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/deploy" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/logcollector" @@ -22,6 +21,8 @@ import ( "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/metadata/cloudprovider" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/metadata/fallback" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/server" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer/streamer" awscloud "github.com/edgelesssys/constellation/v2/internal/cloud/aws" azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure" platform "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" @@ -45,7 +46,8 @@ func main() { log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity)) fs := afero.NewOsFs() - streamer := bootstrapper.NewFileStreamer(fs) + streamer := streamer.New(fs) + filetransferer := filetransfer.New(log.Named("filetransfer"), streamer, filetransfer.DontShowProgress) serviceManager := deploy.NewServiceManager(log.Named("serviceManager")) ctx, cancel := context.WithCancel(context.Background()) @@ -92,13 +94,10 @@ func main() { logcollector.NewStartTrigger(ctx, wg, platform.FromString(csp), fetcher, log.Named("logcollector")), ) - download := deploy.New(log.Named("download"), &net.Dialer{}, serviceManager, streamer, infoMap) + download := deploy.New(log.Named("download"), &net.Dialer{}, serviceManager, filetransferer, infoMap) sched := metadata.NewScheduler(log.Named("scheduler"), fetcher, download) - serv := server.New(log.Named("server"), serviceManager, streamer, infoMap) - if err := deploy.DefaultServiceUnit(ctx, serviceManager); err != nil { - log.With(zap.Error(err)).Fatalf("Failed to create default service unit") - } + serv := server.New(log.Named("server"), serviceManager, filetransferer, infoMap) writeDebugBanner(log) diff --git a/debugd/internal/cdbg/cmd/deploy.go b/debugd/internal/cdbg/cmd/deploy.go index 56bb9f7af..d321ebf62 100644 --- a/debugd/internal/cdbg/cmd/deploy.go +++ b/debugd/internal/cdbg/cmd/deploy.go @@ -10,18 +10,19 @@ import ( "context" "fmt" "io" - "log" "net" "strconv" "strings" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/logcollector" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer/streamer" pb "github.com/edgelesssys/constellation/v2/debugd/service" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/spf13/afero" "github.com/spf13/cobra" "google.golang.org/grpc" @@ -41,38 +42,52 @@ func newDeployCmd() *cobra.Command { } deployCmd.Flags().StringSlice("ips", nil, "override the ips that the bootstrapper will be uploaded to (defaults to ips from constellation config)") deployCmd.Flags().String("bootstrapper", "./bootstrapper", "override the path to the bootstrapper binary uploaded to instances") + deployCmd.Flags().String("upgrade-agent", "./upgrade-agent", "override the path to the upgrade-agent binary uploaded to instances") deployCmd.Flags().StringToString("info", nil, "additional info to be passed to the debugd, in the form --info key1=value1,key2=value2") + deployCmd.Flags().Int("verbosity", 0, logger.CmdLineVerbosityDescription) return deployCmd } func runDeploy(cmd *cobra.Command, args []string) error { + verbosity, err := cmd.Flags().GetInt("verbosity") + if err != nil { + return err + } + log := logger.New(logger.PlainLog, logger.VerbosityFromInt(verbosity)) configName, err := cmd.Flags().GetString("config") if err != nil { return fmt.Errorf("parsing config path argument: %w", err) } - fileHandler := file.NewHandler(afero.NewOsFs()) + fs := afero.NewOsFs() + fileHandler := file.NewHandler(fs) + streamer := streamer.New(fs) + transfer := filetransfer.New(log, streamer, filetransfer.ShowProgress) constellationConfig, err := config.FromFile(fileHandler, configName) if err != nil { return err } - return deploy(cmd, fileHandler, constellationConfig, bootstrapper.NewFileStreamer(afero.NewOsFs())) + return deploy(cmd, fileHandler, constellationConfig, transfer, log) } -func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *config.Config, reader fileToStreamReader) error { +func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *config.Config, transfer fileTransferer, log *logger.Logger) error { bootstrapperPath, err := cmd.Flags().GetString("bootstrapper") if err != nil { return err } + upgradeAgentPath, err := cmd.Flags().GetString("upgrade-agent") + if err != nil { + return err + } if constellationConfig.IsReleaseImage() { - log.Println("WARNING: Constellation image does not look like a debug image. Are you using a debug image?") + log.Infof("WARNING: Constellation image does not look like a debug image. Are you using a debug image?") } if !constellationConfig.IsDebugCluster() { - log.Println("WARNING: The Constellation config has debugCluster set to false.") - log.Println("cdbg will likely not work unless you manually adjust the firewall / load balancing rules.") - log.Println("If you create the cluster with a debug image, you should also set debugCluster to true.") + log.Infof("WARNING: The Constellation config has debugCluster set to false.") + log.Infof("cdbg will likely not work unless you manually adjust the firewall / load balancing rules.") + log.Infof("If you create the cluster with a debug image, you should also set debugCluster to true.") } ips, err := cmd.Flags().GetStringSlice("ips") @@ -95,13 +110,28 @@ func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *c return err } - for _, ip := range ips { + files := []filetransfer.FileStat{ + { + SourcePath: bootstrapperPath, + TargetPath: debugd.BootstrapperDeployFilename, + Mode: debugd.BinaryAccessMode, + OverrideServiceUnit: "constellation-bootstrapper", + }, + { + SourcePath: upgradeAgentPath, + TargetPath: debugd.UpgradeAgentDeployFilename, + Mode: debugd.BinaryAccessMode, + OverrideServiceUnit: "constellation-upgrade-agent", + }, + } + for _, ip := range ips { input := deployOnEndpointInput{ - debugdEndpoint: ip, - infos: info, - bootstrapperPath: bootstrapperPath, - reader: reader, + debugdEndpoint: ip, + infos: info, + files: files, + transfer: transfer, + log: log, } if err := deployOnEndpoint(cmd.Context(), input); err != nil { return err @@ -112,15 +142,16 @@ func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *c } type deployOnEndpointInput struct { - debugdEndpoint string - bootstrapperPath string - infos map[string]string - reader fileToStreamReader + debugdEndpoint string + files []filetransfer.FileStat + infos map[string]string + transfer fileTransferer + log *logger.Logger } // deployOnEndpoint deploys a custom built bootstrapper binary to a debugd endpoint. func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error { - log.Printf("Deploying on %v\n", in.debugdEndpoint) + in.log.Infof("Deploying on %v", in.debugdEndpoint) client, closer, err := newDebugdClient(ctx, in.debugdEndpoint) if err != nil { @@ -128,11 +159,11 @@ func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error { } defer closer.Close() - if err := setInfo(ctx, client, in.infos); err != nil { + if err := setInfo(ctx, in.log, client, in.infos); err != nil { return fmt.Errorf("sending info: %w", err) } - if err := uploadBootstrapper(ctx, client, in); err != nil { + if err := uploadFiles(ctx, client, in); err != nil { return fmt.Errorf("uploading bootstrapper: %w", err) } @@ -152,8 +183,8 @@ func newDebugdClient(ctx context.Context, ip string) (pb.DebugdClient, io.Closer return pb.NewDebugdClient(conn), conn, nil } -func setInfo(ctx context.Context, client pb.DebugdClient, infos map[string]string) error { - log.Printf("Setting info with length %d", len(infos)) +func setInfo(ctx context.Context, log *logger.Logger, client pb.DebugdClient, infos map[string]string) error { + log.Infof("Setting info with length %d", len(infos)) var infosPb []*pb.Info for key, value := range infos { @@ -162,36 +193,52 @@ func setInfo(ctx context.Context, client pb.DebugdClient, infos map[string]strin req := &pb.SetInfoRequest{Info: infosPb} - if _, err := client.SetInfo(ctx, req, grpc.WaitForReady(true)); err != nil { + status, err := client.SetInfo(ctx, req, grpc.WaitForReady(true)) + if err != nil { return fmt.Errorf("setting info: %w", err) } - log.Println("Info set") + switch status.Status { + case pb.SetInfoStatus_SET_INFO_SUCCESS: + log.Infof("Info set") + case pb.SetInfoStatus_SET_INFO_ALREADY_SET: + log.Infof("Info already set") + default: + log.Warnf("Unknown status %v", status.Status) + } return nil } -func uploadBootstrapper(ctx context.Context, client pb.DebugdClient, in deployOnEndpointInput) error { - log.Println("Uploading bootstrapper") +func uploadFiles(ctx context.Context, client pb.DebugdClient, in deployOnEndpointInput) error { + in.log.Infof("Uploading files") - stream, err := client.UploadBootstrapper(ctx, grpc.WaitForReady(true)) + stream, err := client.UploadFiles(ctx, grpc.WaitForReady(true)) if err != nil { return fmt.Errorf("starting bootstrapper upload to instance %v: %w", in.debugdEndpoint, err) } - streamErr := in.reader.ReadStream(in.bootstrapperPath, stream, debugd.Chunksize, true) + + in.transfer.SetFiles(in.files) + if err := in.transfer.SendFiles(stream); err != nil { + return fmt.Errorf("sending files to %v: %w", in.debugdEndpoint, err) + } uploadResponse, closeErr := stream.CloseAndRecv() if closeErr != nil { - return fmt.Errorf("closing upload stream after uploading bootstrapper to %v: %w", in.debugdEndpoint, closeErr) + return fmt.Errorf("closing upload stream after uploading files to %v: %w", in.debugdEndpoint, closeErr) } - if uploadResponse.Status == pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_FILE_EXISTS { - log.Println("Bootstrapper was already uploaded") - return nil - } - if uploadResponse.Status != pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS || streamErr != nil { - return fmt.Errorf("uploading bootstrapper to instance %v failed: %v / %w", in.debugdEndpoint, uploadResponse, streamErr) + switch uploadResponse.Status { + case pb.UploadFilesStatus_UPLOAD_FILES_SUCCESS: + in.log.Infof("Upload successful") + case pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED: + in.log.Infof("Files already uploaded") + case pb.UploadFilesStatus_UPLOAD_FILES_UPLOAD_FAILED: + return fmt.Errorf("uploading files to %v failed: %v", in.debugdEndpoint, uploadResponse) + case pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED: + return fmt.Errorf("upload already started on %v", in.debugdEndpoint) + default: + return fmt.Errorf("unknown upload status %v", uploadResponse.Status) } - log.Println("Uploaded bootstrapper") return nil } @@ -211,8 +258,9 @@ func checkInfoMap(info map[string]string) error { return nil } -type fileToStreamReader interface { - ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error +type fileTransferer interface { + SendFiles(stream filetransfer.SendFilesStream) error + SetFiles(files []filetransfer.FileStat) } type clusterIDsFile struct { diff --git a/debugd/internal/debugd/constants.go b/debugd/internal/debugd/constants.go index d5c3fa8d4..12b597c44 100644 --- a/debugd/internal/debugd/constants.go +++ b/debugd/internal/debugd/constants.go @@ -14,7 +14,9 @@ const ( GRPCTimeout = 5 * time.Minute DiscoverDebugdInterval = 30 * time.Second DownloadRetryBackoff = 1 * time.Minute + BinaryAccessMode = 0o755 // -rwxr-xr-x BootstrapperDeployFilename = "/run/state/bin/bootstrapper" + UpgradeAgentDeployFilename = "/run/state/bin/upgrade-agent" Chunksize = 1024 BootstrapperSystemdUnitName = "bootstrapper.service" BootstrapperSystemdUnitContents = `[Unit] diff --git a/debugd/internal/debugd/deploy/download.go b/debugd/internal/debugd/deploy/download.go index 09312b70a..b4000ff2c 100644 --- a/debugd/internal/debugd/deploy/download.go +++ b/debugd/internal/debugd/deploy/download.go @@ -8,13 +8,13 @@ package deploy import ( "context" + "errors" "fmt" "io" "net" "strconv" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" - "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" pb "github.com/edgelesssys/constellation/v2/debugd/service" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/logger" @@ -27,19 +27,19 @@ import ( type Download struct { log *logger.Logger dialer NetDialer - writer streamToFileWriter + transfer fileTransferer serviceManager serviceManager info infoSetter } // New creates a new Download. func New(log *logger.Logger, dialer NetDialer, serviceManager serviceManager, - writer streamToFileWriter, info infoSetter, + transfer fileTransferer, info infoSetter, ) *Download { return &Download{ log: log, dialer: dialer, - writer: writer, + transfer: transfer, info: info, serviceManager: serviceManager, } @@ -47,6 +47,10 @@ func New(log *logger.Logger, dialer NetDialer, serviceManager serviceManager, // DownloadInfo will try to download the info from another instance. func (d *Download) DownloadInfo(ctx context.Context, ip string) error { + if d.info.Received() { + return nil + } + log := d.log.With(zap.String("ip", ip)) serverAddr := net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort)) @@ -66,7 +70,7 @@ func (d *Download) DownloadInfo(ctx context.Context, ip string) error { return d.info.SetProto(resp.Info) } -// DownloadDeployment will open a new grpc connection to another instance, attempting to download a bootstrapper from that instance. +// DownloadDeployment will open a new grpc connection to another instance, attempting to download files from that instance. func (d *Download) DownloadDeployment(ctx context.Context, ip string) error { log := d.log.With(zap.String("ip", ip)) serverAddr := net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort)) @@ -77,23 +81,38 @@ func (d *Download) DownloadDeployment(ctx context.Context, ip string) error { } defer closer.Close() - log.Infof("Trying to download bootstrapper") - stream, err := client.DownloadBootstrapper(ctx, &pb.DownloadBootstrapperRequest{}) + log.Infof("Trying to download files") + stream, err := client.DownloadFiles(ctx, &pb.DownloadFilesRequest{}) if err != nil { - return fmt.Errorf("starting bootstrapper download from other instance: %w", err) + return fmt.Errorf("starting file download from other instance: %w", err) } - if err := d.writer.WriteStream(debugd.BootstrapperDeployFilename, stream, true); err != nil { - return fmt.Errorf("streaming bootstrapper from other instance: %w", err) - } - log.Infof("Successfully downloaded bootstrapper") - // after the upload succeeds, try to restart the bootstrapper - restartAction := ServiceManagerRequest{ - Unit: debugd.BootstrapperSystemdUnitName, - Action: Restart, + err = d.transfer.RecvFiles(stream) + switch { + case err == nil: + d.log.Infof("Downloading files succeeded") + case errors.Is(err, filetransfer.ErrReceiveRunning): + d.log.Warnf("Download already in progress") + return err + case errors.Is(err, filetransfer.ErrReceiveFinished): + d.log.Warnf("Download already finished") + return nil + default: + d.log.With(zap.Error(err)).Errorf("Downloading files failed") + return err } - if err := d.serviceManager.SystemdAction(ctx, restartAction); err != nil { - return fmt.Errorf("restarting bootstrapper: %w", err) + + files := d.transfer.GetFiles() + for _, file := range files { + if file.OverrideServiceUnit == "" { + continue + } + if err := d.serviceManager.OverrideServiceUnitExecStart( + ctx, file.OverrideServiceUnit, file.TargetPath, + ); err != nil { + // continue on error to allow other units to be overridden + d.log.With(zap.Error(err)).Errorf("Failed to override service unit %s", file.OverrideServiceUnit) + } } return nil @@ -123,14 +142,17 @@ func (d *Download) grpcWithDialer() grpc.DialOption { type infoSetter interface { SetProto(infos []*pb.Info) error + Received() bool } type serviceManager interface { SystemdAction(ctx context.Context, request ServiceManagerRequest) error + OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error } -type streamToFileWriter interface { - WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error +type fileTransferer interface { + RecvFiles(stream filetransfer.RecvFilesStream) error + GetFiles() []filetransfer.FileStat } // NetDialer can open a net.Conn. diff --git a/debugd/internal/debugd/deploy/download_test.go b/debugd/internal/debugd/deploy/download_test.go index 56000dc10..0c27d7b8a 100644 --- a/debugd/internal/debugd/deploy/download_test.go +++ b/debugd/internal/debugd/deploy/download_test.go @@ -9,14 +9,11 @@ package deploy import ( "context" "errors" - "fmt" - "io" "net" "strconv" "testing" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" - "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" pb "github.com/edgelesssys/constellation/v2/debugd/service" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" @@ -33,41 +30,72 @@ func TestMain(m *testing.M) { ) } -func TestDownloadBootstrapper(t *testing.T) { - filename := "/run/state/bin/bootstrapper" - someErr := errors.New("failed") - +func TestDownloadDeployment(t *testing.T) { testCases := map[string]struct { - server fakeDownloadServer - serviceManager stubServiceManager - wantChunks [][]byte - wantDownloadErr bool - wantFile bool - wantSystemdAction bool - wantDeployed bool + files []filetransfer.FileStat + recvFilesErr error + overrideServiceUnitErr error + wantErr bool + wantOverrideCalls []struct{ UnitName, ExecStart string } }{ "download works": { - server: fakeDownloadServer{ - chunks: [][]byte{[]byte("test")}, + files: []filetransfer.FileStat{ + { + SourcePath: "source/testfileA", + TargetPath: "target/testfileA", + Mode: 0o644, + OverrideServiceUnit: "unitA", + }, + { + SourcePath: "source/testfileB", + TargetPath: "target/testfileB", + Mode: 0o644, + }, + }, + wantOverrideCalls: []struct{ UnitName, ExecStart string }{ + {"unitA", "target/testfileA"}, }, - wantChunks: [][]byte{[]byte("test")}, - wantDownloadErr: false, - wantFile: true, - wantSystemdAction: true, - wantDeployed: true, }, - "download rpc call error is detected": { - server: fakeDownloadServer{downladErr: someErr}, - wantDownloadErr: true, + "recv files error is detected": { + recvFilesErr: errors.New("some error"), + wantErr: true, }, - "service restart error is detected": { - server: fakeDownloadServer{chunks: [][]byte{[]byte("test")}}, - serviceManager: stubServiceManager{systemdActionErr: someErr}, - wantChunks: [][]byte{[]byte("test")}, - wantDownloadErr: true, - wantFile: true, - wantDeployed: true, - wantSystemdAction: false, + "recv already running": { + recvFilesErr: filetransfer.ErrReceiveRunning, + wantErr: true, + }, + "recv already finished": { + files: []filetransfer.FileStat{ + { + SourcePath: "source/testfileA", + TargetPath: "target/testfileA", + Mode: 0o644, + OverrideServiceUnit: "unitA", + }, + }, + recvFilesErr: filetransfer.ErrReceiveFinished, + wantErr: false, + }, + "service unit fail does not stop further tries": { + files: []filetransfer.FileStat{ + { + SourcePath: "source/testfileA", + TargetPath: "target/testfileA", + Mode: 0o644, + OverrideServiceUnit: "unitA", + }, + { + SourcePath: "source/testfileB", + TargetPath: "target/testfileB", + Mode: 0o644, + OverrideServiceUnit: "unitB", + }, + }, + overrideServiceUnitErr: errors.New("some error"), + wantOverrideCalls: []struct{ UnitName, ExecStart string }{ + {"unitA", "target/testfileA"}, + {"unitB", "target/testfileB"}, + }, }, } @@ -76,11 +104,13 @@ func TestDownloadBootstrapper(t *testing.T) { assert := assert.New(t) ip := "192.0.2.0" - writer := &fakeStreamToFileWriter{} + transfer := &stubTransfer{recvFilesErr: tc.recvFilesErr, files: tc.files} + serviceMgr := &stubServiceManager{overrideServiceUnitExecStartErr: tc.overrideServiceUnitErr} dialer := testdialer.NewBufconnDialer() + server := &stubDownloadServer{} grpcServ := grpc.NewServer() - pb.RegisterDebugdServer(grpcServ, &tc.server) + pb.RegisterDebugdServer(grpcServ, server) lis := dialer.GetListener(net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort))) go grpcServ.Serve(lis) defer grpcServ.GracefulStop() @@ -88,30 +118,19 @@ func TestDownloadBootstrapper(t *testing.T) { download := &Download{ log: logger.NewTest(t), dialer: dialer, - writer: writer, - serviceManager: &tc.serviceManager, + transfer: transfer, + serviceManager: serviceMgr, } err := download.DownloadDeployment(context.Background(), ip) - if tc.wantDownloadErr { + if tc.wantErr { assert.Error(err) } else { assert.NoError(err) } - if tc.wantFile { - assert.Equal(tc.wantChunks, writer.chunks) - assert.Equal(filename, writer.filename) - } - if tc.wantSystemdAction { - assert.ElementsMatch( - []ServiceManagerRequest{ - {Unit: debugd.BootstrapperSystemdUnitName, Action: Restart}, - }, - tc.serviceManager.requests, - ) - } + assert.Equal(tc.wantOverrideCalls, serviceMgr.overrideCalls) }) } } @@ -189,6 +208,9 @@ func TestDownloadInfo(t *testing.T) { type stubServiceManager struct { requests []ServiceManagerRequest systemdActionErr error + + overrideCalls []struct{ UnitName, ExecStart string } + overrideServiceUnitExecStartErr error } func (s *stubServiceManager) SystemdAction(ctx context.Context, request ServiceManagerRequest) error { @@ -196,39 +218,34 @@ func (s *stubServiceManager) SystemdAction(ctx context.Context, request ServiceM return s.systemdActionErr } -type fakeStreamToFileWriter struct { - chunks [][]byte - filename string +func (s *stubServiceManager) OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error { + s.overrideCalls = append(s.overrideCalls, struct { + UnitName, ExecStart string + }{UnitName: unitName, ExecStart: execStart}) + return s.overrideServiceUnitExecStartErr } -func (f *fakeStreamToFileWriter) WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error { - f.filename = filename - for { - chunk, err := stream.Recv() - if err != nil { - if errors.Is(err, io.EOF) { - return nil - } - return fmt.Errorf("reading stream: %w", err) - } - f.chunks = append(f.chunks, chunk.Content) - } +type stubTransfer struct { + recvFilesErr error + files []filetransfer.FileStat } -// fakeDownloadServer implements DebugdServer; only fakes DownloadBootstrapper, panics on every other rpc. -type fakeDownloadServer struct { - chunks [][]byte +func (t *stubTransfer) RecvFiles(_ filetransfer.RecvFilesStream) error { + return t.recvFilesErr +} + +func (t *stubTransfer) GetFiles() []filetransfer.FileStat { + return t.files +} + +// stubDownloadServer implements DebugdServer; only stubs DownloadFiles, panics on every other rpc. +type stubDownloadServer struct { downladErr error pb.UnimplementedDebugdServer } -func (s *fakeDownloadServer) DownloadBootstrapper(request *pb.DownloadBootstrapperRequest, stream pb.Debugd_DownloadBootstrapperServer) error { - for _, chunk := range s.chunks { - if err := stream.Send(&pb.Chunk{Content: chunk}); err != nil { - return fmt.Errorf("sending chunk: %w", err) - } - } +func (s *stubDownloadServer) DownloadFiles(request *pb.DownloadFilesRequest, stream pb.Debugd_DownloadFilesServer) error { return s.downladErr } @@ -244,6 +261,7 @@ func (s *stubDebugdServer) GetInfo(ctx context.Context, request *pb.GetInfoReque type stubInfoSetter struct { info []*pb.Info + received bool setProtoErr error } @@ -251,3 +269,7 @@ func (s *stubInfoSetter) SetProto(infos []*pb.Info) error { s.info = infos return s.setProtoErr } + +func (s *stubInfoSetter) Received() bool { + return s.received +} diff --git a/debugd/internal/debugd/deploy/service.go b/debugd/internal/debugd/deploy/service.go index 8c45dd568..f82e09c25 100644 --- a/debugd/internal/debugd/deploy/service.go +++ b/debugd/internal/debugd/deploy/service.go @@ -9,9 +9,12 @@ package deploy import ( "context" "fmt" + "os" + "path/filepath" + "regexp" + "strings" "sync" - "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/spf13/afero" "go.uber.org/zap" @@ -21,6 +24,10 @@ const ( systemdUnitFolder = "/run/systemd/system" ) +// systemdUnitNameRegexp is a regular expression that matches valid systemd unit names. +// This is only the unit name, without the .service suffix. +var systemdUnitNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9@._\-\\]+$`) + // SystemdAction encodes the available actions. // //go:generate stringer -type=SystemdAction @@ -73,7 +80,7 @@ func NewServiceManager(log *logger.Logger) *ServiceManager { type dbusClient interface { // NewSystemConnectionContext establishes a connection to the system bus and authenticates. // Callers should call Close() when done with the connection. - NewSystemdConnectionContext(ctx context.Context) (dbusConn, error) + NewSystemConnectionContext(ctx context.Context) (dbusConn, error) } type dbusConn interface { @@ -89,15 +96,18 @@ type dbusConn interface { // ReloadContext instructs systemd to scan for and reload unit files. This is // an equivalent to systemctl daemon-reload. ReloadContext(ctx context.Context) error + // Close closes the connection. + Close() } // SystemdAction will perform a systemd action on a service unit (start, stop, restart, reload). func (s *ServiceManager) SystemdAction(ctx context.Context, request ServiceManagerRequest) error { log := s.log.With(zap.String("unit", request.Unit), zap.String("action", request.Action.String())) - conn, err := s.dbus.NewSystemdConnectionContext(ctx) + conn, err := s.dbus.NewSystemConnectionContext(ctx) if err != nil { return fmt.Errorf("establishing systemd connection: %w", err) } + defer conn.Close() resultChan := make(chan string, 1) switch request.Action { @@ -149,28 +159,41 @@ func (s *ServiceManager) WriteSystemdUnitFile(ctx context.Context, unit SystemdU } log.Infof("Wrote systemd unit file and performed daemon-reload") - return nil } -// DefaultServiceUnit will write the default "bootstrapper.service" unit file. -func DefaultServiceUnit(ctx context.Context, serviceManager *ServiceManager) error { - if err := serviceManager.WriteSystemdUnitFile(ctx, SystemdUnit{ - Name: debugd.BootstrapperSystemdUnitName, - Contents: debugd.BootstrapperSystemdUnitContents, - }); err != nil { - return fmt.Errorf("writing systemd unit file %q: %w", debugd.BootstrapperSystemdUnitName, err) - } - - // try to start the default service if the binary exists but ignore failure. - // this is meant to start the bootstrapper after a reboot - // if a bootstrapper binary was uploaded before. - if ok, err := afero.Exists(serviceManager.fs, debugd.BootstrapperDeployFilename); ok && err == nil { - _ = serviceManager.SystemdAction(ctx, ServiceManagerRequest{ - Unit: debugd.BootstrapperSystemdUnitName, - Action: Start, - }) +// OverrideServiceUnitExecStart will override the ExecStart of a systemd unit. +func (s *ServiceManager) OverrideServiceUnitExecStart(ctx context.Context, unitName, execStart string) error { + log := s.log.With(zap.String("unitFile", fmt.Sprintf("%s/%s", systemdUnitFolder, unitName))) + log.Infof("Overriding systemd unit file execStart") + if !systemdUnitNameRegexp.MatchString(unitName) { + return fmt.Errorf("unit name %q is invalid", unitName) + } + // validate execStart (no newlines) + if strings.Contains(execStart, "\n") || strings.Contains(execStart, "\r") { + return fmt.Errorf("execStart must not contain newlines") + } + overrideUnitContents := fmt.Sprintf("[Service]\nExecStart=\nExecStart=%s\n", execStart) + s.systemdUnitFilewriteLock.Lock() + defer s.systemdUnitFilewriteLock.Unlock() + path := filepath.Join(systemdUnitFolder, unitName+".service.d", "override.conf") + if err := s.fs.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { + return fmt.Errorf("creating systemd unit file override directory %q: %w", filepath.Dir(path), err) + } + if err := afero.WriteFile(s.fs, path, []byte(overrideUnitContents), 0o644); err != nil { + return fmt.Errorf("writing systemd unit override file %q: %w", unitName, err) + } + if err := s.SystemdAction(ctx, ServiceManagerRequest{Unit: unitName, Action: Reload}); err != nil { + // do not return early here + // the "daemon-reload" command may return an unrelated error + // and there is no way to know if the override was successful + log.Warnf("Failed to perform systemd daemon-reload: %v", err) + } + if err := s.SystemdAction(ctx, ServiceManagerRequest{Unit: unitName + ".service", Action: Restart}); err != nil { + log.Warnf("Failed to perform unit restart: %v", err) + return fmt.Errorf("performing systemd unit restart: %w", err) } + log.Infof("Overrode systemd unit file execStart, performed daemon-reload and restarted unit %v", unitName) return nil } diff --git a/debugd/internal/debugd/deploy/service_test.go b/debugd/internal/debugd/deploy/service_test.go index f7e458c02..785373320 100644 --- a/debugd/internal/debugd/deploy/service_test.go +++ b/debugd/internal/debugd/deploy/service_test.go @@ -158,7 +158,7 @@ func TestWriteSystemdUnitFile(t *testing.T) { "systemd reload fails": { dbus: stubDbus{ conn: &fakeDbusConn{ - actionErr: errors.New("reload error"), + reloadErr: errors.New("reload error"), }, }, unit: SystemdUnit{ @@ -200,12 +200,127 @@ func TestWriteSystemdUnitFile(t *testing.T) { } } +func TestOverrideServiceUnitExecStart(t *testing.T) { + testCases := map[string]struct { + dbus stubDbus + unitName, execStart string + readonly bool + wantErr bool + wantFileContents string + wantActionCalls []dbusConnActionInput + wantReloads int + }{ + "override works": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + }, + }, + unitName: "test", + execStart: "/run/state/bin/test", + wantFileContents: "[Service]\nExecStart=\nExecStart=/run/state/bin/test\n", + wantActionCalls: []dbusConnActionInput{ + {name: "test.service", mode: "replace"}, + }, + wantReloads: 1, + }, + "unit name invalid": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + }, + }, + unitName: "invalid name", + execStart: "/run/state/bin/test", + wantErr: true, + }, + "exec start invalid": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + }, + }, + unitName: "test", + execStart: "/run/state/bin/\r\ntest", + wantErr: true, + }, + "write fails": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + }, + }, + unitName: "test", + execStart: "/run/state/bin/test", + readonly: true, + wantErr: true, + }, + "reload fails but restart is still attempted": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + reloadErr: errors.New("reload error"), + }, + }, + unitName: "test", + execStart: "/run/state/bin/test", + wantFileContents: "[Service]\nExecStart=\nExecStart=/run/state/bin/test\n", + wantActionCalls: []dbusConnActionInput{ + {name: "test.service", mode: "replace"}, + }, + wantReloads: 1, + }, + "restart fails": { + dbus: stubDbus{ + conn: &fakeDbusConn{ + result: "done", + actionErr: errors.New("action error"), + }, + }, + unitName: "test", + execStart: "/run/state/bin/test", + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + fs := afero.NewMemMapFs() + assert.NoError(fs.MkdirAll(systemdUnitFolder, 0o755)) + if tc.readonly { + fs = afero.NewReadOnlyFs(fs) + } + manager := ServiceManager{ + log: logger.NewTest(t), + dbus: &tc.dbus, + fs: fs, + systemdUnitFilewriteLock: sync.Mutex{}, + } + err := manager.OverrideServiceUnitExecStart(context.Background(), tc.unitName, tc.execStart) + + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + fileContents, err := afero.ReadFile(fs, "/run/systemd/system/test.service.d/override.conf") + assert.NoError(err) + assert.Equal(tc.wantFileContents, string(fileContents)) + assert.Equal(tc.wantActionCalls, tc.dbus.conn.(*fakeDbusConn).inputs) + assert.Equal(tc.wantReloads, tc.dbus.conn.(*fakeDbusConn).reloadCalls) + }) + } +} + type stubDbus struct { conn dbusConn connErr error } -func (s *stubDbus) NewSystemdConnectionContext(ctx context.Context) (dbusConn, error) { +func (s *stubDbus) NewSystemConnectionContext(ctx context.Context) (dbusConn, error) { return s.conn, s.connErr } @@ -215,11 +330,13 @@ type dbusConnActionInput struct { } type fakeDbusConn struct { - inputs []dbusConnActionInput - result string + inputs []dbusConnActionInput + result string + reloadCalls int jobID int actionErr error + reloadErr error } func (c *fakeDbusConn) StartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { @@ -244,5 +361,9 @@ func (c *fakeDbusConn) RestartUnitContext(ctx context.Context, name string, mode } func (c *fakeDbusConn) ReloadContext(ctx context.Context) error { - return c.actionErr + c.reloadCalls++ + + return c.reloadErr } + +func (c *fakeDbusConn) Close() {} diff --git a/debugd/internal/debugd/deploy/wrappers.go b/debugd/internal/debugd/deploy/wrappers.go index c64586338..9ec9f0b01 100644 --- a/debugd/internal/debugd/deploy/wrappers.go +++ b/debugd/internal/debugd/deploy/wrappers.go @@ -15,8 +15,8 @@ import ( // wraps go-systemd dbus. type dbusWrapper struct{} -func (d *dbusWrapper) NewSystemdConnectionContext(ctx context.Context) (dbusConn, error) { - conn, err := dbus.NewSystemdConnectionContext(ctx) +func (d *dbusWrapper) NewSystemConnectionContext(ctx context.Context) (dbusConn, error) { + conn, err := dbus.NewSystemConnectionContext(ctx) if err != nil { return nil, err } @@ -44,3 +44,7 @@ func (c *dbusConnWrapper) RestartUnitContext(ctx context.Context, name string, m func (c *dbusConnWrapper) ReloadContext(ctx context.Context) error { return c.conn.ReloadContext(ctx) } + +func (c *dbusConnWrapper) Close() { + c.conn.Close() +} diff --git a/debugd/internal/debugd/info/info.go b/debugd/internal/debugd/info/info.go index 4e43e4551..adbf9049c 100644 --- a/debugd/internal/debugd/info/info.go +++ b/debugd/internal/debugd/info/info.go @@ -29,6 +29,13 @@ func NewMap() *Map { } } +// Received returns true if the info map has been set. +func (i *Map) Received() bool { + i.mux.RLock() + defer i.mux.RUnlock() + return i.received +} + // Get returns the value of the info with the given key. func (i *Map) Get(key string) (string, bool, error) { i.mux.RLock() @@ -67,7 +74,7 @@ func (i *Map) SetProto(infos []*servicepb.Info) error { defer i.mux.Unlock() if i.received { - return errors.New("info already set") + return ErrInfoAlreadySet } infoMap := make(map[string]string) @@ -114,3 +121,6 @@ func (i *Map) GetProto() ([]*servicepb.Info, error) { } return infos, nil } + +// ErrInfoAlreadySet is returned if the info map has already been set. +var ErrInfoAlreadySet = errors.New("info already set") diff --git a/debugd/internal/debugd/info/info_test.go b/debugd/internal/debugd/info/info_test.go index 00aae765f..22bda23a6 100644 --- a/debugd/internal/debugd/info/info_test.go +++ b/debugd/internal/debugd/info/info_test.go @@ -284,6 +284,10 @@ func TestConcurrency(t *testing.T) { _, _ = i.GetProto() } + received := func() { + _ = i.Received() + } + go get() go get() go get() @@ -300,4 +304,8 @@ func TestConcurrency(t *testing.T) { go getProto() go getProto() go getProto() + go received() + go received() + go received() + go received() } diff --git a/debugd/internal/debugd/server/server.go b/debugd/internal/debugd/server/server.go index a250ded2a..8a84c28c5 100644 --- a/debugd/internal/debugd/server/server.go +++ b/debugd/internal/debugd/server/server.go @@ -9,20 +9,18 @@ package server import ( "context" "errors" - "fmt" - "io/fs" "net" "strconv" "sync" "time" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" - "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/deploy" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" pb "github.com/edgelesssys/constellation/v2/debugd/service" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/logger" + "go.uber.org/multierr" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" @@ -31,18 +29,18 @@ import ( type debugdServer struct { log *logger.Logger serviceManager serviceManager - streamer streamer + transfer fileTransferer info *info.Map pb.UnimplementedDebugdServer } // New creates a new debugdServer according to the gRPC spec. -func New(log *logger.Logger, serviceManager serviceManager, streamer streamer, infos *info.Map) pb.DebugdServer { +func New(log *logger.Logger, serviceManager serviceManager, transfer fileTransferer, infos *info.Map) pb.DebugdServer { return &debugdServer{ log: log, serviceManager: serviceManager, - streamer: streamer, + transfer: transfer, info: infos, } } @@ -55,13 +53,23 @@ func (s *debugdServer) SetInfo(ctx context.Context, req *pb.SetInfoRequest) (*pb s.log.Infof("Info is empty") } - if err := s.info.SetProto(req.Info); err != nil { - s.log.With(zap.Error(err)).Errorf("Setting info failed") - return &pb.SetInfoResponse{}, err + setProtoErr := s.info.SetProto(req.Info) + if errors.Is(setProtoErr, info.ErrInfoAlreadySet) { + s.log.Warnf("Setting info failed (already set)") + return &pb.SetInfoResponse{ + Status: pb.SetInfoStatus_SET_INFO_ALREADY_SET, + }, nil + } + + if setProtoErr != nil { + s.log.With(zap.Error(setProtoErr)).Errorf("Setting info failed") + return nil, setProtoErr } s.log.Infof("Info set") - return &pb.SetInfoResponse{}, nil + return &pb.SetInfoResponse{ + Status: pb.SetInfoStatus_SET_INFO_SUCCESS, + }, nil } // GetInfo returns the info of the debugd instance. @@ -76,46 +84,66 @@ func (s *debugdServer) GetInfo(ctx context.Context, req *pb.GetInfoRequest) (*pb return &pb.GetInfoResponse{Info: info}, nil } -// UploadBootstrapper receives a bootstrapper binary in a stream of chunks and writes to a file. -func (s *debugdServer) UploadBootstrapper(stream pb.Debugd_UploadBootstrapperServer) error { - startAction := deploy.ServiceManagerRequest{ - Unit: debugd.BootstrapperSystemdUnitName, - Action: deploy.Start, - } - var responseStatus pb.UploadBootstrapperStatus - defer func() { - if err := s.serviceManager.SystemdAction(stream.Context(), startAction); err != nil { - s.log.With(zap.Error(err)).Errorf("Starting uploaded bootstrapper failed") - if responseStatus == pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS { - responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED - } - } - stream.SendAndClose(&pb.UploadBootstrapperResponse{ - Status: responseStatus, +// UploadFiles receives a stream of files (each consisting of a header and a stream of chunks) and writes them to the filesystem. +func (s *debugdServer) UploadFiles(stream pb.Debugd_UploadFilesServer) error { + s.log.Infof("Received UploadFiles request") + err := s.transfer.RecvFiles(stream) + switch { + case err == nil: + s.log.Infof("Uploading files succeeded") + case errors.Is(err, filetransfer.ErrReceiveRunning): + s.log.Warnf("Upload already in progress") + return stream.SendAndClose(&pb.UploadFilesResponse{ + Status: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED, + }) + case errors.Is(err, filetransfer.ErrReceiveFinished): + s.log.Warnf("Upload already finished") + return stream.SendAndClose(&pb.UploadFilesResponse{ + Status: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED, + }) + default: + s.log.With(zap.Error(err)).Errorf("Uploading files failed") + return stream.SendAndClose(&pb.UploadFilesResponse{ + Status: pb.UploadFilesStatus_UPLOAD_FILES_UPLOAD_FAILED, }) - }() - s.log.Infof("Starting bootstrapper upload") - if err := s.streamer.WriteStream(debugd.BootstrapperDeployFilename, stream, true); err != nil { - if errors.Is(err, fs.ErrExist) { - // bootstrapper was already uploaded - s.log.Warnf("Bootstrapper already uploaded") - responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_FILE_EXISTS - return nil - } - s.log.With(zap.Error(err)).Errorf("Uploading bootstrapper failed") - responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED - return fmt.Errorf("uploading bootstrapper: %w", err) } - s.log.Infof("Successfully uploaded bootstrapper") - responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS - return nil + files := s.transfer.GetFiles() + var overrideUnitErr error + for _, file := range files { + if file.OverrideServiceUnit == "" { + continue + } + // continue on error to allow other units to be overridden + // TODO: switch to native go multierror once 1.20 is released + // err = s.serviceManager.OverrideServiceUnitExecStart(stream.Context(), file.OverrideServiceUnit, file.TargetPath) + // if err != nil { + // overrideUnitErr = errors.Join(overrideUnitErr, err) + // } + err = s.serviceManager.OverrideServiceUnitExecStart(stream.Context(), file.OverrideServiceUnit, file.TargetPath) + if err != nil { + overrideUnitErr = multierr.Append(overrideUnitErr, err) + } + } + + if overrideUnitErr != nil { + s.log.With(zap.Error(overrideUnitErr)).Errorf("Overriding service units failed") + return stream.SendAndClose(&pb.UploadFilesResponse{ + Status: pb.UploadFilesStatus_UPLOAD_FILES_START_FAILED, + }) + } + return stream.SendAndClose(&pb.UploadFilesResponse{ + Status: pb.UploadFilesStatus_UPLOAD_FILES_SUCCESS, + }) } -// DownloadBootstrapper streams the local bootstrapper binary to other instances. -func (s *debugdServer) DownloadBootstrapper(request *pb.DownloadBootstrapperRequest, stream pb.Debugd_DownloadBootstrapperServer) error { - s.log.Infof("Sending bootstrapper to other instance") - return s.streamer.ReadStream(debugd.BootstrapperDeployFilename, stream, debugd.Chunksize, true) +// DownloadFiles streams the previously received files to other instances. +func (s *debugdServer) DownloadFiles(request *pb.DownloadFilesRequest, stream pb.Debugd_DownloadFilesServer) error { + s.log.Infof("Sending files to other instance") + if !s.transfer.CanSend() { + return errors.New("cannot send files at this time") + } + return s.transfer.SendFiles(stream) } // UploadSystemServiceUnits receives systemd service units, writes them to a service file and schedules a daemon-reload. @@ -157,9 +185,12 @@ func Start(log *logger.Logger, wg *sync.WaitGroup, serv pb.DebugdServer) { type serviceManager interface { SystemdAction(ctx context.Context, request deploy.ServiceManagerRequest) error WriteSystemdUnitFile(ctx context.Context, unit deploy.SystemdUnit) error + OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error } -type streamer interface { - WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error - ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error +type fileTransferer interface { + RecvFiles(stream filetransfer.RecvFilesStream) error + SendFiles(stream filetransfer.SendFilesStream) error + GetFiles() []filetransfer.FileStat + CanSend() bool } diff --git a/debugd/internal/debugd/server/server_test.go b/debugd/internal/debugd/server/server_test.go index 9dc7e3c5e..ab4c9849f 100644 --- a/debugd/internal/debugd/server/server_test.go +++ b/debugd/internal/debugd/server/server_test.go @@ -9,15 +9,14 @@ package server import ( "context" "errors" - "fmt" "io" "net" "strconv" "testing" - "github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/deploy" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer" pb "github.com/edgelesssys/constellation/v2/debugd/service" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" @@ -40,21 +39,23 @@ func TestSetInfo(t *testing.T) { info *info.Map infoReceived bool setInfo []*pb.Info - wantErr bool + wantStatus pb.SetInfoStatus }{ "set info works": { - setInfo: []*pb.Info{{Key: "foo", Value: "bar"}}, - info: info.NewMap(), + setInfo: []*pb.Info{{Key: "foo", Value: "bar"}}, + info: info.NewMap(), + wantStatus: pb.SetInfoStatus_SET_INFO_SUCCESS, }, "set empty info works": { - setInfo: []*pb.Info{}, - info: info.NewMap(), + setInfo: []*pb.Info{}, + info: info.NewMap(), + wantStatus: pb.SetInfoStatus_SET_INFO_SUCCESS, }, "set fails when info already set": { info: info.NewMap(), infoReceived: true, setInfo: []*pb.Info{{Key: "foo", Value: "bar"}}, - wantErr: true, + wantStatus: pb.SetInfoStatus_SET_INFO_ALREADY_SET, }, } @@ -78,19 +79,16 @@ func TestSetInfo(t *testing.T) { defer conn.Close() client := pb.NewDebugdClient(conn) - _, err = client.SetInfo(context.Background(), &pb.SetInfoRequest{Info: tc.setInfo}) + setInfoStatus, err := client.SetInfo(context.Background(), &pb.SetInfoRequest{Info: tc.setInfo}) grpcServ.GracefulStop() - if tc.wantErr { - assert.Error(err) - } else { + assert.NoError(err) + assert.Equal(tc.wantStatus, setInfoStatus.Status) + for i := range tc.setInfo { + value, ok, err := tc.info.Get(tc.setInfo[i].Key) assert.NoError(err) - for i := range tc.setInfo { - value, ok, err := tc.info.Get(tc.setInfo[i].Key) - assert.NoError(err) - assert.True(ok) - assert.Equal(tc.setInfo[i].Value, value) - } + assert.True(ok) + assert.Equal(tc.setInfo[i].Value, value) } }) } @@ -152,35 +150,36 @@ func TestGetInfo(t *testing.T) { } } -func TestUploadBootstrapper(t *testing.T) { +func TestUploadFiles(t *testing.T) { endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort) testCases := map[string]struct { - serviceManager stubServiceManager - streamer fakeStreamer - uploadChunks [][]byte - wantErr bool - wantResponseStatus pb.UploadBootstrapperStatus - wantFile bool - wantChunks [][]byte + files []filetransfer.FileStat + recvFilesErr error + wantResponseStatus pb.UploadFilesStatus + wantOverrideCalls []struct{ UnitName, ExecStart string } }{ "upload works": { - uploadChunks: [][]byte{[]byte("test")}, - wantFile: true, - wantChunks: [][]byte{[]byte("test")}, - wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS, + files: []filetransfer.FileStat{ + {SourcePath: "source/testA", TargetPath: "target/testA", Mode: 0o644, OverrideServiceUnit: "testA"}, + {SourcePath: "source/testB", TargetPath: "target/testB", Mode: 0o644}, + }, + wantOverrideCalls: []struct{ UnitName, ExecStart string }{ + {"testA", "target/testA"}, + }, + wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_SUCCESS, }, "recv fails": { - streamer: fakeStreamer{writeStreamErr: errors.New("recv error")}, - wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED, - wantErr: true, + recvFilesErr: errors.New("recv error"), + wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_UPLOAD_FAILED, }, - "starting bootstrapper fails": { - uploadChunks: [][]byte{[]byte("test")}, - serviceManager: stubServiceManager{systemdActionErr: errors.New("starting bootstrapper error")}, - wantFile: true, - wantChunks: [][]byte{[]byte("test")}, - wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED, + "upload in progress": { + recvFilesErr: filetransfer.ErrReceiveRunning, + wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED, + }, + "upload already finished": { + recvFilesErr: filetransfer.ErrReceiveFinished, + wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED, }, } @@ -189,60 +188,49 @@ func TestUploadBootstrapper(t *testing.T) { assert := assert.New(t) require := require.New(t) + serviceMgr := &stubServiceManager{} + transfer := &stubTransfer{files: tc.files, recvFilesErr: tc.recvFilesErr} + serv := debugdServer{ log: logger.NewTest(t), - serviceManager: &tc.serviceManager, - streamer: &tc.streamer, + serviceManager: serviceMgr, + transfer: transfer, } grpcServ, conn, err := setupServerWithConn(endpoint, &serv) require.NoError(err) defer conn.Close() client := pb.NewDebugdClient(conn) - stream, err := client.UploadBootstrapper(context.Background()) + stream, err := client.UploadFiles(context.Background()) require.NoError(err) - require.NoError(fakeWrite(stream, tc.uploadChunks)) resp, err := stream.CloseAndRecv() grpcServ.GracefulStop() - if tc.wantErr { - assert.Error(err) - return - } require.NoError(err) assert.Equal(tc.wantResponseStatus, resp.Status) - if tc.wantFile { - assert.Equal(tc.wantChunks, tc.streamer.writeStreamChunks) - assert.Equal("/run/state/bin/bootstrapper", tc.streamer.writeStreamFilename) - } else { - assert.Empty(tc.streamer.writeStreamChunks) - assert.Empty(tc.streamer.writeStreamFilename) - } + assert.Equal(tc.wantOverrideCalls, serviceMgr.overrideCalls) }) } } -func TestDownloadBootstrapper(t *testing.T) { +func TestDownloadFiles(t *testing.T) { endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort) testCases := map[string]struct { - serviceManager stubServiceManager - request *pb.DownloadBootstrapperRequest - streamer fakeStreamer - wantErr bool - wantChunks [][]byte + request *pb.DownloadFilesRequest + canSend bool + wantRecvErr bool + wantSendFileCalls int }{ "download works": { - request: &pb.DownloadBootstrapperRequest{}, - streamer: fakeStreamer{readStreamChunks: [][]byte{[]byte("test")}}, - wantErr: false, - wantChunks: [][]byte{[]byte("test")}, + request: &pb.DownloadFilesRequest{}, + canSend: true, + wantSendFileCalls: 1, }, - "download fails": { - request: &pb.DownloadBootstrapperRequest{}, - streamer: fakeStreamer{readStreamErr: errors.New("read bootstrapper fails")}, - wantErr: true, + "transfer is not ready for sending": { + request: &pb.DownloadFilesRequest{}, + wantRecvErr: true, }, } @@ -251,28 +239,29 @@ func TestDownloadBootstrapper(t *testing.T) { assert := assert.New(t) require := require.New(t) + transfer := &stubTransfer{canSend: tc.canSend} serv := debugdServer{ - log: logger.NewTest(t), - serviceManager: &tc.serviceManager, - streamer: &tc.streamer, + log: logger.NewTest(t), + transfer: transfer, } grpcServ, conn, err := setupServerWithConn(endpoint, &serv) require.NoError(err) defer conn.Close() client := pb.NewDebugdClient(conn) - stream, err := client.DownloadBootstrapper(context.Background(), tc.request) + stream, err := client.DownloadFiles(context.Background(), tc.request) require.NoError(err) - chunks, err := fakeRead(stream) - grpcServ.GracefulStop() - - if tc.wantErr { - assert.Error(err) - return + _, recvErr := stream.Recv() + if tc.wantRecvErr { + require.Error(recvErr) + } else { + require.ErrorIs(recvErr, io.EOF) } + require.NoError(stream.CloseSend()) + grpcServ.GracefulStop() require.NoError(err) - assert.Equal(tc.wantChunks, chunks) - assert.Equal("/run/state/bin/bootstrapper", tc.streamer.readStreamFilename) + + assert.Equal(tc.wantSendFileCalls, transfer.sendFilesCount) }) } } @@ -334,7 +323,6 @@ func TestUploadSystemServiceUnits(t *testing.T) { serv := debugdServer{ log: logger.NewTest(t), serviceManager: &tc.serviceManager, - streamer: &fakeStreamer{}, } grpcServ, conn, err := setupServerWithConn(endpoint, &serv) require.NoError(err) @@ -357,10 +345,13 @@ func TestUploadSystemServiceUnits(t *testing.T) { } type stubServiceManager struct { - requests []deploy.ServiceManagerRequest - unitFiles []deploy.SystemdUnit - systemdActionErr error - writeSystemdUnitFileErr error + requests []deploy.ServiceManagerRequest + unitFiles []deploy.SystemdUnit + overrideCalls []struct{ UnitName, ExecStart string } + + systemdActionErr error + writeSystemdUnitFileErr error + overrideServiceUnitExecStartErr error } func (s *stubServiceManager) SystemdAction(ctx context.Context, request deploy.ServiceManagerRequest) error { @@ -373,6 +364,13 @@ func (s *stubServiceManager) WriteSystemdUnitFile(ctx context.Context, unit depl return s.writeSystemdUnitFileErr } +func (s *stubServiceManager) OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error { + s.overrideCalls = append(s.overrideCalls, struct { + UnitName, ExecStart string + }{UnitName: unitName, ExecStart: execStart}) + return s.overrideServiceUnitExecStartErr +} + type netDialer interface { DialContext(ctx context.Context, network, address string) (net.Conn, error) } @@ -386,37 +384,31 @@ func dial(ctx context.Context, dialer netDialer, target string) (*grpc.ClientCon ) } -type fakeStreamer struct { - writeStreamChunks [][]byte - writeStreamFilename string - writeStreamErr error - readStreamChunks [][]byte - readStreamFilename string - readStreamErr error +type stubTransfer struct { + recvFilesCount int + sendFilesCount int + files []filetransfer.FileStat + canSend bool + recvFilesErr error + sendFilesErr error } -func (f *fakeStreamer) WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error { - f.writeStreamFilename = filename - for { - chunk, err := stream.Recv() - if err != nil { - if errors.Is(err, io.EOF) { - return f.writeStreamErr - } - return fmt.Errorf("reading stream: %w", err) - } - f.writeStreamChunks = append(f.writeStreamChunks, chunk.Content) - } +func (t *stubTransfer) RecvFiles(_ filetransfer.RecvFilesStream) error { + t.recvFilesCount++ + return t.recvFilesErr } -func (f *fakeStreamer) ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error { - f.readStreamFilename = filename - for _, chunk := range f.readStreamChunks { - if err := stream.Send(&pb.Chunk{Content: chunk}); err != nil { - panic(err) - } - } - return f.readStreamErr +func (t *stubTransfer) SendFiles(_ filetransfer.SendFilesStream) error { + t.sendFilesCount++ + return t.sendFilesErr +} + +func (t *stubTransfer) GetFiles() []filetransfer.FileStat { + return t.files +} + +func (t *stubTransfer) CanSend() bool { + return t.canSend } func setupServerWithConn(endpoint string, serv *debugdServer) (*grpc.Server, *grpc.ClientConn, error) { @@ -433,29 +425,3 @@ func setupServerWithConn(endpoint string, serv *debugdServer) (*grpc.Server, *gr return grpcServ, conn, nil } - -func fakeWrite(stream bootstrapper.WriteChunkStream, chunks [][]byte) error { - for _, chunk := range chunks { - err := stream.Send(&pb.Chunk{ - Content: chunk, - }) - if err != nil { - return err - } - } - return nil -} - -func fakeRead(stream bootstrapper.ReadChunkStream) ([][]byte, error) { - var chunks [][]byte - for { - chunk, err := stream.Recv() - if err != nil { - if err == io.EOF { - return chunks, nil - } - return nil, err - } - chunks = append(chunks, chunk.Content) - } -} diff --git a/debugd/internal/filetransfer/chunkstream.go b/debugd/internal/filetransfer/chunkstream.go new file mode 100644 index 000000000..9c36b968f --- /dev/null +++ b/debugd/internal/filetransfer/chunkstream.go @@ -0,0 +1,47 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package filetransfer + +import ( + "errors" + + pb "github.com/edgelesssys/constellation/v2/debugd/service" +) + +// recvChunkStream is a wrapper around a RecvFilesStream that only returns chunks. +type recvChunkStream struct { + stream RecvFilesStream +} + +// Recv receives a FileTransferMessage and returns the chunk. +func (s *recvChunkStream) Recv() (*pb.Chunk, error) { + msg, err := s.stream.Recv() + if err != nil { + return nil, err + } + chunk := msg.GetChunk() + if chunk == nil { + return nil, errors.New("expected chunk") + } + return chunk, nil +} + +// sendChunkStream is a wrapper around a SendFilesStream that wraps chunks for every message. +type sendChunkStream struct { + stream SendFilesStream +} + +// Send wraps the given chunk in a FileTransferMessage and sends it. +func (s *sendChunkStream) Send(chunk *pb.Chunk) error { + chunkMessage := &pb.FileTransferMessage_Chunk{ + Chunk: chunk, + } + message := &pb.FileTransferMessage{ + Kind: chunkMessage, + } + return s.stream.Send(message) +} diff --git a/debugd/internal/filetransfer/chunkstream_test.go b/debugd/internal/filetransfer/chunkstream_test.go new file mode 100644 index 000000000..f01cbc136 --- /dev/null +++ b/debugd/internal/filetransfer/chunkstream_test.go @@ -0,0 +1,134 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package filetransfer + +import ( + "errors" + "testing" + + pb "github.com/edgelesssys/constellation/v2/debugd/service" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRecv(t *testing.T) { + testCases := map[string]struct { + stream stubRecvFilesStream + wantChunk *pb.Chunk + wantErr bool + }{ + "chunk is received": { + stream: stubRecvFilesStream{ + msg: &pb.FileTransferMessage{ + Kind: &pb.FileTransferMessage_Chunk{ + Chunk: &pb.Chunk{ + Content: []byte("test"), + }, + }, + }, + }, + wantChunk: &pb.Chunk{ + Content: []byte("test"), + }, + }, + "wrong type": { + stream: stubRecvFilesStream{ + msg: &pb.FileTransferMessage{ + Kind: &pb.FileTransferMessage_Header{}, + }, + }, + wantErr: true, + }, + "empty msg": { + stream: stubRecvFilesStream{}, + wantErr: true, + }, + "recv fails": { + stream: stubRecvFilesStream{ + recvErr: errors.New("someErr"), + }, + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + stream := recvChunkStream{stream: &tc.stream} + chunk, err := stream.Recv() + + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.Equal(tc.wantChunk, chunk) + }) + } +} + +func TestSend(t *testing.T) { + testCases := map[string]struct { + stream stubSendFilesStream + chunk *pb.Chunk + wantMsgs []*pb.FileTransferMessage + wantErr bool + }{ + "chunk is wrapped correctly": { + chunk: &pb.Chunk{ + Content: []byte("test"), + }, + wantMsgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Chunk{ + Chunk: &pb.Chunk{ + Content: []byte("test"), + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + stream := sendChunkStream{stream: &tc.stream} + err := stream.Send(tc.chunk) + + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.EqualValues(tc.wantMsgs, tc.stream.msgs) + }) + } +} + +type stubRecvFilesStream struct { + msg *pb.FileTransferMessage + recvErr error +} + +func (s *stubRecvFilesStream) Recv() (*pb.FileTransferMessage, error) { + return s.msg, s.recvErr +} + +type stubSendFilesStream struct { + msgs []*pb.FileTransferMessage + sendErr error +} + +func (s *stubSendFilesStream) Send(msg *pb.FileTransferMessage) error { + s.msgs = append(s.msgs, msg) + return s.sendErr +} diff --git a/debugd/internal/filetransfer/filetransfer.go b/debugd/internal/filetransfer/filetransfer.go new file mode 100644 index 000000000..be8c62b7d --- /dev/null +++ b/debugd/internal/filetransfer/filetransfer.go @@ -0,0 +1,233 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package filetransfer + +import ( + "errors" + "io" + "io/fs" + "sync" + + "github.com/edgelesssys/constellation/v2/debugd/internal/debugd" + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer/streamer" + pb "github.com/edgelesssys/constellation/v2/debugd/service" + "github.com/edgelesssys/constellation/v2/internal/logger" + "go.uber.org/zap" +) + +// RecvFilesStream is a stream that receives FileTransferMessages. +type RecvFilesStream interface { + Recv() (*pb.FileTransferMessage, error) +} + +// SendFilesStream is a stream that sends FileTransferMessages. +type SendFilesStream interface { + Send(*pb.FileTransferMessage) error +} + +// FileTransferer manages sending and receiving of files. +type FileTransferer struct { + mux sync.RWMutex + log *logger.Logger + receiveStarted bool + receiveFinished bool + files []FileStat + streamer streamReadWriter + showProgress bool +} + +// New creates a new FileTransferer. +func New(log *logger.Logger, streamer streamReadWriter, showProgress bool) *FileTransferer { + return &FileTransferer{ + log: log, + streamer: streamer, + showProgress: showProgress, + } +} + +// SendFiles sends files to the given stream. +func (s *FileTransferer) SendFiles(stream SendFilesStream) error { + s.mux.RLock() + defer s.mux.RUnlock() + if !s.receiveFinished { + return errors.New("cannot send files before receiving them") + } + for _, file := range s.files { + if err := s.handleFileSend(stream, file); err != nil { + return err + } + } + return nil +} + +// RecvFiles receives files from the given stream. +func (s *FileTransferer) RecvFiles(stream RecvFilesStream) (err error) { + if err := s.startRecv(); err != nil { + return err + } + defer func() { + if err != nil { + s.abortRecv() + } else { + s.finishRecv() + } + }() + var done bool + for !done && err == nil { + done, err = s.handleFileRecv(stream) + } + return err +} + +// GetFiles returns the a copy of the list of files that have been received. +func (s *FileTransferer) GetFiles() []FileStat { + s.mux.RLock() + defer s.mux.RUnlock() + res := make([]FileStat, len(s.files)) + copy(res, s.files) + return res +} + +// SetFiles sets the list of files that can be sent. +func (s *FileTransferer) SetFiles(files []FileStat) { + s.mux.Lock() + defer s.mux.Unlock() + res := make([]FileStat, len(files)) + copy(res, files) + s.files = res + s.receiveFinished = true +} + +// CanSend returns true if the file receive has finished. +// This is called to determine if a debugd instance can request files from this server. +func (s *FileTransferer) CanSend() bool { + s.mux.RLock() + defer s.mux.RUnlock() + ret := s.receiveFinished + return ret +} + +func (s *FileTransferer) handleFileSend(stream SendFilesStream, file FileStat) error { + header := &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: file.TargetPath, + Mode: uint32(file.Mode), + }, + } + if file.OverrideServiceUnit != "" { + header.Header.OverrideServiceUnit = &file.OverrideServiceUnit + } + if err := stream.Send(&pb.FileTransferMessage{Kind: header}); err != nil { + return err + } + sendChunkStream := &sendChunkStream{stream: stream} + return s.streamer.ReadStream(file.SourcePath, sendChunkStream, debugd.Chunksize, s.showProgress) +} + +// handleFileRecv handles the file receive of a single file. +// It returns true if the stream is finished (all of the file consumed) and false otherwise. +func (s *FileTransferer) handleFileRecv(stream RecvFilesStream) (bool, error) { + // first message must be a header message + msg, err := stream.Recv() + switch { + case err == nil: + // nop + case errors.Is(err, io.EOF): + return true, nil // stream is finished + default: + return false, err + } + header := msg.GetHeader() + if header == nil { + return false, errors.New("first message must be a header message") + } + s.log.Infof("Starting file receive of %q", header.TargetPath) + s.addFile(FileStat{ + SourcePath: header.TargetPath, + TargetPath: header.TargetPath, + Mode: fs.FileMode(header.Mode), + OverrideServiceUnit: func() string { + if header.OverrideServiceUnit != nil { + return *header.OverrideServiceUnit + } + return "" + }(), + }) + recvChunkStream := &recvChunkStream{stream: stream} + if err := s.streamer.WriteStream(header.TargetPath, recvChunkStream, s.showProgress); err != nil { + s.log.With(zap.Error(err)).Errorf("Receive of file %q failed", header.TargetPath) + return false, err + } + s.log.Infof("Finished file receive of %q", header.TargetPath) + return false, nil +} + +// startRecv marks the file receive as started. It returns an error if receiving has already started. +func (s *FileTransferer) startRecv() error { + s.mux.Lock() + defer s.mux.Unlock() + switch { + case s.receiveFinished: + return ErrReceiveFinished + case s.receiveStarted: + return ErrReceiveRunning + } + s.receiveStarted = true + return nil +} + +// abortRecv marks the file receive as failed. +// This allows for a retry of the file receive. +func (s *FileTransferer) abortRecv() { + s.mux.Lock() + defer s.mux.Unlock() + s.receiveStarted = false + s.files = nil +} + +// finishRecv marks the file receive as completed. +// This allows other debugd instances to request files from this server. +func (s *FileTransferer) finishRecv() { + s.mux.Lock() + defer s.mux.Unlock() + s.receiveStarted = false + s.receiveFinished = true +} + +// addFile adds a file to the list of received files. +func (s *FileTransferer) addFile(file FileStat) { + s.mux.Lock() + defer s.mux.Unlock() + s.files = append(s.files, file) +} + +// FileStat contains the metadata of a file that can be up/downloaded. +type FileStat struct { + SourcePath string + TargetPath string + Mode fs.FileMode + OverrideServiceUnit string // optional name of the service unit to override +} + +var ( + // ErrReceiveRunning is returned if a file receive is already running. + ErrReceiveRunning = errors.New("receive already running") + // ErrReceiveFinished is returned if a file receive has already finished. + ErrReceiveFinished = errors.New("receive finished") +) + +const ( + // ShowProgress indicates that progress should be shown. + ShowProgress = true + // DontShowProgress indicates that progress should not be shown. + DontShowProgress = false +) + +type streamReadWriter interface { + WriteStream(filename string, stream streamer.ReadChunkStream, showProgress bool) error + ReadStream(filename string, stream streamer.WriteChunkStream, chunksize uint, showProgress bool) error +} diff --git a/debugd/internal/filetransfer/filetransfer_test.go b/debugd/internal/filetransfer/filetransfer_test.go new file mode 100644 index 000000000..515dd8390 --- /dev/null +++ b/debugd/internal/filetransfer/filetransfer_test.go @@ -0,0 +1,411 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package filetransfer + +import ( + "errors" + "io" + "testing" + + "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer/streamer" + pb "github.com/edgelesssys/constellation/v2/debugd/service" + "github.com/edgelesssys/constellation/v2/internal/logger" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +func TestSendFiles(t *testing.T) { + testCases := map[string]struct { + files *[]FileStat + sendErr error + readStreamErr error + wantHeaders []*pb.FileTransferMessage + wantErr bool + }{ + "can send files": { + files: &[]FileStat{ + { + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + { + TargetPath: "testfileB", + Mode: 0o644, + OverrideServiceUnit: "somesvcB", + }, + }, + wantHeaders: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcA"; return &s }(), + }, + }, + }, + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileB", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcB"; return &s }(), + }, + }, + }, + }, + }, + "no files set": { + wantErr: true, + }, + "send fails": { + files: &[]FileStat{ + { + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + }, + sendErr: errors.New("send failed"), + wantErr: true, + }, + "read stream fails": { + files: &[]FileStat{ + { + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + }, + readStreamErr: errors.New("read stream failed"), + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + streamer := &stubStreamReadWriter{readStreamErr: tc.readStreamErr} + stream := &stubSendFilesStream{sendErr: tc.sendErr} + transfer := New(logger.NewTest(t), streamer, false) + if tc.files != nil { + transfer.SetFiles(*tc.files) + } + err := transfer.SendFiles(stream) + + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.Equal(tc.wantHeaders, stream.msgs) + }) + } +} + +func TestRecvFiles(t *testing.T) { + testCases := map[string]struct { + msgs []*pb.FileTransferMessage + recvAlreadyStarted bool + recvAlreadyFinished bool + recvErr error + writeStreamErr error + wantFiles []FileStat + wantErr bool + }{ + "can recv files": { + msgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcA"; return &s }(), + }, + }, + }, + // Chunk messages left out since they would be consumed by the streamReadWriter + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileB", + Mode: 0o644, + }, + }, + }, + // Chunk messages left out since they would be consumed by the streamReadWriter + }, + wantFiles: []FileStat{ + { + SourcePath: "testfileA", + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + { + SourcePath: "testfileB", + TargetPath: "testfileB", + Mode: 0o644, + }, + }, + }, + "no messages": {}, + "recv fails": { + recvErr: errors.New("recv failed"), + wantErr: true, + }, + "first recv does not yield file header": { + msgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Chunk{}, + }, + }, + wantErr: true, + }, + "write stream fails": { + msgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcA"; return &s }(), + }, + }, + }, + // Chunk messages left out since they would be consumed by the streamReadWriter + }, + writeStreamErr: errors.New("write stream failed"), + wantErr: true, + }, + "recv has already started": { + msgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcA"; return &s }(), + }, + }, + }, + // Chunk messages left out since they would be consumed by the streamReadWriter + }, + recvAlreadyStarted: true, + wantErr: true, + }, + "recv has already finished": { + msgs: []*pb.FileTransferMessage{ + { + Kind: &pb.FileTransferMessage_Header{ + Header: &pb.FileTransferHeader{ + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: func() *string { s := "somesvcA"; return &s }(), + }, + }, + }, + // Chunk messages left out since they would be consumed by the streamReadWriter + }, + recvAlreadyFinished: true, + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + streamer := &stubStreamReadWriter{writeStreamErr: tc.writeStreamErr} + stream := &fakeRecvFilesStream{msgs: tc.msgs, recvErr: tc.recvErr} + transfer := New(logger.NewTest(t), streamer, false) + if tc.recvAlreadyStarted { + transfer.receiveStarted = true + } + if tc.recvAlreadyFinished { + transfer.receiveFinished = true + } + err := transfer.RecvFiles(stream) + + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.Equal(tc.wantFiles, transfer.files) + }) + } +} + +func TestGetSetFiles(t *testing.T) { + testCases := map[string]struct { + setFiles *[]FileStat + wantFiles []FileStat + wantErr bool + }{ + "no files": { + wantFiles: []FileStat{}, + }, + "files": { + setFiles: &[]FileStat{ + { + SourcePath: "testfileA", + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + }, + wantFiles: []FileStat{ + { + SourcePath: "testfileA", + TargetPath: "testfileA", + Mode: 0o644, + OverrideServiceUnit: "somesvcA", + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + streamer := &dummyStreamReadWriter{} + transfer := New(logger.NewTest(t), streamer, false) + if tc.setFiles != nil { + transfer.SetFiles(*tc.setFiles) + } + gotFiles := transfer.GetFiles() + assert.Equal(tc.wantFiles, gotFiles) + assert.Equal(tc.setFiles != nil, transfer.receiveFinished) + }) + } +} + +func TestCanSend(t *testing.T) { + assert := assert.New(t) + + streamer := &stubStreamReadWriter{} + stream := &stubRecvFilesStream{recvErr: io.EOF} + transfer := New(logger.NewTest(t), streamer, false) + assert.False(transfer.CanSend()) + + // manual set + transfer.SetFiles(nil) + assert.True(transfer.CanSend()) + + // reset + transfer.receiveStarted = false + transfer.receiveFinished = false + transfer.files = nil + assert.False(transfer.CanSend()) + + // receive files (empty) + assert.NoError(transfer.RecvFiles(stream)) + assert.True(transfer.CanSend()) +} + +func TestConcurrency(t *testing.T) { + ft := New(logger.NewTest(t), &stubStreamReadWriter{}, false) + + sendFiles := func() { + _ = ft.SendFiles(&stubSendFilesStream{}) + } + + recvFiles := func() { + _ = ft.RecvFiles(&stubRecvFilesStream{}) + } + + getFiles := func() { + _ = ft.GetFiles() + } + + setFiles := func() { + ft.SetFiles([]FileStat{{SourcePath: "file", TargetPath: "file", Mode: 0o644}}) + } + + canSend := func() { + _ = ft.CanSend() + } + + go sendFiles() + go sendFiles() + go sendFiles() + go sendFiles() + go recvFiles() + go recvFiles() + go recvFiles() + go recvFiles() + go getFiles() + go getFiles() + go getFiles() + go getFiles() + go setFiles() + go setFiles() + go setFiles() + go setFiles() + go canSend() + go canSend() + go canSend() + go canSend() +} + +type stubStreamReadWriter struct { + readStreamFilename string + readStreamErr error + + writeStreamFilename string + writeStreamErr error +} + +func (s *stubStreamReadWriter) ReadStream(filename string, _ streamer.WriteChunkStream, _ uint, _ bool) error { + s.readStreamFilename = filename + return s.readStreamErr +} + +func (s *stubStreamReadWriter) WriteStream(filename string, _ streamer.ReadChunkStream, _ bool) error { + s.writeStreamFilename = filename + return s.writeStreamErr +} + +type fakeRecvFilesStream struct { + msgs []*pb.FileTransferMessage + pos int + recvErr error +} + +func (s *fakeRecvFilesStream) Recv() (*pb.FileTransferMessage, error) { + if s.recvErr != nil { + return nil, s.recvErr + } + + if s.pos < len(s.msgs) { + s.pos++ + return s.msgs[s.pos-1], nil + } + + return nil, io.EOF +} + +type dummyStreamReadWriter struct{} + +func (s *dummyStreamReadWriter) ReadStream(_ string, _ streamer.WriteChunkStream, _ uint, _ bool) error { + panic("dummy") +} + +func (s *dummyStreamReadWriter) WriteStream(_ string, _ streamer.ReadChunkStream, _ bool) error { + panic("dummy") +} diff --git a/debugd/internal/bootstrapper/streamer.go b/debugd/internal/filetransfer/streamer/streamer.go similarity index 53% rename from debugd/internal/bootstrapper/streamer.go rename to debugd/internal/filetransfer/streamer/streamer.go index ca8314556..89950f382 100644 --- a/debugd/internal/bootstrapper/streamer.go +++ b/debugd/internal/filetransfer/streamer/streamer.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package bootstrapper +package streamer import ( "errors" @@ -34,8 +34,8 @@ type WriteChunkStream interface { Send(chunk *pb.Chunk) error } -// NewFileStreamer creates a new FileStreamer. -func NewFileStreamer(fs afero.Fs) *FileStreamer { +// New creates a new FileStreamer. +func New(fs afero.Fs) *FileStreamer { return &FileStreamer{ fs: fs, mux: sync.RWMutex{}, @@ -44,52 +44,25 @@ func NewFileStreamer(fs afero.Fs) *FileStreamer { // WriteStream opens a file to write to and streams chunks from a gRPC stream into the file. func (f *FileStreamer) WriteStream(filename string, stream ReadChunkStream, showProgress bool) error { - // try to read from stream once before acquiring write lock - chunk, err := stream.Recv() - if err != nil { - return fmt.Errorf("reading stream: %w", err) - } - f.mux.Lock() defer f.mux.Unlock() - file, err := f.fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o755) + file, err := f.fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE, os.ModePerm) if err != nil { return fmt.Errorf("open %v for writing: %w", filename, err) } defer file.Close() + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("performing stat on %v to get the file size: %w", filename, err) + } var bar *progressbar.ProgressBar if showProgress { - bar = progressbar.NewOptions64( - -1, - progressbar.OptionSetDescription("receiving bootstrapper"), - progressbar.OptionShowBytes(true), - progressbar.OptionClearOnFinish(), - ) + bar = newProgressBar(stat.Size()) defer bar.Close() } - for { - if err != nil { - if errors.Is(err, io.EOF) { - break - } - _ = file.Close() - _ = f.fs.Remove(filename) - return fmt.Errorf("reading stream: %w", err) - } - if _, err := file.Write(chunk.Content); err != nil { - _ = file.Close() - _ = f.fs.Remove(filename) - return fmt.Errorf("writing chunk to disk: %w", err) - } - if showProgress { - _ = bar.Add(len(chunk.Content)) - } - chunk, err = stream.Recv() - } - - return nil + return writeInner(file, stream, bar) } // ReadStream opens a file to read from and streams its contents chunkwise over gRPC. @@ -101,44 +74,73 @@ func (f *FileStreamer) ReadStream(filename string, stream WriteChunkStream, chun if f.mux.TryRLock() { defer f.mux.RUnlock() } else { - return errors.New("file is opened for writing cannot be read at this time") + return errors.New("a file is opened for writing so cannot read at this time") } file, err := f.fs.OpenFile(filename, os.O_RDONLY, 0o755) if err != nil { return fmt.Errorf("open %v for reading: %w", filename, err) } defer file.Close() + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("performing stat on %v to get the file size: %w", filename, err) + } var bar *progressbar.ProgressBar if showProgress { - stat, err := file.Stat() - if err != nil { - return fmt.Errorf("performing stat on %v to get the file size: %w", filename, err) - } - bar = progressbar.NewOptions64( - stat.Size(), - progressbar.OptionSetDescription("uploading bootstrapper"), - progressbar.OptionShowBytes(true), - progressbar.OptionClearOnFinish(), - ) + bar = newProgressBar(stat.Size()) defer bar.Close() } + return readInner(file, stream, chunksize, bar) +} + +// readInner reads from a an io.Reader and sends chunks over a gRPC stream. +func readInner(fp io.Reader, stream WriteChunkStream, chunksize uint, bar *progressbar.ProgressBar) error { buf := make([]byte, chunksize) for { - n, err := file.Read(buf) - if err != nil { - if errors.Is(err, io.EOF) { - return nil - } - return fmt.Errorf("reading file chunk: %w", err) + n, readErr := fp.Read(buf) + isLast := errors.Is(readErr, io.EOF) + if readErr != nil && !isLast { + return fmt.Errorf("reading file chunk: %w", readErr) } - - if err = stream.Send(&pb.Chunk{Content: buf[:n]}); err != nil { + if err := stream.Send(&pb.Chunk{Content: buf[:n], Last: isLast}); err != nil { return fmt.Errorf("sending chunk: %w", err) } - if showProgress { + if bar != nil { _ = bar.Add(n) } + if isLast { + return nil + } } } + +// writeInner writes chunks from a gRPC stream to an io.Writer. +func writeInner(fp io.Writer, stream ReadChunkStream, bar *progressbar.ProgressBar) error { + for { + chunk, recvErr := stream.Recv() + if recvErr != nil { + return fmt.Errorf("reading stream: %w", recvErr) + } + if _, err := fp.Write(chunk.Content); err != nil { + return fmt.Errorf("writing chunk to disk: %w", err) + } + if bar != nil { + _ = bar.Add(len(chunk.Content)) + } + if chunk.Last { + return nil + } + } +} + +// newProgressBar creates a new progress bar. +func newProgressBar(size int64) *progressbar.ProgressBar { + return progressbar.NewOptions64( + size, + progressbar.OptionSetDescription("transferring file"), + progressbar.OptionShowBytes(true), + progressbar.OptionClearOnFinish(), + ) +} diff --git a/debugd/internal/bootstrapper/streamer_test.go b/debugd/internal/filetransfer/streamer/streamer_test.go similarity index 95% rename from debugd/internal/bootstrapper/streamer_test.go rename to debugd/internal/filetransfer/streamer/streamer_test.go index 3d3e32c13..a1cd333dd 100644 --- a/debugd/internal/bootstrapper/streamer_test.go +++ b/debugd/internal/filetransfer/streamer/streamer_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package bootstrapper +package streamer import ( "errors" @@ -82,7 +82,7 @@ func TestWriteStream(t *testing.T) { assert := assert.New(t) require := require.New(t) - writer := NewFileStreamer(tc.fs) + writer := New(tc.fs) err := writer.WriteStream(filename, &tc.readChunkStream, tc.showProgress) if tc.wantErr { @@ -99,6 +99,7 @@ func TestWriteStream(t *testing.T) { func TestReadStream(t *testing.T) { correctFilename := "testfile" + eof := []byte{} testCases := map[string]struct { writeChunkStream stubWriteChunkStream @@ -114,6 +115,7 @@ func TestReadStream(t *testing.T) { chunksize: 4, wantChunks: [][]byte{ []byte("test"), + eof, }, wantErr: false, }, @@ -124,6 +126,7 @@ func TestReadStream(t *testing.T) { wantChunks: [][]byte{ []byte("te"), []byte("st"), + eof, }, wantErr: false, }, @@ -140,6 +143,7 @@ func TestReadStream(t *testing.T) { showProgress: true, wantChunks: [][]byte{ []byte("test"), + eof, }, wantErr: false, }, @@ -165,7 +169,7 @@ func TestReadStream(t *testing.T) { fs := afero.NewMemMapFs() assert.NoError(afero.WriteFile(fs, correctFilename, []byte("test"), 0o755)) - reader := NewFileStreamer(fs) + reader := New(fs) err := reader.ReadStream(tc.filename, &tc.writeChunkStream, tc.chunksize, tc.showProgress) if tc.wantErr { @@ -189,8 +193,10 @@ func (s *fakeReadChunkStream) Recv() (*pb.Chunk, error) { return nil, s.recvErr } + isLastChunk := s.pos == len(s.chunks)-1 + if s.pos < len(s.chunks) { - result := &pb.Chunk{Content: s.chunks[s.pos]} + result := &pb.Chunk{Content: s.chunks[s.pos], Last: isLastChunk} s.pos++ return result, nil } diff --git a/debugd/service/debugd.pb.go b/debugd/service/debugd.pb.go index 65a6d0eb9..69ac46f8b 100644 --- a/debugd/service/debugd.pb.go +++ b/debugd/service/debugd.pb.go @@ -20,58 +20,107 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type UploadBootstrapperStatus int32 +type SetInfoStatus int32 const ( - UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS UploadBootstrapperStatus = 0 - UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED UploadBootstrapperStatus = 1 - UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED UploadBootstrapperStatus = 2 - UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_FILE_EXISTS UploadBootstrapperStatus = 3 + SetInfoStatus_SET_INFO_SUCCESS SetInfoStatus = 0 + SetInfoStatus_SET_INFO_ALREADY_SET SetInfoStatus = 1 ) -// Enum value maps for UploadBootstrapperStatus. +// Enum value maps for SetInfoStatus. var ( - UploadBootstrapperStatus_name = map[int32]string{ - 0: "UPLOAD_BOOTSTRAPPER_SUCCESS", - 1: "UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED", - 2: "UPLOAD_BOOTSTRAPPER_START_FAILED", - 3: "UPLOAD_BOOTSTRAPPER_FILE_EXISTS", + SetInfoStatus_name = map[int32]string{ + 0: "SET_INFO_SUCCESS", + 1: "SET_INFO_ALREADY_SET", } - UploadBootstrapperStatus_value = map[string]int32{ - "UPLOAD_BOOTSTRAPPER_SUCCESS": 0, - "UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED": 1, - "UPLOAD_BOOTSTRAPPER_START_FAILED": 2, - "UPLOAD_BOOTSTRAPPER_FILE_EXISTS": 3, + SetInfoStatus_value = map[string]int32{ + "SET_INFO_SUCCESS": 0, + "SET_INFO_ALREADY_SET": 1, } ) -func (x UploadBootstrapperStatus) Enum() *UploadBootstrapperStatus { - p := new(UploadBootstrapperStatus) +func (x SetInfoStatus) Enum() *SetInfoStatus { + p := new(SetInfoStatus) *p = x return p } -func (x UploadBootstrapperStatus) String() string { +func (x SetInfoStatus) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (UploadBootstrapperStatus) Descriptor() protoreflect.EnumDescriptor { +func (SetInfoStatus) Descriptor() protoreflect.EnumDescriptor { return file_debugd_proto_enumTypes[0].Descriptor() } -func (UploadBootstrapperStatus) Type() protoreflect.EnumType { +func (SetInfoStatus) Type() protoreflect.EnumType { return &file_debugd_proto_enumTypes[0] } -func (x UploadBootstrapperStatus) Number() protoreflect.EnumNumber { +func (x SetInfoStatus) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use UploadBootstrapperStatus.Descriptor instead. -func (UploadBootstrapperStatus) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use SetInfoStatus.Descriptor instead. +func (SetInfoStatus) EnumDescriptor() ([]byte, []int) { return file_debugd_proto_rawDescGZIP(), []int{0} } +type UploadFilesStatus int32 + +const ( + UploadFilesStatus_UPLOAD_FILES_SUCCESS UploadFilesStatus = 0 + UploadFilesStatus_UPLOAD_FILES_UPLOAD_FAILED UploadFilesStatus = 1 + UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED UploadFilesStatus = 2 + UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED UploadFilesStatus = 3 + UploadFilesStatus_UPLOAD_FILES_START_FAILED UploadFilesStatus = 4 +) + +// Enum value maps for UploadFilesStatus. +var ( + UploadFilesStatus_name = map[int32]string{ + 0: "UPLOAD_FILES_SUCCESS", + 1: "UPLOAD_FILES_UPLOAD_FAILED", + 2: "UPLOAD_FILES_ALREADY_STARTED", + 3: "UPLOAD_FILES_ALREADY_FINISHED", + 4: "UPLOAD_FILES_START_FAILED", + } + UploadFilesStatus_value = map[string]int32{ + "UPLOAD_FILES_SUCCESS": 0, + "UPLOAD_FILES_UPLOAD_FAILED": 1, + "UPLOAD_FILES_ALREADY_STARTED": 2, + "UPLOAD_FILES_ALREADY_FINISHED": 3, + "UPLOAD_FILES_START_FAILED": 4, + } +) + +func (x UploadFilesStatus) Enum() *UploadFilesStatus { + p := new(UploadFilesStatus) + *p = x + return p +} + +func (x UploadFilesStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UploadFilesStatus) Descriptor() protoreflect.EnumDescriptor { + return file_debugd_proto_enumTypes[1].Descriptor() +} + +func (UploadFilesStatus) Type() protoreflect.EnumType { + return &file_debugd_proto_enumTypes[1] +} + +func (x UploadFilesStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UploadFilesStatus.Descriptor instead. +func (UploadFilesStatus) EnumDescriptor() ([]byte, []int) { + return file_debugd_proto_rawDescGZIP(), []int{1} +} + type UploadSystemdServiceUnitsStatus int32 const ( @@ -102,11 +151,11 @@ func (x UploadSystemdServiceUnitsStatus) String() string { } func (UploadSystemdServiceUnitsStatus) Descriptor() protoreflect.EnumDescriptor { - return file_debugd_proto_enumTypes[1].Descriptor() + return file_debugd_proto_enumTypes[2].Descriptor() } func (UploadSystemdServiceUnitsStatus) Type() protoreflect.EnumType { - return &file_debugd_proto_enumTypes[1] + return &file_debugd_proto_enumTypes[2] } func (x UploadSystemdServiceUnitsStatus) Number() protoreflect.EnumNumber { @@ -115,7 +164,7 @@ func (x UploadSystemdServiceUnitsStatus) Number() protoreflect.EnumNumber { // Deprecated: Use UploadSystemdServiceUnitsStatus.Descriptor instead. func (UploadSystemdServiceUnitsStatus) EnumDescriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{1} + return file_debugd_proto_rawDescGZIP(), []int{2} } type SetInfoRequest struct { @@ -169,6 +218,8 @@ type SetInfoResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Status SetInfoStatus `protobuf:"varint,1,opt,name=status,proto3,enum=debugd.SetInfoStatus" json:"status,omitempty"` } func (x *SetInfoResponse) Reset() { @@ -203,6 +254,13 @@ func (*SetInfoResponse) Descriptor() ([]byte, []int) { return file_debugd_proto_rawDescGZIP(), []int{1} } +func (x *SetInfoResponse) GetStatus() SetInfoStatus { + if x != nil { + return x.Status + } + return SetInfoStatus_SET_INFO_SUCCESS +} + type GetInfoRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -343,14 +401,14 @@ func (x *Info) GetValue() string { return "" } -type DownloadBootstrapperRequest struct { +type DownloadFilesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *DownloadBootstrapperRequest) Reset() { - *x = DownloadBootstrapperRequest{} +func (x *DownloadFilesRequest) Reset() { + *x = DownloadFilesRequest{} if protoimpl.UnsafeEnabled { mi := &file_debugd_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -358,13 +416,13 @@ func (x *DownloadBootstrapperRequest) Reset() { } } -func (x *DownloadBootstrapperRequest) String() string { +func (x *DownloadFilesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DownloadBootstrapperRequest) ProtoMessage() {} +func (*DownloadFilesRequest) ProtoMessage() {} -func (x *DownloadBootstrapperRequest) ProtoReflect() protoreflect.Message { +func (x *DownloadFilesRequest) ProtoReflect() protoreflect.Message { mi := &file_debugd_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -376,23 +434,168 @@ func (x *DownloadBootstrapperRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DownloadBootstrapperRequest.ProtoReflect.Descriptor instead. -func (*DownloadBootstrapperRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use DownloadFilesRequest.ProtoReflect.Descriptor instead. +func (*DownloadFilesRequest) Descriptor() ([]byte, []int) { return file_debugd_proto_rawDescGZIP(), []int{5} } +type FileTransferMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Kind: + // + // *FileTransferMessage_Header + // *FileTransferMessage_Chunk + Kind isFileTransferMessage_Kind `protobuf_oneof:"kind"` +} + +func (x *FileTransferMessage) Reset() { + *x = FileTransferMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_debugd_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FileTransferMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FileTransferMessage) ProtoMessage() {} + +func (x *FileTransferMessage) ProtoReflect() protoreflect.Message { + mi := &file_debugd_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FileTransferMessage.ProtoReflect.Descriptor instead. +func (*FileTransferMessage) Descriptor() ([]byte, []int) { + return file_debugd_proto_rawDescGZIP(), []int{6} +} + +func (m *FileTransferMessage) GetKind() isFileTransferMessage_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (x *FileTransferMessage) GetHeader() *FileTransferHeader { + if x, ok := x.GetKind().(*FileTransferMessage_Header); ok { + return x.Header + } + return nil +} + +func (x *FileTransferMessage) GetChunk() *Chunk { + if x, ok := x.GetKind().(*FileTransferMessage_Chunk); ok { + return x.Chunk + } + return nil +} + +type isFileTransferMessage_Kind interface { + isFileTransferMessage_Kind() +} + +type FileTransferMessage_Header struct { + Header *FileTransferHeader `protobuf:"bytes,1,opt,name=header,proto3,oneof"` // start of transfer +} + +type FileTransferMessage_Chunk struct { + Chunk *Chunk `protobuf:"bytes,2,opt,name=chunk,proto3,oneof"` // file content as chunks +} + +func (*FileTransferMessage_Header) isFileTransferMessage_Kind() {} + +func (*FileTransferMessage_Chunk) isFileTransferMessage_Kind() {} + +type FileTransferHeader struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TargetPath string `protobuf:"bytes,1,opt,name=targetPath,proto3" json:"targetPath,omitempty"` + Mode uint32 `protobuf:"varint,3,opt,name=mode,proto3" json:"mode,omitempty"` + OverrideServiceUnit *string `protobuf:"bytes,4,opt,name=overrideServiceUnit,proto3,oneof" json:"overrideServiceUnit,omitempty"` +} + +func (x *FileTransferHeader) Reset() { + *x = FileTransferHeader{} + if protoimpl.UnsafeEnabled { + mi := &file_debugd_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FileTransferHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FileTransferHeader) ProtoMessage() {} + +func (x *FileTransferHeader) ProtoReflect() protoreflect.Message { + mi := &file_debugd_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FileTransferHeader.ProtoReflect.Descriptor instead. +func (*FileTransferHeader) Descriptor() ([]byte, []int) { + return file_debugd_proto_rawDescGZIP(), []int{7} +} + +func (x *FileTransferHeader) GetTargetPath() string { + if x != nil { + return x.TargetPath + } + return "" +} + +func (x *FileTransferHeader) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *FileTransferHeader) GetOverrideServiceUnit() string { + if x != nil && x.OverrideServiceUnit != nil { + return *x.OverrideServiceUnit + } + return "" +} + type Chunk struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` + Last bool `protobuf:"varint,2,opt,name=last,proto3" json:"last,omitempty"` } func (x *Chunk) Reset() { *x = Chunk{} if protoimpl.UnsafeEnabled { - mi := &file_debugd_proto_msgTypes[6] + mi := &file_debugd_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -405,7 +608,7 @@ func (x *Chunk) String() string { func (*Chunk) ProtoMessage() {} func (x *Chunk) ProtoReflect() protoreflect.Message { - mi := &file_debugd_proto_msgTypes[6] + mi := &file_debugd_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -418,7 +621,7 @@ func (x *Chunk) ProtoReflect() protoreflect.Message { // Deprecated: Use Chunk.ProtoReflect.Descriptor instead. func (*Chunk) Descriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{6} + return file_debugd_proto_rawDescGZIP(), []int{8} } func (x *Chunk) GetContent() []byte { @@ -428,31 +631,38 @@ func (x *Chunk) GetContent() []byte { return nil } -type UploadBootstrapperResponse struct { +func (x *Chunk) GetLast() bool { + if x != nil { + return x.Last + } + return false +} + +type UploadFilesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status UploadBootstrapperStatus `protobuf:"varint,1,opt,name=status,proto3,enum=debugd.UploadBootstrapperStatus" json:"status,omitempty"` + Status UploadFilesStatus `protobuf:"varint,1,opt,name=status,proto3,enum=debugd.UploadFilesStatus" json:"status,omitempty"` } -func (x *UploadBootstrapperResponse) Reset() { - *x = UploadBootstrapperResponse{} +func (x *UploadFilesResponse) Reset() { + *x = UploadFilesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_debugd_proto_msgTypes[7] + mi := &file_debugd_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *UploadBootstrapperResponse) String() string { +func (x *UploadFilesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UploadBootstrapperResponse) ProtoMessage() {} +func (*UploadFilesResponse) ProtoMessage() {} -func (x *UploadBootstrapperResponse) ProtoReflect() protoreflect.Message { - mi := &file_debugd_proto_msgTypes[7] +func (x *UploadFilesResponse) ProtoReflect() protoreflect.Message { + mi := &file_debugd_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -463,16 +673,16 @@ func (x *UploadBootstrapperResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UploadBootstrapperResponse.ProtoReflect.Descriptor instead. -func (*UploadBootstrapperResponse) Descriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{7} +// Deprecated: Use UploadFilesResponse.ProtoReflect.Descriptor instead. +func (*UploadFilesResponse) Descriptor() ([]byte, []int) { + return file_debugd_proto_rawDescGZIP(), []int{9} } -func (x *UploadBootstrapperResponse) GetStatus() UploadBootstrapperStatus { +func (x *UploadFilesResponse) GetStatus() UploadFilesStatus { if x != nil { return x.Status } - return UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS + return UploadFilesStatus_UPLOAD_FILES_SUCCESS } type ServiceUnit struct { @@ -487,7 +697,7 @@ type ServiceUnit struct { func (x *ServiceUnit) Reset() { *x = ServiceUnit{} if protoimpl.UnsafeEnabled { - mi := &file_debugd_proto_msgTypes[8] + mi := &file_debugd_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -500,7 +710,7 @@ func (x *ServiceUnit) String() string { func (*ServiceUnit) ProtoMessage() {} func (x *ServiceUnit) ProtoReflect() protoreflect.Message { - mi := &file_debugd_proto_msgTypes[8] + mi := &file_debugd_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -513,7 +723,7 @@ func (x *ServiceUnit) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceUnit.ProtoReflect.Descriptor instead. func (*ServiceUnit) Descriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{8} + return file_debugd_proto_rawDescGZIP(), []int{10} } func (x *ServiceUnit) GetName() string { @@ -541,7 +751,7 @@ type UploadSystemdServiceUnitsRequest struct { func (x *UploadSystemdServiceUnitsRequest) Reset() { *x = UploadSystemdServiceUnitsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_debugd_proto_msgTypes[9] + mi := &file_debugd_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -554,7 +764,7 @@ func (x *UploadSystemdServiceUnitsRequest) String() string { func (*UploadSystemdServiceUnitsRequest) ProtoMessage() {} func (x *UploadSystemdServiceUnitsRequest) ProtoReflect() protoreflect.Message { - mi := &file_debugd_proto_msgTypes[9] + mi := &file_debugd_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -567,7 +777,7 @@ func (x *UploadSystemdServiceUnitsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadSystemdServiceUnitsRequest.ProtoReflect.Descriptor instead. func (*UploadSystemdServiceUnitsRequest) Descriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{9} + return file_debugd_proto_rawDescGZIP(), []int{11} } func (x *UploadSystemdServiceUnitsRequest) GetUnits() []*ServiceUnit { @@ -588,7 +798,7 @@ type UploadSystemdServiceUnitsResponse struct { func (x *UploadSystemdServiceUnitsResponse) Reset() { *x = UploadSystemdServiceUnitsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_debugd_proto_msgTypes[10] + mi := &file_debugd_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -601,7 +811,7 @@ func (x *UploadSystemdServiceUnitsResponse) String() string { func (*UploadSystemdServiceUnitsResponse) ProtoMessage() {} func (x *UploadSystemdServiceUnitsResponse) ProtoReflect() protoreflect.Message { - mi := &file_debugd_proto_msgTypes[10] + mi := &file_debugd_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -614,7 +824,7 @@ func (x *UploadSystemdServiceUnitsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use UploadSystemdServiceUnitsResponse.ProtoReflect.Descriptor instead. func (*UploadSystemdServiceUnitsResponse) Descriptor() ([]byte, []int) { - return file_debugd_proto_rawDescGZIP(), []int{10} + return file_debugd_proto_rawDescGZIP(), []int{12} } func (x *UploadSystemdServiceUnitsResponse) GetStatus() UploadSystemdServiceUnitsStatus { @@ -631,89 +841,113 @@ var file_debugd_proto_rawDesc = []byte{ 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x22, 0x32, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x33, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, - 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x2e, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 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, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, - 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x21, 0x0a, 0x05, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x56, 0x0a, 0x1a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3d, - 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x4d, 0x0a, - 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x29, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x64, 0x0a, 0x21, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, - 0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x2a, 0xad, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, - 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, - 0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, - 0x12, 0x25, 0x0a, 0x21, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, - 0x54, 0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x4c, 0x4f, 0x41, - 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x53, - 0x54, 0x41, 0x52, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x23, 0x0a, - 0x1f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x52, 0x41, - 0x50, 0x50, 0x45, 0x52, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, - 0x10, 0x03, 0x2a, 0x75, 0x0a, 0x1f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, - 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, - 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, - 0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, - 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x06, 0x44, 0x65, - 0x62, 0x75, 0x67, 0x64, 0x12, 0x3c, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x16, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, - 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x4b, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, - 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x4e, 0x0a, - 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, - 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x64, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, - 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x62, 0x75, - 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, - 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, - 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x62, 0x75, - 0x67, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x40, 0x0a, 0x0f, 0x53, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x10, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x33, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, + 0x6e, 0x66, 0x6f, 0x22, 0x2e, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 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, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x13, 0x46, + 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, + 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x42, + 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x97, 0x01, 0x0a, 0x12, 0x46, 0x69, 0x6c, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1e, + 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, + 0x64, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x76, + 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, + 0x74, 0x22, 0x35, 0x0a, 0x05, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x22, 0x48, 0x0a, 0x13, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x31, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x19, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x3d, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x4d, 0x0a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, + 0x22, 0x64, 0x0a, 0x21, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x3f, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x54, 0x5f, 0x49, + 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x18, 0x0a, + 0x14, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, + 0x59, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x01, 0x2a, 0xb1, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, + 0x14, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x53, 0x55, + 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x50, 0x4c, 0x4f, 0x41, + 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x50, 0x4c, 0x4f, 0x41, + 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, + 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x50, 0x4c, + 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, + 0x59, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19, + 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x53, 0x54, 0x41, + 0x52, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x75, 0x0a, 0x1f, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, + 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44, + 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x53, + 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, + 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, + 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x06, 0x44, 0x65, 0x62, 0x75, 0x67, 0x64, 0x12, 0x3c, 0x0a, + 0x07, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, + 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x07, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0b, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, + 0x64, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1b, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x4e, 0x0a, 0x0d, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, + 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x46, + 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, + 0x74, 0x73, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, + 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, + 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -728,44 +962,50 @@ func file_debugd_proto_rawDescGZIP() []byte { return file_debugd_proto_rawDescData } -var file_debugd_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_debugd_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_debugd_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_debugd_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_debugd_proto_goTypes = []interface{}{ - (UploadBootstrapperStatus)(0), // 0: debugd.UploadBootstrapperStatus - (UploadSystemdServiceUnitsStatus)(0), // 1: debugd.UploadSystemdServiceUnitsStatus - (*SetInfoRequest)(nil), // 2: debugd.SetInfoRequest - (*SetInfoResponse)(nil), // 3: debugd.SetInfoResponse - (*GetInfoRequest)(nil), // 4: debugd.GetInfoRequest - (*GetInfoResponse)(nil), // 5: debugd.GetInfoResponse - (*Info)(nil), // 6: debugd.Info - (*DownloadBootstrapperRequest)(nil), // 7: debugd.DownloadBootstrapperRequest - (*Chunk)(nil), // 8: debugd.Chunk - (*UploadBootstrapperResponse)(nil), // 9: debugd.UploadBootstrapperResponse - (*ServiceUnit)(nil), // 10: debugd.ServiceUnit - (*UploadSystemdServiceUnitsRequest)(nil), // 11: debugd.UploadSystemdServiceUnitsRequest - (*UploadSystemdServiceUnitsResponse)(nil), // 12: debugd.UploadSystemdServiceUnitsResponse + (SetInfoStatus)(0), // 0: debugd.SetInfoStatus + (UploadFilesStatus)(0), // 1: debugd.UploadFilesStatus + (UploadSystemdServiceUnitsStatus)(0), // 2: debugd.UploadSystemdServiceUnitsStatus + (*SetInfoRequest)(nil), // 3: debugd.SetInfoRequest + (*SetInfoResponse)(nil), // 4: debugd.SetInfoResponse + (*GetInfoRequest)(nil), // 5: debugd.GetInfoRequest + (*GetInfoResponse)(nil), // 6: debugd.GetInfoResponse + (*Info)(nil), // 7: debugd.Info + (*DownloadFilesRequest)(nil), // 8: debugd.DownloadFilesRequest + (*FileTransferMessage)(nil), // 9: debugd.FileTransferMessage + (*FileTransferHeader)(nil), // 10: debugd.FileTransferHeader + (*Chunk)(nil), // 11: debugd.Chunk + (*UploadFilesResponse)(nil), // 12: debugd.UploadFilesResponse + (*ServiceUnit)(nil), // 13: debugd.ServiceUnit + (*UploadSystemdServiceUnitsRequest)(nil), // 14: debugd.UploadSystemdServiceUnitsRequest + (*UploadSystemdServiceUnitsResponse)(nil), // 15: debugd.UploadSystemdServiceUnitsResponse } var file_debugd_proto_depIdxs = []int32{ - 6, // 0: debugd.SetInfoRequest.info:type_name -> debugd.Info - 6, // 1: debugd.GetInfoResponse.info:type_name -> debugd.Info - 0, // 2: debugd.UploadBootstrapperResponse.status:type_name -> debugd.UploadBootstrapperStatus - 10, // 3: debugd.UploadSystemdServiceUnitsRequest.units:type_name -> debugd.ServiceUnit - 1, // 4: debugd.UploadSystemdServiceUnitsResponse.status:type_name -> debugd.UploadSystemdServiceUnitsStatus - 2, // 5: debugd.Debugd.SetInfo:input_type -> debugd.SetInfoRequest - 4, // 6: debugd.Debugd.GetInfo:input_type -> debugd.GetInfoRequest - 8, // 7: debugd.Debugd.UploadBootstrapper:input_type -> debugd.Chunk - 7, // 8: debugd.Debugd.DownloadBootstrapper:input_type -> debugd.DownloadBootstrapperRequest - 11, // 9: debugd.Debugd.UploadSystemServiceUnits:input_type -> debugd.UploadSystemdServiceUnitsRequest - 3, // 10: debugd.Debugd.SetInfo:output_type -> debugd.SetInfoResponse - 5, // 11: debugd.Debugd.GetInfo:output_type -> debugd.GetInfoResponse - 9, // 12: debugd.Debugd.UploadBootstrapper:output_type -> debugd.UploadBootstrapperResponse - 8, // 13: debugd.Debugd.DownloadBootstrapper:output_type -> debugd.Chunk - 12, // 14: debugd.Debugd.UploadSystemServiceUnits:output_type -> debugd.UploadSystemdServiceUnitsResponse - 10, // [10:15] is the sub-list for method output_type - 5, // [5:10] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 7, // 0: debugd.SetInfoRequest.info:type_name -> debugd.Info + 0, // 1: debugd.SetInfoResponse.status:type_name -> debugd.SetInfoStatus + 7, // 2: debugd.GetInfoResponse.info:type_name -> debugd.Info + 10, // 3: debugd.FileTransferMessage.header:type_name -> debugd.FileTransferHeader + 11, // 4: debugd.FileTransferMessage.chunk:type_name -> debugd.Chunk + 1, // 5: debugd.UploadFilesResponse.status:type_name -> debugd.UploadFilesStatus + 13, // 6: debugd.UploadSystemdServiceUnitsRequest.units:type_name -> debugd.ServiceUnit + 2, // 7: debugd.UploadSystemdServiceUnitsResponse.status:type_name -> debugd.UploadSystemdServiceUnitsStatus + 3, // 8: debugd.Debugd.SetInfo:input_type -> debugd.SetInfoRequest + 5, // 9: debugd.Debugd.GetInfo:input_type -> debugd.GetInfoRequest + 9, // 10: debugd.Debugd.UploadFiles:input_type -> debugd.FileTransferMessage + 8, // 11: debugd.Debugd.DownloadFiles:input_type -> debugd.DownloadFilesRequest + 14, // 12: debugd.Debugd.UploadSystemServiceUnits:input_type -> debugd.UploadSystemdServiceUnitsRequest + 4, // 13: debugd.Debugd.SetInfo:output_type -> debugd.SetInfoResponse + 6, // 14: debugd.Debugd.GetInfo:output_type -> debugd.GetInfoResponse + 12, // 15: debugd.Debugd.UploadFiles:output_type -> debugd.UploadFilesResponse + 9, // 16: debugd.Debugd.DownloadFiles:output_type -> debugd.FileTransferMessage + 15, // 17: debugd.Debugd.UploadSystemServiceUnits:output_type -> debugd.UploadSystemdServiceUnitsResponse + 13, // [13:18] is the sub-list for method output_type + 8, // [8:13] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_debugd_proto_init() } @@ -835,7 +1075,7 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DownloadBootstrapperRequest); i { + switch v := v.(*DownloadFilesRequest); i { case 0: return &v.state case 1: @@ -847,7 +1087,7 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Chunk); i { + switch v := v.(*FileTransferMessage); i { case 0: return &v.state case 1: @@ -859,7 +1099,7 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadBootstrapperResponse); i { + switch v := v.(*FileTransferHeader); i { case 0: return &v.state case 1: @@ -871,7 +1111,7 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceUnit); i { + switch v := v.(*Chunk); i { case 0: return &v.state case 1: @@ -883,7 +1123,7 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UploadSystemdServiceUnitsRequest); i { + switch v := v.(*UploadFilesResponse); i { case 0: return &v.state case 1: @@ -895,6 +1135,30 @@ func file_debugd_proto_init() { } } file_debugd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServiceUnit); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_debugd_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadSystemdServiceUnitsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_debugd_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UploadSystemdServiceUnitsResponse); i { case 0: return &v.state @@ -907,13 +1171,18 @@ func file_debugd_proto_init() { } } } + file_debugd_proto_msgTypes[6].OneofWrappers = []interface{}{ + (*FileTransferMessage_Header)(nil), + (*FileTransferMessage_Chunk)(nil), + } + file_debugd_proto_msgTypes[7].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_debugd_proto_rawDesc, - NumEnums: 2, - NumMessages: 11, + NumEnums: 3, + NumMessages: 13, NumExtensions: 0, NumServices: 1, }, diff --git a/debugd/service/debugd.proto b/debugd/service/debugd.proto index f8ec1fbfc..7cc9b1635 100644 --- a/debugd/service/debugd.proto +++ b/debugd/service/debugd.proto @@ -7,8 +7,8 @@ package debugd; service Debugd { rpc SetInfo (SetInfoRequest) returns (SetInfoResponse) {} rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) {} - rpc UploadBootstrapper(stream Chunk) returns (UploadBootstrapperResponse) {} - rpc DownloadBootstrapper(DownloadBootstrapperRequest) returns (stream Chunk) {} + rpc UploadFiles(stream FileTransferMessage) returns (UploadFilesResponse) {} + rpc DownloadFiles(DownloadFilesRequest) returns (stream FileTransferMessage) {} rpc UploadSystemServiceUnits(UploadSystemdServiceUnitsRequest) returns (UploadSystemdServiceUnitsResponse) {} } @@ -16,7 +16,14 @@ message SetInfoRequest { repeated Info info = 1; } -message SetInfoResponse {} +message SetInfoResponse { + SetInfoStatus status = 1; +} + +enum SetInfoStatus { + SET_INFO_SUCCESS = 0; + SET_INFO_ALREADY_SET = 1; +} message GetInfoRequest {} @@ -29,22 +36,36 @@ message Info { string value = 2; } +message DownloadFilesRequest {} -message DownloadBootstrapperRequest {} +message FileTransferMessage { + oneof kind { + FileTransferHeader header = 1; // start of transfer + Chunk chunk = 2; // file content as chunks + } +} + +message FileTransferHeader { + string targetPath = 1; + uint32 mode = 3; + optional string overrideServiceUnit = 4; +} message Chunk { bytes content = 1; + bool last = 2; } -message UploadBootstrapperResponse { - UploadBootstrapperStatus status = 1; +message UploadFilesResponse { + UploadFilesStatus status = 1; } -enum UploadBootstrapperStatus { - UPLOAD_BOOTSTRAPPER_SUCCESS = 0; - UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED = 1; - UPLOAD_BOOTSTRAPPER_START_FAILED = 2; - UPLOAD_BOOTSTRAPPER_FILE_EXISTS = 3; +enum UploadFilesStatus { + UPLOAD_FILES_SUCCESS = 0; + UPLOAD_FILES_UPLOAD_FAILED = 1; + UPLOAD_FILES_ALREADY_STARTED = 2; + UPLOAD_FILES_ALREADY_FINISHED = 3; + UPLOAD_FILES_START_FAILED = 4; } message ServiceUnit { diff --git a/debugd/service/debugd_grpc.pb.go b/debugd/service/debugd_grpc.pb.go index 825406eae..dabb6ba95 100644 --- a/debugd/service/debugd_grpc.pb.go +++ b/debugd/service/debugd_grpc.pb.go @@ -24,8 +24,8 @@ const _ = grpc.SupportPackageIsVersion7 type DebugdClient interface { SetInfo(ctx context.Context, in *SetInfoRequest, opts ...grpc.CallOption) (*SetInfoResponse, error) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) - UploadBootstrapper(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadBootstrapperClient, error) - DownloadBootstrapper(ctx context.Context, in *DownloadBootstrapperRequest, opts ...grpc.CallOption) (Debugd_DownloadBootstrapperClient, error) + UploadFiles(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadFilesClient, error) + DownloadFiles(ctx context.Context, in *DownloadFilesRequest, opts ...grpc.CallOption) (Debugd_DownloadFilesClient, error) UploadSystemServiceUnits(ctx context.Context, in *UploadSystemdServiceUnitsRequest, opts ...grpc.CallOption) (*UploadSystemdServiceUnitsResponse, error) } @@ -55,46 +55,46 @@ func (c *debugdClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ... return out, nil } -func (c *debugdClient) UploadBootstrapper(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadBootstrapperClient, error) { - stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[0], "/debugd.Debugd/UploadBootstrapper", opts...) +func (c *debugdClient) UploadFiles(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadFilesClient, error) { + stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[0], "/debugd.Debugd/UploadFiles", opts...) if err != nil { return nil, err } - x := &debugdUploadBootstrapperClient{stream} + x := &debugdUploadFilesClient{stream} return x, nil } -type Debugd_UploadBootstrapperClient interface { - Send(*Chunk) error - CloseAndRecv() (*UploadBootstrapperResponse, error) +type Debugd_UploadFilesClient interface { + Send(*FileTransferMessage) error + CloseAndRecv() (*UploadFilesResponse, error) grpc.ClientStream } -type debugdUploadBootstrapperClient struct { +type debugdUploadFilesClient struct { grpc.ClientStream } -func (x *debugdUploadBootstrapperClient) Send(m *Chunk) error { +func (x *debugdUploadFilesClient) Send(m *FileTransferMessage) error { return x.ClientStream.SendMsg(m) } -func (x *debugdUploadBootstrapperClient) CloseAndRecv() (*UploadBootstrapperResponse, error) { +func (x *debugdUploadFilesClient) CloseAndRecv() (*UploadFilesResponse, error) { if err := x.ClientStream.CloseSend(); err != nil { return nil, err } - m := new(UploadBootstrapperResponse) + m := new(UploadFilesResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } -func (c *debugdClient) DownloadBootstrapper(ctx context.Context, in *DownloadBootstrapperRequest, opts ...grpc.CallOption) (Debugd_DownloadBootstrapperClient, error) { - stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[1], "/debugd.Debugd/DownloadBootstrapper", opts...) +func (c *debugdClient) DownloadFiles(ctx context.Context, in *DownloadFilesRequest, opts ...grpc.CallOption) (Debugd_DownloadFilesClient, error) { + stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[1], "/debugd.Debugd/DownloadFiles", opts...) if err != nil { return nil, err } - x := &debugdDownloadBootstrapperClient{stream} + x := &debugdDownloadFilesClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -104,17 +104,17 @@ func (c *debugdClient) DownloadBootstrapper(ctx context.Context, in *DownloadBoo return x, nil } -type Debugd_DownloadBootstrapperClient interface { - Recv() (*Chunk, error) +type Debugd_DownloadFilesClient interface { + Recv() (*FileTransferMessage, error) grpc.ClientStream } -type debugdDownloadBootstrapperClient struct { +type debugdDownloadFilesClient struct { grpc.ClientStream } -func (x *debugdDownloadBootstrapperClient) Recv() (*Chunk, error) { - m := new(Chunk) +func (x *debugdDownloadFilesClient) Recv() (*FileTransferMessage, error) { + m := new(FileTransferMessage) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } @@ -136,8 +136,8 @@ func (c *debugdClient) UploadSystemServiceUnits(ctx context.Context, in *UploadS type DebugdServer interface { SetInfo(context.Context, *SetInfoRequest) (*SetInfoResponse, error) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) - UploadBootstrapper(Debugd_UploadBootstrapperServer) error - DownloadBootstrapper(*DownloadBootstrapperRequest, Debugd_DownloadBootstrapperServer) error + UploadFiles(Debugd_UploadFilesServer) error + DownloadFiles(*DownloadFilesRequest, Debugd_DownloadFilesServer) error UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error) mustEmbedUnimplementedDebugdServer() } @@ -152,11 +152,11 @@ func (UnimplementedDebugdServer) SetInfo(context.Context, *SetInfoRequest) (*Set func (UnimplementedDebugdServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") } -func (UnimplementedDebugdServer) UploadBootstrapper(Debugd_UploadBootstrapperServer) error { - return status.Errorf(codes.Unimplemented, "method UploadBootstrapper not implemented") +func (UnimplementedDebugdServer) UploadFiles(Debugd_UploadFilesServer) error { + return status.Errorf(codes.Unimplemented, "method UploadFiles not implemented") } -func (UnimplementedDebugdServer) DownloadBootstrapper(*DownloadBootstrapperRequest, Debugd_DownloadBootstrapperServer) error { - return status.Errorf(codes.Unimplemented, "method DownloadBootstrapper not implemented") +func (UnimplementedDebugdServer) DownloadFiles(*DownloadFilesRequest, Debugd_DownloadFilesServer) error { + return status.Errorf(codes.Unimplemented, "method DownloadFiles not implemented") } func (UnimplementedDebugdServer) UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UploadSystemServiceUnits not implemented") @@ -210,50 +210,50 @@ func _Debugd_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } -func _Debugd_UploadBootstrapper_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(DebugdServer).UploadBootstrapper(&debugdUploadBootstrapperServer{stream}) +func _Debugd_UploadFiles_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(DebugdServer).UploadFiles(&debugdUploadFilesServer{stream}) } -type Debugd_UploadBootstrapperServer interface { - SendAndClose(*UploadBootstrapperResponse) error - Recv() (*Chunk, error) +type Debugd_UploadFilesServer interface { + SendAndClose(*UploadFilesResponse) error + Recv() (*FileTransferMessage, error) grpc.ServerStream } -type debugdUploadBootstrapperServer struct { +type debugdUploadFilesServer struct { grpc.ServerStream } -func (x *debugdUploadBootstrapperServer) SendAndClose(m *UploadBootstrapperResponse) error { +func (x *debugdUploadFilesServer) SendAndClose(m *UploadFilesResponse) error { return x.ServerStream.SendMsg(m) } -func (x *debugdUploadBootstrapperServer) Recv() (*Chunk, error) { - m := new(Chunk) +func (x *debugdUploadFilesServer) Recv() (*FileTransferMessage, error) { + m := new(FileTransferMessage) if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } return m, nil } -func _Debugd_DownloadBootstrapper_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(DownloadBootstrapperRequest) +func _Debugd_DownloadFiles_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(DownloadFilesRequest) if err := stream.RecvMsg(m); err != nil { return err } - return srv.(DebugdServer).DownloadBootstrapper(m, &debugdDownloadBootstrapperServer{stream}) + return srv.(DebugdServer).DownloadFiles(m, &debugdDownloadFilesServer{stream}) } -type Debugd_DownloadBootstrapperServer interface { - Send(*Chunk) error +type Debugd_DownloadFilesServer interface { + Send(*FileTransferMessage) error grpc.ServerStream } -type debugdDownloadBootstrapperServer struct { +type debugdDownloadFilesServer struct { grpc.ServerStream } -func (x *debugdDownloadBootstrapperServer) Send(m *Chunk) error { +func (x *debugdDownloadFilesServer) Send(m *FileTransferMessage) error { return x.ServerStream.SendMsg(m) } @@ -297,13 +297,13 @@ var Debugd_ServiceDesc = grpc.ServiceDesc{ }, Streams: []grpc.StreamDesc{ { - StreamName: "UploadBootstrapper", - Handler: _Debugd_UploadBootstrapper_Handler, + StreamName: "UploadFiles", + Handler: _Debugd_UploadFiles_Handler, ClientStreams: true, }, { - StreamName: "DownloadBootstrapper", - Handler: _Debugd_DownloadBootstrapper_Handler, + StreamName: "DownloadFiles", + Handler: _Debugd_DownloadFiles_Handler, ServerStreams: true, }, },