Add "network delete" service command

And add all the infrastructure required to stop and delete networks.

References: https://todo.sr.ht/~emersion/soju/17
This commit is contained in:
Simon Ser 2020-04-01 15:40:20 +02:00
parent 1f11976e7a
commit 96039320b6
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48
3 changed files with 101 additions and 4 deletions

23
db.go
View File

@ -215,6 +215,29 @@ func (db *DB) StoreNetwork(username string, network *Network) error {
return err return err
} }
func (db *DB) DeleteNetwork(id int64) error {
db.lock.Lock()
defer db.lock.Unlock()
tx, err := db.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
_, err = tx.Exec("DELETE FROM Network WHERE id = ?", id)
if err != nil {
return err
}
_, err = tx.Exec("DELETE FROM Channel WHERE network = ?", id)
if err != nil {
return err
}
return tx.Commit()
}
func (db *DB) ListChannels(networkID int64) ([]Channel, error) { func (db *DB) ListChannels(networkID int64) ([]Channel, error) {
db.lock.RLock() db.lock.RLock()
defer db.lock.RUnlock() defer db.lock.RUnlock()

View File

@ -97,6 +97,11 @@ func init() {
desc: "show a list of saved networks and their current status", desc: "show a list of saved networks and their current status",
handle: handleServiceNetworkStatus, handle: handleServiceNetworkStatus,
}, },
"delete": {
usage: "<name>",
desc: "delete a network",
handle: handleServiceNetworkDelete,
},
}, },
}, },
} }
@ -143,9 +148,14 @@ func handleServiceHelp(dc *downstreamConn, params []string) error {
return nil return nil
} }
func handleServiceCreateNetwork(dc *downstreamConn, params []string) error { func newFlagSet() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError) fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.SetOutput(ioutil.Discard) fs.SetOutput(ioutil.Discard)
return fs
}
func handleServiceCreateNetwork(dc *downstreamConn, params []string) error {
fs := newFlagSet()
addr := fs.String("addr", "", "") addr := fs.String("addr", "", "")
name := fs.String("name", "", "") name := fs.String("name", "", "")
username := fs.String("username", "", "") username := fs.String("username", "", "")
@ -177,7 +187,7 @@ func handleServiceCreateNetwork(dc *downstreamConn, params []string) error {
return fmt.Errorf("could not create network: %v", err) return fmt.Errorf("could not create network: %v", err)
} }
sendServicePRIVMSG(dc, fmt.Sprintf("created network %s successfully", network.GetName())) sendServicePRIVMSG(dc, fmt.Sprintf("created network %q", network.GetName()))
return nil return nil
} }
@ -204,3 +214,21 @@ func handleServiceNetworkStatus(dc *downstreamConn, params []string) error {
}) })
return nil return nil
} }
func handleServiceNetworkDelete(dc *downstreamConn, params []string) error {
if len(params) != 1 {
return fmt.Errorf("expected exactly one argument")
}
net := dc.user.getNetwork(params[0])
if net == nil {
return fmt.Errorf("unknown network %q", params[0])
}
if err := dc.user.deleteNetwork(net.ID); err != nil {
return err
}
sendServicePRIVMSG(dc, fmt.Sprintf("deleted network %q", net.GetName()))
return nil
}

50
user.go
View File

@ -37,8 +37,9 @@ type eventDownstreamDisconnected struct {
type network struct { type network struct {
Network Network
user *user user *user
ring *Ring ring *Ring
stopped chan struct{}
lock sync.Mutex lock sync.Mutex
conn *upstreamConn conn *upstreamConn
@ -50,6 +51,7 @@ func newNetwork(user *user, record *Network) *network {
Network: *record, Network: *record,
user: user, user: user,
ring: NewRing(user.srv.RingCap), ring: NewRing(user.srv.RingCap),
stopped: make(chan struct{}),
history: make(map[string]uint64), history: make(map[string]uint64),
} }
} }
@ -57,6 +59,13 @@ func newNetwork(user *user, record *Network) *network {
func (net *network) run() { func (net *network) run() {
var lastTry time.Time var lastTry time.Time
for { for {
select {
case <-net.stopped:
return
default:
// This space is intentionally left blank
}
if dur := time.Now().Sub(lastTry); dur < retryConnectMinDelay { if dur := time.Now().Sub(lastTry); dur < retryConnectMinDelay {
delay := retryConnectMinDelay - dur delay := retryConnectMinDelay - dur
net.user.srv.Logger.Printf("waiting %v before trying to reconnect to %q", delay.Truncate(time.Second), net.Addr) net.user.srv.Logger.Printf("waiting %v before trying to reconnect to %q", delay.Truncate(time.Second), net.Addr)
@ -92,6 +101,19 @@ func (net *network) upstream() *upstreamConn {
return net.conn return net.conn
} }
func (net *network) Stop() {
select {
case <-net.stopped:
return
default:
close(net.stopped)
}
if uc := net.upstream(); uc != nil {
uc.Close()
}
}
type user struct { type user struct {
User User
srv *Server srv *Server
@ -265,3 +287,27 @@ func (u *user) createNetwork(net *Network) (*network, error) {
go network.run() go network.run()
return network, nil return network, nil
} }
func (u *user) deleteNetwork(id int64) error {
for i, net := range u.networks {
if net.ID != id {
continue
}
if err := u.srv.db.DeleteNetwork(net.ID); err != nil {
return err
}
u.forEachDownstream(func(dc *downstreamConn) {
if dc.network != nil && dc.network == net {
dc.Close()
}
})
net.Stop()
u.networks = append(u.networks[:i], u.networks[i+1:]...)
return nil
}
panic("tried deleting a non-existing network")
}