- Running the daemon
- Using the CLI client
- Using Curl to hit the API
- Code style
- Running the tests
- Docs
- Creating a release
Hacking on Pebble is easy. It's written in Go, so install or download a copy of the latest version of Go. Pebble uses Go modules for managing dependencies, so all of the standard Go tooling just works.
To compile and run Pebble, use the go run command on the cmd/pebble directory. The first time you run it, it will download dependencies and build packages, so will take a few seconds (but after that be very fast):
$ go run ./cmd/pebble
Pebble lets you control services and perform management actions on
the system that is running them.
Usage: pebble <command> [<options>...]
...
If you want to build and install the executable to your ~/go/bin directory (which you may want to add to your path), use go install:
go install ./cmd/pebble
However, during development it's easiest just to use go run, as that will automatically recompile if you've made any changes.
To run the Pebble daemon, set the $PEBBLE environment variable and use the pebble run sub-command, something like this:
$ mkdir ~/pebble
$ export PEBBLE=~/pebble
$ go run ./cmd/pebble run
2021-09-15T01:37:23.962Z [pebble] Started daemon.
...
The use the Pebble command line client, run one of the other Pebble sub-commands, such as pebble plan or pebble services (if the server is running in one terminal, do this in another):
$ export PEBBLE=~/pebble
$ go run ./cmd/pebble plan
services:
snappass:
override: replace
command: sleep 60
$ go run ./cmd/pebble services
Service Startup Current
snappass disabled inactive
For debugging, you can also use curl in unix socket mode to hit the Pebble API:
$ curl --unix-socket ~/pebble/.pebble.socket 'http://localhost/v1/services?names=snappass' | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 120 100 120 0 0 117k 0 --:--:-- --:--:-- --:--:-- 117k
{
"type": "sync",
"status-code": 200,
"status": "OK",
"result": [
{
"name": "snappass",
"startup": "disabled",
"current": "inactive"
}
]
}
Please format your commits following the conventional commit style.
Optionally, use the brackets to scope to a particular component where applicable.
See below for some examples of commit headings:
feat: checks inherit context from services
test: increase unit test stability
feat(daemon): foo the bar correctly in the baz
test(daemon): ensure the foo bars correctly in the baz
ci(snap): upload the snap artefacts to Github
chore(deps): update go.mod dependencies
Recommended prefixes are: fix:, feat:, build:, chore:, ci:, docs:, style:, refactor:,perf: and test:
Pebble imports should be arranged in three groups:
- standard library imports
- third-party / non-Pebble imports
- Pebble imports (i.e. those prefixed with
github.com/canonical/pebble)
Imports should be sorted alphabetically within each group.
We use the gopkg.in/check.v1 package for testing. Inside a test file, import this as follows:
. "gopkg.in/check.v1"so that identifiers from that package will be added to the local namespace.
Here is an example of correctly arranged imports:
import (
"fmt"
"net"
"os"
"github.com/gorilla/mux"
. "gopkg.in/check.v1"
"github.com/canonical/pebble/internals/systemd"
"github.com/canonical/pebble/internals/testutil"
)Log messages (that is, those passed to logger.Noticef or logger.Debugf) should begin with a capital letter, and use "Cannot X" rather than "Error Xing":
logger.Noticef("Cannot marshal logs to JSON: %v", err)Error messages should be lowercase, and again use "cannot ..." instead of "error ...":
fmt.Errorf("cannot create log client: %w", err)Pebble has a suite of Go unit tests, which you can run using the regular go test command. To test all packages in the Pebble repository:
$ go test ./...
ok github.com/canonical/pebble/client (cached)
? github.com/canonical/pebble/cmd [no test files]
ok github.com/canonical/pebble/cmd/pebble 0.095s
...
To test a single package, simply pass the package path to go test:
$ go test ./cmd/pebble
ok github.com/canonical/pebble/cmd/pebble 0.115s
To run a single suite or a single test, pass the suite or test name to the gocheck test runner:
$ go test ./cmd/pebble -v -check.v -check.f PebbleSuite
=== RUN Test
PASS: cmd_add_test.go:38: PebbleSuite.TestAdd 0.002s
PASS: format_test.go:52: PebbleSuite.TestCanUnicode 0.000s
...
PASS: cmd_version_test.go:26: PebbleSuite.TestVersionCommand 0.000s
OK: 20 passed
--- PASS: Test (0.02s)
PASS
ok github.com/canonical/pebble/cmd/pebble 0.022s
$ go test ./cmd/pebble -v -check.v -check.f PebbleSuite.TestAdd
=== RUN Test
PASS: cmd_add_test.go:38: PebbleSuite.TestAdd 0.002s
OK: 1 passed
--- PASS: Test (0.00s)
PASS
ok github.com/canonical/pebble/cmd/pebble 0.007s
Note that during CI we run the tests with -race to catch data races:
$ go test -race ./...
ok github.com/canonical/pebble/client (cached)
? github.com/canonical/pebble/cmd [no test files]
ok github.com/canonical/pebble/cmd/pebble 0.165s
...
Pebble also has a suite of integration tests for testing things like pebble run. To run them, use the "integration" build constraint:
$ go test -count=1 -tags=integration ./tests/
ok github.com/canonical/pebble/tests 4.774s
We use sphinx to build the docs with styles preconfigured by the Canonical Documentation Starter Pack.
To build the docs, run make run in the docs directory.
To update the documentation starter pack, run make update in the docs directory.
To add a new CLI command, ensure that it is added in the list at the top of the doc in the appropriate section, and then add a new section for the details in alphabetical order.
The section should look like:
(reference_pebble_{command name}_command)=
## {command name}
The `{command name}` command is used to {describe the command}.
<!-- START AUTOMATED OUTPUT FOR {command name} -->
```{terminal}
pebble {command name} --help
```
<!-- END AUTOMATED OUTPUT FOR {command name} -->
With {command name} replaced by the name of the command and {describe the command} replaced by a suitable description.
To automatically update the CLI reference documentation, run make cli-help in the docs directory.
A CI workflow will fail if the CLI reference documentation does not match the actual output from Pebble.
Note that the OpenAPI spec also needs to be manually updated.
- Use short sentences, ideally with one or two clauses.
- Use headings to split the doc into sections. Make sure that the purpose of each section is clear from its heading.
- Avoid a long introduction. Assume that the reader is only going to scan the first paragraph and the headings.
- Avoid background context unless it's essential for the reader to understand.
Recommended tone:
- Use a casual tone, but avoid idioms. Common contractions such as "it's" and "doesn't" are great.
- Use "we" to include the reader in what you're explaining.
- Avoid passive descriptions. If you expect the reader to do something, give a direct instruction.
When releasing, you need to release the master branch and the fips branch.
Binaries will be created and uploaded automatically to this release by the binaries.yml GitHub Action. In addition, a new Snap version is built and uploaded to the Snap Store (but promotion to stable is a manual step).
Follow these steps:
- Update
Versionincmd/version.goto the version you're about to publish, for examplev1.27.0, open a pull request, have it reviewed and merged into themasterbranch. - Draft a new GitHub release.
- Enter the version tag (for example
v1.27.0) and select "Create new tag: on publish". - Enter a release title: include the version tag and a short summary of the release.
- Write release notes: describe new features and bug fixes, and include a link to the full list of commits.
- Click "Publish release".
- Monitor the release GitHub Actions and check that the snap is uploaded correctly.
- If you're confident it's a compatible release, promote the
candidatesnap tostable. - Find the security scan artifact on the corresponding SBOM and secscan run, and upload it to the SSDLC Pebble folder in Drive. Open the artifact and verify that the security scan has not found any vulnerabilities.
- Open a pull request to merge the
masterbranch into thefipsbranch, resolve any conflicts, and have it reviewed and merged. - Open a second PR on the
fipsbranch to bump theVersionincmd/version.goto the FIPS version, for examplev1.27.0-fips, and have it reviewed and merged. - Publish a FIPS GitHub release (for example
v1.27.0-fips) targeting the tip of thefipsbranch. - As with the main release, monitor the release GitHub Actions, check that the FIPS snap is uploaded, and promote it to stable.