summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/tfstated/lock.go49
-rw-r--r--cmd/tfstated/post.go11
-rw-r--r--cmd/tfstated/routes.go2
-rw-r--r--cmd/tfstated/unlock.go38
4 files changed, 98 insertions, 2 deletions
diff --git a/cmd/tfstated/lock.go b/cmd/tfstated/lock.go
new file mode 100644
index 0000000..e34c5b5
--- /dev/null
+++ b/cmd/tfstated/lock.go
@@ -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)
+ }
+ })
+}
diff --git a/cmd/tfstated/post.go b/cmd/tfstated/post.go
index b88109e..1d570b3 100644
--- a/cmd/tfstated/post.go
+++ b/cmd/tfstated/post.go
@@ -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)
}
diff --git a/cmd/tfstated/routes.go b/cmd/tfstated/routes.go
index da46078..e2700d2 100644
--- a/cmd/tfstated/routes.go
+++ b/cmd/tfstated/routes.go
@@ -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))
}
diff --git a/cmd/tfstated/unlock.go b/cmd/tfstated/unlock.go
new file mode 100644
index 0000000..af5ad57
--- /dev/null
+++ b/cmd/tfstated/unlock.go
@@ -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)
+ }
+ })
+}