-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): add CLI commands for notices (notices, notice, notify) (#298)
This adds the CLI commands for Pebble Notices see spec JU048. The new commands are: * `pebble notices [--type=<type> --key=<key> --timeout=<duration>]` list (or wait for) notices * `pebble notice <id-or-type> [<key>]` fetch details about a single notice by ID or type+key * `pebble notify [--repeat-after=<duration>] <key> [<name=value>...]` record a client ("custom") notice The PR also updates the existing `pebble okay` command to acknowledge notices (as well as warnings). I've also added a section to the README explaining notices and the associated commands.
- Loading branch information
Showing
16 changed files
with
1,151 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -490,6 +490,82 @@ pebble_service: svc2 # default label for Loki | |
``` | ||
|
||
|
||
### Notices | ||
|
||
Pebble includes a subsystem called *notices*, which allows the user to introspect various events that occur in the Pebble server, as well as record custom client events. The server saves notices to disk, so they persist across restarts, and expire after a notice-defined interval. | ||
|
||
Each notice is uniquely identified by its *type* and *key* combination, and the notice's count of occurences is incremented every time a notice with that type and key combination occurs. | ||
|
||
Each notice records the time it first occurred, the time it last occurred, and the time it last repeated. | ||
|
||
A *repeat* happens when a notice occurs with the same type and key as a prior notice, and either the notice has no "repeat after" duration (the default), or the notice happens after the provided "repeat after" interval (since the prior notice). Thus, specifying "repeat after" prevents a notice from appearing again if it happens more frequently than desired. | ||
|
||
In addition, a notice records optional *data* (string key-value pairs) from the last occurrence. | ||
|
||
These notice types are currently available: | ||
|
||
<!-- TODO: * `change-update`: recorded whenever a change is first spawned or its status is updated. The key for this type of notice is the change ID, and the notice's data includes the change `kind`. --> | ||
|
||
* `custom`: a custom client notice reported via `pebble notify`. The key and any data is provided by the user. The key must be in the format `mydomain.io/mykey` to ensure well-namespaced notice keys. | ||
|
||
<!-- TODO: * `warning`: Pebble warnings are implemented in terms of notices. The key for this type of notice is the human-readable warning message. --> | ||
|
||
To record `custom` notices, use `pebble notify`: | ||
|
||
``` | ||
$ pebble notify example.com/foo | ||
Recorded notice 1 | ||
$ pebble notify example.com/foo | ||
Recorded notice 1 | ||
$ pebble notify other.com/bar name=value [email protected] # two data fields | ||
Recorded notice 2 | ||
$ pebble notify example.com/foo | ||
Recorded notice 1 | ||
``` | ||
|
||
The `pebble notices` command lists notices not yet acknowledged, ordered by the last-repeated time (oldest first). After it runs, the notices that were shown may then be acknowledged by running `pebble okay`. When a notice repeats (see above), it needs to be acknowledged again. | ||
|
||
``` | ||
$ pebble notices | ||
ID Type Key First Repeated Occ | ||
1 custom example.com/foo today at 16:16 NZST today at 16:16 NZST 3 | ||
2 custom other.com/bar today at 16:16 NZST today at 16:16 NZST 1 | ||
``` | ||
To fetch details about a single notice, use `pebble notice`, which displays the output in YAML format. You can fetch a notice either by ID or by type/key combination. | ||
To fetch the notice with ID "1": | ||
``` | ||
$ pebble notice 1 | ||
id: "1" | ||
type: custom | ||
key: example.com/foo | ||
first-occurred: 2023-09-15T04:16:09.179395298Z | ||
last-occurred: 2023-09-15T04:16:19.487035209Z | ||
last-repeated: 2023-09-15T04:16:09.179395298Z | ||
occurrences: 3 | ||
expire-after: 168h0m0s | ||
``` | ||
To fetch the notice with type "custom" and key "other.com/bar": | ||
``` | ||
$ pebble notice custom other.com/bar | ||
id: "2" | ||
type: custom | ||
key: other.com/bar | ||
first-occurred: 2023-09-15T04:16:17.180049768Z | ||
last-occurred: 2023-09-15T04:16:17.180049768Z | ||
last-repeated: 2023-09-15T04:16:17.180049768Z | ||
occurrences: 1 | ||
last-data: | ||
name: value | ||
email: [email protected] | ||
expire-after: 168h0m0s | ||
``` | ||
## Container usage | ||
Pebble works well as a local service manager, but if running Pebble in a separate container, you can use the exec and file management APIs to coordinate with the remote system over the shared unix socket. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright (c) 2023 Canonical Ltd | ||
// | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License version 3 as | ||
// published by the Free Software Foundation. | ||
// | ||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/canonical/go-flags" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/canonical/pebble/client" | ||
) | ||
|
||
const cmdNoticeSummary = "Fetch a single notice" | ||
const cmdNoticeDescription = ` | ||
The notice command fetches a single notice, either by ID (1-arg variant), or | ||
by unique type and key combination (2-arg variant). | ||
` | ||
|
||
type cmdNotice struct { | ||
client *client.Client | ||
|
||
Positional struct { | ||
IDOrType string `positional-arg-name:"<id-or-type>" required:"1"` | ||
Key string `positional-arg-name:"<key>"` | ||
} `positional-args:"yes"` | ||
} | ||
|
||
func init() { | ||
AddCommand(&CmdInfo{ | ||
Name: "notice", | ||
Summary: cmdNoticeSummary, | ||
Description: cmdNoticeDescription, | ||
ArgsHelp: map[string]string{}, | ||
New: func(opts *CmdOptions) flags.Commander { | ||
return &cmdNotice{client: opts.Client} | ||
}, | ||
}) | ||
} | ||
|
||
func (cmd *cmdNotice) Execute(args []string) error { | ||
if len(args) > 0 { | ||
return ErrExtraArgs | ||
} | ||
|
||
var notice *client.Notice | ||
if cmd.Positional.Key != "" { | ||
notices, err := cmd.client.Notices(&client.NoticesOptions{ | ||
Types: []client.NoticeType{client.NoticeType(cmd.Positional.IDOrType)}, | ||
Keys: []string{cmd.Positional.Key}, | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
if len(notices) == 0 { | ||
return fmt.Errorf("cannot find %s notice with key %q", cmd.Positional.IDOrType, cmd.Positional.Key) | ||
} | ||
notice = notices[0] | ||
} else { | ||
var err error | ||
notice, err = cmd.client.Notice(cmd.Positional.IDOrType) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
// Notice can be assigned directly to yamlNotice as only the tags are different. | ||
yn := yamlNotice(*notice) | ||
|
||
b, err := yaml.Marshal(yn) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Fprint(Stdout, string(b)) // yaml.Marshal includes the trailing newline | ||
return nil | ||
} | ||
|
||
// yamlNotice exists to add "yaml" tags to the Notice fields. | ||
type yamlNotice struct { | ||
ID string `yaml:"id"` | ||
Type client.NoticeType `yaml:"type"` | ||
Key string `yaml:"key"` | ||
FirstOccurred time.Time `yaml:"first-occurred"` | ||
LastOccurred time.Time `yaml:"last-occurred"` | ||
LastRepeated time.Time `yaml:"last-repeated"` | ||
Occurrences int `yaml:"occurrences"` | ||
LastData map[string]string `yaml:"last-data,omitempty"` | ||
RepeatAfter time.Duration `yaml:"repeat-after,omitempty"` | ||
ExpireAfter time.Duration `yaml:"expire-after,omitempty"` | ||
} |
Oops, something went wrong.