-
Notifications
You must be signed in to change notification settings - Fork 0
Store layer consolidation #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: codex/04-sqlite-storage-baseline
Are you sure you want to change the base?
Changes from all commits
4632596
c78aba9
65d0b96
4908429
3b4776a
985585a
45e072e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package image | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
|
|
||
| appimage "github.com/JSYoo5B/SandStack/internal/app/image" | ||
| ) | ||
|
|
||
| type imagePatchOperation struct { | ||
| Op string `json:"op"` | ||
| Path string `json:"path"` | ||
| Value json.RawMessage `json:"value"` | ||
| } | ||
|
|
||
| func toPatchImage(operations []imagePatchOperation) (appimage.PatchImage, error) { | ||
| var patch appimage.PatchImage | ||
| for _, operation := range operations { | ||
| if operation.Op != "replace" { | ||
| return appimage.PatchImage{}, fmt.Errorf("unsupported patch operation") | ||
| } | ||
|
|
||
| if err := applyPatchOperation(&patch, operation); err != nil { | ||
| return appimage.PatchImage{}, err | ||
| } | ||
| } | ||
|
|
||
| return patch, nil | ||
| } | ||
|
|
||
| func applyPatchOperation( | ||
| patch *appimage.PatchImage, | ||
| operation imagePatchOperation, | ||
| ) error { | ||
| switch operation.Path { | ||
| case "/name": | ||
| var value string | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.Name = &value | ||
| case "/min_disk": | ||
| var value int | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.MinDisk = &value | ||
| case "/min_ram": | ||
| var value int | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.MinRAM = &value | ||
| case "/protected": | ||
| var value bool | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.Protected = &value | ||
| case "/visibility": | ||
| var value string | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.Visibility = &value | ||
| case "/tags": | ||
| var value []string | ||
| if err := json.Unmarshal(operation.Value, &value); err != nil { | ||
| return err | ||
| } | ||
| patch.Tags = value | ||
| patch.HasTags = true | ||
| default: | ||
| return fmt.Errorf("unsupported patch path") | ||
| } | ||
|
|
||
| return nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,13 @@ import ( | |
| appnetwork "github.com/JSYoo5B/SandStack/internal/app/network" | ||
| "github.com/JSYoo5B/SandStack/internal/app/requestlog" | ||
| appvolume "github.com/JSYoo5B/SandStack/internal/app/volume" | ||
| "github.com/JSYoo5B/SandStack/internal/platform/clock" | ||
| "github.com/JSYoo5B/SandStack/internal/platform/config" | ||
| "github.com/JSYoo5B/SandStack/internal/platform/idgen" | ||
| storecompute "github.com/JSYoo5B/SandStack/internal/store/compute" | ||
| storeimage "github.com/JSYoo5B/SandStack/internal/store/image" | ||
| storenetwork "github.com/JSYoo5B/SandStack/internal/store/network" | ||
| storevolume "github.com/JSYoo5B/SandStack/internal/store/volume" | ||
| "github.com/go-chi/chi/v5" | ||
| ) | ||
|
|
||
|
|
@@ -25,10 +31,27 @@ func NewRouter(cfg config.Config) http.Handler { | |
| requests := requestlog.NewService() | ||
| router.Use(recordRequests(requests)) | ||
| identityHandler := identity.NewHandler(cfg) | ||
| computeService := appcompute.NewService() | ||
| imageService := appimage.NewService() | ||
| networkService := appnetwork.NewService() | ||
| volumeService := appvolume.NewService() | ||
| computeService := appcompute.NewServiceWithRuntime( | ||
| storecompute.NewMemoryServerRepository(), | ||
| clock.Wall(), | ||
| idgen.Random(), | ||
| ) | ||
| imageService := appimage.NewServiceWithRuntime( | ||
| storeimage.NewMemoryRepository(), | ||
| clock.Wall(), | ||
| idgen.Random(), | ||
| ) | ||
| networkService := appnetwork.NewServiceWithRepositories( | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Network has multiple repositories earlier than the other services because networks, subnets, and ports already have independent state. This is the representative wiring pattern for services that grow more than one resource store. |
||
| storenetwork.NewMemoryNetworkRepository(), | ||
| storenetwork.NewMemorySubnetRepository(), | ||
| storenetwork.NewMemoryPortRepository(), | ||
| idgen.Random(), | ||
| ) | ||
| volumeService := appvolume.NewServiceWithRuntime( | ||
| storevolume.NewMemoryRepository(), | ||
| clock.Wall(), | ||
| idgen.Random(), | ||
| ) | ||
|
|
||
| router.Mount("/_sandstack", admin.NewRouterWithState(func() { | ||
| computeService.Reset() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,31 @@ func (h Handler) getVolume(w http.ResponseWriter, r *http.Request) { | |
| }) | ||
| } | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update endpoints follow the existing handler convention rather than introducing a generic patch layer. That keeps this slice scoped to the compatibility calls currently needed. |
||
|
|
||
| func (h Handler) updateVolume(w http.ResponseWriter, r *http.Request) { | ||
| var request updateVolumeRequest | ||
| if err := json.NewDecoder(r.Body).Decode(&request); err != nil { | ||
| respond.Error(w, http.StatusBadRequest, "invalid JSON request body") | ||
| return | ||
| } | ||
|
|
||
| volume, err := h.service.Update( | ||
| chi.URLParam(r, "volume_id"), | ||
| request.updateVolume(), | ||
| ) | ||
| if errors.Is(err, appvolume.ErrVolumeNotFound) { | ||
| respond.Error(w, http.StatusNotFound, "volume not found") | ||
| return | ||
| } | ||
| if err != nil { | ||
| respond.Error(w, http.StatusInternalServerError, "volume update failed") | ||
| return | ||
| } | ||
|
|
||
| respond.JSON(w, http.StatusOK, volumeResponse{ | ||
| Volume: toVolumeDocument(volume), | ||
| }) | ||
| } | ||
|
|
||
| func (h Handler) deleteVolume(w http.ResponseWriter, r *http.Request) { | ||
| err := h.service.Delete(chi.URLParam(r, "volume_id")) | ||
| if errors.Is(err, appvolume.ErrVolumeNotFound) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This router now acts as the composition root for default in-memory services. The important dependency direction is API -> app interfaces plus store implementations supplied here, not app -> store concrete types.