Skip to content

Commit 8b04e13

Browse files
committed
Add Bonus Part framework and instructions
1 parent 1a60a96 commit 8b04e13

File tree

11 files changed

+539
-7
lines changed

11 files changed

+539
-7
lines changed

PartBonus.md

+133
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,135 @@
11
# Adding a new Plugin
22

3+
In the bonus part of the tutorial, we will be adding a new plugin that instruments every method for a service (both on the client-side and the server-side) to print a `Hello, SOSP` message to standard output every time a method is executed.
4+
5+
We have provided the plugin implementation for instrumenting the server side. Your goal is to complete the implementation for instrumenting the client side. Once you have completed the plugin, we will then test out the newly completed plugin with the leaf application.
6+
7+
## Instrumenting the Client Side
8+
9+
To implement the client side portion of the plugin, you need to complete 3 steps: (i) Implementing a client-side template which will represent the modified code that will contain the instrumentation; (ii) Implementing the client IR node so that Blueprint's compiler can generate the modified code from the IR node; and (iii) Implementing the wiring API so that this plugin can be used in wiring specifications.
10+
11+
### Implementing the client-side template
12+
13+
To implement the client-side template, complete the template attributed to the variable `clientInstrumentTemplate` in [templates.go](./sosp_plugins/hellososp/templates.go) file.
14+
15+
The template is generated using the `templateArgs` struct defined on lines 9-17 of the `templates.go` file.
16+
17+
> Hint: Use the serverInstrumentTemplate as a guide for implementing the various steps in the clientInstrumentTemplate.
18+
19+
### Implementing the IR client node
20+
21+
To implement the client-side IR node, complete the TODO list in the [ir_client.go](./sosp_plugins/hellososp/ir_client.go) file.
22+
23+
We suggest first completing the implementation of the function `newHelloInstrumentClientWrapper` that constructs the IR Node as well as the implementation of the struct `HelloInstrumentClientWrapper` before finishing other TODOs.
24+
25+
> Hint: Use the HelloInstrumentServerWrapper defined in [ir_server.go](./sosp_plugins/hellososp/ir_server.go) as a guide for implementing various steps in the serverInstrumentTemplate.
26+
27+
### Implementing the IR wiring
28+
29+
To implement the IR wiring, complete the `AddHelloSOSPStatement` function in the [wiring.go](./sosp_plugins/hellososp/wiring.go) file.
30+
31+
To complete this, you will need add the client-side wrapper to the client-side node chain of the desired service. Wrappers can be added to the node chains of the desired service as follows:
32+
33+
```go
34+
import "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/pointer"
35+
36+
func addToChain(serviceName string, serverWrapper string, clientWrapper string) {
37+
// Get the pointer for the serviceName that has access
38+
ptr := pointer.GetPointer(spec, serviceName)
39+
serverNext := ptr.AddDstModifier(spec, serverWrapper) // Add serverWrapper to the server-side node chain
40+
clientNext := ptr.AddSrcModifier(spec, clientWrapper) // Add clientWrapper to the client-side node chain
41+
42+
// Use serverNext to instantiate the IR node.
43+
44+
// Use clientNext to instantiate the IR node.
45+
}
46+
```
47+
48+
Once you have added the client-side wrapper to the node chain, you must then use the return value `clientNext` which is the IR node that will be wrapped by the new IR Node. The following snippet shows how to add a new IR node to the wiring spec for the server side.
49+
50+
```go
51+
spec.Define(wrapper_name, &HelloInstrumentServerWrapper{}, func(ns wiring.Namespace) (ir.IRNode, error) {
52+
// Get the IRNode that will be wrapped by HelloWrapper
53+
var server golang.Service
54+
if err := ns.Get(serverNext, &server); err != nil {
55+
return nil, blueprint.Errorf("Tutorial Plugin %s expected %s to be a golang.Service, but encountered %s", wrapper_name, serverNext, err)
56+
}
57+
58+
// Instantiate the IRNode
59+
return newHelloInstrumentServerWrapper(wrapper_name, server)
60+
})
61+
```
62+
63+
## Running Leaf application
64+
65+
To run the leaf application with our new plugin, no extra code needs to be written. We have already provided a wiring specification that generates the implementation with our plugin.
66+
67+
### Generating the application
68+
69+
To generate the application, execute the following steps:
70+
71+
```bash
72+
cd leaf/wiring
73+
go run main.go -w bonus -o build
74+
```
75+
76+
### Building the application
77+
78+
We will use `docker compose` to build the containers and then eventually deploy them.
79+
80+
To build the application, execute the following steps:
81+
82+
```bash
83+
# Assuming you are in the leaf/wiring folder
84+
cd build/docker # Switch to the docker directory where the containers are
85+
cp ../../.env . # Copy the provided .env file from the wiring folder. Note that we are not using the generated .env file for simplicity.
86+
docker compose build # Build the containers
87+
```
88+
89+
> Note: executing docker commands may require sudo access
90+
91+
### Running the application
92+
93+
To launch the built containers, execute the following command:
94+
95+
```bash
96+
docker compose up -d
97+
```
98+
99+
### Testing the application
100+
101+
To test the application, you can execute the following `curl` command:
102+
103+
```bash
104+
curl http://localhost:12348/Hello?a=5
105+
```
106+
107+
The Hello API simply returns a counter value indicating the number of previous requests processed.
108+
109+
Thus, you should see the following output:
110+
111+
```bash
112+
{"Ret0":0}
113+
```
114+
115+
On subsequent executions of the curl command, the counter will increase monotonically.
116+
117+
### Checking logs
118+
119+
To check if the message was printed, open the docker logs for the non-leaf container. To do so, execute the following command:
120+
121+
```bash
122+
docker logs nonleaf_ctr-1
123+
```
124+
125+
### Stopping the application
126+
127+
To stop the launched containers, execute the following command:
128+
129+
```bash
130+
docker compose down
131+
```
132+
133+
## Conclusion
134+
135+
Congratulations on finishing all parts of the SOSP tutorial! You are now officially a semi-pro in Blueprint fundamentals!

