eintopf.info@v0.13.16/service/killswitch/store.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package killswitch 17 18 import ( 19 "context" 20 "fmt" 21 "log" 22 "os" 23 "sync" 24 "time" 25 ) 26 27 func NewMemoryStore() *MemoryStore { 28 return &MemoryStore{on: true, rwm: &sync.RWMutex{}} 29 } 30 31 type MemoryStore struct { 32 on bool 33 rwm *sync.RWMutex 34 } 35 36 func (m *MemoryStore) SetState(ctx context.Context, on bool) error { 37 m.rwm.Lock() 38 m.on = on 39 m.rwm.Unlock() 40 return nil 41 } 42 43 func (m *MemoryStore) GetState(ctx context.Context) (bool, error) { 44 m.rwm.RLock() 45 on := m.on 46 m.rwm.RUnlock() 47 return on, nil 48 } 49 50 func NewFileStore(filePath string, watcherRate time.Duration) *FileStore { 51 store := &FileStore{ 52 filePath: filePath, 53 watcherRate: watcherRate, 54 55 memoryStore: NewMemoryStore(), 56 stop: make(chan bool), 57 } 58 59 go store.watchForFileChanges() 60 61 return store 62 } 63 64 type FileStore struct { 65 filePath string 66 watcherRate time.Duration 67 memoryStore *MemoryStore 68 stop chan bool 69 } 70 71 func (f *FileStore) SetState(ctx context.Context, on bool) error { 72 f.memoryStore.SetState(ctx, on) 73 if on { 74 if _, err := os.Stat(f.filePath); err == nil { 75 if err := os.Remove(f.filePath); err != nil { 76 return fmt.Errorf("remove killswitch file: %s", err) 77 } 78 } 79 } else { 80 if err := os.WriteFile(f.filePath, []byte{}, 0644); err != nil { 81 return fmt.Errorf("write killswitch file: %s", err) 82 } 83 } 84 return nil 85 } 86 87 func (f *FileStore) GetState(ctx context.Context) (bool, error) { 88 return f.memoryStore.GetState(ctx) 89 } 90 91 func (f *FileStore) Stop() { 92 close(f.stop) 93 } 94 95 func (f *FileStore) watchForFileChanges() { 96 ticker := time.NewTicker(f.watcherRate) 97 defer func() { ticker.Stop() }() 98 99 for { 100 select { 101 case <-f.stop: 102 return 103 case <-ticker.C: 104 if _, err := os.Stat(f.filePath); err == nil { 105 f.memoryStore.SetState(context.Background(), false) 106 } else if os.IsNotExist(err) { 107 f.memoryStore.SetState(context.Background(), true) 108 } else { 109 log.Printf("killswitch: fileWatcher: %s\n", err) 110 } 111 } 112 } 113 }