You and your team will program a small string manipulation program in Golang. To test your code you are provided with a <plugin-name>_test.go
file. You can execute the tests using the command go test
which will run all tests in your working directory.
Your plugin will contain the method with the following signature:
func Decode(reader io.Reader) io.Reader {
// your code here
return ...
}
The io.Reader
interface is a very small but common in Golang and looks like this:
type Reader interface {
Read(p []byte) (n int, err error)
}
The Read(byte) (int, error)
method reads bytes from the underlying data (e.g. a file) into the byte
slice passed to the function. Here are some utility methods you might need for your task. For error handling in this workshop, just use panic(err)
:
var reader io.Reader
// Read all data from an io.Reader into a byte slice
content, err := io.ReadAll(reader)
if err != nil {
panic(err)
}
// Convert a byte slice to a string
stringContent := string(content)
// Create an io.Reader from a string
strings.NewReader(stringContent)
To assert everybody has the same development environment, start up a docker container from within the oci-workshop
directory and use cd
to navigate into the directory.
docker run -it -v $(pwd):/oci lieberlois/ociworkshop:0.0.1 bash
cd /oci
Now navigate into the directory that will contain the plugin code of your team:
cd decoders
ls
cd <your-team-code>
Run the tests within the docker container to assert your local setup is working and then initialize a Go module:
go test
go mod init decoder
Your team is responsible for the base64 decoder. The decoder accepts a reader that will contain a base64 encoded string.
It is your job to fill in the function Decode(reader io.Reader) io.Reader
. Your function should in this case return an io.Reader
containing the base64 decoded data.
Your team is responsible for the hex decoder. The decoder accepts a reader that will contain a hex encoded string.
It is your job to fill in the function Decode(reader io.Reader) io.Reader
. Your function should in this case return an io.Reader
containing the hex decoded data.
Your team is responsible for the reverse decoder. The decoder accepts a reader that will contain a string.
It is your job to fill in the function Decode(reader io.Reader) io.Reader
. Your function should in this case return an io.Reader
containing the same string but reversed.
Your team is responsible for the JSON decoder. The decoder accepts a reader that will contain JSON text in the following form:
{
"value": "some-data"
}
It is your job to fill in the function Decode(reader io.Reader) io.Reader
. Your function should in this case return an io.Reader
containing the data some-data
. If you finish early, please notify the trainer to get an additional extra task 😉👋.
To demonstrate that OCI can handle any type of arbitrary artifacts, we will build our Golang Plugins as a Shared Object file. To build this file, use the following command from within your plugin directory:
plugin_name="<your-plugin-name>" # e.g. json
go build -buildmode=plugin -o "out/${plugin_name}/decoder.so" "decoder.go"
You should now see a directory out
that will contain your plugin as a Shared Object (.so) file.
We will now use the ORAS CLI to push the artifact to an OCI registry. Use the oras login
command to log in to the OCI registry. The variables will be provided via Chat.
export OCI_REGISTRY=<acrname.azurecr.io>
export OCI_USERNAME=<username>
export OCI_PASSWORD=<password>
oras login $OCI_REGISTRY --username "${OCI_USERNAME}" --password "${OCI_PASSWORD}"
You should now be able to push the artifact. Please keep the version as is:
plugin_name="<your-plugin-name>" # e.g. json
version="v0.0.1"
oras push \
"${OCI_REGISTRY}/${plugin_name}:${version}" \
--artifact-type application/vnd.oci.plugin.golang.so \
out/${plugin_name}/decoder.so:goplugin/so
# Verify that everything worked
oras discover "${OCI_REGISTRY}/${plugin_name}:${version}" -o tree
Wait for everyone to finish to then test the artifacts together!
The open-source tool Trivy allows you to "find vulnerabilities, misconfigurations, secrets, SBOM in containers, Kubernetes, code repositories, clouds and more". We will use the Trivy CLI to generate an SBOM in CycloneDX format for your plugin code using the following command from within your plugin directory:
plugin_name="<your-plugin-name>" # e.g. json
trivy fs \
--format cyclonedx \
--output "out/${plugin_name}/sbom.json" .
We will now use the subject
field in the OCI image manifest to attach our newly created SBOM file to the plugin artifact within the OCI registry. For this, we will again use the ORAS CLI. Again, please keep the version as is.
plugin_name="<your-plugin-name>" # e.g. json
version="v0.0.1"
oras attach \
"${OCI_REGISTRY}/${plugin_name}:${version}" \
"out/${plugin_name}/sbom.json" \
--artifact-type goplugin/sbom
You should now be able to see the initial artifact and the attached SBOM in the OCI registry using the following command:
oras discover "${OCI_REGISTRY}/${plugin_name}:${version}" -o tree
Wait for everyone to finish to then test the artifacts together!