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  }