|
@@ -28,12 +28,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
package kyoketsu
|
|
|
|
|
|
import (
|
|
|
- "context"
|
|
|
- "fmt"
|
|
|
- "time"
|
|
|
+ "database/sql"
|
|
|
+ "errors"
|
|
|
|
|
|
- "go.mongodb.org/mongo-driver/mongo"
|
|
|
- "go.mongodb.org/mongo-driver/mongo/options"
|
|
|
+ "github.com/mattn/go-sqlite3"
|
|
|
)
|
|
|
|
|
|
type TopologyDatabaseIO interface {
|
|
@@ -42,40 +40,144 @@ type TopologyDatabaseIO interface {
|
|
|
for an appropriate implementation of the data storage that the distributed system will use.
|
|
|
When I get around to implementing the client-to-client format of this, it could be anything.
|
|
|
*/
|
|
|
- AddHostToDb(*Host) error // Add a host to the hosts table
|
|
|
- UpdateHostEntry(string, *Host) error //Update a host entry, indexing by its ip address
|
|
|
- RemoveHostEntry(string) error // Remove a host from the database
|
|
|
+ Migrate() error
|
|
|
+ Create(host Host) (*Host, error)
|
|
|
+ All() ([]Host, error)
|
|
|
+ GetByFqdn(dn string) (*Host, error)
|
|
|
+ Update(id int64, updated Host) (*Host, error)
|
|
|
+ Delete(id int64) error
|
|
|
}
|
|
|
|
|
|
-type MongoClient struct {
|
|
|
- conn *mongo.Client
|
|
|
+var (
|
|
|
+ ErrDuplicate = errors.New("record already exists")
|
|
|
+ ErrNotExists = errors.New("row not exists")
|
|
|
+ ErrUpdateFailed = errors.New("update failed")
|
|
|
+ ErrDeleteFailed = errors.New("delete failed")
|
|
|
+)
|
|
|
+
|
|
|
+type SQLiteRepo struct {
|
|
|
+ db *sql.DB
|
|
|
}
|
|
|
|
|
|
-func NewMongoClient(host string, port int) *MongoClient {
|
|
|
+// Instantiate a new SQLiteRepo struct
|
|
|
+func NewSQLiteRepo(db *sql.DB) *SQLiteRepo {
|
|
|
+ return &SQLiteRepo{
|
|
|
+ db: db,
|
|
|
+ }
|
|
|
|
|
|
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
|
- defer cancel()
|
|
|
- client, err := mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%v", host, port)))
|
|
|
- defer func() {
|
|
|
- if err = client.Disconnect(ctx); err != nil {
|
|
|
- panic(err)
|
|
|
+}
|
|
|
+
|
|
|
+// Creates a new SQL table with necessary data
|
|
|
+func (r *SQLiteRepo) Migrate() error {
|
|
|
+ query := `
|
|
|
+ CREATE TABLE IF NOT EXISTS hosts(
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
+ fqdn TEXT NOT NULL,
|
|
|
+ ipv4_address TEXT NOT NULL UNIQUE,
|
|
|
+ listening_port TEXT NOT NULL
|
|
|
+ );
|
|
|
+ `
|
|
|
+
|
|
|
+ _, err := r.db.Exec(query)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+Create an entry in the hosts table
|
|
|
+
|
|
|
+ :param host: a Host entry from a port scan
|
|
|
+*/
|
|
|
+func (r *SQLiteRepo) Create(host Host) (*Host, error) {
|
|
|
+ res, err := r.db.Exec("INSERT INTO hosts(fqdn, ipv4_address, listening_port) values(?,?,?)", host.Fqdn, host.IpAddress, host.PortString)
|
|
|
+ if err != nil {
|
|
|
+ var sqliteErr sqlite3.Error
|
|
|
+ if errors.As(err, &sqliteErr) {
|
|
|
+ if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
|
|
|
+ return nil, ErrDuplicate
|
|
|
+ }
|
|
|
}
|
|
|
- }()
|
|
|
- return &MongoClient{conn: client}
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ id, err := res.LastInsertId()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ host.Id = id
|
|
|
+
|
|
|
+ return &host, nil
|
|
|
}
|
|
|
|
|
|
-func (m *MongoClient) addDocument(id string, data interface{}) error {
|
|
|
- return nil
|
|
|
+// Get all Hosts from the host table
|
|
|
+func (r *SQLiteRepo) All() ([]Host, error) {
|
|
|
+ rows, err := r.db.Query("SELECT * FROM hosts")
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ defer rows.Close()
|
|
|
+
|
|
|
+ var all []Host
|
|
|
+ for rows.Next() {
|
|
|
+ var host Host
|
|
|
+ if err := rows.Scan(&host.Id, &host.Fqdn, &host.IpAddress, &host.PortString); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ all = append(all, host)
|
|
|
+ }
|
|
|
+ return all, nil
|
|
|
}
|
|
|
|
|
|
-func (m *MongoClient) AddHostToDb(host *Host) error {
|
|
|
- return nil
|
|
|
+// Get a record by its FQDN
|
|
|
+func (r *SQLiteRepo) GetByFqdn(dn string) (*Host, error) {
|
|
|
+ row := r.db.QueryRow("SELECT * FROM hosts WHERE fqdn = ?", dn)
|
|
|
+
|
|
|
+ var host Host
|
|
|
+ if err := row.Scan(&host.Id, &host.Fqdn, &host.IpAddress, &host.PortString); err != nil {
|
|
|
+ if errors.Is(err, sql.ErrNoRows) {
|
|
|
+ return nil, ErrNotExists
|
|
|
+ }
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &host, nil
|
|
|
}
|
|
|
|
|
|
-func (m *MongoClient) UpdateHostEntry(id string, host *Host) error {
|
|
|
- return nil
|
|
|
+// Update a record by its ID
|
|
|
+func (r *SQLiteRepo) Update(id int64, updated Host) (*Host, error) {
|
|
|
+ if id == 0 {
|
|
|
+ return nil, errors.New("invalid updated ID")
|
|
|
+ }
|
|
|
+ res, err := r.db.Exec("UPDATE hosts SET name = ?, url = ?, rank = ? WHERE id = ?", updated.Fqdn, updated.IpAddress, updated.PortString, id)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ rowsAffected, err := res.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if rowsAffected == 0 {
|
|
|
+ return nil, ErrUpdateFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ return &updated, nil
|
|
|
}
|
|
|
|
|
|
-func (m *MongoClient) RemoveHostEntry(id string) error {
|
|
|
- return nil
|
|
|
+// Delete a record by its ID
|
|
|
+func (r *SQLiteRepo) Delete(id int64) error {
|
|
|
+ res, err := r.db.Exec("DELETE FROM hosts WHERE id = ?", id)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ rowsAffected, err := res.RowsAffected()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if rowsAffected == 0 {
|
|
|
+ return ErrDeleteFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ return err
|
|
|
}
|