Skip to content

Commit

Permalink
Properly handle call/query states.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed May 6, 2024
1 parent 32babe5 commit 2179652
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 70 deletions.
15 changes: 13 additions & 2 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,12 +443,23 @@ type Call struct {
data []byte
}

// Call calls a method on a canister, it does not wait for the result.
func (c Call) Call() error {
c.a.logger.Printf("[AGENT] CALL %s %s (%x)", c.effectiveCanisterID, c.methodName, c.requestID)
_, err := c.a.call(c.effectiveCanisterID, c.data)
return err
}

// CallAndWait calls a method on a canister and waits for the result.
func (c Call) CallAndWait(values ...any) error {
c.a.logger.Printf("[AGENT] CALL %s %s (%x)", c.effectiveCanisterID, c.methodName, c.requestID)
if _, err := c.a.call(c.effectiveCanisterID, c.data); err != nil {
if err := c.Call(); err != nil {
return err
}
return c.Wait(values...)
}

// Wait waits for the result of the call and unmarshals it into the given values.
func (c Call) Wait(values ...any) error {
raw, err := c.a.poll(c.effectiveCanisterID, c.requestID)
if err != nil {
return err
Expand Down
19 changes: 16 additions & 3 deletions cmd/goic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ var root = cmd.NewCommandFork(
Name: "packageName",
HasValue: true,
},
{
Name: "indirect",
HasValue: false,
},
},
func(args []string, options map[string]string) error {
inputPath := args[0]
Expand All @@ -89,7 +93,8 @@ var root = cmd.NewCommandFork(
packageName = p
}

return writeDID(canisterName, packageName, path, rawDID)
_, indirect := options["indirect"]
return writeDID(canisterName, packageName, path, rawDID, indirect)
},
),
cmd.NewCommand(
Expand All @@ -105,6 +110,10 @@ var root = cmd.NewCommandFork(
Name: "packageName",
HasValue: true,
},
{
Name: "indirect",
HasValue: false,
},
},
func(args []string, options map[string]string) error {
id := args[0]
Expand All @@ -128,7 +137,8 @@ var root = cmd.NewCommandFork(
packageName = p
}

return writeDID(canisterName, packageName, path, rawDID)
_, indirect := options["indirect"]
return writeDID(canisterName, packageName, path, rawDID, indirect)
},
),
),
Expand Down Expand Up @@ -156,11 +166,14 @@ func main() {
}
}

