Skip to content

Commit 62b596e

Browse files
committed
Allow client to set the snapshot id.
- ID values are constrained by the following regex "^([a-z0-9\\-]+)$".
1 parent 88190ff commit 62b596e

File tree

7 files changed

+52
-27
lines changed

7 files changed

+52
-27
lines changed

e2etests/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,7 @@ require (
6565
)
6666

6767
replace github.com/google/android-cuttlefish/frontend/src/libhoclient => ../frontend/src/libhoclient
68+
6869
replace github.com/google/android-cuttlefish/frontend/src/liboperator => ../frontend/src/liboperator
70+
71+
replace github.com/google/android-cuttlefish/frontend/src/host_orchestrator => ../frontend/src/host_orchestrator

e2etests/orchestration/snapshot_test/main_test.go

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"errors"
2020
"fmt"
2121
"log"
22-
"net/http"
2322
"testing"
2423

2524
"github.com/google/android-cuttlefish/e2etests/orchestration/common"
@@ -31,7 +30,7 @@ import (
3130

3231
func TestSnapshot(t *testing.T) {
3332
t.Skip("b/403233780: Skipping snapshot tests due to out of disk issues on standard runners.")
34-
33+
3534
ctx, err := common.Setup()
3635
if err != nil {
3736
t.Fatal(err)
@@ -67,11 +66,15 @@ func TestSnapshot(t *testing.T) {
6766
if _, err := dh.ExecADBShellCommand(cID, adbBin, cvd.ADBSerial, []string{"touch", tmpFile}); err != nil {
6867
t.Fatal(err)
6968
}
69+
snapshotID := "00000000-0000-0000-0000-000000000000"
7070
// Create a snapshot containing the temporary file.
71-
createSnapshotRes, err := createSnapshot(ctx.ServiceURL, groupName, cvd.Name)
71+
createSnapshotRes, err := srv.CreateSnapshot(groupName, cvd.Name, &hoapi.CreateSnapshotRequest{SnapshotID: snapshotID})
7272
if err != nil {
7373
t.Fatal(err)
7474
}
75+
if diff := cmp.Diff(snapshotID, createSnapshotRes.SnapshotID); diff != "" {
76+
t.Fatalf("snapshot id mismatch (-want +got):\n%s", diff)
77+
}
7578
// Powerwash the device removing the temporary file.
7679
if err := srv.Powerwash(groupName, cvd.Name); err != nil {
7780
t.Fatal(err)
@@ -165,26 +168,6 @@ func createDevice(srv hoclient.HostOrchestratorService, group_name, artifactsDir
165168
return res.CVDs[0], nil
166169
}
167170

168-
// TODO(b/370550070) Remove once this method is added to the client implementation.
169-
func createSnapshot(srvURL, group, name string) (*hoapi.CreateSnapshotResponse, error) {
170-
helper := hoclient.HTTPHelper{
171-
Client: http.DefaultClient,
172-
RootEndpoint: srvURL,
173-
}
174-
op := &hoapi.Operation{}
175-
path := fmt.Sprintf("/cvds/%s/%s/snapshots", group, name)
176-
rb := helper.NewPostRequest(path, nil)
177-
if err := rb.JSONResDo(op); err != nil {
178-
return nil, err
179-
}
180-
srv := hoclient.NewHostOrchestratorService(srvURL)
181-
res := &hoapi.CreateSnapshotResponse{}
182-
if err := srv.WaitForOperation(op.Name, res); err != nil {
183-
return nil, err
184-
}
185-
return res, nil
186-
}
187-
188171
func getCVD(srv hoclient.HostOrchestratorService) (*hoapi.CVD, error) {
189172
cvds, err := srv.ListCVDs()
190173
if err != nil {

frontend/src/host_orchestrator/api/v1/messages.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ type ListUploadDirectoriesResponse struct {
134134
Items []*UploadDirectory `json:"items"`
135135
}
136136

137+
type CreateSnapshotRequest struct {
138+
// [Optional]
139+
// Value must match regex: "^([a-z0-9\\-]+)$".
140+
SnapshotID string `json:"snapshot_id,omitempty"`
141+
}
142+
137143
type CreateSnapshotResponse struct {
138144
SnapshotID string `json:"snapshot_id"`
139145
}

frontend/src/host_orchestrator/orchestrator/controller.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,16 @@ func newCreateSnapshotHandler(c Config, om OperationManager) *createSnapshotHand
294294
}
295295

296296
func (h *createSnapshotHandler) Handle(r *http.Request) (interface{}, error) {
297+
req := &apiv1.CreateSnapshotRequest{}
298+
err := json.NewDecoder(r.Body).Decode(req)
299+
if err != nil {
300+
return nil, operator.NewBadRequestError("malformed JSON in request", err)
301+
}
297302
vars := mux.Vars(r)
298303
group := vars["group"]
299304
name := vars["name"]
300305
opts := CreateSnapshotActionOpts{
306+
Request: req,
301307
Selector: cvd.Selector{Group: group, Instance: name},
302308
Paths: h.Config.Paths,
303309
OperationManager: h.OM,

frontend/src/host_orchestrator/orchestrator/createsnapshotaction.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
package orchestrator
1616

1717
import (
18+
"fmt"
1819
"log"
1920
"os/user"
2021
"path/filepath"
22+
"regexp"
2123

2224
apiv1 "github.com/google/android-cuttlefish/frontend/src/host_orchestrator/api/v1"
2325
"github.com/google/android-cuttlefish/frontend/src/host_orchestrator/orchestrator/cvd"
@@ -26,6 +28,7 @@ import (
2628
)
2729

2830
type CreateSnapshotActionOpts struct {
31+
Request *apiv1.CreateSnapshotRequest
2932
Selector cvd.Selector
3033
Paths IMPaths
3134
OperationManager OperationManager
@@ -34,6 +37,7 @@ type CreateSnapshotActionOpts struct {
3437
}
3538

3639
type CreateSnapshotAction struct {
40+
req *apiv1.CreateSnapshotRequest
3741
selector cvd.Selector
3842
paths IMPaths
3943
om OperationManager
@@ -42,6 +46,7 @@ type CreateSnapshotAction struct {
4246

4347
func NewCreateSnapshotAction(opts CreateSnapshotActionOpts) *CreateSnapshotAction {
4448
return &CreateSnapshotAction{
49+
req: opts.Request,
4550
selector: opts.Selector,
4651
paths: opts.Paths,
4752
om: opts.OperationManager,
@@ -50,6 +55,9 @@ func NewCreateSnapshotAction(opts CreateSnapshotActionOpts) *CreateSnapshotActio
5055
}
5156

5257
func (a *CreateSnapshotAction) Run() (apiv1.Operation, error) {
58+
if err := a.validateRequest(); err != nil {
59+
return apiv1.Operation{}, err
60+
}
5361
if err := createDir(a.paths.SnapshotsRootDir); err != nil {
5462
return apiv1.Operation{}, err
5563
}
@@ -64,12 +72,29 @@ func (a *CreateSnapshotAction) Run() (apiv1.Operation, error) {
6472
return op, nil
6573
}
6674

75+
var snapshotIDRegex = regexp.MustCompile(`^([a-z0-9\-]+)$`)
76+
77+
func (a *CreateSnapshotAction) validateRequest() error {
78+
if a.req.SnapshotID != "" {
79+
if !snapshotIDRegex.MatchString(a.req.SnapshotID) {
80+
return operator.NewBadRequestError(
81+
"invalid snapshot id value", fmt.Errorf("%s does not match %s", a.req.SnapshotID, snapshotIDRegex))
82+
}
83+
}
84+
return nil
85+
}
86+
6787
func (a *CreateSnapshotAction) createSnapshot(op apiv1.Operation) (*apiv1.CreateSnapshotResponse, error) {
6888
if err := a.cvdCLI.Suspend(a.selector); err != nil {
6989
return nil, operator.NewInternalError("cvd suspend failed", err)
7090
}
71-
snapshotID := op.Name
91+
snapshotID := a.req.SnapshotID
92+
if snapshotID == "" {
93+
snapshotID = op.Name
94+
}
7295
dir := filepath.Join(a.paths.SnapshotsRootDir, snapshotID)
96+
// snapshot_util_cvd makes sure the directory is not being used before.
97+
// https://github.com/google/android-cuttlefish/blob/7fb47855a2c937e4531044437616bd82e11e3b2e/base/cvd/cuttlefish/host/commands/snapshot_util_cvd/main.cc#L97
7398
if err := a.cvdCLI.TakeSnapshot(a.selector, dir); err != nil {
7499
return nil, operator.NewInternalError("cvd snapshot_take failed", err)
75100
}

frontend/src/libhoclient/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ require (
4747
)
4848

4949
replace github.com/google/android-cuttlefish/frontend/src/liboperator => ../liboperator
50+
51+
replace github.com/google/android-cuttlefish/frontend/src/host_orchestrator => ../host_orchestrator

frontend/src/libhoclient/host_orchestrator_client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ type HostOrchestratorService interface {
124124
Start(groupName, instanceName string, req *hoapi.StartCVDRequest) error
125125

126126
// Create device snapshot.
127-
CreateSnapshot(groupName, instanceName string) (*hoapi.CreateSnapshotResponse, error)
127+
CreateSnapshot(groupName, instanceName string, req *hoapi.CreateSnapshotRequest) (*hoapi.CreateSnapshotResponse, error)
128128
}
129129

130130
const (
@@ -429,9 +429,9 @@ func (c *HostOrchestratorServiceImpl) Start(groupName, instanceName string, req
429429
return c.doEmptyResponseRequest(rb)
430430
}
431431

432-
func (c *HostOrchestratorServiceImpl) CreateSnapshot(groupName, instanceName string) (*hoapi.CreateSnapshotResponse, error) {
432+
func (c *HostOrchestratorServiceImpl) CreateSnapshot(groupName, instanceName string, req *hoapi.CreateSnapshotRequest) (*hoapi.CreateSnapshotResponse, error) {
433433
path := fmt.Sprintf("/cvds/%s/%s/snapshots", groupName, instanceName)
434-
rb := c.HTTPHelper.NewPostRequest(path, nil)
434+
rb := c.HTTPHelper.NewPostRequest(path, req)
435435
op := &hoapi.Operation{}
436436
if err := rb.JSONResDo(op); err != nil {
437437
return nil, err

0 commit comments

Comments
 (0)