leaf/wiring/go.mod

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
module github.com/blueprint-uservices/sosp_tutorial/leaf/wiring
22

3-
go 1.22
3+
go 1.22.1
44

55
require (
6-
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20240405152959-f078915d2306
6+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d
77
github.com/blueprint-uservices/blueprint/examples/leaf/workflow v0.0.0-20241015110303-ca8bcf724c6d
8-
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241015110303-ca8bcf724c6d
8+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2
99
)
1010

11+
require github.com/blueprint-uservices/sosp_tutorial/sosp_plugins v0.0.0
12+
13+
replace github.com/blueprint-uservices/sosp_tutorial/sosp_plugins => ../../sosp_plugins
14+
1115
require (
1216
github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e // indirect
1317
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect

leaf/wiring/go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20240405152959-f078915d2306 h1:qN3n6Pr7fGalqno6+sbGH/Wx8HC+izyTft5waFKVDRQ=
2-
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20240405152959-f078915d2306/go.mod h1:zV7A7jdUp7+9i/ypOd3IPstNrXtf+KKZbcCYVF6PggU=
1+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d h1:2pLsYbQAQjEcdlLfdHXFZqzi6ieqUnNuxFkmNxKpjpo=
2+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d/go.mod h1:UtwYAD0WuuSf/U18tqZ1eGVp3UzzxLTUhAkXdmRggQ4=
33
github.com/blueprint-uservices/blueprint/examples/leaf/workflow v0.0.0-20241015110303-ca8bcf724c6d h1:BDDhabHs28/AcpNjNZ7jKxgv41a3CJMJI5ugdY610CY=
44
github.com/blueprint-uservices/blueprint/examples/leaf/workflow v0.0.0-20241015110303-ca8bcf724c6d/go.mod h1:f4rLdgsgKPffRFflio7Snkyoate87JFAvdMrfmX7sCM=
5-
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241015110303-ca8bcf724c6d h1:CXj1kOnU20MQSAc+lpIlCitrNW0teUpSfWu/DxLR/BU=
6-
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241015110303-ca8bcf724c6d/go.mod h1:lrHuvmxLSVgMzYqrOkyIWpv4X/FKg8PsFM6ODK87lUo=
5+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2 h1:asZxZIh+aesbr68Djy3R9vzjzC/IAeLqfBFXvxuT4+c=
6+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2/go.mod h1:lrHuvmxLSVgMzYqrOkyIWpv4X/FKg8PsFM6ODK87lUo=
77
github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e h1:yRCPuCqLAQDDPB5yE+zPLy6cvX9ysAFQGt/hv5S7e+8=
88
github.com/blueprint-uservices/blueprint/runtime v0.0.0-20240619221802-d064c5861c1e/go.mod h1:pWgaE60Wfg1GDkFUz17NcxxaFk66/s2RRO5UZG9lCXA=
99
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=

leaf/wiring/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ func main() {
1414
cmdbuilder.MakeAndExecute(
1515
name,
1616
specs.Docker,
17+
specs.Bonus,
1718
)
1819
}

leaf/wiring/specs/bonus.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package specs
2+
3+
import (
4+
"github.com/blueprint-uservices/blueprint/blueprint/pkg/wiring"
5+
"github.com/blueprint-uservices/blueprint/examples/leaf/workflow/leaf"
6+
"github.com/blueprint-uservices/blueprint/plugins/cmdbuilder"
7+
"github.com/blueprint-uservices/blueprint/plugins/goproc"
8+
"github.com/blueprint-uservices/blueprint/plugins/healthchecker"
9+
"github.com/blueprint-uservices/blueprint/plugins/http"
10+
"github.com/blueprint-uservices/blueprint/plugins/linuxcontainer"
11+
"github.com/blueprint-uservices/blueprint/plugins/memcached"
12+
"github.com/blueprint-uservices/blueprint/plugins/mongodb"
13+
"github.com/blueprint-uservices/blueprint/plugins/workflow"
14+
"github.com/blueprint-uservices/sosp_tutorial/sosp_plugins/hellososp"
15+
)
16+
17+
var Bonus = cmdbuilder.SpecOption{
18+
Name: "bonus",
19+
Description: "Deploys each service in a separate container with http, uses mongodb as NoSQL database backends, and applies a number of modifiers",
20+
Build: makeBonusSpec,
21+
}
22+
23+
func makeBonusSpec(spec wiring.WiringSpec) ([]string, error) {
24+
applyBonusDefaults := func(spec wiring.WiringSpec, serviceName string) string {
25+
hellososp.AddHelloSOSPStatement(spec, serviceName)
26+
healthchecker.AddHealthCheckAPI(spec, serviceName)
27+
http.Deploy(spec, serviceName)
28+
goproc.Deploy(spec, serviceName)
29+
return linuxcontainer.Deploy(spec, serviceName)
30+
}
31+
32+
leaf_db := mongodb.Container(spec, "leaf_db")
33+
leaf_cache := memcached.Container(spec, "leaf_cache")
34+
leaf_service := workflow.Service[*leaf.LeafServiceImpl](spec, "leaf_service", leaf_cache, leaf_db)
35+
leaf_ctr := applyBonusDefaults(spec, leaf_service)
36+
37+
nonleaf_service := workflow.Service[leaf.NonLeafService](spec, "nonleaf_service", leaf_service)
38+
nonleaf_ctr := applyBonusDefaults(spec, nonleaf_service)
39+
40+
return []string{leaf_ctr, nonleaf_ctr}, nil
41+
}

sosp_plugins/go.mod

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module github.com/blueprint-uservices/sosp_tutorial/sosp_plugins
2+
3+
go 1.22.1
4+
5+
require (
6+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d
7+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2
8+
)
9+
10+
require (
11+
github.com/otiai10/copy v1.14.0 // indirect
12+
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
13+
golang.org/x/mod v0.17.0 // indirect
14+
golang.org/x/sync v0.7.0 // indirect
15+
golang.org/x/sys v0.19.0 // indirect
16+
golang.org/x/tools v0.20.0 // indirect
17+
)

sosp_plugins/go.sum

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d h1:2pLsYbQAQjEcdlLfdHXFZqzi6ieqUnNuxFkmNxKpjpo=
2+
github.com/blueprint-uservices/blueprint/blueprint v0.0.0-20241015110303-ca8bcf724c6d/go.mod h1:UtwYAD0WuuSf/U18tqZ1eGVp3UzzxLTUhAkXdmRggQ4=
3+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2 h1:asZxZIh+aesbr68Djy3R9vzjzC/IAeLqfBFXvxuT4+c=
4+
github.com/blueprint-uservices/blueprint/plugins v0.0.0-20241029180336-5902a9377ed2/go.mod h1:lrHuvmxLSVgMzYqrOkyIWpv4X/FKg8PsFM6ODK87lUo=
5+
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
6+
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
7+
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
8+
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
9+
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
10+
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
11+
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
12+
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
13+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
14+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
15+
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
16+
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
17+
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
18+
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=

sosp_plugins/hellososp/ir_client.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package hellososp
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
"path/filepath"
7+
8+
"github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/service"
9+
"github.com/blueprint-uservices/blueprint/blueprint/pkg/ir"
10+
"github.com/blueprint-uservices/blueprint/plugins/golang"
11+
"github.com/blueprint-uservices/blueprint/plugins/golang/gocode"
12+
"github.com/blueprint-uservices/blueprint/plugins/golang/gogen"
13+
)
14+
15+
// Blueprint IRNode for representing the wrapper node that instruments every client-side method in the node that gets wrapped
16+
type HelloInstrumentClientWrapper struct {
17+
golang.Service
18+
golang.GeneratesFuncs
19+
golang.Instantiable
20+
21+
// TODO: Add other relevant fields
22+
}
23+
24+
// Implements ir.IRNode
25+
func (node *HelloInstrumentClientWrapper) ImplementsGolangNode() {}
26+
27+
// Implements ir.IRNode
28+
func (node *HelloInstrumentClientWrapper) Name() string {
29+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
30+
return ""
31+
}
32+
33+
// Implements ir.IRNode
34+
func (node *HelloInstrumentClientWrapper) String() string {
35+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
36+
return ""
37+
}
38+
39+
// Implements golang.ProvidesInterface
40+
func (node *HelloInstrumentClientWrapper) AddInterfaces(builder golang.ModuleBuilder) error {
41+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
42+
return nil
43+
}
44+
45+
func newHelloInstrumentClientWrapper(name string, client ir.IRNode) (*HelloInstrumentClientWrapper, error) {
46+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
47+
return nil, nil
48+
}
49+
50+
// Implements service.ServiceNode
51+
func (node *HelloInstrumentClientWrapper) GetInterface(ctx ir.BuildContext) (service.ServiceInterface, error) {
52+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
53+
return nil, nil
54+
}
55+
56+
// Implements golang.GeneratesFuncs
57+
// More info: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/golang#GeneratesFuncs
58+
func (node *HelloInstrumentClientWrapper) GenerateFuncs(builder golang.ModuleBuilder) error {
59+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
60+
return nil
61+
}
62+
63+
// Implements golang.Instantiable
64+
// More Info: https://github.com/Blueprint-uServices/blueprint/tree/main/plugins/golang#type-instantiable
65+
func (node *HelloInstrumentClientWrapper) AddInstantiation(builder golang.NamespaceBuilder) error {
66+
// TODO: Implement this function. Add any relevant fields to the HelloInstrumentClientWrapper as needed.
67+
return nil
68+
}
69+
70+
func generateClientInstrumentHandler(builder golang.ModuleBuilder, wrapped *gocode.ServiceInterface, outputPackage string) error {
71+
pkg, err := builder.CreatePackage(outputPackage)
72+
if err != nil {
73+
return err
74+
}
75+
76+
server := &templateArgs{
77+
Package: pkg,
78+
Service: wrapped,
79+
Iface: wrapped,
80+
Name: wrapped.BaseName + "_TutorialInstrumentClientWrapper",
81+
IfaceName: wrapped.Name,
82+
Imports: gogen.NewImports(pkg.Name),
83+
}
84+
85+
server.Imports.AddPackages("context", "log")
86+
87+
slog.Info(fmt.Sprintf("Generating %v/%v", server.Package.PackageName, wrapped.BaseName+"_TutorialInstrumentClientWrapper"))
88+
outputFile := filepath.Join(server.Package.Path, wrapped.BaseName+"_TutorialInstrumentClientWrapper.go")
89+
return gogen.ExecuteTemplateToFile("Tutorial", clientInstrumentTemplate, server, outputFile)
90+
}

0 commit comments

Comments
 (0)