feat(helm,tiller): add support for list filters

This adds support for filtering list results. Filter strings are
passed from Helm to Tiller, where they are compiled as regular
expressions and executed against the list of releases. Only matching
releases are returned.

Filters are applied before limits and sorts.
This commit is contained in:
Matt Butcher 2016-05-09 16:09:07 -06:00
parent ea6b82a43c
commit dfc9693afe
6 changed files with 179 additions and 53 deletions

View File

@ -66,6 +66,11 @@ message ListReleasesRequest {
// SortBy is the sort field that the ListReleases server should sort data before returning.
ListSort.SortBy sort_by = 3;
// Filter is a regular expression used to filter which releases should be listed.
//
// Anything that matches the regexp will be included in the results.
string filter = 4;
}
message ListSort{

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"strings"
"github.com/gosuri/uitable"
"github.com/kubernetes/helm/pkg/helm"
@ -14,13 +15,28 @@ import (
var listHelp = `
This command lists all of the currently deployed releases.
By default, items are sorted alphabetically. Sorting is done client-side, so if
the number of releases is less than the setting in '--max', some values will
be omitted, and in no particular lexicographic order.
By default, items are sorted alphabetically. Use the '-d' flag to sort by
release date.
If an argument is provided, it will be treated as a filter. Filters are
regular expressions (Perl compatible) that are applied to the list of releases.
Only items that match the filter will be returned.
$ helm list -l 'ara[a-z]+'
NAME UPDATED CHART
maudlin-arachnid Mon May 9 16:07:08 2016 alpine-0.1.0
If no results are found, 'helm list' will exit 0, but with no output (or in
the case of '-l', only headers).
By default, up to 256 items may be returned. To limit this, use the '--max' flag.
Setting '--max' to 0 will not return all results. Rather, it will return the
server's default, which may be much higher than 256. Pairing the '--max'
flag with the '--offset' flag allows you to page through results.
`
var listCommand = &cobra.Command{
Use: "list [flags]",
Use: "list [flags] [FILTER]",
Short: "List releases",
Long: listHelp,
RunE: listCmd,
@ -41,8 +57,9 @@ func init() {
}
func listCmd(cmd *cobra.Command, args []string) error {
var filter string
if len(args) > 0 {
fmt.Println("TODO: Implement filter.")
filter = strings.Join(args, " ")
}
sortBy := services.ListSort_NAME
@ -50,14 +67,14 @@ func listCmd(cmd *cobra.Command, args []string) error {
sortBy = services.ListSort_LAST_RELEASED
}
res, err := helm.ListReleases(listMax, listOffset, sortBy)
res, err := helm.ListReleases(listMax, listOffset, sortBy, filter)
if err != nil {
return prettyError(err)
}
rels := res.Releases
if res.Count+res.Offset < res.Total {
fmt.Println("Not all values were fetched.")
if flagVerbose && res.Count+res.Offset < res.Total {
fmt.Println("==> Not all values were fetched.")
}
// Purty output, ya'll

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
"regexp"
"sort"
"github.com/kubernetes/helm/cmd/tiller/environment"
@ -48,12 +49,15 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
return err
}
total := int64(len(rels))
if req.SortBy != services.ListSort_UNKNOWN {
log.Printf("Sorting by %q", req.SortBy)
if len(req.Filter) != 0 {
rels, err = filterReleases(req.Filter, rels)
if err != nil {
return err
}
}
total := int64(len(rels))
switch req.SortBy {
case services.ListSort_NAME:
sort.Sort(byName(rels))
@ -89,6 +93,20 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
return nil
}
func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) {
preg, err := regexp.Compile(filter)
if err != nil {
return rels, err
}
matches := []*release.Release{}
for _, r := range rels {
if preg.MatchString(r.Name) {
matches = append(matches, r)
}
}
return matches, nil
}
func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) {
if req.Name == "" {
return nil, errMissingRelease

View File

@ -217,6 +217,86 @@ func TestListReleases(t *testing.T) {
}
}
func TestListReleasesSort(t *testing.T) {
rs := rsFixture()
// Put them in by reverse order so that the mock doesn't "accidentally"
// sort.
num := 7
for i := num; i > 0; i-- {
rel := releaseMock()
rel.Name = fmt.Sprintf("rel-%d", i)
if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err)
}
}
limit := 6
mrs := &mockListServer{}
req := &services.ListReleasesRequest{
Offset: 0,
Limit: int64(limit),
SortBy: services.ListSort_NAME,
}
if err := rs.ListReleases(req, mrs); err != nil {
t.Fatalf("Failed listing: %s", err)
}
if len(mrs.val.Releases) != limit {
t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases))
}
for i := 0; i < limit; i++ {
n := fmt.Sprintf("rel-%d", i+1)
if mrs.val.Releases[i].Name != n {
t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name)
}
}
}
func TestListReleasesFilter(t *testing.T) {
rs := rsFixture()
names := []string{
"axon",
"dendrite",
"neuron",
"neuroglia",
"synapse",
"nucleus",
"organelles",
}
num := 7
for i := 0; i < num; i++ {
rel := releaseMock()
rel.Name = names[i]
if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err)
}
}
mrs := &mockListServer{}
req := &services.ListReleasesRequest{
Offset: 0,
Limit: 64,
Filter: "neuro[a-z]+",
SortBy: services.ListSort_NAME,
}
if err := rs.ListReleases(req, mrs); err != nil {
t.Fatalf("Failed listing: %s", err)
}
if len(mrs.val.Releases) != 2 {
t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases))
}
if mrs.val.Releases[0].Name != "neuroglia" {
t.Errorf("Unexpected sort order: %v.", mrs.val.Releases)
}
if mrs.val.Releases[1].Name != "neuron" {
t.Errorf("Unexpected sort order: %v.", mrs.val.Releases)
}
}
func mockEnvironment() *environment.Environment {
e := environment.New()
e.Releases = storage.NewMemory()

View File

@ -13,7 +13,7 @@ var Config = &config{
}
// ListReleases lists the current releases.
func ListReleases(limit, offset int, sort services.ListSort_SortBy) (*services.ListReleasesResponse, error) {
func ListReleases(limit, offset int, sort services.ListSort_SortBy, filter string) (*services.ListReleasesResponse, error) {
c := Config.client()
if err := c.dial(); err != nil {
return nil, err
@ -24,6 +24,7 @@ func ListReleases(limit, offset int, sort services.ListSort_SortBy) (*services.L
Limit: int64(limit),
Offset: int64(offset),
SortBy: sort,
Filter: filter,
}
cli, err := c.impl.ListReleases(context.TODO(), req, c.cfg.CallOpts()...)
if err != nil {

View File

@ -83,6 +83,10 @@ type ListReleasesRequest struct {
Offset int64 `protobuf:"varint,2,opt,name=offset" json:"offset,omitempty"`
// SortBy is the sort field that the ListReleases server should sort data before returning.
SortBy ListSort_SortBy `protobuf:"varint,3,opt,name=sort_by,json=sortBy,enum=hapi.services.tiller.ListSort_SortBy" json:"sort_by,omitempty"`
// Filter is a regular expression used to filter which releases should be listed.
//
// Anything that matches the regexp will be included in the results.
Filter string `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"`
}
func (m *ListReleasesRequest) Reset() { *m = ListReleasesRequest{} }
@ -547,44 +551,45 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
}
var fileDescriptor0 = []byte{
// 623 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0x5d, 0x6f, 0xda, 0x3c,
0x18, 0x6d, 0x5a, 0x1a, 0x78, 0x1f, 0xde, 0x22, 0xf0, 0xf8, 0xc8, 0x72, 0x55, 0x59, 0xda, 0xd6,
0x75, 0x6b, 0xd8, 0xd8, 0x7d, 0x25, 0xda, 0xa1, 0x09, 0x95, 0x31, 0xc9, 0x8c, 0x4d, 0xda, 0x0d,
0x4a, 0xa9, 0x59, 0x33, 0x85, 0x84, 0xc5, 0x06, 0x89, 0xeb, 0x69, 0x97, 0xfb, 0x3f, 0xfb, 0x79,
0x4b, 0xec, 0x38, 0x22, 0x90, 0x6c, 0xa8, 0x37, 0x4e, 0xed, 0x73, 0xec, 0xf3, 0x7c, 0x9c, 0xa7,
0x80, 0x79, 0x6f, 0x2f, 0x9c, 0x36, 0xa3, 0xc1, 0xca, 0x99, 0x52, 0xd6, 0xe6, 0x8e, 0xeb, 0xd2,
0xc0, 0x5a, 0x04, 0x3e, 0xf7, 0x51, 0x3d, 0xc2, 0x2c, 0x85, 0x59, 0x12, 0x33, 0x9b, 0xe2, 0xc6,
0xf4, 0xde, 0x0e, 0xb8, 0x5c, 0x25, 0xdb, 0x6c, 0x6d, 0x9e, 0xfb, 0xde, 0xcc, 0xf9, 0x1a, 0x03,
0x52, 0x22, 0xa0, 0x2e, 0xb5, 0x19, 0x55, 0xdf, 0xd4, 0x25, 0x85, 0x39, 0xde, 0xcc, 0x97, 0x00,
0xfe, 0xa1, 0xc1, 0xa3, 0x81, 0xc3, 0x38, 0x91, 0x10, 0x23, 0xf4, 0xfb, 0x92, 0x32, 0x8e, 0xea,
0x70, 0xec, 0x3a, 0x73, 0x87, 0x1b, 0xda, 0xa9, 0x76, 0x76, 0x44, 0xe4, 0x06, 0x35, 0x41, 0xf7,
0x67, 0x33, 0x46, 0xb9, 0x71, 0x28, 0x8e, 0xe3, 0x1d, 0xba, 0x84, 0x22, 0xf3, 0x03, 0x3e, 0xb9,
0x5d, 0x1b, 0x47, 0x21, 0x50, 0xe9, 0x3c, 0xb1, 0xb2, 0x72, 0xb2, 0x22, 0xa5, 0x51, 0x48, 0xb4,
0xa2, 0xe5, 0x6a, 0x4d, 0x74, 0x26, 0xbe, 0xf8, 0x12, 0x4a, 0x0a, 0xc2, 0x1d, 0xd0, 0x25, 0x8a,
0xca, 0x50, 0x1c, 0x0f, 0x6f, 0x86, 0x1f, 0x3e, 0x0f, 0xab, 0x07, 0xa8, 0x04, 0x85, 0x61, 0xf7,
0x7d, 0xaf, 0xaa, 0xa1, 0x1a, 0x9c, 0x0c, 0xba, 0xa3, 0x8f, 0x13, 0xd2, 0x1b, 0xf4, 0xba, 0xa3,
0xde, 0xdb, 0xea, 0x21, 0xfe, 0xa5, 0x41, 0x3d, 0x9d, 0x05, 0x5b, 0xf8, 0x1e, 0xa3, 0x51, 0x1a,
0x53, 0x7f, 0xe9, 0x25, 0x69, 0x88, 0x4d, 0x6e, 0x1a, 0x21, 0x9b, 0xfb, 0xdc, 0x76, 0x45, 0x12,
0x21, 0x5b, 0x6c, 0xd0, 0x6b, 0x28, 0xc5, 0x85, 0x63, 0x46, 0xe1, 0xf4, 0xe8, 0xac, 0xdc, 0x69,
0xc8, 0xec, 0x54, 0x89, 0x63, 0x55, 0x92, 0xd0, 0xf0, 0x05, 0xb4, 0xde, 0x51, 0x15, 0xcd, 0x88,
0xdb, 0x7c, 0x99, 0x14, 0x16, 0x41, 0xc1, 0xb3, 0xe7, 0x54, 0x04, 0xf4, 0x1f, 0x11, 0x7f, 0xe3,
0x4f, 0x60, 0xec, 0xd2, 0xe3, 0x0c, 0x32, 0xf8, 0xe8, 0x29, 0x14, 0xa2, 0x16, 0x8a, 0xe8, 0xcb,
0x1d, 0x94, 0x8e, 0xa6, 0x1f, 0x22, 0x44, 0xe0, 0xd8, 0xda, 0x7c, 0xf7, 0xda, 0xf7, 0x38, 0xf5,
0xf8, 0xdf, 0xe2, 0x18, 0xc0, 0xe3, 0x0c, 0x7e, 0x1c, 0x48, 0x1b, 0x8a, 0xb1, 0x84, 0xb8, 0x93,
0x5b, 0x05, 0xc5, 0xc2, 0x4d, 0xa8, 0x8f, 0x17, 0x77, 0x36, 0xa7, 0x0a, 0x91, 0xca, 0xb8, 0x05,
0x8d, 0xad, 0x73, 0xa9, 0x80, 0x7f, 0x6a, 0xd0, 0xe8, 0x7b, 0x2c, 0xac, 0xb9, 0x9b, 0xbe, 0x82,
0x9e, 0x85, 0x6d, 0x8c, 0x0c, 0x1f, 0x2b, 0xd7, 0xa4, 0xb2, 0x9c, 0x8a, 0xeb, 0x68, 0x25, 0x12,
0x47, 0xe7, 0xa0, 0xaf, 0x6c, 0x37, 0xbc, 0x93, 0xae, 0x4d, 0xcc, 0x14, 0xd3, 0x42, 0x62, 0x06,
0x6a, 0x41, 0xf1, 0x2e, 0x58, 0x4f, 0x82, 0xa5, 0x27, 0xfa, 0x5d, 0x22, 0x7a, 0xb8, 0x25, 0x4b,
0x0f, 0xf7, 0xa1, 0xb9, 0x1d, 0xc6, 0x43, 0x6b, 0x10, 0x1a, 0x61, 0xec, 0x39, 0x99, 0x39, 0x65,
0x35, 0xe0, 0x06, 0x8c, 0x5d, 0xfa, 0x03, 0xb5, 0x3b, 0xbf, 0x8f, 0xa1, 0xa2, 0x3c, 0x25, 0xe7,
0x10, 0x39, 0xf0, 0xff, 0xe6, 0x98, 0xa0, 0xe7, 0xf9, 0x63, 0xba, 0xf5, 0x0f, 0xc1, 0x3c, 0xdf,
0x87, 0x1a, 0x37, 0xf2, 0xe0, 0x95, 0x86, 0x18, 0x54, 0xb7, 0x3d, 0x8d, 0x2e, 0xb2, 0xdf, 0xc8,
0x19, 0x15, 0xd3, 0xda, 0x97, 0xae, 0x64, 0xd1, 0x0a, 0x6a, 0x3b, 0x06, 0x46, 0xff, 0x7c, 0x26,
0x3d, 0x19, 0x66, 0x7b, 0x6f, 0x7e, 0xa2, 0xfb, 0x0d, 0x4e, 0x52, 0x96, 0x46, 0x39, 0xd5, 0xca,
0x9a, 0x07, 0xf3, 0xc5, 0x5e, 0xdc, 0x44, 0x6b, 0x0e, 0x95, 0xb4, 0x3b, 0x51, 0xce, 0x03, 0x99,
0xa3, 0x64, 0xbe, 0xdc, 0x8f, 0x9c, 0xc8, 0x85, 0x7d, 0xdc, 0xb6, 0x64, 0x5e, 0x1f, 0x73, 0x9c,
0x9e, 0xd7, 0xc7, 0x3c, 0xa7, 0xe3, 0x83, 0x2b, 0xf8, 0x52, 0x52, 0xec, 0x5b, 0x5d, 0xfc, 0x50,
0xbd, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xbb, 0x4a, 0x7d, 0x42, 0x07, 0x00, 0x00,
// 637 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xdf, 0x6e, 0xd3, 0x3e,
0x14, 0x5e, 0xb6, 0x2e, 0xed, 0xce, 0x7e, 0x9b, 0x3a, 0xff, 0xba, 0x36, 0xe4, 0x0a, 0x59, 0x02,
0xc6, 0x60, 0x29, 0x94, 0xfb, 0x49, 0xdd, 0xa8, 0x50, 0xb5, 0x52, 0x24, 0x97, 0x82, 0xc4, 0x4d,
0x95, 0x75, 0x2e, 0x0b, 0x4a, 0x93, 0x12, 0xbb, 0x95, 0xfa, 0x00, 0x5c, 0xf2, 0x04, 0xbc, 0x08,
0x8f, 0x87, 0x63, 0x3b, 0x51, 0xd3, 0x26, 0x50, 0xed, 0xc6, 0xd9, 0xf1, 0xf7, 0x1d, 0x7f, 0xe7,
0xef, 0x0a, 0xf6, 0xbd, 0x3b, 0xf3, 0x9a, 0x8c, 0x46, 0x0b, 0x6f, 0x4c, 0x59, 0x93, 0x7b, 0xbe,
0x4f, 0x23, 0x67, 0x16, 0x85, 0x3c, 0x44, 0xb5, 0x18, 0x73, 0x12, 0xcc, 0x51, 0x98, 0x5d, 0x97,
0x1e, 0xe3, 0x7b, 0x37, 0xe2, 0xea, 0x54, 0x6c, 0xbb, 0xb1, 0x7a, 0x1f, 0x06, 0x13, 0xef, 0xab,
0x06, 0x94, 0x44, 0x44, 0x7d, 0xea, 0x32, 0x9a, 0x7c, 0x33, 0x4e, 0x09, 0xe6, 0x05, 0x93, 0x50,
0x01, 0xf8, 0x97, 0x01, 0xff, 0xf7, 0x3c, 0xc6, 0x89, 0x82, 0x18, 0xa1, 0xdf, 0xe7, 0x94, 0x71,
0x54, 0x83, 0x7d, 0xdf, 0x9b, 0x7a, 0xdc, 0x32, 0x1e, 0x1b, 0x67, 0x7b, 0x44, 0x19, 0xa8, 0x0e,
0x66, 0x38, 0x99, 0x30, 0xca, 0xad, 0x5d, 0x79, 0xad, 0x2d, 0x74, 0x09, 0x65, 0x16, 0x46, 0x7c,
0x74, 0xbb, 0xb4, 0xf6, 0x04, 0x70, 0xdc, 0x7a, 0xe2, 0xe4, 0xe5, 0xe4, 0xc4, 0x4a, 0x03, 0x41,
0x74, 0xe2, 0xe3, 0x6a, 0x49, 0x4c, 0x26, 0xbf, 0xf1, 0xbb, 0x13, 0xcf, 0xe7, 0x34, 0xb2, 0x4a,
0xc2, 0xfd, 0x80, 0x68, 0x0b, 0x5f, 0x42, 0x25, 0x71, 0xc1, 0x2d, 0x30, 0x95, 0x17, 0x3a, 0x84,
0xf2, 0xb0, 0x7f, 0xd3, 0xff, 0xf0, 0xb9, 0x5f, 0xdd, 0x41, 0x15, 0x28, 0xf5, 0xdb, 0xef, 0x3b,
0x55, 0x03, 0x9d, 0xc0, 0x51, 0xaf, 0x3d, 0xf8, 0x38, 0x22, 0x9d, 0x5e, 0xa7, 0x3d, 0xe8, 0xbc,
0xad, 0xee, 0xe2, 0x9f, 0x06, 0xd4, 0xb2, 0xd9, 0xb1, 0x59, 0x18, 0x30, 0x1a, 0xa7, 0x37, 0x0e,
0xe7, 0x41, 0x9a, 0x9e, 0x34, 0x0a, 0xd3, 0x13, 0x6c, 0x1e, 0x72, 0xd7, 0x97, 0xc9, 0x09, 0xb6,
0x34, 0xd0, 0x6b, 0xa8, 0xe8, 0x82, 0x32, 0x11, 0xf6, 0xde, 0xd9, 0x61, 0xeb, 0x54, 0x65, 0x9d,
0x94, 0x5e, 0xab, 0x92, 0x94, 0x86, 0x2f, 0xa0, 0xf1, 0x8e, 0x26, 0xd1, 0x0c, 0xb8, 0xcb, 0xe7,
0x69, 0xc1, 0x11, 0x94, 0x02, 0x77, 0x4a, 0x65, 0x40, 0x07, 0x44, 0xfe, 0x8d, 0x3f, 0x81, 0xb5,
0x49, 0xd7, 0x19, 0xe4, 0xf0, 0xd1, 0x53, 0x28, 0xc5, 0xad, 0x95, 0xd1, 0x1f, 0xb6, 0x50, 0x36,
0x9a, 0xae, 0x40, 0x88, 0xc4, 0xb1, 0xb3, 0xfa, 0xee, 0x75, 0x18, 0x70, 0x1a, 0xf0, 0xbf, 0xc5,
0xd1, 0x83, 0x47, 0x39, 0x7c, 0x1d, 0x48, 0x13, 0xca, 0x5a, 0x42, 0xfa, 0x14, 0x56, 0x21, 0x61,
0xe1, 0x3a, 0xd4, 0x86, 0xb3, 0x3b, 0x97, 0xd3, 0x04, 0x51, 0xca, 0xb8, 0x01, 0xa7, 0x6b, 0xf7,
0x4a, 0x01, 0xff, 0x30, 0xe0, 0xb4, 0x1b, 0x30, 0x51, 0x73, 0x3f, 0xeb, 0x82, 0x9e, 0x89, 0x36,
0xc6, 0x8b, 0xa0, 0x95, 0x4f, 0x94, 0xb2, 0xda, 0x96, 0xeb, 0xf8, 0x24, 0x0a, 0x47, 0xe7, 0x60,
0x2e, 0x5c, 0x5f, 0xf8, 0x64, 0x6b, 0xa3, 0x99, 0x72, 0x8b, 0x88, 0x66, 0xa0, 0x06, 0x94, 0xef,
0xa2, 0xe5, 0x28, 0x9a, 0x07, 0xb2, 0xdf, 0x15, 0x62, 0x0a, 0x93, 0xcc, 0x03, 0xdc, 0x85, 0xfa,
0x7a, 0x18, 0x0f, 0xad, 0x81, 0x18, 0x84, 0x61, 0xe0, 0xe5, 0xe6, 0x94, 0xd7, 0x80, 0x1b, 0xb0,
0x36, 0xe9, 0x0f, 0xd4, 0x6e, 0xfd, 0xde, 0x87, 0xe3, 0x64, 0xa6, 0xd4, 0x7e, 0x22, 0x0f, 0xfe,
0x5b, 0x5d, 0x13, 0xf4, 0xbc, 0x78, 0x7d, 0xd7, 0xfe, 0x51, 0xd8, 0xe7, 0xdb, 0x50, 0x75, 0x23,
0x77, 0x5e, 0x19, 0x88, 0x41, 0x75, 0x7d, 0xa6, 0xd1, 0x45, 0xfe, 0x1b, 0x05, 0xab, 0x62, 0x3b,
0xdb, 0xd2, 0x13, 0x59, 0xb4, 0x80, 0x93, 0x8d, 0x01, 0x46, 0xff, 0x7c, 0x26, 0xbb, 0x19, 0x76,
0x73, 0x6b, 0x7e, 0xaa, 0xfb, 0x0d, 0x8e, 0x32, 0x23, 0x8d, 0x0a, 0xaa, 0x95, 0xb7, 0x0f, 0xf6,
0x8b, 0xad, 0xb8, 0xa9, 0xd6, 0x14, 0x8e, 0xb3, 0xd3, 0x89, 0x0a, 0x1e, 0xc8, 0x5d, 0x25, 0xfb,
0xe5, 0x76, 0xe4, 0x54, 0x4e, 0xf4, 0x71, 0x7d, 0x24, 0x8b, 0xfa, 0x58, 0x30, 0xe9, 0x45, 0x7d,
0x2c, 0x9a, 0x74, 0xbc, 0x73, 0x05, 0x5f, 0x2a, 0x09, 0xfb, 0xd6, 0x94, 0x3f, 0x60, 0x6f, 0xfe,
0x04, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x16, 0x42, 0xc7, 0x5a, 0x07, 0x00, 0x00,
}