debugd: implement upload of multiple binaries

This commit is contained in:
Malte Poll 2023-01-20 10:11:41 +01:00 committed by Malte Poll
parent e6ac8e2a91
commit 6f56ed69f8
21 changed files with 2040 additions and 661 deletions

View File

@ -14,7 +14,6 @@ import (
"os" "os"
"sync" "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/deploy"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/logcollector" "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/cloudprovider"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/metadata/fallback" "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/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" awscloud "github.com/edgelesssys/constellation/v2/internal/cloud/aws"
azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure" azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure"
platform "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" platform "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -45,7 +46,8 @@ func main() {
log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity)) log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity))
fs := afero.NewOsFs() 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")) serviceManager := deploy.NewServiceManager(log.Named("serviceManager"))
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -92,13 +94,10 @@ func main() {
logcollector.NewStartTrigger(ctx, wg, platform.FromString(csp), fetcher, log.Named("logcollector")), 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) sched := metadata.NewScheduler(log.Named("scheduler"), fetcher, download)
serv := server.New(log.Named("server"), serviceManager, streamer, infoMap) serv := server.New(log.Named("server"), serviceManager, filetransferer, infoMap)
if err := deploy.DefaultServiceUnit(ctx, serviceManager); err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create default service unit")
}
writeDebugBanner(log) writeDebugBanner(log)

View File

