feat(tfstated): implement states locking
This commit is contained in:
parent
8949cee8b3
commit
3d74311931
7 changed files with 174 additions and 13 deletions
49
cmd/tfstated/lock.go
Normal file
49
cmd/tfstated/lock.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.adyxax.org/adyxax/tfstated/pkg/database"
|
||||
)
|
||||
|
||||
type lockRequest struct {
|
||||
Created time.Time `json:"Created"`
|
||||
ID string `json:"ID"`
|
||||
Info string `json:"Info"`
|
||||
Operation string `json:"Operation"`
|
||||
Path string `json:"Path"`
|
||||
Version string `json:"Version"`
|
||||
Who string `json:"Who"`
|
||||
}
|
||||
|
||||
func handleLock(db *database.DB) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
_ = encode(w, http.StatusBadRequest,
|
||||
fmt.Errorf("no state path provided, cannot LOCK /"))
|
||||
return
|
||||
}
|
||||
|
||||
var lock lockRequest
|
||||
if err := decode(r, &lock); err != nil {
|
||||
_ = encode(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if success, err := db.SetLockOrGetExistingLock(r.URL.Path, &lock); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
_ = encode(w, http.StatusNotFound,
|
||||
fmt.Errorf("state path not found: %s", r.URL.Path))
|
||||
} else {
|
||||
_ = errorResponse(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
} else if success {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
_ = encode(w, http.StatusConflict, lock)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -16,13 +16,20 @@ func handlePost(db *database.DB) http.Handler {
|
|||
)
|
||||
return
|
||||
}
|
||||
|
||||
id := r.URL.Query().Get("ID")
|
||||
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
_ = errorResponse(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if err := db.SetState(r.URL.Path, data); err != nil {
|
||||
_ = errorResponse(w, http.StatusInternalServerError, err)
|
||||
if idMismatch, err := db.SetState(r.URL.Path, data, id); err != nil {
|
||||
if idMismatch {
|
||||
_ = errorResponse(w, http.StatusConflict, err)
|
||||
} else {
|
||||
_ = errorResponse(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
|
|
@ -14,5 +14,7 @@ func addRoutes(
|
|||
|
||||
mux.Handle("DELETE /", handleDelete(db))
|
||||
mux.Handle("GET /", handleGet(db))
|
||||
mux.Handle("LOCK /", handleLock(db))
|
||||
mux.Handle("POST /", handlePost(db))
|
||||
mux.Handle("UNLOCK /", handleUnlock(db))
|
||||
}
|
||||
|
|
38
cmd/tfstated/unlock.go
Normal file
38
cmd/tfstated/unlock.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.adyxax.org/adyxax/tfstated/pkg/database"
|
||||
)
|
||||
|
||||
func handleUnlock(db *database.DB) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
_ = encode(w, http.StatusBadRequest,
|
||||
fmt.Errorf("no state path provided, cannot LOCK /"))
|
||||
return
|
||||
}
|
||||
|
||||
var lock lockRequest
|
||||
if err := decode(r, &lock); err != nil {
|
||||
_ = encode(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if success, err := db.Unlock(r.URL.Path, &lock); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
_ = encode(w, http.StatusNotFound,
|
||||
fmt.Errorf("state path not found: %s", r.URL.Path))
|
||||
} else {
|
||||
_ = errorResponse(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
} else if success {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
_ = encode(w, http.StatusConflict, lock)
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue