diff --git a/cmd/indra/root.go b/cmd/indra/root.go index 1d8575f4..3039d4af 100644 --- a/cmd/indra/root.go +++ b/cmd/indra/root.go @@ -92,7 +92,7 @@ func initConfig() { home, err := os.UserHomeDir() cobra.CheckErr(err) - cfgFile = home + "/.indra.toml" + cfgFile = home + "/.indra/config.toml" } viper.SetConfigFile(cfgFile) diff --git a/cmd/indra/seed_rpc.go b/cmd/indra/seed_rpc.go index 43791366..55ad0bf1 100644 --- a/cmd/indra/seed_rpc.go +++ b/cmd/indra/seed_rpc.go @@ -44,7 +44,7 @@ var ( func initUnlock(cmd *cobra.Command) { - cmd.PersistentFlags().StringVarP(&unlockTarget, unlockTargetFlag, "", + cmd.Flags().StringVarP(&unlockTarget, unlockTargetFlag, "", "unix:///tmp/indra.sock", "the url of the rpc server", ) diff --git a/cmd/indra/seed_serve.go b/cmd/indra/seed_serve.go index e1245528..4552a8ae 100644 --- a/cmd/indra/seed_serve.go +++ b/cmd/indra/seed_serve.go @@ -39,6 +39,9 @@ var seedServeCommand = &cobra.Command{ go seed.Run(ctx) select { + case err := <-seed.WhenStartFailed(): + log.E.Ln("startup error:", err) + return case <-seed.IsShutdown(): log.I.Ln("shutdown complete") } diff --git a/pkg/rpc/cmd.go b/pkg/rpc/configure.go similarity index 100% rename from pkg/rpc/cmd.go rename to pkg/rpc/configure.go diff --git a/pkg/rpc/flags.go b/pkg/rpc/flags.go index 560abefa..b3f19585 100644 --- a/pkg/rpc/flags.go +++ b/pkg/rpc/flags.go @@ -34,12 +34,12 @@ func InitFlags(cmd *cobra.Command) { viper.BindPFlag(tunEnableFlag, cmd.PersistentFlags().Lookup(tunEnableFlag)) - cmd.PersistentFlags().StringVarP(&tunKeyRaw, tunKeyFlag, "", + cmd.Flags().StringVarP(&tunKeyRaw, tunKeyFlag, "", "", "the base58 encoded pre-shared key for accessing the rpc", ) - viper.BindPFlag(tunKeyFlag, cmd.PersistentFlags().Lookup(tunKeyFlag)) + viper.BindPFlag(tunKeyFlag, cmd.Flags().Lookup(tunKeyFlag)) cmd.PersistentFlags().IntVarP(&tunnelPort, tunPortFlag, "", tunnelPort, diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index 6809b6c3..550b2eba 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -1,7 +1,6 @@ package rpc import ( - "context" "google.golang.org/grpc" ) @@ -10,15 +9,19 @@ func init() { } var ( - server *grpc.Server - startupErrors = make(chan error, 128) - isReady = make(chan bool, 1) + server *grpc.Server ) -func RunWith(ctx context.Context, r func(srv *grpc.Server)) { +func RunWith(r func(srv *grpc.Server), opts ...ServerOption) { log.I.Ln("initializing the rpc server") + serverOpts := serverOptions{} + + for _, opt := range opts { + opt.apply(&serverOpts) + } + configureUnixSocket() configureTunnel() @@ -26,18 +29,10 @@ func RunWith(ctx context.Context, r func(srv *grpc.Server)) { log.I.Ln("starting rpc server") - go Start(ctx) + go Start() } -func CantStart() chan error { - return startupErrors -} - -func IsReady() chan bool { - return isReady -} - -func Start(ctx context.Context) { +func Start() { var err error @@ -49,15 +44,12 @@ func Start(ctx context.Context) { startupErrors <- err } - isReady <- true + log.I.Ln("rpc server is ready") - select { - case <-ctx.Done(): - Shutdown(context.Background()) - } + isReady <- true } -func Shutdown(ctx context.Context) { +func Shutdown() { log.I.Ln("shutting down rpc server") diff --git a/pkg/rpc/server_options.go b/pkg/rpc/server_options.go new file mode 100644 index 00000000..7ee46fc1 --- /dev/null +++ b/pkg/rpc/server_options.go @@ -0,0 +1,29 @@ +package rpc + +type serverOptions struct { + disableTunnel bool +} + +type ServerOption interface { + apply(*serverOptions) +} + +type funcServerOption struct { + f func(*serverOptions) +} + +func (fdo *funcServerOption) apply(do *serverOptions) { + fdo.f(do) +} + +func newFuncServerOption(f func(*serverOptions)) *funcServerOption { + return &funcServerOption{ + f: f, + } +} + +func WithDisableTunnel() ServerOption { + return newFuncServerOption(func(o *serverOptions) { + o.disableTunnel = true + }) +} diff --git a/pkg/rpc/signals.go b/pkg/rpc/signals.go new file mode 100644 index 00000000..8d727cca --- /dev/null +++ b/pkg/rpc/signals.go @@ -0,0 +1,14 @@ +package rpc + +var ( + startupErrors = make(chan error, 128) + isReady = make(chan bool, 1) +) + +func WhenStartFailed() chan error { + return startupErrors +} + +func IsReady() chan bool { + return isReady +} diff --git a/pkg/seed/proc.go b/pkg/seed/proc.go deleted file mode 100644 index 47cca06e..00000000 --- a/pkg/seed/proc.go +++ /dev/null @@ -1,80 +0,0 @@ -package seed - -import ( - "context" - "git-indra.lan/indra-labs/indra/pkg/storage" - "sync" -) - -var ( - inUse sync.Mutex -) - -var ( - startupErrors = make(chan error, 32) - isReadyChan = make(chan bool, 1) - isShutdownChan = make(chan bool, 1) -) - -func CantStart() chan error { - return startupErrors -} - -func IsReady() chan bool { - return isReadyChan -} - -func IsShutdown() chan bool { - return isShutdownChan -} - -func Shutdown() { - - log.I.Ln("shutting down seed") - - err := storage.Shutdown() - check(err) - - isShutdownChan <- true -} - -func Run(ctx context.Context) { - - if !inUse.TryLock() { - log.E.Ln("seed is in use") - return - } - - log.I.Ln("running seed") - - var err error - - go storage.Run(ctx) - -signals: - for { - select { - case err = <-CantStart(): - log.E.Ln("startup error:", err) - return - case <-storage.IsLocked(): - - log.I.Ln("storage is locked, waiting for unlock") - - //go rpc.RunWith(ctx, func(srv *grpc.Server) { - // storage.RegisterUnlockServiceServer(srv, storage.NewUnlockService()) - //}) - - // Run RPC unlock - case <-storage.IsReady(): - break signals - case <-ctx.Done(): - Shutdown() - return - } - } - - log.I.Ln("seed is ready") - - isReadyChan <- true -} diff --git a/pkg/seed/server.go b/pkg/seed/server.go new file mode 100644 index 00000000..b1ee9291 --- /dev/null +++ b/pkg/seed/server.go @@ -0,0 +1,85 @@ +package seed + +import ( + "context" + "git-indra.lan/indra-labs/indra/pkg/rpc" + "git-indra.lan/indra-labs/indra/pkg/storage" + "google.golang.org/grpc" + "sync" +) + +var ( + inUse sync.Mutex +) + +func Run(ctx context.Context) { + + if !inUse.TryLock() { + log.E.Ln("seed is in use") + return + } + + log.I.Ln("running seed") + + go storage.Run() + +signals: + for { + select { + case <-storage.WhenIsLocked(): + + log.I.Ln("storage is locked") + + // Run an unlock RPC server + go rpc.RunWith( + func(srv *grpc.Server) { + storage.RegisterUnlockServiceServer(srv, storage.NewUnlockService()) + }, + rpc.WithDisableTunnel(), + ) + + case err := <-rpc.WhenStartFailed(): + startupErrors <- err + case <-rpc.IsReady(): + log.I.Ln("waiting for unlock") + case <-storage.WhenIsUnlocked(): + + log.I.Ln("restarting rpc server") + + // Shut down unlock RPC server to we can launch the main one + rpc.Shutdown() + + //go rpc.RunWith(func(srv *grpc.Server) { + // chat.RegisterChatServiceServer(srv, &chat.Server{}) + //}) + + case <-storage.WhenIsReady(): + break signals + case <-ctx.Done(): + Shutdown() + return + } + } + + log.I.Ln("seed is ready") + + isReadyChan <- true + + select { + case <-ctx.Done(): + Shutdown() + return + } +} + +func Shutdown() { + + log.I.Ln("shutting down seed") + + rpc.Shutdown() + + err := storage.Shutdown() + check(err) + + isShutdownChan <- true +} diff --git a/pkg/seed/signals.go b/pkg/seed/signals.go new file mode 100644 index 00000000..2cec9a98 --- /dev/null +++ b/pkg/seed/signals.go @@ -0,0 +1,19 @@ +package seed + +var ( + startupErrors = make(chan error, 32) + isReadyChan = make(chan bool, 1) + isShutdownChan = make(chan bool, 1) +) + +func WhenStartFailed() chan error { + return startupErrors +} + +func IsReady() chan bool { + return isReadyChan +} + +func IsShutdown() chan bool { + return isShutdownChan +} diff --git a/pkg/storage/cmd.go b/pkg/storage/configure.go similarity index 95% rename from pkg/storage/cmd.go rename to pkg/storage/configure.go index a4876ba4..8a07a990 100644 --- a/pkg/storage/cmd.go +++ b/pkg/storage/configure.go @@ -7,10 +7,10 @@ import ( ) var ( - isNewKey bool - isNewDB bool - isLocked bool - key Key + newKeyGenerated bool + isNewDB bool + noKeyProvided bool + key Key ) func configure() { @@ -71,15 +71,13 @@ func configureKey() { log.I.Ln("no keyfile found") - isLocked = true + noKeyProvided = true return } log.I.Ln("no keyfile found, generating a new key") - isNewKey = true - if key, err = KeyGen(); err != nil { startupErrors <- err return @@ -115,6 +113,8 @@ func configureKey() { log.W.Ln("-------------------------------------------------------") log.W.Ln("") + newKeyGenerated = true + viper.Set(storeKeyFlag, key.Encode()) } diff --git a/pkg/storage/flags.go b/pkg/storage/flags.go index f9c058a4..7b05605f 100644 --- a/pkg/storage/flags.go +++ b/pkg/storage/flags.go @@ -6,29 +6,29 @@ import ( ) var ( - storeKeyFlag = "store-key" - storeKeyFileFlag = "store-keyfile" - storeKeyRPCFlag = "store-key-rpc" + storeKeyFlag = "store-key" + storeKeyFileFlag = "store-keyfile" + //storeKeyRPCFlag = "store-key-rpc" storeFilePathFlag = "store-path" - storeAskPassFlag = "store-ask-pass" + //storeAskPassFlag = "store-ask-pass" ) var ( storeEncryptionKey string storeEncryptionKeyFile string - storeEncryptionKeyRPC bool - storeFilePath string - storeAskPass bool + //storeEncryptionKeyRPC bool + storeFilePath string + //storeAskPass bool ) func InitFlags(cmd *cobra.Command) { - cmd.PersistentFlags().StringVarP(&storeEncryptionKey, storeKeyFlag, "", + cmd.Flags().StringVarP(&storeEncryptionKey, storeKeyFlag, "", "", "the key required to unlock storage", ) - viper.BindPFlag(storeKeyFlag, cmd.PersistentFlags().Lookup(storeKeyFlag)) + viper.BindPFlag(storeKeyFlag, cmd.Flags().Lookup(storeKeyFlag)) cmd.PersistentFlags().StringVarP(&storeEncryptionKeyFile, storeKeyFileFlag, "", "", @@ -44,17 +44,17 @@ func InitFlags(cmd *cobra.Command) { viper.BindPFlag(storeFilePathFlag, cmd.PersistentFlags().Lookup(storeFilePathFlag)) - cmd.PersistentFlags().BoolVarP(&storeEncryptionKeyRPC, storeKeyRPCFlag, "", - false, - "looks for the encryption key via RPC", - ) + //cmd.PersistentFlags().BoolVarP(&storeEncryptionKeyRPC, storeKeyRPCFlag, "", + // false, + // "looks for the encryption key via RPC", + //) + // + //viper.BindPFlag(storeKeyRPCFlag, cmd.PersistentFlags().Lookup(storeKeyRPCFlag)) - viper.BindPFlag(storeKeyRPCFlag, cmd.PersistentFlags().Lookup(storeKeyRPCFlag)) - - cmd.PersistentFlags().BoolVarP(&storeAskPass, storeAskPassFlag, "", - false, - "prompts the user for a password to unlock storage", - ) - - viper.BindPFlag(storeAskPassFlag, cmd.PersistentFlags().Lookup(storeAskPassFlag)) + //cmd.PersistentFlags().BoolVarP(&storeAskPass, storeAskPassFlag, "", + // false, + // "prompts the user for a password to unlock storage", + //) + // + //viper.BindPFlag(storeAskPassFlag, cmd.PersistentFlags().Lookup(storeAskPassFlag)) } diff --git a/pkg/storage/service.go b/pkg/storage/service.go index 75d00946..3b42dd26 100644 --- a/pkg/storage/service.go +++ b/pkg/storage/service.go @@ -1,8 +1,6 @@ package storage import ( - "context" - "git-indra.lan/indra-labs/indra/pkg/interrupt" "github.com/dgraph-io/badger/v3" "github.com/spf13/viper" "sync" @@ -10,26 +8,29 @@ import ( var ( fileName string = "indra.db" + db *badger.DB + opts badger.Options ) var ( - db *badger.DB - opts badger.Options - startupErrors = make(chan error, 128) - isLockedChan = make(chan bool, 1) - isReadyChan = make(chan bool, 1) + running sync.Mutex ) -func CantStart() chan error { - return startupErrors -} +func Run() { -func IsLocked() chan bool { - return isLockedChan -} + if !running.TryLock() { + return + } -func IsReady() chan bool { - return isReadyChan + configure() + + if !attempt_unlock() { + isLockedChan <- true + return + } + + log.I.Ln("storage is ready") + isReadyChan <- true } func Shutdown() (err error) { @@ -40,6 +41,10 @@ func Shutdown() (err error) { return nil } + if db.IsClosed() { + return nil + } + if err = db.Close(); check(err) { return } @@ -56,66 +61,28 @@ func Txn(tx func(txn *badger.Txn) error, update bool) error { return tx(txn) } -var ( - running sync.Mutex -) +func attempt_unlock() bool { -func open() { + if noKeyProvided { + return false + } var err error + log.I.Ln("attempting to unlock database") + + opts = badger.DefaultOptions(viper.GetString(storeFilePathFlag)) + opts.Logger = nil + opts.IndexCacheSize = 128 << 20 opts.EncryptionKey = key.Bytes() if db, err = badger.Open(opts); check(err) { startupErrors <- err - return + return false } - log.I.Ln("successfully opened database") - log.I.Ln("storage is ready") + log.I.Ln("successfully unlocked database") + isUnlockedChan <- true - isReadyChan <- true -} - -func Run(ctx context.Context) { - - if !running.TryLock() { - return - } - - configure() - - opts = badger.DefaultOptions(viper.GetString(storeFilePathFlag)) - opts.IndexCacheSize = 128 << 20 - opts.Logger = nil - - if !isLocked { - - log.I.Ln("attempting to open database with key") - - open() - } - - isLockedChan <- true - - lockedCtx, cancel := context.WithCancel(context.Background()) - - interrupt.AddHandler(cancel) - - for { - select { - case <-IsReady(): - log.I.Ln("storage is ready") - - //case <-unlock.IsSuccessful(): - // - // log.I.Ln("storage successfully unlocked") - // - // isReadyChan <- true - - case <-lockedCtx.Done(): - Shutdown() - return - } - } + return true } diff --git a/pkg/storage/signals.go b/pkg/storage/signals.go new file mode 100644 index 00000000..07cedd13 --- /dev/null +++ b/pkg/storage/signals.go @@ -0,0 +1,24 @@ +package storage + +var ( + startupErrors = make(chan error, 128) + isLockedChan = make(chan bool, 1) + isUnlockedChan = make(chan bool, 1) + isReadyChan = make(chan bool, 1) +) + +func CantStart() chan error { + return startupErrors +} + +func WhenIsLocked() chan bool { + return isLockedChan +} + +func WhenIsUnlocked() chan bool { + return isUnlockedChan +} + +func WhenIsReady() chan bool { + return isReadyChan +}