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.
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.
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.
To implement the client-side template, complete the template attributed to the variable clientInstrumentTemplate
in templates.go file.
The template is generated using the templateArgs
struct defined on lines 9-17 of the templates.go
file.
Hint: Use the serverInstrumentTemplate as a guide for implementing the various steps in the clientInstrumentTemplate.
To implement the client-side IR node, complete the TODO list in the ir_client.go file.
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.
Hint: Use the HelloInstrumentServerWrapper defined in ir_server.go as a guide for implementing various steps in the serverInstrumentTemplate.
To implement the IR wiring, complete the AddHelloSOSPStatement
function in the wiring.go file.
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:
import "github.com/blueprint-uservices/blueprint/blueprint/pkg/coreplugins/pointer"
func addToChain(serviceName string, serverWrapper string, clientWrapper string) {
// Get the pointer for the serviceName that has access
ptr := pointer.GetPointer(spec, serviceName)
serverNext := ptr.AddDstModifier(spec, serverWrapper) // Add serverWrapper to the server-side node chain
clientNext := ptr.AddSrcModifier(spec, clientWrapper) // Add clientWrapper to the client-side node chain
// Use serverNext to instantiate the IR node.
// Use clientNext to instantiate the IR node.
}
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.
spec.Define(wrapper_name, &HelloInstrumentServerWrapper{}, func(ns wiring.Namespace) (ir.IRNode, error) {
// Get the IRNode that will be wrapped by HelloWrapper
var server golang.Service
if err := ns.Get(serverNext, &server); err != nil {
return nil, blueprint.Errorf("Tutorial Plugin %s expected %s to be a golang.Service, but encountered %s", wrapper_name, serverNext, err)
}
// Instantiate the IRNode
return newHelloInstrumentServerWrapper(wrapper_name, server)
})
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.
To generate the application, execute the following steps:
cd leaf/wiring
go run main.go -w bonus -o build
We will use docker compose
to build the containers and then eventually deploy them.
To build the application, execute the following steps:
# Assuming you are in the leaf/wiring folder
cd build/docker # Switch to the docker directory where the containers are
cp ../../.env . # Copy the provided .env file from the wiring folder. Note that we are not using the generated .env file for simplicity.
docker compose build # Build the containers
Note: executing docker commands may require sudo access
To launch the built containers, execute the following command:
docker compose up -d
To test the application, you can execute the following curl
command:
curl http://localhost:12348/Hello?a=5
The Hello API simply returns a counter value indicating the number of previous requests processed.
Thus, you should see the following output:
{"Ret0":0}
On subsequent executions of the curl command, the counter will increase monotonically.
To check if the message was printed, open the docker logs for the non-leaf container. To do so, execute the following command:
docker logs nonleaf_ctr-1
To stop the launched containers, execute the following command:
docker compose down
Congratulations on finishing all parts of the SOSP tutorial! You are now officially a semi-pro in Blueprint fundamentals!