Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sys v0.44.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.73.4 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.53.0 // indirect
)
21 changes: 21 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gophercloud/gophercloud/v2 v2.12.0 h1:Gxmc/Bog1UDKkxTcQW7MSPTDviJXpLeEgVeN5KrxoCo=
github.com/gophercloud/gophercloud/v2 v2.12.0/go.mod h1:H7TTOxbLy8RIaHSNhI2GCrWIzw4Xpw8Xn2mBhCUT5kA=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/libc v1.73.4 h1:+ra4Ui8ngyt8HDcO1FTDPWlkAh6yOdaO2yAoh8MddQA=
modernc.org/libc v1.73.4/go.mod h1:DXZ3eO8qMCNn2SnmTNCiC71nJ9Rcq3PsnpU6Vc4rWK8=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.53.0 h1:20WG8N9q4ji/dEqGk4uiI0c6OPjSeLTNYGFCc3+7c1M=
modernc.org/sqlite v1.53.0/go.mod h1:xoEpOIpGrgT48H5iiyt/YXPCZPEzlfmfFwtk8Lklw8s=
260 changes: 260 additions & 0 deletions internal/app/compute/sqlite_server_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package compute

import (
"database/sql"
"encoding/json"
"errors"
"fmt"

_ "modernc.org/sqlite"
)

type SQLiteServerRepository struct {
db *sql.DB
}

func OpenSQLiteServerRepository(
dataSourceName string,
) (*SQLiteServerRepository, error) {
db, err := sql.Open("sqlite", dataSourceName)
if err != nil {
return nil, err
}

repository := &SQLiteServerRepository{db: db}
if err := repository.Init(); err != nil {
_ = db.Close()
return nil, err
}

return repository, nil
}

func (r *SQLiteServerRepository) Init() error {
_, err := r.db.Exec(`
CREATE TABLE IF NOT EXISTS compute_servers (
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
image_id TEXT NOT NULL,
flavor_id TEXT NOT NULL,
tenant_id TEXT NOT NULL,
user_id TEXT NOT NULL,
status TEXT NOT NULL,
progress INTEGER NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
metadata_json TEXT NOT NULL
)`)

return err
}

func (r *SQLiteServerRepository) Close() error {
return r.db.Close()
}

func (r *SQLiteServerRepository) Create(server Server) Server {
metadataJSON := marshalStringMap(server.Metadata)

_, err := r.db.Exec(
`INSERT INTO compute_servers (
id,
name,
image_id,
flavor_id,
tenant_id,
user_id,
status,
progress,
created_at,
updated_at,
metadata_json
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
server.ID,
server.Name,
server.ImageID,
server.FlavorID,
server.TenantID,
server.UserID,
server.Status,
server.Progress,
server.CreatedAt,
server.UpdatedAt,
metadataJSON,
)
if err != nil {
panic(fmt.Errorf("insert server: %w", err))
}

return server
}

func (r *SQLiteServerRepository) List() []Server {
rows, err := r.db.Query(`
SELECT
id,
name,
image_id,
flavor_id,
tenant_id,
user_id,
status,
progress,
created_at,
updated_at,
metadata_json
FROM compute_servers
ORDER BY sequence`)
if err != nil {
panic(fmt.Errorf("list servers: %w", err))
}
defer rows.Close()

servers := []Server{}
for rows.Next() {
server, err := scanServer(rows)
if err != nil {
panic(fmt.Errorf("scan server: %w", err))
}
servers = append(servers, server)
}
if err := rows.Err(); err != nil {
panic(fmt.Errorf("iterate servers: %w", err))
}

return servers
}

func (r *SQLiteServerRepository) Get(id string) (Server, error) {
row := r.db.QueryRow(`
SELECT
id,
name,
image_id,
flavor_id,
tenant_id,
user_id,
status,
progress,
created_at,
updated_at,
metadata_json
FROM compute_servers
WHERE id = ?`, id)

server, err := scanServer(row)
if errors.Is(err, sql.ErrNoRows) {
return Server{}, ErrServerNotFound
}
if err != nil {
return Server{}, err
}

return server, nil
}

func (r *SQLiteServerRepository) Update(server Server) (Server, error) {
metadataJSON := marshalStringMap(server.Metadata)
result, err := r.db.Exec(
`UPDATE compute_servers
SET name = ?,
image_id = ?,
flavor_id = ?,
tenant_id = ?,
user_id = ?,
status = ?,
progress = ?,
created_at = ?,
updated_at = ?,
metadata_json = ?
WHERE id = ?`,
server.Name,
server.ImageID,
server.FlavorID,
server.TenantID,
server.UserID,
server.Status,
server.Progress,
server.CreatedAt,
server.UpdatedAt,
metadataJSON,
server.ID,
)
if err != nil {
return Server{}, err
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return Server{}, err
}
if rowsAffected == 0 {
return Server{}, ErrServerNotFound
}

return server, nil
}

func (r *SQLiteServerRepository) Delete(id string) error {
result, err := r.db.Exec(`DELETE FROM compute_servers WHERE id = ?`, id)
if err != nil {
return err
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
return ErrServerNotFound
}

return nil
}

func (r *SQLiteServerRepository) Reset() {
if _, err := r.db.Exec(`DELETE FROM compute_servers`); err != nil {
panic(fmt.Errorf("reset servers: %w", err))
}
}

type serverScanner interface {
Scan(dest ...any) error
}

func scanServer(scanner serverScanner) (Server, error) {
var server Server
var metadataJSON string

if err := scanner.Scan(
&server.ID,
&server.Name,
&server.ImageID,
&server.FlavorID,
&server.TenantID,
&server.UserID,
&server.Status,
&server.Progress,
&server.CreatedAt,
&server.UpdatedAt,
&metadataJSON,
); err != nil {
return Server{}, err
}

if err := json.Unmarshal([]byte(metadataJSON), &server.Metadata); err != nil {
return Server{}, err
}

return server, nil
}

func marshalStringMap(value map[string]string) string {
data, err := json.Marshal(value)
if err != nil {
panic(fmt.Errorf("marshal string map: %w", err))
}

return string(data)
}
Loading
Loading