func writeDID(canisterName, packageName, outputPath string, rawDID []byte) error {
func writeDID(canisterName, packageName, outputPath string, rawDID []byte, indirect bool) error {
g, err := gen.NewGenerator("", canisterName, packageName, rawDID)
if err != nil {
return err
}
if indirect {
g.Indirect()
}
raw, err := g.Generate()
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion ic/testdata/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func main() {
log.Panic(err)
}
if name == "ic" {
g = g.Indirect()
g.Indirect()
}
raw, err := g.Generate()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pocketic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The client is not yet stable and is subject to change.
|| POST | /blobstore |
|| GET | /blobstore/{id} |
|| POST | /verify_signature |
| | GET | /read_graph/{state_label}/{op_id} |
| ✳️ | GET | /read_graph/{state_label}/{op_id} |
|| GET | /instances/ |
|| POST | /instances/ |
|| DELETE | /instances/{id} |
Expand Down
18 changes: 18 additions & 0 deletions pocketic/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ func (a Agent) HelloQuery(arg0 string) (*string, error) {
return &r0, nil
}

// HelloQueryQuery creates an indirect representation of the "helloQuery" method on the "hello" canister.
func (a Agent) HelloQueryQuery(arg0 string) (*agent.Query, error) {
return a.a.CreateQuery(
a.canisterId,
"helloQuery",
arg0,
)
}

// HelloUpdate calls the "helloUpdate" method on the "hello" canister.
func (a Agent) HelloUpdate(arg0 string) (*string, error) {
var r0 string
Expand All @@ -53,3 +62,12 @@ func (a Agent) HelloUpdate(arg0 string) (*string, error) {
}
return &r0, nil
}

// HelloUpdateCall creates an indirect representation of the "helloUpdate" method on the "hello" canister.
func (a Agent) HelloUpdateCall(arg0 string) (*agent.Call, error) {
return a.a.CreateCall(
a.canisterId,
"helloUpdate",
arg0,
)
}
1 change: 0 additions & 1 deletion pocketic/blobstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ func (pic PocketIC) GetBlob(blobID []byte) ([]byte, error) {
if err := pic.do(
http.MethodGet,
fmt.Sprintf("%s/blobstore/%s", pic.server.URL(), hex.EncodeToString(blobID)),
http.StatusOK,
nil,
&bytes,
); err != nil {
Expand Down
99 changes: 99 additions & 0 deletions pocketic/do.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package pocketic

import (
"encoding/json"
"fmt"
"net/http"
"time"
)

func (pic PocketIC) do(method, url string, input, output any) error {
start := time.Now()
for {
if pic.timeout < time.Since(start) {
return fmt.Errorf("timeout exceeded")
}

pic.logger.Printf("[POCKETIC] %s %s %+v", method, url, input)
req, err := newRequest(method, url, input)
if err != nil {
return err
}
resp, err := pic.client.Do(req)
if err != nil {
return err
}
switch resp.StatusCode {
case http.StatusOK, http.StatusCreated:
if resp.Body == nil || output == nil {
// No need to decode the response body.
return nil
}
return json.NewDecoder(resp.Body).Decode(output)
case http.StatusAccepted:
var response startedOrBusyResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return err
}
if method == http.MethodGet {
continue
}
for {
if pic.timeout < time.Since(start) {
return fmt.Errorf("timeout exceeded")
}

req, err := newRequest(
http.MethodGet,
fmt.Sprintf(
"%s/read_graph/%s/%s",
pic.server.URL(),
response.StateLabel,
response.OpID,
),
nil,
)
if err != nil {
return err
}
resp, err := pic.client.Do(req)
if err != nil {
return err
}
switch resp.StatusCode {
case http.StatusOK:
if resp.Body == nil || output == nil {
// No need to decode the response body.
return nil
}
return json.NewDecoder(resp.Body).Decode(output)
case http.StatusAccepted, http.StatusConflict:
default:
var errResp ErrorMessage
if err := json.NewDecoder(resp.Body).Decode(&errResp); err != nil {
return err
}
return errResp
}
}
case http.StatusConflict:
var response startedOrBusyResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return err
}
time.Sleep(pic.delay) // Retry after a short delay.
continue
default:
var errResp ErrorMessage
if err := json.NewDecoder(resp.Body).Decode(&errResp); err != nil {
return err
}
return errResp
}
}
}

type startedOrBusyResponse struct {
StateLabel string `json:"state_label"`
OpID string `json:"op_id"`
}
2 changes: 1 addition & 1 deletion pocketic/gen.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package pocketic

//go:generate go run ../cmd/goic/main.go generate did testdata/main.did hello --output=agent_test.go --packageName=pocketic_test
//go:generate go run ../cmd/goic/main.go generate did testdata/main.did hello --output=agent_test.go --packageName=pocketic_test --indirect
26 changes: 14 additions & 12 deletions pocketic/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ func (pic PocketIC) AutoProgress() error {
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/auto_progress", pic.instanceURL()),
http.StatusOK,
nil,
nil,
)
Expand All @@ -105,33 +104,38 @@ func (pic PocketIC) MakeLive(port *int) (string, error) {
if pic.httpGateway != nil {
return fmt.Sprintf("http://127.0.0.1:%d", pic.httpGateway.Port), nil
}
var resp CreateHttpGatewayResponse
if err := pic.do(
req, err := newRequest(
http.MethodPost,
fmt.Sprintf("%s/http_gateway", pic.server.URL()),
http.StatusCreated,
HttpGatewayConfig{
ListenAt: port,
ForwardTo: HttpGatewayBackendPocketICInstance{
PocketIcInstance: pic.InstanceID,
},
},
&resp,
); err != nil {
)
if err != nil {
return "", err
}
resp, err := pic.client.Do(req)
if err != nil {
return "", err
}
var gatewayResp CreateHttpGatewayResponse
if err := json.NewDecoder(resp.Body).Decode(&gatewayResp); err != nil {
return "", err
}
if resp.Error != nil {
return "", resp.Error
if gatewayResp.Error != nil {
return "", gatewayResp.Error
}
return fmt.Sprintf("http://127.0.0.1:%d", resp.Created.Port), nil
return fmt.Sprintf("http://127.0.0.1:%d", gatewayResp.Created.Port), nil
}

// SetTime sets the current time of the IC, on all subnets.
func (pic PocketIC) SetTime(time time.Time) error {
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/update/set_time", pic.instanceURL()),
http.StatusOK,
RawTime{
NanosSinceEpoch: time.UnixNano(),
},
Expand All @@ -144,7 +148,6 @@ func (pic PocketIC) StopProgress() error {
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/stop_progress", pic.instanceURL()),
http.StatusOK,
nil,
nil,
)
Expand All @@ -155,7 +158,6 @@ func (pic *PocketIC) stopHttpGateway() error {
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/http_gateway/%d/stop", pic.server.URL(), pic.httpGateway.InstanceID),
http.StatusOK,
nil,
nil,
); err != nil {
Expand Down
10 changes: 0 additions & 10 deletions pocketic/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ func (pic PocketIC) CreateInstance(config SubnetConfigSet) (*InstanceConfig, err
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/instances", pic.server.URL()),
http.StatusCreated,
config,
&a,
); err != nil {
Expand All @@ -30,7 +29,6 @@ func (pic PocketIC) DeleteInstance(instanceID int) error {
return pic.do(
http.MethodDelete,
fmt.Sprintf("%s/instances/%d", pic.server.URL(), instanceID),
http.StatusOK,
nil,
nil,
)
Expand All @@ -42,7 +40,6 @@ func (pic PocketIC) GetCycles(canisterID principal.Principal) (int, error) {
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/read/get_cycles", pic.instanceURL()),
http.StatusOK,
&RawCanisterID{CanisterID: canisterID.Raw},
&cycles,
); err != nil {
Expand All @@ -57,7 +54,6 @@ func (pic PocketIC) GetInstances() ([]string, error) {
if err := pic.do(
http.MethodGet,
fmt.Sprintf("%s/instances", pic.server.URL()),
http.StatusOK,
nil,
&instances,
); err != nil {
Expand All @@ -73,7 +69,6 @@ func (pic PocketIC) GetStableMemory(canisterID principal.Principal) ([]byte, err
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/read/get_stable_memory", pic.instanceURL()),
http.StatusOK,
&RawCanisterID{CanisterID: canisterID.Raw},
&data,
); err != nil {
Expand All @@ -88,7 +83,6 @@ func (pic PocketIC) GetSubnet(canisterID principal.Principal) (*principal.Princi
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/read/get_subnet", pic.instanceURL()),
http.StatusOK,
&RawCanisterID{CanisterID: canisterID.Raw},
&subnetID,
); err != nil {
Expand All @@ -103,7 +97,6 @@ func (pic PocketIC) GetTime() (*time.Time, error) {
if err := pic.do(
http.MethodGet,
fmt.Sprintf("%s/read/get_time", pic.instanceURL()),
http.StatusOK,
nil,
&t,
); err != nil {
Expand Down Expand Up @@ -133,7 +126,6 @@ func (pic PocketIC) RootKey() ([]byte, error) {
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/read/pub_key", pic.instanceURL()),
http.StatusOK,
&RawSubnetID{SubnetID: subnetID.Raw},
&key,
); err != nil {
Expand All @@ -151,7 +143,6 @@ func (pic PocketIC) SetStableMemory(canisterID principal.Principal, data []byte,
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/update/set_stable_memory", pic.instanceURL()),
http.StatusOK,
RawSetStableMemory{
CanisterID: canisterID.Raw,
BlobID: blobID,
Expand All @@ -164,7 +155,6 @@ func (pic PocketIC) Tick() error {
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/update/tick", pic.instanceURL()),
http.StatusOK,
nil,
nil,
)
Expand Down
Loading

0 comments on commit 2179652

Please sign in to comment.