diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 00000000..e1616f58 --- /dev/null +++ b/HACKING.md @@ -0,0 +1,130 @@ +# Hacking on Pebble + +Hacking on Pebble is easy. It's written in Go, so install or [download](https://golang.org/dl/) a copy of the latest version of Go. Pebble uses [Go modules](https://golang.org/ref/mod) 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 [...] +... +``` + +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. + + +## Running the daemon + +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. +... +``` + + +## Using the CLI client + +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 +``` + + +## Using Curl to hit the API + +For debugging, you can also use [curl](https://curl.se/) 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" + } + ] +} +``` + + +## Running the tests + +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](https://labix.org/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 +... +``` \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 96b94e12..00000000 --- a/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -# -# Makefile for pebble. -# -PROJECT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -PROJECT := github.com/canonical/pebble - -export CGO_ENABLED=0 - -BUILD_DIR ?= $(PROJECT_DIR)/_build -BIN_DIR = ${BUILD_DIR}/${GOOS}_${GOARCH}/bin - -define MAIN_PACKAGES - github.com/canonical/pebble/cmd/pebble -endef - -GIT_COMMIT ?= $(shell git -C $(PROJECT_DIR) rev-parse HEAD 2>/dev/null) - -# Build tags passed to go install/build. -# Example: BUILD_TAGS="minimal provider_kubernetes" -BUILD_TAGS ?= - -# Build number passed in must be a monotonic int representing -# the build. -PEBBLE_BUILD_NUMBER ?= - -COMPILE_FLAGS = -gcflags "all=-N -l" -LINK_FLAGS = -ldflags "-X $(PROJECT)/version.GitCommit=$(GIT_COMMIT) -X $(PROJECT)/version.GitTreeState=$(GIT_TREE_STATE) -X $(PROJECT)/version.build=$(PEBBLE_BUILD_NUMBER)" - -TEST_TIMEOUT ?= 2700s - -default: build - -.PHONY: test - -build: go-build -## build: Create Pebble binaries - -test: run-tests -## test: Verify PEBBLE code using unit tests - -run-tests: -## run-tests: Run the unit tests - $(eval TMP := $(shell mktemp -d $${TMPDIR:-/tmp}/pbbl-XXX)) - $(eval TEST_PACKAGES := $(shell go list $(PROJECT)/... | grep -v $(PROJECT)$$ | grep -v mocks)) - @echo 'go test -tags "$(BUILD_TAGS)" $(CHECK_ARGS) -test.timeout=$(TEST_TIMEOUT) $$TEST_PACKAGES' - @TMPDIR=$(TMP) go test -tags "$(BUILD_TAGS)" $(CHECK_ARGS) -test.timeout=$(TEST_TIMEOUT) $(TEST_PACKAGES) - @rm -r $(TMP) - - -clean: -## clean: Clean the cache and test caches - go clean -n -r --cache --testcache $(PROJECT)/... - - -go-build: -## build: Build PEBBLE binaries without updating dependencies - @mkdir -p ${BIN_DIR} - @echo 'go build -o ${BIN_DIR} -tags "$(BUILD_TAGS)" $(COMPILE_FLAGS) $(LINK_FLAGS) -v $$MAIN_PACKAGES' - @go build -o ${BIN_DIR} -tags "$(BUILD_TAGS)" $(COMPILE_FLAGS) $(LINK_FLAGS) -v $(strip $(MAIN_PACKAGES)) diff --git a/README.md b/README.md index 86ea0630..d2e5b5b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -## Take control of your internal daemons! +# The Pebble service manager + +_Take control of your internal daemons!_ **Pebble** helps you to orchestrate a set of local service processes as an organized set. It resembles well known tools such as _supervisord_, _runit_, or _s6_, in that it can @@ -9,7 +11,10 @@ designed with unique features that help with more specific use cases. - [Layer configuration examples](#layer-configuration-examples) - [Running pebble](#running-pebble) - [Layer specification](#layer-specification) - - [TODO/Contributing](#todo-contributing) + - [API and clients](#api-and-clients) + - [Roadmap/TODO](#roadmap--todo) + - [Hacking / Development](#hacking--development) + - [Contributing](#contributing) ## General model @@ -115,7 +120,7 @@ services: ## Running pebble -Once the `$PEBBLE` directory is setup, running it is easy: +If pebble is installed and the `$PEBBLE` directory is set up, running it is easy: $ pebble run @@ -209,10 +214,31 @@ services: group-id: ``` -## TODO/Contributing +## API and clients + +The Pebble daemon exposes an API (HTTP over a Unix socket) to allow remote clients to interact with the daemon. It can start and stop services, add configuration layers the plan, and so on. There is currently no official documentation for the API (apart from the [code itself](https://github.com/canonical/pebble/blob/master/internal/daemon/api.go)!); most users will interact with it via the Pebble command line interface or the Go or Python client. + +The [Go client](https://pkg.go.dev/github.com/canonical/pebble/client) is used by the CLI to connect to the Pebble API. You can use this as follows: + +```go +pebble, err := client.New(&client.Config{Socket: ".pebble.socket"}) +if err != nil { + return err +} +files, err := pebble.Start(&client.ServiceOptions{Names: []string{"srv1"}}) +if err != nil { + return err +} +``` + +We try to never change the underlying API itself in a backwards-incompatible way, however, we may sometimes change the Go client in backwards-incompatible ways. + +In addition to the Go client, there's also a [Python client](https://github.com/canonical/operator/blob/master/ops/pebble.py) for the Pebble API that's part of the Python Operator Framework used by Juju charms ([documentation here](https://juju.is/docs/sdk/pebble)). + +## Roadmap / TODO This is a preview of what Pebble is becoming. Please keep that in mind while you -explore around. +explore. Here are some of the things coming soon: @@ -224,11 +250,23 @@ Here are some of the things coming soon: - [x] General system modification commands (writing configuration files, etc) - [x] Better log caching and retrieval support - [x] Consider showing unified log as output of `pebble run` (use `-v`) - - [ ] Add support for automatically removing (double) timestamps from logs + - [ ] Automatically restart services that fail + - [ ] Support for custom health checks (HTTP, TCP, command) + - [ ] Automatically remove (double) timestamps from logs - [ ] Improve signal handling, e.g., sending SIGHUP to a service - [ ] Terminate all services before exiting run command - [ ] More tests for existing CLI commands +## Hacking / Development + +See [HACKING.md](HACKING.md) for information on how to run and hack on the Pebble codebase during development. In short, use `go run ./cmd/pebble`. + +## Contributing + +We welcome quality external contributions. We have good unit tests for much of the code, and a thorough code review process. Please note that unless it's a trivial fix, it's generally worth opening an issue to discuss before submitting a pull request. + +Before you contribute a pull request you should sign the [Canonical contributor agreement](https://ubuntu.com/legal/contributors) -- it's the easiest way for you to give us permission to use your contributions. + ## Have fun! -... and enjoy the rest of 2021! +... and enjoy the rest of the year!