github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/tlfupgrade/tlfupgrade.go (about)

     1  package tlfupgrade
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/keybase/client/go/libkb"
    10  	"github.com/keybase/client/go/protocol/chat1"
    11  	"github.com/keybase/client/go/protocol/gregor1"
    12  	"github.com/keybase/client/go/protocol/keybase1"
    13  	"github.com/keybase/clockwork"
    14  )
    15  
    16  type BackgroundTLFUpdater struct {
    17  	libkb.Contextified
    18  	sync.Mutex
    19  
    20  	initialWait time.Duration
    21  	errWait     time.Duration
    22  	successWait time.Duration
    23  	clock       clockwork.Clock
    24  	shutdownCh  chan struct{}
    25  	running     bool
    26  
    27  	// testing
    28  	testingAPIServer   libkb.API
    29  	testingChatHelper  libkb.ChatHelper
    30  	testingDisableKBFS bool
    31  	upgradeCh          *chan keybase1.TLFID
    32  }
    33  
    34  func NewBackgroundTLFUpdater(g *libkb.GlobalContext) *BackgroundTLFUpdater {
    35  	b := &BackgroundTLFUpdater{
    36  		Contextified: libkb.NewContextified(g),
    37  		initialWait:  10 * time.Second,
    38  		errWait:      20 * time.Second,
    39  		successWait:  20 * time.Second,
    40  		shutdownCh:   make(chan struct{}),
    41  		clock:        clockwork.NewRealClock(),
    42  	}
    43  	g.PushShutdownHook(b.Shutdown)
    44  	return b
    45  }
    46  
    47  func (b *BackgroundTLFUpdater) debug(ctx context.Context, msg string, args ...interface{}) {
    48  	b.G().Log.CDebugf(ctx, "BackgroundTLFUpdater: %s", fmt.Sprintf(msg, args...))
    49  }
    50  
    51  func (b *BackgroundTLFUpdater) api() libkb.API {
    52  	if b.testingAPIServer != nil {
    53  		return b.testingAPIServer
    54  	}
    55  	return b.G().API
    56  }
    57  
    58  func (b *BackgroundTLFUpdater) chat() libkb.ChatHelper {
    59  	if b.testingChatHelper != nil {
    60  		return b.testingChatHelper
    61  	}
    62  	return b.G().ChatHelper
    63  }
    64  
    65  func (b *BackgroundTLFUpdater) Run() {
    66  	b.runAll()
    67  }
    68  
    69  func (b *BackgroundTLFUpdater) runAll() {
    70  	b.Lock()
    71  	defer b.Unlock()
    72  	if b.G().IsMobileAppType() {
    73  		b.debug(context.Background(), "tlf updater disabled on mobile")
    74  		return
    75  	}
    76  	uid := b.G().Env.GetUID()
    77  	if uid.IsNil() {
    78  		b.debug(context.Background(), "not logged in, not starting")
    79  		return
    80  	}
    81  	if !b.running && b.G().Env.GetChatMemberType() != "kbfs" {
    82  		b.debug(context.Background(), "starting up")
    83  		b.shutdownCh = make(chan struct{})
    84  		b.running = true
    85  		go b.runAppType(keybase1.TeamApplication_CHAT)
    86  		if !b.testingDisableKBFS {
    87  			go b.runAppType(keybase1.TeamApplication_KBFS)
    88  		}
    89  	}
    90  }
    91  
    92  func (b *BackgroundTLFUpdater) Shutdown(mctx libkb.MetaContext) error {
    93  	b.Lock()
    94  	defer b.Unlock()
    95  	if b.running {
    96  		b.debug(mctx.Ctx(), "shutting down")
    97  		b.running = false
    98  		close(b.shutdownCh)
    99  	}
   100  	return nil
   101  }
   102  
   103  func (b *BackgroundTLFUpdater) runAppType(appType keybase1.TeamApplication) {
   104  	var tlf *GetTLFForUpgradeAvailableRes
   105  	ctx := context.Background()
   106  	nextTime := b.deadline(b.initialWait)
   107  	for {
   108  		b.debug(ctx, "runAppType(%v): waiting until %v", appType, nextTime)
   109  		select {
   110  		case <-b.shutdownCh:
   111  			b.debug(ctx, "runAppType(%v): shutdown", appType)
   112  			return
   113  		case <-b.clock.AfterTime(nextTime):
   114  			b.debug(ctx, "runAppType(%v): woken up", appType)
   115  			tlf, nextTime = b.getTLFToUpgrade(ctx, appType)
   116  			if tlf != nil {
   117  				b.upgradeTLF(ctx, tlf.TlfName, tlf.TlfID, tlf.IsPublic, appType)
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  type getUpgradeRes struct {
   124  	Status libkb.AppStatus `json:"status"`
   125  	GetTLFForUpgradeRes
   126  }
   127  
   128  func (r *getUpgradeRes) GetAppStatus() *libkb.AppStatus {
   129  	return &r.Status
   130  }
   131  
   132  func (b *BackgroundTLFUpdater) deadline(d time.Duration) time.Time {
   133  	return b.clock.Now().Add(d)
   134  }
   135  
   136  func (b *BackgroundTLFUpdater) getTLFToUpgrade(ctx context.Context, appType keybase1.TeamApplication) (*GetTLFForUpgradeAvailableRes, time.Time) {
   137  	mctx := libkb.NewMetaContext(ctx, b.G())
   138  	if !b.G().ActiveDevice.HaveKeys() {
   139  		return nil, time.Now().Add(time.Minute)
   140  	}
   141  	arg := libkb.NewAPIArg("kbfs/upgrade")
   142  	arg.Args = libkb.NewHTTPArgs()
   143  	arg.SessionType = libkb.APISessionTypeREQUIRED
   144  	arg.Args.Add("app_type", libkb.I{Val: int(appType)})
   145  	var res getUpgradeRes
   146  	if err := b.api().GetDecode(mctx, arg, &res); err != nil {
   147  		b.debug(ctx, "getTLFToUpgrade: API fail: %s", err)
   148  		return nil, b.deadline(b.errWait)
   149  	}
   150  	typ, err := res.Typ()
   151  	if err != nil {
   152  		b.debug(ctx, "getTLFToUpgrade: failed to get typ: %s", err)
   153  		return nil, b.deadline(b.errWait)
   154  	}
   155  	switch typ {
   156  	case GetTLFForUpgradeResType_TLFAVAILABLE:
   157  		tlf := res.Tlfavailable()
   158  		b.debug(ctx, "getTLFUpgrade: found TLF to upgrade: %s apptype: %v", tlf.TlfID, appType)
   159  		return &tlf, b.deadline(b.successWait)
   160  	case GetTLFForUpgradeResType_DELAY:
   161  		b.debug(ctx, "getTLFUpgrade: delayed: reason: %s delay: %v", res.Delay().Reason, res.Delay().Delay)
   162  		return nil, gregor1.FromTime(res.Delay().Delay)
   163  	case GetTLFForUpgradeResType_DISABLED:
   164  		b.debug(ctx, "getTLFUpgrade: disabled: delay: %v", res.Disabled().Delay)
   165  		return nil, gregor1.FromTime(res.Disabled().Delay)
   166  	case GetTLFForUpgradeResType_ERR:
   167  		b.debug(ctx, "getTLFUpgrade: server err: %s delay: %v", res.Err().Error, res.Err().Delay)
   168  		return nil, gregor1.FromTime(res.Err().Delay)
   169  	default:
   170  		b.debug(ctx, "getTLFUpgrade: unknown result type: %v", typ)
   171  	}
   172  	return nil, b.deadline(b.errWait)
   173  }
   174  
   175  func (b *BackgroundTLFUpdater) upgradeTLF(ctx context.Context, tlfName string, tlfID keybase1.TLFID,
   176  	public bool, appType keybase1.TeamApplication) {
   177  	switch appType {
   178  	case keybase1.TeamApplication_CHAT:
   179  		b.upgradeTLFForChat(ctx, tlfName, tlfID, public)
   180  	case keybase1.TeamApplication_KBFS:
   181  		if err := UpgradeTLFForKBFS(ctx, b.G(), tlfName, public); err != nil {
   182  			b.debug(ctx, "upgradeTLF: KBFS upgrade failed: %s", err)
   183  		}
   184  	default:
   185  		b.debug(ctx, "upgradeTLF: unknown app type: %v", appType)
   186  	}
   187  }
   188  
   189  func (b *BackgroundTLFUpdater) upgradeTLFForChat(ctx context.Context, tlfName string, tlfID keybase1.TLFID,
   190  	public bool) {
   191  	defer func() {
   192  		if b.upgradeCh != nil {
   193  			*b.upgradeCh <- tlfID
   194  		}
   195  	}()
   196  	chatTLFID, err := chat1.MakeTLFID(tlfID.String())
   197  	if err != nil {
   198  		b.debug(ctx, "upgradeTLFForChat: invalid TLFID: %s", err)
   199  		return
   200  	}
   201  	if err := b.chat().UpgradeKBFSToImpteam(ctx, tlfName, chatTLFID, public); err != nil {
   202  		b.debug(ctx, "upgradeTLFForChat: failed to upgrade TLFID for chat: tlfID: %v err: %s", tlfID, err)
   203  		return
   204  	}
   205  }