-
Notifications
You must be signed in to change notification settings - Fork 9
/
invokefunction.go
108 lines (87 loc) · 3.39 KB
/
invokefunction.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Copyright 2022 Namespace Labs Inc; All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package tools
import (
"bytes"
"context"
"google.golang.org/protobuf/encoding/protojson"
"namespacelabs.dev/foundation/internal/compute"
"namespacelabs.dev/foundation/internal/console"
"namespacelabs.dev/foundation/internal/fnerrors"
"namespacelabs.dev/foundation/internal/planning/tool/protocol"
"namespacelabs.dev/foundation/internal/runtime/rtypes"
"namespacelabs.dev/foundation/internal/sdk/deno"
"namespacelabs.dev/foundation/internal/sdk/host"
"namespacelabs.dev/foundation/std/pkggraph"
"namespacelabs.dev/foundation/std/tasks"
"namespacelabs.dev/foundation/std/types"
)
const (
invocationProtocol = "namespacelabs.dev/foundation/std/protocol/invocation"
denoRuntime = "namespacelabs.dev/foundation/std/experimental/deno"
)
func InvokeFunction(ctx context.Context, loc pkggraph.Location, rootDir string, inv *types.DeferredInvocation) (compute.Computable[*protocol.InvokeResponse], error) {
if inv.ExperimentalFunction.Kind != invocationProtocol {
return nil, fnerrors.BadInputError("unsupported protocol, expected %q got %q", invocationProtocol, inv.ExperimentalFunction.Kind)
}
if inv.WithInput != nil {
return nil, fnerrors.InternalError("%s: invoke function does not support arbitrary inputs", inv.ExperimentalFunction.Kind)
}
switch inv.ExperimentalFunction.Runtime {
case denoRuntime:
d, err := deno.SDK(ctx, host.HostPlatform())
if err != nil {
return nil, fnerrors.New("failed to invoke deno: %w", err)
}
return &invokeDeno{
deno: d,
request: &protocol.InvokeRequest{},
rootDir: rootDir,
source: loc.Abs(inv.ExperimentalFunction.Source),
}, nil
default:
return nil, fnerrors.BadInputError("%s: unsupported runtime", inv.ExperimentalFunction.Runtime)
}
}
type invokeDeno struct {
deno compute.Computable[deno.Deno]
request *protocol.InvokeRequest
rootDir string
source string
compute.LocalScoped[*protocol.InvokeResponse]
}
func (inv *invokeDeno) Action() *tasks.ActionEvent {
return tasks.Action("deno.invocation").Arg("rootDir", inv.rootDir).Arg("source", inv.source)
}
func (inv *invokeDeno) Inputs() *compute.In {
return compute.Inputs().Computable("deno", inv.deno).Proto("request", inv.request).Indigestible("rootDir", inv.rootDir).Indigestible("source", inv.source)
}
func (inv *invokeDeno) Output() compute.Output {
return compute.Output{NotCacheable: true}
}
func (inv *invokeDeno) Compute(ctx context.Context, deps compute.Resolved) (*protocol.InvokeResponse, error) {
d := compute.MustGetDepValue(deps, inv.deno, "deno")
requestBytes, err := protojson.Marshal(inv.request)
if err != nil {
return nil, err
}
// Pre-cache the available imports to modules, so we can later on make sure that no new downloads are made.
if err := d.CacheImports(ctx, inv.rootDir); err != nil {
return nil, err
}
var out bytes.Buffer
rio := rtypes.IO{
Stdin: bytes.NewReader(requestBytes),
Stdout: &out,
Stderr: console.Output(ctx, "deno"),
}
if err := d.Run(ctx, inv.rootDir, rio, "run", "--cached-only", "--import-map=std/experimental/deno/import_map.json", inv.source); err != nil {
return nil, err
}
response := &protocol.InvokeResponse{}
if err := protojson.Unmarshal(out.Bytes(), response); err != nil {
return nil, err
}
return response, nil
}