Skip to content

Commit

Permalink
cmd/storagenode: add forget-satellite-status command
Browse files Browse the repository at this point in the history
Updates #6541

Change-Id: I159d323b090de6ad39eb4da6d3ca105234541284
  • Loading branch information
profclems authored and Storj Robot committed Jan 29, 2024
1 parent 45d99ed commit e4d6ce8
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 39 deletions.
124 changes: 113 additions & 11 deletions cmd/storagenode/cmd_forget_satellite.go
Expand Up @@ -28,11 +28,11 @@ type forgetSatelliteCfg struct {
Identity identity.Config
Server server.Config

InitFSOptions
ForgetSatelliteOptions
}

// InitFSOptions defines options for forget-satellite command.
type InitFSOptions struct {
// ForgetSatelliteOptions defines options for forget-satellite command.
type ForgetSatelliteOptions struct {
SatelliteIDs []string `internal:"true"`

AllUntrusted bool `help:"Clean up all untrusted satellites" default:"false"`
Expand All @@ -41,6 +41,14 @@ type InitFSOptions struct {
Stdout io.Writer `internal:"true"`
}

type forgetSatelliteStatusCfg struct {
Identity identity.Config
Server server.Config

SatelliteIDs []string `internal:"true"`
Stdout io.Writer `internal:"true"`
}

func newForgetSatelliteCmd(f *Factory) *cobra.Command {
var cfg forgetSatelliteCfg
cmd := &cobra.Command{
Expand Down Expand Up @@ -87,6 +95,36 @@ $ storagenode forget-satellite satellite_ID1 satellite_ID2 --force --identity-di
return cmd
}

func newForgetSatelliteStatusCmd(f *Factory) *cobra.Command {
var cfg forgetSatelliteStatusCfg
cmd := &cobra.Command{
Use: "forget-satellite-status [satellite_IDs...]",
Short: "Get forget satellite status",
Long: "The command returns the status of the forget-satellite process for a satellite.\n",
Example: `
# Get status for all processes
$ storagenode forget-satellite-status --identity-dir /path/to/identityDir --config-dir /path/to/configDir
# Specify satellite ID to get status
$ storagenode forget-satellite-status --identity-dir /path/to/identityDir --config-dir /path/to/configDir satellite_ID
# Specify multiple satellite IDs to get status
$ storagenode forget-satellite-status satellite_ID1 satellite_ID2 --identity-dir /path/to/identityDir --config-dir /path/to/configDir
`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg.SatelliteIDs = args

ctx, _ := process.Ctx(cmd)
return cmdForgetSatelliteStatus(ctx, zap.L(), &cfg)
},
Annotations: map[string]string{"type": "helper"},
}

process.Bind(cmd, &cfg, f.Defaults, cfgstruct.ConfDir(f.ConfDir), cfgstruct.IdentityDir(f.IdentityDir))

return cmd
}

func cmdForgetSatellite(ctx context.Context, log *zap.Logger, cfg *forgetSatelliteCfg) (err error) {
if cfg.Stdout == nil {
cfg.Stdout = os.Stdout
Expand All @@ -111,11 +149,11 @@ func cmdForgetSatellite(ctx context.Context, log *zap.Logger, cfg *forgetSatelli
}
}()

return initForgetSatellite(ctx, log, client, cfg.InitFSOptions)
return startForgetSatellite(ctx, log, client, cfg.ForgetSatelliteOptions)
}

func initForgetSatellite(ctx context.Context, log *zap.Logger, client *forgetSatelliteClient, cfg InitFSOptions) (err error) {
if cfg.AllUntrusted {
func startForgetSatellite(ctx context.Context, log *zap.Logger, client *forgetSatelliteClient, opts ForgetSatelliteOptions) (err error) {
if opts.AllUntrusted {
resp, err := client.getUntrustedSatellites(ctx)
if err != nil {
return errs.Wrap(err)
Expand All @@ -127,18 +165,18 @@ func initForgetSatellite(ctx context.Context, log *zap.Logger, client *forgetSat
}

for _, satelliteID := range resp.SatelliteIds {
cfg.SatelliteIDs = append(cfg.SatelliteIDs, satelliteID.String())
opts.SatelliteIDs = append(opts.SatelliteIDs, satelliteID.String())
}
}

statuses := make([]*forgetSatelliteStatus, 0, len(cfg.SatelliteIDs))
for _, satelliteID := range cfg.SatelliteIDs {
statuses := make([]*forgetSatelliteStatus, 0, len(opts.SatelliteIDs))
for _, satelliteID := range opts.SatelliteIDs {
id, err := storj.NodeIDFromString(satelliteID)
if err != nil {
return errs.Wrap(err)
}

resp, err := client.initForgetSatellite(ctx, id, cfg.Force)
resp, err := client.startForgetSatellite(ctx, id, opts.Force)
if err != nil {
inProgress := false
switch rpcstatus.Code(err) {
Expand All @@ -159,6 +197,62 @@ func initForgetSatellite(ctx context.Context, log *zap.Logger, client *forgetSat
statuses = append(statuses, &forgetSatelliteStatus{satelliteID: id, inProgress: resp.InProgress, successful: false})
}

w := tabwriter.NewWriter(opts.Stdout, 0, 0, 2, ' ', 0)
return displayStatus(w, statuses)
}

func cmdForgetSatelliteStatus(ctx context.Context, log *zap.Logger, cfg *forgetSatelliteStatusCfg) (err error) {
if cfg.Stdout == nil {
cfg.Stdout = os.Stdout
}
// we don't really need the identity, but we load it as a sanity check
ident, err := cfg.Identity.Load()
if err != nil {
log.Fatal("Failed to load identity.", zap.Error(err))
} else {
log.Info("Identity loaded.", zap.Stringer("Node ID", ident.ID))
}

client, err := dialForgetSatelliteClient(ctx, cfg.Server.PrivateAddress)
if err != nil {
return errs.Wrap(err)
}

defer func() {
err = errs.Combine(err, client.close())
if err != nil {
log.Debug("error closing forget-satellite client", zap.Error(err))
}
}()

statuses := make([]*forgetSatelliteStatus, 0, len(cfg.SatelliteIDs))
for _, satelliteID := range cfg.SatelliteIDs {
id, err := storj.NodeIDFromString(satelliteID)
if err != nil {
return errs.Wrap(err)
}

resp, err := client.getForgetSatelliteStatus(ctx, id)
if err != nil {
log.Error("Failed to get forget satellite status", zap.Stringer("Satellite ID", id), zap.Error(err))
statuses = append(statuses, &forgetSatelliteStatus{satelliteID: id, inProgress: false, successful: false})
continue
}

statuses = append(statuses, &forgetSatelliteStatus{satelliteID: id, inProgress: resp.InProgress, successful: resp.Successful})
}

if len(cfg.SatelliteIDs) == 0 {
resp, err := client.getAllForgetSatelliteStatus(ctx)
if err != nil {
return errs.Wrap(err)
}

for _, status := range resp.Statuses {
statuses = append(statuses, &forgetSatelliteStatus{satelliteID: status.SatelliteId, inProgress: status.InProgress, successful: status.Successful})
}
}

w := tabwriter.NewWriter(cfg.Stdout, 0, 0, 2, ' ', 0)
return displayStatus(w, statuses)
}
Expand Down Expand Up @@ -221,10 +315,18 @@ func (client *forgetSatelliteClient) getUntrustedSatellites(ctx context.Context)

}

func (client *forgetSatelliteClient) initForgetSatellite(ctx context.Context, id storj.NodeID, force bool) (*internalpb.InitForgetSatelliteResponse, error) {
func (client *forgetSatelliteClient) startForgetSatellite(ctx context.Context, id storj.NodeID, force bool) (*internalpb.InitForgetSatelliteResponse, error) {
return internalpb.NewDRPCNodeForgetSatelliteClient(client.conn).InitForgetSatellite(ctx, &internalpb.InitForgetSatelliteRequest{SatelliteId: id, ForceCleanup: force})
}

func (client *forgetSatelliteClient) getForgetSatelliteStatus(ctx context.Context, id storj.NodeID) (*internalpb.ForgetSatelliteStatusResponse, error) {
return internalpb.NewDRPCNodeForgetSatelliteClient(client.conn).ForgetSatelliteStatus(ctx, &internalpb.ForgetSatelliteStatusRequest{SatelliteId: id})
}

func (client *forgetSatelliteClient) getAllForgetSatelliteStatus(ctx context.Context) (*internalpb.GetAllForgetSatelliteStatusResponse, error) {
return internalpb.NewDRPCNodeForgetSatelliteClient(client.conn).GetAllForgetSatelliteStatus(ctx, &internalpb.GetAllForgetSatelliteStatusRequest{})
}

func (client *forgetSatelliteClient) close() error {
return client.conn.Close()
}
2 changes: 1 addition & 1 deletion cmd/storagenode/cmd_forget_satellite_test.go
Expand Up @@ -132,7 +132,7 @@ func Test_cmdForgetSatellite(t *testing.T) {

cmdCtx, cmdCancel := context.WithCancel(ctx)
defer cmdCancel()
err = initForgetSatellite(cmdCtx, log, client, InitFSOptions{
err = startForgetSatellite(cmdCtx, log, client, ForgetSatelliteOptions{
AllUntrusted: true,
Stdout: &stdout,
})
Expand Down
1 change: 1 addition & 0 deletions cmd/storagenode/root.go
Expand Up @@ -60,6 +60,7 @@ func newRootCmd(setDefaults bool) (*cobra.Command, *Factory) {
newGracefulExitInitCmd(factory),
newGracefulExitStatusCmd(factory),
newForgetSatelliteCmd(factory),
newForgetSatelliteStatusCmd(factory),
// internal hidden commands
internalcmd.NewUsedSpaceFilewalkerCmd().Command,
internalcmd.NewGCFilewalkerCmd().Command,
Expand Down
33 changes: 33 additions & 0 deletions storagenode/forgetsatellite/endpoint.go
Expand Up @@ -134,3 +134,36 @@ func (e *Endpoint) GetUntrustedSatellites(ctx context.Context, req *internalpb.G
SatelliteIds: untrustedSatellites,
}, nil
}

// GetAllForgetSatelliteStatus returns the status of the forget-satellite process for all satellites.
func (e *Endpoint) GetAllForgetSatelliteStatus(ctx context.Context, _ *internalpb.GetAllForgetSatelliteStatusRequest) (_ *internalpb.GetAllForgetSatelliteStatusResponse, err error) {
defer mon.Task()(&ctx)(&err)

logger := e.log.With(zap.String("action", "GetAllForgetSatelliteStatus"))

sats, err := e.satellites.GetSatellites(ctx)
if err != nil {
logger.Error("failed to get satellites", zap.Error(err))
return nil, rpcstatus.Error(rpcstatus.Internal, err.Error())
}

var statuses []*internalpb.ForgetSatelliteStatusResponse
for _, satellite := range sats {
if !isCleanupSatellite(&satellite) {
continue
}
statuses = append(statuses, &internalpb.ForgetSatelliteStatusResponse{
SatelliteId: satellite.SatelliteID,
InProgress: satellite.Status == satellites.CleanupInProgress,
Successful: satellite.Status == satellites.CleanupSucceeded,
})
}

return &internalpb.GetAllForgetSatelliteStatusResponse{
Statuses: statuses,
}, nil
}

func isCleanupSatellite(satellite *satellites.Satellite) bool {
return satellite.Status == satellites.CleanupInProgress || satellite.Status == satellites.CleanupSucceeded || satellite.Status == satellites.CleanupFailed
}
126 changes: 100 additions & 26 deletions storagenode/internalpb/forgetsatellite.pb.go
Expand Up @@ -243,42 +243,116 @@ func (m *ForgetSatelliteStatusResponse) GetSuccessful() bool {
return false
}

type GetAllForgetSatelliteStatusRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}

func (m *GetAllForgetSatelliteStatusRequest) Reset() { *m = GetAllForgetSatelliteStatusRequest{} }
func (m *GetAllForgetSatelliteStatusRequest) String() string { return proto.CompactTextString(m) }
func (*GetAllForgetSatelliteStatusRequest) ProtoMessage() {}
func (*GetAllForgetSatelliteStatusRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_5ee03a9da2a32ad8, []int{6}
}
func (m *GetAllForgetSatelliteStatusRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetAllForgetSatelliteStatusRequest.Unmarshal(m, b)
}
func (m *GetAllForgetSatelliteStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetAllForgetSatelliteStatusRequest.Marshal(b, m, deterministic)
}
func (m *GetAllForgetSatelliteStatusRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetAllForgetSatelliteStatusRequest.Merge(m, src)
}
func (m *GetAllForgetSatelliteStatusRequest) XXX_Size() int {
return xxx_messageInfo_GetAllForgetSatelliteStatusRequest.Size(m)
}
func (m *GetAllForgetSatelliteStatusRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetAllForgetSatelliteStatusRequest.DiscardUnknown(m)
}

var xxx_messageInfo_GetAllForgetSatelliteStatusRequest proto.InternalMessageInfo

type GetAllForgetSatelliteStatusResponse struct {
// statuses is the list of forget satellite statuses.
Statuses []*ForgetSatelliteStatusResponse `protobuf:"bytes,1,rep,name=statuses,proto3" json:"statuses,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}

func (m *GetAllForgetSatelliteStatusResponse) Reset() { *m = GetAllForgetSatelliteStatusResponse{} }
func (m *GetAllForgetSatelliteStatusResponse) String() string { return proto.CompactTextString(m) }
func (*GetAllForgetSatelliteStatusResponse) ProtoMessage() {}
func (*GetAllForgetSatelliteStatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_5ee03a9da2a32ad8, []int{7}
}
func (m *GetAllForgetSatelliteStatusResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetAllForgetSatelliteStatusResponse.Unmarshal(m, b)
}
func (m *GetAllForgetSatelliteStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetAllForgetSatelliteStatusResponse.Marshal(b, m, deterministic)
}
func (m *GetAllForgetSatelliteStatusResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetAllForgetSatelliteStatusResponse.Merge(m, src)
}
func (m *GetAllForgetSatelliteStatusResponse) XXX_Size() int {
return xxx_messageInfo_GetAllForgetSatelliteStatusResponse.Size(m)
}
func (m *GetAllForgetSatelliteStatusResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetAllForgetSatelliteStatusResponse.DiscardUnknown(m)
}

var xxx_messageInfo_GetAllForgetSatelliteStatusResponse proto.InternalMessageInfo

func (m *GetAllForgetSatelliteStatusResponse) GetStatuses() []*ForgetSatelliteStatusResponse {
if m != nil {
return m.Statuses
}
return nil
}

func init() {
proto.RegisterType((*InitForgetSatelliteRequest)(nil), "storagenode.forgetsatellite.InitForgetSatelliteRequest")
proto.RegisterType((*InitForgetSatelliteResponse)(nil), "storagenode.forgetsatellite.InitForgetSatelliteResponse")
proto.RegisterType((*GetUntrustedSatellitesRequest)(nil), "storagenode.forgetsatellite.GetUntrustedSatellitesRequest")
proto.RegisterType((*GetUntrustedSatellitesResponse)(nil), "storagenode.forgetsatellite.GetUntrustedSatellitesResponse")
proto.RegisterType((*ForgetSatelliteStatusRequest)(nil), "storagenode.forgetsatellite.ForgetSatelliteStatusRequest")
proto.RegisterType((*ForgetSatelliteStatusResponse)(nil), "storagenode.forgetsatellite.ForgetSatelliteStatusResponse")
proto.RegisterType((*GetAllForgetSatelliteStatusRequest)(nil), "storagenode.forgetsatellite.GetAllForgetSatelliteStatusRequest")
proto.RegisterType((*GetAllForgetSatelliteStatusResponse)(nil), "storagenode.forgetsatellite.GetAllForgetSatelliteStatusResponse")
}

func init() { proto.RegisterFile("forgetsatellite.proto", fileDescriptor_5ee03a9da2a32ad8) }

var fileDescriptor_5ee03a9da2a32ad8 = []byte{
// 388 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0xc1, 0x6a, 0xe2, 0x50,
0x14, 0x9d, 0x37, 0x82, 0xcc, 0x5c, 0x75, 0x16, 0x4f, 0x1c, 0x42, 0x1c, 0x8d, 0x64, 0x18, 0xc6,
0x55, 0xa4, 0x75, 0xd1, 0xd6, 0xee, 0x6c, 0x69, 0x71, 0x53, 0x5a, 0xc5, 0x4d, 0x37, 0x12, 0x93,
0x6b, 0x48, 0x09, 0xef, 0xc5, 0xf7, 0x5e, 0xfe, 0xa1, 0xab, 0x42, 0xe9, 0x37, 0x15, 0xfa, 0x0d,
0x5d, 0xf8, 0x2d, 0xc5, 0x24, 0xb5, 0x22, 0x31, 0xd0, 0x40, 0x77, 0xe1, 0xe6, 0x9c, 0x73, 0xcf,
0xbd, 0xf7, 0xf0, 0xa0, 0xb1, 0xe0, 0xc2, 0x43, 0x25, 0x6d, 0x85, 0x41, 0xe0, 0x2b, 0xb4, 0x42,
0xc1, 0x15, 0xa7, 0x4d, 0xa9, 0xb8, 0xb0, 0x3d, 0x64, 0xdc, 0x45, 0x6b, 0x07, 0xa2, 0x83, 0xc7,
0x3d, 0x9e, 0x00, 0x4d, 0x05, 0xfa, 0x88, 0xf9, 0xea, 0x22, 0x86, 0x4c, 0xde, 0x21, 0x63, 0x5c,
0x46, 0x28, 0x15, 0x3d, 0x80, 0xea, 0x86, 0x36, 0xf3, 0x5d, 0x8d, 0x74, 0x48, 0xf7, 0xe7, 0xf0,
0xd7, 0xcb, 0xca, 0xf8, 0xf6, 0xba, 0x32, 0xca, 0x57, 0xdc, 0xc5, 0xd1, 0xf9, 0xb8, 0xb2, 0xc1,
0x8c, 0x5c, 0xfa, 0x17, 0x6a, 0x0b, 0x2e, 0x1c, 0x9c, 0x39, 0x01, 0xda, 0x2c, 0x0a, 0xb5, 0xef,
0x1d, 0xd2, 0xfd, 0x31, 0xae, 0xc6, 0xc5, 0xb3, 0xa4, 0x66, 0x2e, 0xa1, 0x99, 0xd9, 0x55, 0x86,
0x9c, 0x49, 0x2c, 0xd2, 0xd6, 0x80, 0x8a, 0xcf, 0x66, 0xa1, 0xe0, 0x9e, 0x40, 0x29, 0xd3, 0xa6,
0xe0, 0xb3, 0xeb, 0xb4, 0x62, 0x1a, 0xd0, 0xba, 0x44, 0x35, 0x65, 0x4a, 0x44, 0x52, 0xa1, 0xbb,
0x69, 0x2a, 0xd3, 0x59, 0xcd, 0x29, 0xb4, 0xf7, 0x01, 0x52, 0x5b, 0x7d, 0xa8, 0x6d, 0xdb, 0x92,
0x1a, 0xe9, 0x94, 0x32, 0x7c, 0x55, 0xb7, 0x7c, 0x49, 0xf3, 0x06, 0xfe, 0xec, 0x8c, 0x39, 0x51,
0xb6, 0x8a, 0x64, 0xf1, 0x15, 0x9b, 0x4f, 0x04, 0x5a, 0x7b, 0x34, 0xbf, 0x6e, 0x81, 0xb4, 0x0d,
0x20, 0x23, 0xc7, 0x41, 0x29, 0x17, 0x51, 0xa0, 0x95, 0x92, 0xff, 0x1f, 0x95, 0xc3, 0xe7, 0x12,
0xd4, 0xd7, 0xc2, 0x3b, 0xce, 0xe8, 0x3d, 0x81, 0x7a, 0xc6, 0xb1, 0xe9, 0x91, 0x95, 0x93, 0x51,
0x6b, 0x7f, 0x28, 0xf5, 0xe3, 0xcf, 0x13, 0xd3, 0xb5, 0x3c, 0x12, 0xf8, 0x9d, 0x7d, 0x63, 0x3a,
0xc8, 0x15, 0xcd, 0x4d, 0x8e, 0x7e, 0x5a, 0x88, 0x9b, 0x7a, 0x7a, 0x20, 0xd0, 0xc8, 0x3c, 0x26,
0x3d, 0xc9, 0x95, 0xcd, 0x0b, 0x95, 0x3e, 0x28, 0x42, 0x4d, 0x0c, 0x0d, 0xff, 0xdf, 0xfe, 0x5b,
0x93, 0xef, 0x2c, 0x9f, 0xf7, 0xe2, 0x8f, 0xde, 0x96, 0x56, 0xcf, 0x67, 0x0a, 0x05, 0xb3, 0x83,
0x70, 0x3e, 0x2f, 0xc7, 0x2f, 0x48, 0xff, 0x2d, 0x00, 0x00, 0xff, 0xff, 0xea, 0x72, 0x60, 0x50,
0x83, 0x04, 0x00, 0x00,
// 444 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
0x14, 0x64, 0x29, 0xaa, 0xca, 0x4b, 0xca, 0x61, 0xab, 0x22, 0xcb, 0xa1, 0x75, 0xb4, 0x05, 0xd1,
0x93, 0x23, 0xda, 0x03, 0x5f, 0x07, 0xa0, 0x20, 0xaa, 0x5c, 0x10, 0xb8, 0x2a, 0x07, 0x2e, 0x91,
0x6b, 0xbf, 0x58, 0x46, 0xd6, 0xae, 0xbb, 0x6f, 0x7d, 0xe4, 0xce, 0x09, 0x09, 0xf1, 0x07, 0xf8,
0x39, 0xfc, 0x06, 0x0e, 0x39, 0xf3, 0x33, 0x50, 0x6c, 0x63, 0xa2, 0xc8, 0xd9, 0x08, 0xa3, 0xde,
0xac, 0xe7, 0xd9, 0x79, 0x33, 0xb3, 0x63, 0xc3, 0xee, 0x54, 0xe9, 0x04, 0x0d, 0x85, 0x06, 0xb3,
0x2c, 0x35, 0xe8, 0xe7, 0x5a, 0x19, 0xc5, 0x07, 0x64, 0x94, 0x0e, 0x13, 0x94, 0x2a, 0x46, 0x7f,
0x09, 0xe2, 0x42, 0xa2, 0x12, 0x55, 0x01, 0x85, 0x01, 0x77, 0x2c, 0x53, 0xf3, 0xba, 0x84, 0x9c,
0xfd, 0x81, 0x04, 0x78, 0x59, 0x20, 0x19, 0xfe, 0x00, 0xfa, 0xcd, 0xb1, 0x49, 0x1a, 0x3b, 0x6c,
0xc8, 0x0e, 0x6f, 0x9e, 0xdc, 0xfa, 0x31, 0xf3, 0xae, 0xfd, 0x9c, 0x79, 0x9b, 0x6f, 0x54, 0x8c,
0xe3, 0x57, 0x41, 0xaf, 0xc1, 0x8c, 0x63, 0x7e, 0x00, 0xdb, 0x53, 0xa5, 0x23, 0x9c, 0x44, 0x19,
0x86, 0xb2, 0xc8, 0x9d, 0xeb, 0x43, 0x76, 0xb8, 0x15, 0xf4, 0xcb, 0xe1, 0xcb, 0x6a, 0x26, 0x2e,
0x61, 0xd0, 0xba, 0x95, 0x72, 0x25, 0x09, 0xbb, 0xac, 0xf5, 0xa0, 0x97, 0xca, 0x49, 0xae, 0x55,
0xa2, 0x91, 0xa8, 0x5e, 0x0a, 0xa9, 0x7c, 0x5b, 0x4f, 0x84, 0x07, 0x7b, 0xa7, 0x68, 0xce, 0xa5,
0xd1, 0x05, 0x19, 0x8c, 0x9b, 0xa5, 0x54, 0x7b, 0x15, 0xe7, 0xb0, 0xbf, 0x0a, 0x50, 0xcb, 0x3a,
0x86, 0xed, 0x45, 0x59, 0xe4, 0xb0, 0xe1, 0x46, 0x8b, 0xae, 0xfe, 0x82, 0x2e, 0x12, 0xef, 0xe0,
0xce, 0x92, 0xcd, 0x33, 0x13, 0x9a, 0x82, 0xba, 0x47, 0x2c, 0xbe, 0x31, 0xd8, 0x5b, 0xc1, 0x79,
0x75, 0x01, 0xf2, 0x7d, 0x00, 0x2a, 0xa2, 0x08, 0x89, 0xa6, 0x45, 0xe6, 0x6c, 0x54, 0xef, 0xff,
0x4e, 0xc4, 0x5d, 0x10, 0xa7, 0x68, 0x5e, 0x64, 0x99, 0xcd, 0xae, 0xf8, 0x04, 0x07, 0x56, 0x54,
0x6d, 0xe0, 0x3d, 0x6c, 0x51, 0x39, 0xc1, 0x2a, 0xe5, 0xde, 0xd1, 0x13, 0xdf, 0x52, 0x69, 0xdf,
0xca, 0x16, 0x34, 0x5c, 0x47, 0xbf, 0x6e, 0xc0, 0xce, 0xdc, 0xfd, 0x12, 0x9e, 0x7f, 0x66, 0xb0,
0xd3, 0xd2, 0x48, 0xfe, 0xd0, 0xba, 0x75, 0xf5, 0x97, 0xe3, 0x3e, 0xfa, 0xf7, 0x83, 0xb5, 0xf5,
0xaf, 0x0c, 0x6e, 0xb7, 0x17, 0x91, 0xdb, 0x33, 0xb0, 0xd6, 0xdb, 0x7d, 0xda, 0xe9, 0x6c, 0xad,
0xe9, 0x0b, 0x83, 0xdd, 0xd6, 0x88, 0xf9, 0xe3, 0x2e, 0xd7, 0x52, 0x29, 0xfa, 0x8f, 0x1b, 0xe5,
0xdf, 0x19, 0x0c, 0x2c, 0x3d, 0xe2, 0xcf, 0xd6, 0xb9, 0x5d, 0xd3, 0x53, 0xf7, 0x79, 0x77, 0x82,
0x4a, 0xe2, 0xc9, 0xfd, 0x0f, 0xf7, 0xe6, 0x14, 0x1f, 0xfd, 0x54, 0x8d, 0xca, 0x87, 0xd1, 0x02,
0xe3, 0x28, 0x95, 0x06, 0xb5, 0x0c, 0xb3, 0xfc, 0xe2, 0x62, 0xb3, 0xfc, 0x13, 0x1f, 0xff, 0x0e,
0x00, 0x00, 0xff, 0xff, 0x6b, 0x7e, 0xbd, 0xfe, 0xcb, 0x05, 0x00, 0x00,
}

0 comments on commit e4d6ce8

Please sign in to comment.