@ -10,18 +10,19 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"net" "net"
"strconv" "strconv"
"strings" "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"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/logcollector" "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" pb "github.com/edgelesssys/constellation/v2/debugd/service"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc" "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().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("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().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 return deployCmd
} }
func runDeploy(cmd *cobra.Command, args []string) error { 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") configName, err := cmd.Flags().GetString("config")
if err != nil { if err != nil {
return fmt.Errorf("parsing config path argument: %w", err) 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) constellationConfig, err := config.FromFile(fileHandler, configName)
if err != nil { if err != nil {
return err 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") bootstrapperPath, err := cmd.Flags().GetString("bootstrapper")
if err != nil { if err != nil {
return err return err
} }
upgradeAgentPath, err := cmd.Flags().GetString("upgrade-agent")
if err != nil {
return err
}
if constellationConfig.IsReleaseImage() { 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() { if !constellationConfig.IsDebugCluster() {
log.Println("WARNING: The Constellation config has debugCluster set to false.") log.Infof("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.Infof("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("If you create the cluster with a debug image, you should also set debugCluster to true.")
} }
ips, err := cmd.Flags().GetStringSlice("ips") ips, err := cmd.Flags().GetStringSlice("ips")
@ -95,13 +110,28 @@ func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *c
return err 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{ input := deployOnEndpointInput{
debugdEndpoint: ip, debugdEndpoint: ip,
infos: info, infos: info,
bootstrapperPath: bootstrapperPath, files: files,
reader: reader, transfer: transfer,
log: log,
} }
if err := deployOnEndpoint(cmd.Context(), input); err != nil { if err := deployOnEndpoint(cmd.Context(), input); err != nil {
return err return err
@ -112,15 +142,16 @@ func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *c
} }
type deployOnEndpointInput struct { type deployOnEndpointInput struct {
debugdEndpoint string debugdEndpoint string
bootstrapperPath string files []filetransfer.FileStat
infos map[string]string infos map[string]string
reader fileToStreamReader transfer fileTransferer
log *logger.Logger
} }
// deployOnEndpoint deploys a custom built bootstrapper binary to a debugd endpoint. // deployOnEndpoint deploys a custom built bootstrapper binary to a debugd endpoint.
func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error { 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) client, closer, err := newDebugdClient(ctx, in.debugdEndpoint)
if err != nil { if err != nil {
@ -128,11 +159,11 @@ func deployOnEndpoint(ctx context.Context, in deployOnEndpointInput) error {
} }
defer closer.Close() 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) 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) 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 return pb.NewDebugdClient(conn), conn, nil
} }
func setInfo(ctx context.Context, client pb.DebugdClient, infos map[string]string) error { func setInfo(ctx context.Context, log *logger.Logger, client pb.DebugdClient, infos map[string]string) error {
log.Printf("Setting info with length %d", len(infos)) log.Infof("Setting info with length %d", len(infos))
var infosPb []*pb.Info var infosPb []*pb.Info
for key, value := range infos { 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} 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) 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 return nil
} }
func uploadBootstrapper(ctx context.Context, client pb.DebugdClient, in deployOnEndpointInput) error { func uploadFiles(ctx context.Context, client pb.DebugdClient, in deployOnEndpointInput) error {
log.Println("Uploading bootstrapper") in.log.Infof("Uploading files")
stream, err := client.UploadBootstrapper(ctx, grpc.WaitForReady(true)) stream, err := client.UploadFiles(ctx, grpc.WaitForReady(true))
if err != nil { if err != nil {
return fmt.Errorf("starting bootstrapper upload to instance %v: %w", in.debugdEndpoint, err) 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() uploadResponse, closeErr := stream.CloseAndRecv()
if closeErr != nil { 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 { switch uploadResponse.Status {
log.Println("Bootstrapper was already uploaded") case pb.UploadFilesStatus_UPLOAD_FILES_SUCCESS:
return nil in.log.Infof("Upload successful")
} case pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED:
if uploadResponse.Status != pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS || streamErr != nil { in.log.Infof("Files already uploaded")
return fmt.Errorf("uploading bootstrapper to instance %v failed: %v / %w", in.debugdEndpoint, uploadResponse, streamErr) 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 return nil
} }
@ -211,8 +258,9 @@ func checkInfoMap(info map[string]string) error {
return nil return nil
} }
type fileToStreamReader interface { type fileTransferer interface {
ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error SendFiles(stream filetransfer.SendFilesStream) error
SetFiles(files []filetransfer.FileStat)
} }
type clusterIDsFile struct { type clusterIDsFile struct {

View File

@ -14,7 +14,9 @@ const (
GRPCTimeout = 5 * time.Minute GRPCTimeout = 5 * time.Minute
DiscoverDebugdInterval = 30 * time.Second DiscoverDebugdInterval = 30 * time.Second
DownloadRetryBackoff = 1 * time.Minute DownloadRetryBackoff = 1 * time.Minute
BinaryAccessMode = 0o755 // -rwxr-xr-x
BootstrapperDeployFilename = "/run/state/bin/bootstrapper" BootstrapperDeployFilename = "/run/state/bin/bootstrapper"
UpgradeAgentDeployFilename = "/run/state/bin/upgrade-agent"
Chunksize = 1024 Chunksize = 1024
BootstrapperSystemdUnitName = "bootstrapper.service" BootstrapperSystemdUnitName = "bootstrapper.service"
BootstrapperSystemdUnitContents = `[Unit] BootstrapperSystemdUnitContents = `[Unit]

View File

@ -8,13 +8,13 @@ package deploy
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
"strconv" "strconv"
"github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd"
pb "github.com/edgelesssys/constellation/v2/debugd/service" pb "github.com/edgelesssys/constellation/v2/debugd/service"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
@ -27,19 +27,19 @@ import (
type Download struct { type Download struct {
log *logger.Logger log *logger.Logger
dialer NetDialer dialer NetDialer
writer streamToFileWriter transfer fileTransferer
serviceManager serviceManager serviceManager serviceManager
info infoSetter info infoSetter
} }
// New creates a new Download. // New creates a new Download.
func New(log *logger.Logger, dialer NetDialer, serviceManager serviceManager, func New(log *logger.Logger, dialer NetDialer, serviceManager serviceManager,
writer streamToFileWriter, info infoSetter, transfer fileTransferer, info infoSetter,
) *Download { ) *Download {
return &Download{ return &Download{
log: log, log: log,
dialer: dialer, dialer: dialer,
writer: writer, transfer: transfer,
info: info, info: info,
serviceManager: serviceManager, 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. // DownloadInfo will try to download the info from another instance.
func (d *Download) DownloadInfo(ctx context.Context, ip string) error { func (d *Download) DownloadInfo(ctx context.Context, ip string) error {
if d.info.Received() {
return nil
}
log := d.log.With(zap.String("ip", ip)) log := d.log.With(zap.String("ip", ip))
serverAddr := net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort)) 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) 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 { func (d *Download) DownloadDeployment(ctx context.Context, ip string) error {
log := d.log.With(zap.String("ip", ip)) log := d.log.With(zap.String("ip", ip))
serverAddr := net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort)) 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() defer closer.Close()
log.Infof("Trying to download bootstrapper") log.Infof("Trying to download files")
stream, err := client.DownloadBootstrapper(ctx, &pb.DownloadBootstrapperRequest{}) stream, err := client.DownloadFiles(ctx, &pb.DownloadFilesRequest{})
if err != nil { 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 err = d.transfer.RecvFiles(stream)
restartAction := ServiceManagerRequest{ switch {
Unit: debugd.BootstrapperSystemdUnitName, case err == nil:
Action: Restart, 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 return nil
@ -123,14 +142,17 @@ func (d *Download) grpcWithDialer() grpc.DialOption {
type infoSetter interface { type infoSetter interface {
SetProto(infos []*pb.Info) error SetProto(infos []*pb.Info) error
Received() bool
} }
type serviceManager interface { type serviceManager interface {
SystemdAction(ctx context.Context, request ServiceManagerRequest) error SystemdAction(ctx context.Context, request ServiceManagerRequest) error
OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error
} }
type streamToFileWriter interface { type fileTransferer interface {
WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error RecvFiles(stream filetransfer.RecvFilesStream) error
GetFiles() []filetransfer.FileStat
} }
// NetDialer can open a net.Conn. // NetDialer can open a net.Conn.

View File

@ -9,14 +9,11 @@ package deploy
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io"
"net" "net"
"strconv" "strconv"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/debugd/internal/bootstrapper" "github.com/edgelesssys/constellation/v2/debugd/internal/filetransfer"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd"
pb "github.com/edgelesssys/constellation/v2/debugd/service" pb "github.com/edgelesssys/constellation/v2/debugd/service"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer"
@ -33,41 +30,72 @@ func TestMain(m *testing.M) {
) )
} }
func TestDownloadBootstrapper(t *testing.T) { func TestDownloadDeployment(t *testing.T) {
filename := "/run/state/bin/bootstrapper"
someErr := errors.New("failed")
testCases := map[string]struct { testCases := map[string]struct {
server fakeDownloadServer files []filetransfer.FileStat
serviceManager stubServiceManager recvFilesErr error
wantChunks [][]byte overrideServiceUnitErr error
wantDownloadErr bool wantErr bool
wantFile bool wantOverrideCalls []struct{ UnitName, ExecStart string }
wantSystemdAction bool
wantDeployed bool
}{ }{
"download works": { "download works": {
server: fakeDownloadServer{ files: []filetransfer.FileStat{
chunks: [][]byte{[]byte("test")}, {
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": { "recv files error is detected": {
server: fakeDownloadServer{downladErr: someErr}, recvFilesErr: errors.New("some error"),
wantDownloadErr: true, wantErr: true,
}, },
"service restart error is detected": { "recv already running": {
server: fakeDownloadServer{chunks: [][]byte{[]byte("test")}}, recvFilesErr: filetransfer.ErrReceiveRunning,
serviceManager: stubServiceManager{systemdActionErr: someErr}, wantErr: true,
wantChunks: [][]byte{[]byte("test")}, },
wantDownloadErr: true, "recv already finished": {
wantFile: true, files: []filetransfer.FileStat{
wantDeployed: true, {
wantSystemdAction: false, 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) assert := assert.New(t)
ip := "192.0.2.0" ip := "192.0.2.0"
writer := &fakeStreamToFileWriter{} transfer := &stubTransfer{recvFilesErr: tc.recvFilesErr, files: tc.files}
serviceMgr := &stubServiceManager{overrideServiceUnitExecStartErr: tc.overrideServiceUnitErr}
dialer := testdialer.NewBufconnDialer() dialer := testdialer.NewBufconnDialer()
server := &stubDownloadServer{}
grpcServ := grpc.NewServer() grpcServ := grpc.NewServer()
pb.RegisterDebugdServer(grpcServ, &tc.server) pb.RegisterDebugdServer(grpcServ, server)
lis := dialer.GetListener(net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort))) lis := dialer.GetListener(net.JoinHostPort(ip, strconv.Itoa(constants.DebugdPort)))
go grpcServ.Serve(lis) go grpcServ.Serve(lis)
defer grpcServ.GracefulStop() defer grpcServ.GracefulStop()
@ -88,30 +118,19 @@ func TestDownloadBootstrapper(t *testing.T) {
download := &Download{ download := &Download{
log: logger.NewTest(t), log: logger.NewTest(t),
dialer: dialer, dialer: dialer,
writer: writer, transfer: transfer,
serviceManager: &tc.serviceManager, serviceManager: serviceMgr,
} }
err := download.DownloadDeployment(context.Background(), ip) err := download.DownloadDeployment(context.Background(), ip)
if tc.wantDownloadErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
} else { } else {
assert.NoError(err) assert.NoError(err)
} }
if tc.wantFile { assert.Equal(tc.wantOverrideCalls, serviceMgr.overrideCalls)
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,
)
}
}) })
} }
} }
@ -189,6 +208,9 @@ func TestDownloadInfo(t *testing.T) {
type stubServiceManager struct { type stubServiceManager struct {
requests []ServiceManagerRequest requests []ServiceManagerRequest
systemdActionErr error systemdActionErr error
overrideCalls []struct{ UnitName, ExecStart string }
overrideServiceUnitExecStartErr error
} }
func (s *stubServiceManager) SystemdAction(ctx context.Context, request ServiceManagerRequest) 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 return s.systemdActionErr
} }
type fakeStreamToFileWriter struct { func (s *stubServiceManager) OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error {
chunks [][]byte s.overrideCalls = append(s.overrideCalls, struct {
filename string UnitName, ExecStart string
}{UnitName: unitName, ExecStart: execStart})
return s.overrideServiceUnitExecStartErr
} }
func (f *fakeStreamToFileWriter) WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error { type stubTransfer struct {
f.filename = filename recvFilesErr error
for { files []filetransfer.FileStat
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)
}
} }
// fakeDownloadServer implements DebugdServer; only fakes DownloadBootstrapper, panics on every other rpc. func (t *stubTransfer) RecvFiles(_ filetransfer.RecvFilesStream) error {
type fakeDownloadServer struct { return t.recvFilesErr
chunks [][]byte }
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 downladErr error
pb.UnimplementedDebugdServer pb.UnimplementedDebugdServer
} }
func (s *fakeDownloadServer) DownloadBootstrapper(request *pb.DownloadBootstrapperRequest, stream pb.Debugd_DownloadBootstrapperServer) error { func (s *stubDownloadServer) DownloadFiles(request *pb.DownloadFilesRequest, stream pb.Debugd_DownloadFilesServer) error {
for _, chunk := range s.chunks {
if err := stream.Send(&pb.Chunk{Content: chunk}); err != nil {
return fmt.Errorf("sending chunk: %w", err)
}
}
return s.downladErr return s.downladErr
} }
@ -244,6 +261,7 @@ func (s *stubDebugdServer) GetInfo(ctx context.Context, request *pb.GetInfoReque
type stubInfoSetter struct { type stubInfoSetter struct {
info []*pb.Info info []*pb.Info
received bool
setProtoErr error setProtoErr error
} }
@ -251,3 +269,7 @@ func (s *stubInfoSetter) SetProto(infos []*pb.Info) error {
s.info = infos s.info = infos
return s.setProtoErr return s.setProtoErr
} }
func (s *stubInfoSetter) Received() bool {
return s.received
}

View File

@ -9,9 +9,12 @@ package deploy
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"path/filepath"
"regexp"
"strings"
"sync" "sync"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/spf13/afero" "github.com/spf13/afero"
"go.uber.org/zap" "go.uber.org/zap"
@ -21,6 +24,10 @@ const (
systemdUnitFolder = "/run/systemd/system" 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. // SystemdAction encodes the available actions.
// //
//go:generate stringer -type=SystemdAction //go:generate stringer -type=SystemdAction
@ -73,7 +80,7 @@ func NewServiceManager(log *logger.Logger) *ServiceManager {
type dbusClient interface { type dbusClient interface {
// NewSystemConnectionContext establishes a connection to the system bus and authenticates. // NewSystemConnectionContext establishes a connection to the system bus and authenticates.
// Callers should call Close() when done with the connection. // Callers should call Close() when done with the connection.
NewSystemdConnectionContext(ctx context.Context) (dbusConn, error) NewSystemConnectionContext(ctx context.Context) (dbusConn, error)
} }
type dbusConn interface { type dbusConn interface {
@ -89,15 +96,18 @@ type dbusConn interface {
// ReloadContext instructs systemd to scan for and reload unit files. This is // ReloadContext instructs systemd to scan for and reload unit files. This is
// an equivalent to systemctl daemon-reload. // an equivalent to systemctl daemon-reload.
ReloadContext(ctx context.Context) error ReloadContext(ctx context.Context) error
// Close closes the connection.
Close()
} }
// SystemdAction will perform a systemd action on a service unit (start, stop, restart, reload). // SystemdAction will perform a systemd action on a service unit (start, stop, restart, reload).
func (s *ServiceManager) SystemdAction(ctx context.Context, request ServiceManagerRequest) error { 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())) 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 { if err != nil {
return fmt.Errorf("establishing systemd connection: %w", err) return fmt.Errorf("establishing systemd connection: %w", err)
} }
defer conn.Close()
resultChan := make(chan string, 1) resultChan := make(chan string, 1)
switch request.Action { 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") log.Infof("Wrote systemd unit file and performed daemon-reload")
return nil return nil
} }
// DefaultServiceUnit will write the default "bootstrapper.service" unit file. // OverrideServiceUnitExecStart will override the ExecStart of a systemd unit.
func DefaultServiceUnit(ctx context.Context, serviceManager *ServiceManager) error { func (s *ServiceManager) OverrideServiceUnitExecStart(ctx context.Context, unitName, execStart string) error {
if err := serviceManager.WriteSystemdUnitFile(ctx, SystemdUnit{ log := s.log.With(zap.String("unitFile", fmt.Sprintf("%s/%s", systemdUnitFolder, unitName)))
Name: debugd.BootstrapperSystemdUnitName, log.Infof("Overriding systemd unit file execStart")
Contents: debugd.BootstrapperSystemdUnitContents, if !systemdUnitNameRegexp.MatchString(unitName) {
}); err != nil { return fmt.Errorf("unit name %q is invalid", unitName)
return fmt.Errorf("writing systemd unit file %q: %w", debugd.BootstrapperSystemdUnitName, err) }
} // validate execStart (no newlines)
if strings.Contains(execStart, "\n") || strings.Contains(execStart, "\r") {
// try to start the default service if the binary exists but ignore failure. return fmt.Errorf("execStart must not contain newlines")
// this is meant to start the bootstrapper after a reboot }
// if a bootstrapper binary was uploaded before. overrideUnitContents := fmt.Sprintf("[Service]\nExecStart=\nExecStart=%s\n", execStart)
if ok, err := afero.Exists(serviceManager.fs, debugd.BootstrapperDeployFilename); ok && err == nil { s.systemdUnitFilewriteLock.Lock()
_ = serviceManager.SystemdAction(ctx, ServiceManagerRequest{ defer s.systemdUnitFilewriteLock.Unlock()
Unit: debugd.BootstrapperSystemdUnitName, path := filepath.Join(systemdUnitFolder, unitName+".service.d", "override.conf")
Action: Start, 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 return nil
} }

View File

@ -158,7 +158,7 @@ func TestWriteSystemdUnitFile(t *testing.T) {
"systemd reload fails": { "systemd reload fails": {
dbus: stubDbus{ dbus: stubDbus{
conn: &fakeDbusConn{ conn: &fakeDbusConn{
actionErr: errors.New("reload error"), reloadErr: errors.New("reload error"),
}, },
}, },
unit: SystemdUnit{ 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 { type stubDbus struct {
conn dbusConn conn dbusConn
connErr error 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 return s.conn, s.connErr
} }
@ -215,11 +330,13 @@ type dbusConnActionInput struct {
} }
type fakeDbusConn struct { type fakeDbusConn struct {
inputs []dbusConnActionInput inputs []dbusConnActionInput
result string result string
reloadCalls int
jobID int jobID int
actionErr error actionErr error
reloadErr error
} }
func (c *fakeDbusConn) StartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, 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 { func (c *fakeDbusConn) ReloadContext(ctx context.Context) error {
return c.actionErr c.reloadCalls++
return c.reloadErr
} }
func (c *fakeDbusConn) Close() {}

View File

@ -15,8 +15,8 @@ import (
// wraps go-systemd dbus. // wraps go-systemd dbus.
type dbusWrapper struct{} type dbusWrapper struct{}
func (d *dbusWrapper) NewSystemdConnectionContext(ctx context.Context) (dbusConn, error) { func (d *dbusWrapper) NewSystemConnectionContext(ctx context.Context) (dbusConn, error) {
conn, err := dbus.NewSystemdConnectionContext(ctx) conn, err := dbus.NewSystemConnectionContext(ctx)
if err != nil { if err != nil {
return nil, err 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 { func (c *dbusConnWrapper) ReloadContext(ctx context.Context) error {
return c.conn.ReloadContext(ctx) return c.conn.ReloadContext(ctx)
} }
func (c *dbusConnWrapper) Close() {
c.conn.Close()
}

View File

@ -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. // Get returns the value of the info with the given key.
func (i *Map) Get(key string) (string, bool, error) { func (i *Map) Get(key string) (string, bool, error) {
i.mux.RLock() i.mux.RLock()
@ -67,7 +74,7 @@ func (i *Map) SetProto(infos []*servicepb.Info) error {
defer i.mux.Unlock() defer i.mux.Unlock()
if i.received { if i.received {
return errors.New("info already set") return ErrInfoAlreadySet
} }
infoMap := make(map[string]string) infoMap := make(map[string]string)
@ -114,3 +121,6 @@ func (i *Map) GetProto() ([]*servicepb.Info, error) {
} }
return infos, nil return infos, nil
} }
// ErrInfoAlreadySet is returned if the info map has already been set.
var ErrInfoAlreadySet = errors.New("info already set")

View File

@ -284,6 +284,10 @@ func TestConcurrency(t *testing.T) {
_, _ = i.GetProto() _, _ = i.GetProto()
} }
received := func() {
_ = i.Received()
}
go get() go get()
go get() go get()
go get() go get()
@ -300,4 +304,8 @@ func TestConcurrency(t *testing.T) {
go getProto() go getProto()
go getProto() go getProto()
go getProto() go getProto()
go received()
go received()
go received()
go received()
} }

View File

@ -9,20 +9,18 @@ package server
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io/fs"
"net" "net"
"strconv" "strconv"
"sync" "sync"
"time" "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/deploy"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" "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" pb "github.com/edgelesssys/constellation/v2/debugd/service"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"go.uber.org/multierr"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
@ -31,18 +29,18 @@ import (
type debugdServer struct { type debugdServer struct {
log *logger.Logger log *logger.Logger
serviceManager serviceManager serviceManager serviceManager
streamer streamer transfer fileTransferer
info *info.Map info *info.Map
pb.UnimplementedDebugdServer pb.UnimplementedDebugdServer
} }
// New creates a new debugdServer according to the gRPC spec. // 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{ return &debugdServer{
log: log, log: log,
serviceManager: serviceManager, serviceManager: serviceManager,
streamer: streamer, transfer: transfer,
info: infos, info: infos,
} }
} }
@ -55,13 +53,23 @@ func (s *debugdServer) SetInfo(ctx context.Context, req *pb.SetInfoRequest) (*pb
s.log.Infof("Info is empty") s.log.Infof("Info is empty")
} }
if err := s.info.SetProto(req.Info); err != nil { setProtoErr := s.info.SetProto(req.Info)
s.log.With(zap.Error(err)).Errorf("Setting info failed") if errors.Is(setProtoErr, info.ErrInfoAlreadySet) {
return &pb.SetInfoResponse{}, err 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") 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. // 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 return &pb.GetInfoResponse{Info: info}, nil
} }
// UploadBootstrapper receives a bootstrapper binary in a stream of chunks and writes to a file. // 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) UploadBootstrapper(stream pb.Debugd_UploadBootstrapperServer) error { func (s *debugdServer) UploadFiles(stream pb.Debugd_UploadFilesServer) error {
startAction := deploy.ServiceManagerRequest{ s.log.Infof("Received UploadFiles request")
Unit: debugd.BootstrapperSystemdUnitName, err := s.transfer.RecvFiles(stream)
Action: deploy.Start, switch {
} case err == nil:
var responseStatus pb.UploadBootstrapperStatus s.log.Infof("Uploading files succeeded")
defer func() { case errors.Is(err, filetransfer.ErrReceiveRunning):
if err := s.serviceManager.SystemdAction(stream.Context(), startAction); err != nil { s.log.Warnf("Upload already in progress")
s.log.With(zap.Error(err)).Errorf("Starting uploaded bootstrapper failed") return stream.SendAndClose(&pb.UploadFilesResponse{
if responseStatus == pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS { Status: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED,
responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED })
} case errors.Is(err, filetransfer.ErrReceiveFinished):
} s.log.Warnf("Upload already finished")
stream.SendAndClose(&pb.UploadBootstrapperResponse{ return stream.SendAndClose(&pb.UploadFilesResponse{
Status: responseStatus, 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") files := s.transfer.GetFiles()
responseStatus = pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS var overrideUnitErr error
return nil 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. // DownloadFiles streams the previously received files to other instances.
func (s *debugdServer) DownloadBootstrapper(request *pb.DownloadBootstrapperRequest, stream pb.Debugd_DownloadBootstrapperServer) error { func (s *debugdServer) DownloadFiles(request *pb.DownloadFilesRequest, stream pb.Debugd_DownloadFilesServer) error {
s.log.Infof("Sending bootstrapper to other instance") s.log.Infof("Sending files to other instance")
return s.streamer.ReadStream(debugd.BootstrapperDeployFilename, stream, debugd.Chunksize, true) 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. // 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 { type serviceManager interface {
SystemdAction(ctx context.Context, request deploy.ServiceManagerRequest) error SystemdAction(ctx context.Context, request deploy.ServiceManagerRequest) error
WriteSystemdUnitFile(ctx context.Context, unit deploy.SystemdUnit) error WriteSystemdUnitFile(ctx context.Context, unit deploy.SystemdUnit) error
OverrideServiceUnitExecStart(ctx context.Context, unitName string, execStart string) error
} }
type streamer interface { type fileTransferer interface {
WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error RecvFiles(stream filetransfer.RecvFilesStream) error
ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error SendFiles(stream filetransfer.SendFilesStream) error
GetFiles() []filetransfer.FileStat
CanSend() bool
} }

View File

@ -9,15 +9,14 @@ package server
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"strconv" "strconv"
"testing" "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/deploy"
"github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" "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" pb "github.com/edgelesssys/constellation/v2/debugd/service"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer"
@ -40,21 +39,23 @@ func TestSetInfo(t *testing.T) {
info *info.Map info *info.Map
infoReceived bool infoReceived bool
setInfo []*pb.Info setInfo []*pb.Info
wantErr bool wantStatus pb.SetInfoStatus
}{ }{
"set info works": { "set info works": {
setInfo: []*pb.Info{{Key: "foo", Value: "bar"}}, setInfo: []*pb.Info{{Key: "foo", Value: "bar"}},
info: info.NewMap(), info: info.NewMap(),
wantStatus: pb.SetInfoStatus_SET_INFO_SUCCESS,
}, },
"set empty info works": { "set empty info works": {
setInfo: []*pb.Info{}, setInfo: []*pb.Info{},
info: info.NewMap(), info: info.NewMap(),
wantStatus: pb.SetInfoStatus_SET_INFO_SUCCESS,
}, },
"set fails when info already set": { "set fails when info already set": {
info: info.NewMap(), info: info.NewMap(),
infoReceived: true, infoReceived: true,
setInfo: []*pb.Info{{Key: "foo", Value: "bar"}}, 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() defer conn.Close()
client := pb.NewDebugdClient(conn) 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() grpcServ.GracefulStop()
if tc.wantErr { assert.NoError(err)
assert.Error(err) assert.Equal(tc.wantStatus, setInfoStatus.Status)
} else { for i := range tc.setInfo {
value, ok, err := tc.info.Get(tc.setInfo[i].Key)
assert.NoError(err) assert.NoError(err)
for i := range tc.setInfo { assert.True(ok)
value, ok, err := tc.info.Get(tc.setInfo[i].Key) assert.Equal(tc.setInfo[i].Value, value)
assert.NoError(err)
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) endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort)
testCases := map[string]struct { testCases := map[string]struct {
serviceManager stubServiceManager files []filetransfer.FileStat
streamer fakeStreamer recvFilesErr error
uploadChunks [][]byte wantResponseStatus pb.UploadFilesStatus
wantErr bool wantOverrideCalls []struct{ UnitName, ExecStart string }
wantResponseStatus pb.UploadBootstrapperStatus
wantFile bool
wantChunks [][]byte
}{ }{
"upload works": { "upload works": {
uploadChunks: [][]byte{[]byte("test")}, files: []filetransfer.FileStat{
wantFile: true, {SourcePath: "source/testA", TargetPath: "target/testA", Mode: 0o644, OverrideServiceUnit: "testA"},
wantChunks: [][]byte{[]byte("test")}, {SourcePath: "source/testB", TargetPath: "target/testB", Mode: 0o644},
wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS, },
wantOverrideCalls: []struct{ UnitName, ExecStart string }{
{"testA", "target/testA"},
},
wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_SUCCESS,
}, },
"recv fails": { "recv fails": {
streamer: fakeStreamer{writeStreamErr: errors.New("recv error")}, recvFilesErr: errors.New("recv error"),
wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED, wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_UPLOAD_FAILED,
wantErr: true,
}, },
"starting bootstrapper fails": { "upload in progress": {
uploadChunks: [][]byte{[]byte("test")}, recvFilesErr: filetransfer.ErrReceiveRunning,
serviceManager: stubServiceManager{systemdActionErr: errors.New("starting bootstrapper error")}, wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_STARTED,
wantFile: true, },
wantChunks: [][]byte{[]byte("test")}, "upload already finished": {
wantResponseStatus: pb.UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED, recvFilesErr: filetransfer.ErrReceiveFinished,
wantResponseStatus: pb.UploadFilesStatus_UPLOAD_FILES_ALREADY_FINISHED,
}, },
} }
@ -189,60 +188,49 @@ func TestUploadBootstrapper(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
serviceMgr := &stubServiceManager{}
transfer := &stubTransfer{files: tc.files, recvFilesErr: tc.recvFilesErr}
serv := debugdServer{ serv := debugdServer{
log: logger.NewTest(t), log: logger.NewTest(t),
serviceManager: &tc.serviceManager, serviceManager: serviceMgr,
streamer: &tc.streamer, transfer: transfer,
} }
grpcServ, conn, err := setupServerWithConn(endpoint, &serv) grpcServ, conn, err := setupServerWithConn(endpoint, &serv)
require.NoError(err) require.NoError(err)
defer conn.Close() defer conn.Close()
client := pb.NewDebugdClient(conn) client := pb.NewDebugdClient(conn)
stream, err := client.UploadBootstrapper(context.Background()) stream, err := client.UploadFiles(context.Background())
require.NoError(err) require.NoError(err)
require.NoError(fakeWrite(stream, tc.uploadChunks))
resp, err := stream.CloseAndRecv() resp, err := stream.CloseAndRecv()
grpcServ.GracefulStop() grpcServ.GracefulStop()
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err) require.NoError(err)
assert.Equal(tc.wantResponseStatus, resp.Status) assert.Equal(tc.wantResponseStatus, resp.Status)
if tc.wantFile { assert.Equal(tc.wantOverrideCalls, serviceMgr.overrideCalls)
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)
}
}) })
} }
} }
func TestDownloadBootstrapper(t *testing.T) { func TestDownloadFiles(t *testing.T) {
endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort) endpoint := "192.0.2.1:" + strconv.Itoa(constants.DebugdPort)
testCases := map[string]struct { testCases := map[string]struct {
serviceManager stubServiceManager request *pb.DownloadFilesRequest
request *pb.DownloadBootstrapperRequest canSend bool
streamer fakeStreamer wantRecvErr bool
wantErr bool wantSendFileCalls int
wantChunks [][]byte
}{ }{
"download works": { "download works": {
request: &pb.DownloadBootstrapperRequest{}, request: &pb.DownloadFilesRequest{},
streamer: fakeStreamer{readStreamChunks: [][]byte{[]byte("test")}}, canSend: true,
wantErr: false, wantSendFileCalls: 1,
wantChunks: [][]byte{[]byte("test")},
}, },
"download fails": { "transfer is not ready for sending": {
request: &pb.DownloadBootstrapperRequest{}, request: &pb.DownloadFilesRequest{},
streamer: fakeStreamer{readStreamErr: errors.New("read bootstrapper fails")}, wantRecvErr: true,
wantErr: true,
}, },
} }
@ -251,28 +239,29 @@ func TestDownloadBootstrapper(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
transfer := &stubTransfer{canSend: tc.canSend}
serv := debugdServer{ serv := debugdServer{
log: logger.NewTest(t), log: logger.NewTest(t),
serviceManager: &tc.serviceManager, transfer: transfer,
streamer: &tc.streamer,
} }
grpcServ, conn, err := setupServerWithConn(endpoint, &serv) grpcServ, conn, err := setupServerWithConn(endpoint, &serv)
require.NoError(err) require.NoError(err)
defer conn.Close() defer conn.Close()
client := pb.NewDebugdClient(conn) client := pb.NewDebugdClient(conn)
stream, err := client.DownloadBootstrapper(context.Background(), tc.request) stream, err := client.DownloadFiles(context.Background(), tc.request)
require.NoError(err) require.NoError(err)
chunks, err := fakeRead(stream) _, recvErr := stream.Recv()
grpcServ.GracefulStop() if tc.wantRecvErr {
require.Error(recvErr)
if tc.wantErr { } else {
assert.Error(err) require.ErrorIs(recvErr, io.EOF)
return
} }
require.NoError(stream.CloseSend())
grpcServ.GracefulStop()
require.NoError(err) 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{ serv := debugdServer{
log: logger.NewTest(t), log: logger.NewTest(t),
serviceManager: &tc.serviceManager, serviceManager: &tc.serviceManager,
streamer: &fakeStreamer{},
} }
grpcServ, conn, err := setupServerWithConn(endpoint, &serv) grpcServ, conn, err := setupServerWithConn(endpoint, &serv)
require.NoError(err) require.NoError(err)
@ -357,10 +345,13 @@ func TestUploadSystemServiceUnits(t *testing.T) {
} }
type stubServiceManager struct { type stubServiceManager struct {
requests []deploy.ServiceManagerRequest requests []deploy.ServiceManagerRequest
unitFiles []deploy.SystemdUnit unitFiles []deploy.SystemdUnit
systemdActionErr error overrideCalls []struct{ UnitName, ExecStart string }
writeSystemdUnitFileErr error
systemdActionErr error
writeSystemdUnitFileErr error
overrideServiceUnitExecStartErr error
} }
func (s *stubServiceManager) SystemdAction(ctx context.Context, request deploy.ServiceManagerRequest) 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 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 { type netDialer interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error) 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 { type stubTransfer struct {
writeStreamChunks [][]byte recvFilesCount int
writeStreamFilename string sendFilesCount int
writeStreamErr error files []filetransfer.FileStat
readStreamChunks [][]byte canSend bool
readStreamFilename string recvFilesErr error
readStreamErr error sendFilesErr error
} }
func (f *fakeStreamer) WriteStream(filename string, stream bootstrapper.ReadChunkStream, showProgress bool) error { func (t *stubTransfer) RecvFiles(_ filetransfer.RecvFilesStream) error {
f.writeStreamFilename = filename t.recvFilesCount++
for { return t.recvFilesErr
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 (f *fakeStreamer) ReadStream(filename string, stream bootstrapper.WriteChunkStream, chunksize uint, showProgress bool) error { func (t *stubTransfer) SendFiles(_ filetransfer.SendFilesStream) error {
f.readStreamFilename = filename t.sendFilesCount++
for _, chunk := range f.readStreamChunks { return t.sendFilesErr
if err := stream.Send(&pb.Chunk{Content: chunk}); err != nil { }
panic(err)
} func (t *stubTransfer) GetFiles() []filetransfer.FileStat {
} return t.files
return f.readStreamErr }
func (t *stubTransfer) CanSend() bool {
return t.canSend
} }
func setupServerWithConn(endpoint string, serv *debugdServer) (*grpc.Server, *grpc.ClientConn, error) { 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 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)
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package bootstrapper package streamer
import ( import (
"errors" "errors"
@ -34,8 +34,8 @@ type WriteChunkStream interface {
Send(chunk *pb.Chunk) error Send(chunk *pb.Chunk) error
} }
// NewFileStreamer creates a new FileStreamer. // New creates a new FileStreamer.
func NewFileStreamer(fs afero.Fs) *FileStreamer { func New(fs afero.Fs) *FileStreamer {
return &FileStreamer{ return &FileStreamer{
fs: fs, fs: fs,
mux: sync.RWMutex{}, 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. // 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 { 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() f.mux.Lock()
defer f.mux.Unlock() 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 { if err != nil {
return fmt.Errorf("open %v for writing: %w", filename, err) return fmt.Errorf("open %v for writing: %w", filename, err)
} }
defer file.Close() 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 var bar *progressbar.ProgressBar
if showProgress { if showProgress {
bar = progressbar.NewOptions64( bar = newProgressBar(stat.Size())
-1,
progressbar.OptionSetDescription("receiving bootstrapper"),
progressbar.OptionShowBytes(true),
progressbar.OptionClearOnFinish(),
)
defer bar.Close() defer bar.Close()
} }
for { return writeInner(file, stream, bar)
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
} }
// ReadStream opens a file to read from and streams its contents chunkwise over gRPC. // 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() { if f.mux.TryRLock() {
defer f.mux.RUnlock() defer f.mux.RUnlock()
} else { } 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) file, err := f.fs.OpenFile(filename, os.O_RDONLY, 0o755)
if err != nil { if err != nil {
return fmt.Errorf("open %v for reading: %w", filename, err) return fmt.Errorf("open %v for reading: %w", filename, err)
} }
defer file.Close() 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 var bar *progressbar.ProgressBar
if showProgress { if showProgress {
stat, err := file.Stat() bar = newProgressBar(stat.Size())
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(),
)
defer bar.Close() 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) buf := make([]byte, chunksize)
for { for {
n, err := file.Read(buf) n, readErr := fp.Read(buf)
if err != nil { isLast := errors.Is(readErr, io.EOF)
if errors.Is(err, io.EOF) { if readErr != nil && !isLast {
return nil return fmt.Errorf("reading file chunk: %w", readErr)
}
return fmt.Errorf("reading file chunk: %w", err)
} }
if err := stream.Send(&pb.Chunk{Content: buf[:n], Last: isLast}); err != nil {
if err = stream.Send(&pb.Chunk{Content: buf[:n]}); err != nil {
return fmt.Errorf("sending chunk: %w", err) return fmt.Errorf("sending chunk: %w", err)
} }
if showProgress { if bar != nil {
_ = bar.Add(n) _ = 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(),
)
}

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package bootstrapper package streamer
import ( import (
"errors" "errors"
@ -82,7 +82,7 @@ func TestWriteStream(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
writer := NewFileStreamer(tc.fs) writer := New(tc.fs)
err := writer.WriteStream(filename, &tc.readChunkStream, tc.showProgress) err := writer.WriteStream(filename, &tc.readChunkStream, tc.showProgress)
if tc.wantErr { if tc.wantErr {
@ -99,6 +99,7 @@ func TestWriteStream(t *testing.T) {
func TestReadStream(t *testing.T) { func TestReadStream(t *testing.T) {
correctFilename := "testfile" correctFilename := "testfile"
eof := []byte{}
testCases := map[string]struct { testCases := map[string]struct {
writeChunkStream stubWriteChunkStream writeChunkStream stubWriteChunkStream
@ -114,6 +115,7 @@ func TestReadStream(t *testing.T) {
chunksize: 4, chunksize: 4,
wantChunks: [][]byte{ wantChunks: [][]byte{
[]byte("test"), []byte("test"),
eof,
}, },
wantErr: false, wantErr: false,
}, },
@ -124,6 +126,7 @@ func TestReadStream(t *testing.T) {
wantChunks: [][]byte{ wantChunks: [][]byte{
[]byte("te"), []byte("te"),
[]byte("st"), []byte("st"),
eof,
}, },
wantErr: false, wantErr: false,
}, },
@ -140,6 +143,7 @@ func TestReadStream(t *testing.T) {
showProgress: true, showProgress: true,
wantChunks: [][]byte{ wantChunks: [][]byte{
[]byte("test"), []byte("test"),
eof,
}, },
wantErr: false, wantErr: false,
}, },
@ -165,7 +169,7 @@ func TestReadStream(t *testing.T) {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
assert.NoError(afero.WriteFile(fs, correctFilename, []byte("test"), 0o755)) 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) err := reader.ReadStream(tc.filename, &tc.writeChunkStream, tc.chunksize, tc.showProgress)
if tc.wantErr { if tc.wantErr {
@ -189,8 +193,10 @@ func (s *fakeReadChunkStream) Recv() (*pb.Chunk, error) {
return nil, s.recvErr return nil, s.recvErr
} }
isLastChunk := s.pos == len(s.chunks)-1
if s.pos < len(s.chunks) { 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++ s.pos++
return result, nil return result, nil
} }

View File

@ -20,58 +20,107 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type UploadBootstrapperStatus int32 type SetInfoStatus int32
const ( const (
UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS UploadBootstrapperStatus = 0 SetInfoStatus_SET_INFO_SUCCESS SetInfoStatus = 0
UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED UploadBootstrapperStatus = 1 SetInfoStatus_SET_INFO_ALREADY_SET SetInfoStatus = 1
UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_START_FAILED UploadBootstrapperStatus = 2
UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_FILE_EXISTS UploadBootstrapperStatus = 3
) )
// Enum value maps for UploadBootstrapperStatus. // Enum value maps for SetInfoStatus.
var ( var (
UploadBootstrapperStatus_name = map[int32]string{ SetInfoStatus_name = map[int32]string{
0: "UPLOAD_BOOTSTRAPPER_SUCCESS", 0: "SET_INFO_SUCCESS",
1: "UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED", 1: "SET_INFO_ALREADY_SET",
2: "UPLOAD_BOOTSTRAPPER_START_FAILED",
3: "UPLOAD_BOOTSTRAPPER_FILE_EXISTS",
} }
UploadBootstrapperStatus_value = map[string]int32{ SetInfoStatus_value = map[string]int32{
"UPLOAD_BOOTSTRAPPER_SUCCESS": 0, "SET_INFO_SUCCESS": 0,
"UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED": 1, "SET_INFO_ALREADY_SET": 1,
"UPLOAD_BOOTSTRAPPER_START_FAILED": 2,
"UPLOAD_BOOTSTRAPPER_FILE_EXISTS": 3,
} }
) )
func (x UploadBootstrapperStatus) Enum() *UploadBootstrapperStatus { func (x SetInfoStatus) Enum() *SetInfoStatus {
p := new(UploadBootstrapperStatus) p := new(SetInfoStatus)
*p = x *p = x
return p return p
} }
func (x UploadBootstrapperStatus) String() string { func (x SetInfoStatus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 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() return file_debugd_proto_enumTypes[0].Descriptor()
} }
func (UploadBootstrapperStatus) Type() protoreflect.EnumType { func (SetInfoStatus) Type() protoreflect.EnumType {
return &file_debugd_proto_enumTypes[0] return &file_debugd_proto_enumTypes[0]
} }
func (x UploadBootstrapperStatus) Number() protoreflect.EnumNumber { func (x SetInfoStatus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x) return protoreflect.EnumNumber(x)
} }
// Deprecated: Use UploadBootstrapperStatus.Descriptor instead. // Deprecated: Use SetInfoStatus.Descriptor instead.
func (UploadBootstrapperStatus) EnumDescriptor() ([]byte, []int) { func (SetInfoStatus) EnumDescriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{0} 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 type UploadSystemdServiceUnitsStatus int32
const ( const (
@ -102,11 +151,11 @@ func (x UploadSystemdServiceUnitsStatus) String() string {
} }
func (UploadSystemdServiceUnitsStatus) Descriptor() protoreflect.EnumDescriptor { func (UploadSystemdServiceUnitsStatus) Descriptor() protoreflect.EnumDescriptor {
return file_debugd_proto_enumTypes[1].Descriptor() return file_debugd_proto_enumTypes[2].Descriptor()
} }
func (UploadSystemdServiceUnitsStatus) Type() protoreflect.EnumType { func (UploadSystemdServiceUnitsStatus) Type() protoreflect.EnumType {
return &file_debugd_proto_enumTypes[1] return &file_debugd_proto_enumTypes[2]
} }
func (x UploadSystemdServiceUnitsStatus) Number() protoreflect.EnumNumber { func (x UploadSystemdServiceUnitsStatus) Number() protoreflect.EnumNumber {
@ -115,7 +164,7 @@ func (x UploadSystemdServiceUnitsStatus) Number() protoreflect.EnumNumber {
// Deprecated: Use UploadSystemdServiceUnitsStatus.Descriptor instead. // Deprecated: Use UploadSystemdServiceUnitsStatus.Descriptor instead.
func (UploadSystemdServiceUnitsStatus) EnumDescriptor() ([]byte, []int) { func (UploadSystemdServiceUnitsStatus) EnumDescriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{1} return file_debugd_proto_rawDescGZIP(), []int{2}
} }
type SetInfoRequest struct { type SetInfoRequest struct {
@ -169,6 +218,8 @@ type SetInfoResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Status SetInfoStatus `protobuf:"varint,1,opt,name=status,proto3,enum=debugd.SetInfoStatus" json:"status,omitempty"`
} }
func (x *SetInfoResponse) Reset() { func (x *SetInfoResponse) Reset() {
@ -203,6 +254,13 @@ func (*SetInfoResponse) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{1} 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 { type GetInfoRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -343,14 +401,14 @@ func (x *Info) GetValue() string {
return "" return ""
} }
type DownloadBootstrapperRequest struct { type DownloadFilesRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
} }
func (x *DownloadBootstrapperRequest) Reset() { func (x *DownloadFilesRequest) Reset() {
*x = DownloadBootstrapperRequest{} *x = DownloadFilesRequest{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[5] mi := &file_debugd_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 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) 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] mi := &file_debugd_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -376,23 +434,168 @@ func (x *DownloadBootstrapperRequest) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x) return mi.MessageOf(x)
} }
// Deprecated: Use DownloadBootstrapperRequest.ProtoReflect.Descriptor instead. // Deprecated: Use DownloadFilesRequest.ProtoReflect.Descriptor instead.
func (*DownloadBootstrapperRequest) Descriptor() ([]byte, []int) { func (*DownloadFilesRequest) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{5} 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 { type Chunk struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` 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() { func (x *Chunk) Reset() {
*x = Chunk{} *x = Chunk{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[6] mi := &file_debugd_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -405,7 +608,7 @@ func (x *Chunk) String() string {
func (*Chunk) ProtoMessage() {} func (*Chunk) ProtoMessage() {}
func (x *Chunk) ProtoReflect() protoreflect.Message { func (x *Chunk) ProtoReflect() protoreflect.Message {
mi := &file_debugd_proto_msgTypes[6] mi := &file_debugd_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -418,7 +621,7 @@ func (x *Chunk) ProtoReflect() protoreflect.Message {
// Deprecated: Use Chunk.ProtoReflect.Descriptor instead. // Deprecated: Use Chunk.ProtoReflect.Descriptor instead.
func (*Chunk) Descriptor() ([]byte, []int) { func (*Chunk) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{6} return file_debugd_proto_rawDescGZIP(), []int{8}
} }
func (x *Chunk) GetContent() []byte { func (x *Chunk) GetContent() []byte {
@ -428,31 +631,38 @@ func (x *Chunk) GetContent() []byte {
return nil return nil
} }
type UploadBootstrapperResponse struct { func (x *Chunk) GetLast() bool {
if x != nil {
return x.Last
}
return false
}
type UploadFilesResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields 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() { func (x *UploadFilesResponse) Reset() {
*x = UploadBootstrapperResponse{} *x = UploadFilesResponse{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[7] mi := &file_debugd_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
} }
func (x *UploadBootstrapperResponse) String() string { func (x *UploadFilesResponse) String() string {
return protoimpl.X.MessageStringOf(x) return protoimpl.X.MessageStringOf(x)
} }
func (*UploadBootstrapperResponse) ProtoMessage() {} func (*UploadFilesResponse) ProtoMessage() {}
func (x *UploadBootstrapperResponse) ProtoReflect() protoreflect.Message { func (x *UploadFilesResponse) ProtoReflect() protoreflect.Message {
mi := &file_debugd_proto_msgTypes[7] mi := &file_debugd_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -463,16 +673,16 @@ func (x *UploadBootstrapperResponse) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x) return mi.MessageOf(x)
} }
// Deprecated: Use UploadBootstrapperResponse.ProtoReflect.Descriptor instead. // Deprecated: Use UploadFilesResponse.ProtoReflect.Descriptor instead.
func (*UploadBootstrapperResponse) Descriptor() ([]byte, []int) { func (*UploadFilesResponse) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{7} return file_debugd_proto_rawDescGZIP(), []int{9}
} }
func (x *UploadBootstrapperResponse) GetStatus() UploadBootstrapperStatus { func (x *UploadFilesResponse) GetStatus() UploadFilesStatus {
if x != nil { if x != nil {
return x.Status return x.Status
} }
return UploadBootstrapperStatus_UPLOAD_BOOTSTRAPPER_SUCCESS return UploadFilesStatus_UPLOAD_FILES_SUCCESS
} }
type ServiceUnit struct { type ServiceUnit struct {
@ -487,7 +697,7 @@ type ServiceUnit struct {
func (x *ServiceUnit) Reset() { func (x *ServiceUnit) Reset() {
*x = ServiceUnit{} *x = ServiceUnit{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[8] mi := &file_debugd_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -500,7 +710,7 @@ func (x *ServiceUnit) String() string {
func (*ServiceUnit) ProtoMessage() {} func (*ServiceUnit) ProtoMessage() {}
func (x *ServiceUnit) ProtoReflect() protoreflect.Message { func (x *ServiceUnit) ProtoReflect() protoreflect.Message {
mi := &file_debugd_proto_msgTypes[8] mi := &file_debugd_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -513,7 +723,7 @@ func (x *ServiceUnit) ProtoReflect() protoreflect.Message {
// Deprecated: Use ServiceUnit.ProtoReflect.Descriptor instead. // Deprecated: Use ServiceUnit.ProtoReflect.Descriptor instead.
func (*ServiceUnit) Descriptor() ([]byte, []int) { func (*ServiceUnit) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{8} return file_debugd_proto_rawDescGZIP(), []int{10}
} }
func (x *ServiceUnit) GetName() string { func (x *ServiceUnit) GetName() string {
@ -541,7 +751,7 @@ type UploadSystemdServiceUnitsRequest struct {
func (x *UploadSystemdServiceUnitsRequest) Reset() { func (x *UploadSystemdServiceUnitsRequest) Reset() {
*x = UploadSystemdServiceUnitsRequest{} *x = UploadSystemdServiceUnitsRequest{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[9] mi := &file_debugd_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -554,7 +764,7 @@ func (x *UploadSystemdServiceUnitsRequest) String() string {
func (*UploadSystemdServiceUnitsRequest) ProtoMessage() {} func (*UploadSystemdServiceUnitsRequest) ProtoMessage() {}
func (x *UploadSystemdServiceUnitsRequest) ProtoReflect() protoreflect.Message { func (x *UploadSystemdServiceUnitsRequest) ProtoReflect() protoreflect.Message {
mi := &file_debugd_proto_msgTypes[9] mi := &file_debugd_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -567,7 +777,7 @@ func (x *UploadSystemdServiceUnitsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UploadSystemdServiceUnitsRequest.ProtoReflect.Descriptor instead. // Deprecated: Use UploadSystemdServiceUnitsRequest.ProtoReflect.Descriptor instead.
func (*UploadSystemdServiceUnitsRequest) Descriptor() ([]byte, []int) { func (*UploadSystemdServiceUnitsRequest) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{9} return file_debugd_proto_rawDescGZIP(), []int{11}
} }
func (x *UploadSystemdServiceUnitsRequest) GetUnits() []*ServiceUnit { func (x *UploadSystemdServiceUnitsRequest) GetUnits() []*ServiceUnit {
@ -588,7 +798,7 @@ type UploadSystemdServiceUnitsResponse struct {
func (x *UploadSystemdServiceUnitsResponse) Reset() { func (x *UploadSystemdServiceUnitsResponse) Reset() {
*x = UploadSystemdServiceUnitsResponse{} *x = UploadSystemdServiceUnitsResponse{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_debugd_proto_msgTypes[10] mi := &file_debugd_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -601,7 +811,7 @@ func (x *UploadSystemdServiceUnitsResponse) String() string {
func (*UploadSystemdServiceUnitsResponse) ProtoMessage() {} func (*UploadSystemdServiceUnitsResponse) ProtoMessage() {}
func (x *UploadSystemdServiceUnitsResponse) ProtoReflect() protoreflect.Message { func (x *UploadSystemdServiceUnitsResponse) ProtoReflect() protoreflect.Message {
mi := &file_debugd_proto_msgTypes[10] mi := &file_debugd_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -614,7 +824,7 @@ func (x *UploadSystemdServiceUnitsResponse) ProtoReflect() protoreflect.Message
// Deprecated: Use UploadSystemdServiceUnitsResponse.ProtoReflect.Descriptor instead. // Deprecated: Use UploadSystemdServiceUnitsResponse.ProtoReflect.Descriptor instead.
func (*UploadSystemdServiceUnitsResponse) Descriptor() ([]byte, []int) { func (*UploadSystemdServiceUnitsResponse) Descriptor() ([]byte, []int) {
return file_debugd_proto_rawDescGZIP(), []int{10} return file_debugd_proto_rawDescGZIP(), []int{12}
} }
func (x *UploadSystemdServiceUnitsResponse) GetStatus() UploadSystemdServiceUnitsStatus { 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, 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, 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, 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, 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, 0x22, 0x10, 0x0a, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a,
0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
0x33, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74,
0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x10, 0x0a, 0x0e,
0x32, 0x0c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x33,
0x69, 0x6e, 0x66, 0x6f, 0x22, 0x2e, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x0c, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69,
0x61, 0x6c, 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x6e, 0x66, 0x6f, 0x22, 0x2e, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
0x65, 0x73, 0x74, 0x22, 0x21, 0x0a, 0x05, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x46,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x56, 0x0a, 0x1a, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x7a, 0x0a, 0x13, 0x46,
0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x69, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x46, 0x69, 0x6c, 0x65,
0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3d, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e,
0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x42,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x97, 0x01, 0x0a, 0x12, 0x46, 0x69, 0x6c, 0x65,
0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x4d, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1e,
0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12,
0x74, 0x12, 0x29, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f,
0x32, 0x13, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x64, 0x65, 0x12, 0x35, 0x0a, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65,
0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x64, 0x0a, 0x21, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48,
0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x00, 0x52, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69,
0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6f, 0x76,
0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69,
0x0e, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x74, 0x22, 0x35, 0x0a, 0x05, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f,
0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e,
0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01,
0x75, 0x73, 0x2a, 0xad, 0x01, 0x0a, 0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x22, 0x48, 0x0a, 0x13, 0x55, 0x70, 0x6c, 0x6f,
0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x31, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x19, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46,
0x12, 0x25, 0x0a, 0x21, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,
0x54, 0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x75, 0x73, 0x22, 0x3d, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69,
0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x52, 0x41, 0x50, 0x50, 0x45, 0x52, 0x5f, 0x53, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x54, 0x41, 0x52, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x1f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x42, 0x4f, 0x4f, 0x54, 0x53, 0x54, 0x52, 0x41, 0x73, 0x22, 0x4d, 0x0a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65,
0x50, 0x50, 0x45, 0x52, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x5f, 0x45, 0x58, 0x49, 0x53, 0x54, 0x53, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65,
0x10, 0x03, 0x2a, 0x75, 0x0a, 0x1f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x01,
0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65,
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73,
0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x22, 0x64, 0x0a, 0x21, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73,
0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55,
0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x06, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76,
0x62, 0x75, 0x67, 0x64, 0x12, 0x3c, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
0x16, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a, 0x3f, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x54, 0x5f, 0x49,
0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x18, 0x0a,
0x22, 0x00, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x14, 0x53, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44,
0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x59, 0x5f, 0x53, 0x45, 0x54, 0x10, 0x01, 0x2a, 0xb1, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x47, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a,
0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x14, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x53, 0x55,
0x12, 0x4b, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x50, 0x4c, 0x4f, 0x41,
0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46,
0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x22, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x50, 0x4c, 0x4f, 0x41,
0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f,
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x4e, 0x0a, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x50, 0x4c,
0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x41, 0x4c, 0x52, 0x45, 0x41, 0x44,
0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x44, 0x59, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19,
0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5f, 0x53, 0x54, 0x41,
0x70, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x64, 0x65, 0x62, 0x52, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x75, 0x0a, 0x1f, 0x55,
0x75, 0x67, 0x64, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76,
0x18, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x65, 0x72, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28,
0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44,
0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x53,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x55, 0x50, 0x4c, 0x4f,
0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x55, 0x70, 0x6c, 0x41, 0x44, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49,
0x6f, 0x61, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x49, 0x54, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45,
0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x10, 0x01, 0x32, 0x94, 0x03, 0x0a, 0x06, 0x44, 0x65, 0x62, 0x75, 0x67, 0x64, 0x12, 0x3c, 0x0a,
0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x07, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67,
0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x1a, 0x17, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x64, 0x2e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x66,
0x67, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x07, 0x47,
0x6f, 0x33, 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 ( var (
@ -728,44 +962,50 @@ func file_debugd_proto_rawDescGZIP() []byte {
return file_debugd_proto_rawDescData return file_debugd_proto_rawDescData
} }
var file_debugd_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_debugd_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_debugd_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_debugd_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_debugd_proto_goTypes = []interface{}{ var file_debugd_proto_goTypes = []interface{}{
(UploadBootstrapperStatus)(0), // 0: debugd.UploadBootstrapperStatus (SetInfoStatus)(0), // 0: debugd.SetInfoStatus
(UploadSystemdServiceUnitsStatus)(0), // 1: debugd.UploadSystemdServiceUnitsStatus (UploadFilesStatus)(0), // 1: debugd.UploadFilesStatus
(*SetInfoRequest)(nil), // 2: debugd.SetInfoRequest (UploadSystemdServiceUnitsStatus)(0), // 2: debugd.UploadSystemdServiceUnitsStatus
(*SetInfoResponse)(nil), // 3: debugd.SetInfoResponse (*SetInfoRequest)(nil), // 3: debugd.SetInfoRequest
(*GetInfoRequest)(nil), // 4: debugd.GetInfoRequest (*SetInfoResponse)(nil), // 4: debugd.SetInfoResponse
(*GetInfoResponse)(nil), // 5: debugd.GetInfoResponse (*GetInfoRequest)(nil), // 5: debugd.GetInfoRequest
(*Info)(nil), // 6: debugd.Info (*GetInfoResponse)(nil), // 6: debugd.GetInfoResponse
(*DownloadBootstrapperRequest)(nil), // 7: debugd.DownloadBootstrapperRequest (*Info)(nil), // 7: debugd.Info
(*Chunk)(nil), // 8: debugd.Chunk (*DownloadFilesRequest)(nil), // 8: debugd.DownloadFilesRequest
(*UploadBootstrapperResponse)(nil), // 9: debugd.UploadBootstrapperResponse (*FileTransferMessage)(nil), // 9: debugd.FileTransferMessage
(*ServiceUnit)(nil), // 10: debugd.ServiceUnit (*FileTransferHeader)(nil), // 10: debugd.FileTransferHeader
(*UploadSystemdServiceUnitsRequest)(nil), // 11: debugd.UploadSystemdServiceUnitsRequest (*Chunk)(nil), // 11: debugd.Chunk
(*UploadSystemdServiceUnitsResponse)(nil), // 12: debugd.UploadSystemdServiceUnitsResponse (*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{ var file_debugd_proto_depIdxs = []int32{
6, // 0: debugd.SetInfoRequest.info:type_name -> debugd.Info 7, // 0: debugd.SetInfoRequest.info:type_name -> debugd.Info
6, // 1: debugd.GetInfoResponse.info:type_name -> debugd.Info 0, // 1: debugd.SetInfoResponse.status:type_name -> debugd.SetInfoStatus
0, // 2: debugd.UploadBootstrapperResponse.status:type_name -> debugd.UploadBootstrapperStatus 7, // 2: debugd.GetInfoResponse.info:type_name -> debugd.Info
10, // 3: debugd.UploadSystemdServiceUnitsRequest.units:type_name -> debugd.ServiceUnit 10, // 3: debugd.FileTransferMessage.header:type_name -> debugd.FileTransferHeader
1, // 4: debugd.UploadSystemdServiceUnitsResponse.status:type_name -> debugd.UploadSystemdServiceUnitsStatus 11, // 4: debugd.FileTransferMessage.chunk:type_name -> debugd.Chunk
2, // 5: debugd.Debugd.SetInfo:input_type -> debugd.SetInfoRequest 1, // 5: debugd.UploadFilesResponse.status:type_name -> debugd.UploadFilesStatus
4, // 6: debugd.Debugd.GetInfo:input_type -> debugd.GetInfoRequest 13, // 6: debugd.UploadSystemdServiceUnitsRequest.units:type_name -> debugd.ServiceUnit
8, // 7: debugd.Debugd.UploadBootstrapper:input_type -> debugd.Chunk 2, // 7: debugd.UploadSystemdServiceUnitsResponse.status:type_name -> debugd.UploadSystemdServiceUnitsStatus
7, // 8: debugd.Debugd.DownloadBootstrapper:input_type -> debugd.DownloadBootstrapperRequest 3, // 8: debugd.Debugd.SetInfo:input_type -> debugd.SetInfoRequest
11, // 9: debugd.Debugd.UploadSystemServiceUnits:input_type -> debugd.UploadSystemdServiceUnitsRequest 5, // 9: debugd.Debugd.GetInfo:input_type -> debugd.GetInfoRequest
3, // 10: debugd.Debugd.SetInfo:output_type -> debugd.SetInfoResponse 9, // 10: debugd.Debugd.UploadFiles:input_type -> debugd.FileTransferMessage
5, // 11: debugd.Debugd.GetInfo:output_type -> debugd.GetInfoResponse 8, // 11: debugd.Debugd.DownloadFiles:input_type -> debugd.DownloadFilesRequest
9, // 12: debugd.Debugd.UploadBootstrapper:output_type -> debugd.UploadBootstrapperResponse 14, // 12: debugd.Debugd.UploadSystemServiceUnits:input_type -> debugd.UploadSystemdServiceUnitsRequest
8, // 13: debugd.Debugd.DownloadBootstrapper:output_type -> debugd.Chunk 4, // 13: debugd.Debugd.SetInfo:output_type -> debugd.SetInfoResponse
12, // 14: debugd.Debugd.UploadSystemServiceUnits:output_type -> debugd.UploadSystemdServiceUnitsResponse 6, // 14: debugd.Debugd.GetInfo:output_type -> debugd.GetInfoResponse
10, // [10:15] is the sub-list for method output_type 12, // 15: debugd.Debugd.UploadFiles:output_type -> debugd.UploadFilesResponse
5, // [5:10] is the sub-list for method input_type 9, // 16: debugd.Debugd.DownloadFiles:output_type -> debugd.FileTransferMessage
5, // [5:5] is the sub-list for extension type_name 15, // 17: debugd.Debugd.UploadSystemServiceUnits:output_type -> debugd.UploadSystemdServiceUnitsResponse
5, // [5:5] is the sub-list for extension extendee 13, // [13:18] is the sub-list for method output_type
0, // [0:5] is the sub-list for field type_name 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() } 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{} { file_debugd_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DownloadBootstrapperRequest); i { switch v := v.(*DownloadFilesRequest); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -847,7 +1087,7 @@ func file_debugd_proto_init() {
} }
} }
file_debugd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { file_debugd_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Chunk); i { switch v := v.(*FileTransferMessage); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -859,7 +1099,7 @@ func file_debugd_proto_init() {
} }
} }
file_debugd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { file_debugd_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UploadBootstrapperResponse); i { switch v := v.(*FileTransferHeader); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -871,7 +1111,7 @@ func file_debugd_proto_init() {
} }
} }
file_debugd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { file_debugd_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServiceUnit); i { switch v := v.(*Chunk); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -883,7 +1123,7 @@ func file_debugd_proto_init() {
} }
} }
file_debugd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { file_debugd_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UploadSystemdServiceUnitsRequest); i { switch v := v.(*UploadFilesResponse); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -895,6 +1135,30 @@ func file_debugd_proto_init() {
} }
} }
file_debugd_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { 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 { switch v := v.(*UploadSystemdServiceUnitsResponse); i {
case 0: case 0:
return &v.state 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{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_debugd_proto_rawDesc, RawDescriptor: file_debugd_proto_rawDesc,
NumEnums: 2, NumEnums: 3,
NumMessages: 11, NumMessages: 13,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -7,8 +7,8 @@ package debugd;
service Debugd { service Debugd {
rpc SetInfo (SetInfoRequest) returns (SetInfoResponse) {} rpc SetInfo (SetInfoRequest) returns (SetInfoResponse) {}
rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) {} rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) {}
rpc UploadBootstrapper(stream Chunk) returns (UploadBootstrapperResponse) {} rpc UploadFiles(stream FileTransferMessage) returns (UploadFilesResponse) {}
rpc DownloadBootstrapper(DownloadBootstrapperRequest) returns (stream Chunk) {} rpc DownloadFiles(DownloadFilesRequest) returns (stream FileTransferMessage) {}
rpc UploadSystemServiceUnits(UploadSystemdServiceUnitsRequest) returns (UploadSystemdServiceUnitsResponse) {} rpc UploadSystemServiceUnits(UploadSystemdServiceUnitsRequest) returns (UploadSystemdServiceUnitsResponse) {}
} }
@ -16,7 +16,14 @@ message SetInfoRequest {
repeated Info info = 1; repeated Info info = 1;
} }
message SetInfoResponse {} message SetInfoResponse {
SetInfoStatus status = 1;
}
enum SetInfoStatus {
SET_INFO_SUCCESS = 0;
SET_INFO_ALREADY_SET = 1;
}
message GetInfoRequest {} message GetInfoRequest {}
@ -29,22 +36,36 @@ message Info {
string value = 2; 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 { message Chunk {
bytes content = 1; bytes content = 1;
bool last = 2;
} }
message UploadBootstrapperResponse { message UploadFilesResponse {
UploadBootstrapperStatus status = 1; UploadFilesStatus status = 1;
} }
enum UploadBootstrapperStatus { enum UploadFilesStatus {
UPLOAD_BOOTSTRAPPER_SUCCESS = 0; UPLOAD_FILES_SUCCESS = 0;
UPLOAD_BOOTSTRAPPER_UPLOAD_FAILED = 1; UPLOAD_FILES_UPLOAD_FAILED = 1;
UPLOAD_BOOTSTRAPPER_START_FAILED = 2; UPLOAD_FILES_ALREADY_STARTED = 2;
UPLOAD_BOOTSTRAPPER_FILE_EXISTS = 3; UPLOAD_FILES_ALREADY_FINISHED = 3;
UPLOAD_FILES_START_FAILED = 4;
} }
message ServiceUnit { message ServiceUnit {

View File

@ -24,8 +24,8 @@ const _ = grpc.SupportPackageIsVersion7
type DebugdClient interface { type DebugdClient interface {
SetInfo(ctx context.Context, in *SetInfoRequest, opts ...grpc.CallOption) (*SetInfoResponse, error) SetInfo(ctx context.Context, in *SetInfoRequest, opts ...grpc.CallOption) (*SetInfoResponse, error)
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
UploadBootstrapper(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadBootstrapperClient, error) UploadFiles(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadFilesClient, error)
DownloadBootstrapper(ctx context.Context, in *DownloadBootstrapperRequest, opts ...grpc.CallOption) (Debugd_DownloadBootstrapperClient, error) DownloadFiles(ctx context.Context, in *DownloadFilesRequest, opts ...grpc.CallOption) (Debugd_DownloadFilesClient, error)
UploadSystemServiceUnits(ctx context.Context, in *UploadSystemdServiceUnitsRequest, opts ...grpc.CallOption) (*UploadSystemdServiceUnitsResponse, 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 return out, nil
} }
func (c *debugdClient) UploadBootstrapper(ctx context.Context, opts ...grpc.CallOption) (Debugd_UploadBootstrapperClient, error) { 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/UploadBootstrapper", opts...) stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[0], "/debugd.Debugd/UploadFiles", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x := &debugdUploadBootstrapperClient{stream} x := &debugdUploadFilesClient{stream}
return x, nil return x, nil
} }
type Debugd_UploadBootstrapperClient interface { type Debugd_UploadFilesClient interface {
Send(*Chunk) error Send(*FileTransferMessage) error
CloseAndRecv() (*UploadBootstrapperResponse, error) CloseAndRecv() (*UploadFilesResponse, error)
grpc.ClientStream grpc.ClientStream
} }
type debugdUploadBootstrapperClient struct { type debugdUploadFilesClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *debugdUploadBootstrapperClient) Send(m *Chunk) error { func (x *debugdUploadFilesClient) Send(m *FileTransferMessage) error {
return x.ClientStream.SendMsg(m) return x.ClientStream.SendMsg(m)
} }
func (x *debugdUploadBootstrapperClient) CloseAndRecv() (*UploadBootstrapperResponse, error) { func (x *debugdUploadFilesClient) CloseAndRecv() (*UploadFilesResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil { if err := x.ClientStream.CloseSend(); err != nil {
return nil, err return nil, err
} }
m := new(UploadBootstrapperResponse) m := new(UploadFilesResponse)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
} }
func (c *debugdClient) DownloadBootstrapper(ctx context.Context, in *DownloadBootstrapperRequest, opts ...grpc.CallOption) (Debugd_DownloadBootstrapperClient, error) { 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/DownloadBootstrapper", opts...) stream, err := c.cc.NewStream(ctx, &Debugd_ServiceDesc.Streams[1], "/debugd.Debugd/DownloadFiles", opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x := &debugdDownloadBootstrapperClient{stream} x := &debugdDownloadFilesClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil { if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err return nil, err
} }
@ -104,17 +104,17 @@ func (c *debugdClient) DownloadBootstrapper(ctx context.Context, in *DownloadBoo
return x, nil return x, nil
} }
type Debugd_DownloadBootstrapperClient interface { type Debugd_DownloadFilesClient interface {
Recv() (*Chunk, error) Recv() (*FileTransferMessage, error)
grpc.ClientStream grpc.ClientStream
} }
type debugdDownloadBootstrapperClient struct { type debugdDownloadFilesClient struct {
grpc.ClientStream grpc.ClientStream
} }
func (x *debugdDownloadBootstrapperClient) Recv() (*Chunk, error) { func (x *debugdDownloadFilesClient) Recv() (*FileTransferMessage, error) {
m := new(Chunk) m := new(FileTransferMessage)
if err := x.ClientStream.RecvMsg(m); err != nil { if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
@ -136,8 +136,8 @@ func (c *debugdClient) UploadSystemServiceUnits(ctx context.Context, in *UploadS
type DebugdServer interface { type DebugdServer interface {
SetInfo(context.Context, *SetInfoRequest) (*SetInfoResponse, error) SetInfo(context.Context, *SetInfoRequest) (*SetInfoResponse, error)
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
UploadBootstrapper(Debugd_UploadBootstrapperServer) error UploadFiles(Debugd_UploadFilesServer) error
DownloadBootstrapper(*DownloadBootstrapperRequest, Debugd_DownloadBootstrapperServer) error DownloadFiles(*DownloadFilesRequest, Debugd_DownloadFilesServer) error
UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error) UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error)
mustEmbedUnimplementedDebugdServer() mustEmbedUnimplementedDebugdServer()
} }
@ -152,11 +152,11 @@ func (UnimplementedDebugdServer) SetInfo(context.Context, *SetInfoRequest) (*Set
func (UnimplementedDebugdServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) { func (UnimplementedDebugdServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
} }
func (UnimplementedDebugdServer) UploadBootstrapper(Debugd_UploadBootstrapperServer) error { func (UnimplementedDebugdServer) UploadFiles(Debugd_UploadFilesServer) error {
return status.Errorf(codes.Unimplemented, "method UploadBootstrapper not implemented") return status.Errorf(codes.Unimplemented, "method UploadFiles not implemented")
} }
func (UnimplementedDebugdServer) DownloadBootstrapper(*DownloadBootstrapperRequest, Debugd_DownloadBootstrapperServer) error { func (UnimplementedDebugdServer) DownloadFiles(*DownloadFilesRequest, Debugd_DownloadFilesServer) error {
return status.Errorf(codes.Unimplemented, "method DownloadBootstrapper not implemented") return status.Errorf(codes.Unimplemented, "method DownloadFiles not implemented")
} }
func (UnimplementedDebugdServer) UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error) { func (UnimplementedDebugdServer) UploadSystemServiceUnits(context.Context, *UploadSystemdServiceUnitsRequest) (*UploadSystemdServiceUnitsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UploadSystemServiceUnits not implemented") 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) return interceptor(ctx, in, info, handler)
} }
func _Debugd_UploadBootstrapper_Handler(srv interface{}, stream grpc.ServerStream) error { func _Debugd_UploadFiles_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(DebugdServer).UploadBootstrapper(&debugdUploadBootstrapperServer{stream}) return srv.(DebugdServer).UploadFiles(&debugdUploadFilesServer{stream})
} }
type Debugd_UploadBootstrapperServer interface { type Debugd_UploadFilesServer interface {
SendAndClose(*UploadBootstrapperResponse) error SendAndClose(*UploadFilesResponse) error
Recv() (*Chunk, error) Recv() (*FileTransferMessage, error)
grpc.ServerStream grpc.ServerStream
} }
type debugdUploadBootstrapperServer struct { type debugdUploadFilesServer struct {
grpc.ServerStream grpc.ServerStream
} }
func (x *debugdUploadBootstrapperServer) SendAndClose(m *UploadBootstrapperResponse) error { func (x *debugdUploadFilesServer) SendAndClose(m *UploadFilesResponse) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }
func (x *debugdUploadBootstrapperServer) Recv() (*Chunk, error) { func (x *debugdUploadFilesServer) Recv() (*FileTransferMessage, error) {
m := new(Chunk) m := new(FileTransferMessage)
if err := x.ServerStream.RecvMsg(m); err != nil { if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err return nil, err
} }
return m, nil return m, nil
} }
func _Debugd_DownloadBootstrapper_Handler(srv interface{}, stream grpc.ServerStream) error { func _Debugd_DownloadFiles_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(DownloadBootstrapperRequest) m := new(DownloadFilesRequest)
if err := stream.RecvMsg(m); err != nil { if err := stream.RecvMsg(m); err != nil {
return err return err
} }
return srv.(DebugdServer).DownloadBootstrapper(m, &debugdDownloadBootstrapperServer{stream}) return srv.(DebugdServer).DownloadFiles(m, &debugdDownloadFilesServer{stream})
} }
type Debugd_DownloadBootstrapperServer interface { type Debugd_DownloadFilesServer interface {
Send(*Chunk) error Send(*FileTransferMessage) error
grpc.ServerStream grpc.ServerStream
} }
type debugdDownloadBootstrapperServer struct { type debugdDownloadFilesServer struct {
grpc.ServerStream grpc.ServerStream
} }
func (x *debugdDownloadBootstrapperServer) Send(m *Chunk) error { func (x *debugdDownloadFilesServer) Send(m *FileTransferMessage) error {
return x.ServerStream.SendMsg(m) return x.ServerStream.SendMsg(m)
} }
@ -297,13 +297,13 @@ var Debugd_ServiceDesc = grpc.ServiceDesc{
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
StreamName: "UploadBootstrapper", StreamName: "UploadFiles",
Handler: _Debugd_UploadBootstrapper_Handler, Handler: _Debugd_UploadFiles_Handler,
ClientStreams: true, ClientStreams: true,
}, },
{ {
StreamName: "DownloadBootstrapper", StreamName: "DownloadFiles",
Handler: _Debugd_DownloadBootstrapper_Handler, Handler: _Debugd_DownloadFiles_Handler,
ServerStreams: true, ServerStreams: true,
}, },
}, },