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.
pull/669/head
Matt Butcher 9 years ago
parent ea6b82a43c
commit dfc9693afe

@ -66,6 +66,11 @@ message ListReleasesRequest {
// SortBy is the sort field that the ListReleases server should sort data before returning. // SortBy is the sort field that the ListReleases server should sort data before returning.
ListSort.SortBy sort_by = 3; 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{ message ListSort{

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"strings"
"github.com/gosuri/uitable" "github.com/gosuri/uitable"
"github.com/kubernetes/helm/pkg/helm" "github.com/kubernetes/helm/pkg/helm"
@ -14,13 +15,28 @@ import (
var listHelp = ` var listHelp = `
This command lists all of the currently deployed releases. This command lists all of the currently deployed releases.
By default, items are sorted alphabetically. Sorting is done client-side, so if By default, items are sorted alphabetically. Use the '-d' flag to sort by
the number of releases is less than the setting in '--max', some values will release date.
be omitted, and in no particular lexicographic order.
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{ var listCommand = &cobra.Command{
Use: "list [flags]", Use: "list [flags] [FILTER]",
Short: "List releases", Short: "List releases",
Long: listHelp, Long: listHelp,
RunE: listCmd, RunE: listCmd,
@ -41,8 +57,9 @@ func init() {
} }
func listCmd(cmd *cobra.Command, args []string) error { func listCmd(cmd *cobra.Command, args []string) error {
var filter string
if len(args) > 0 { if len(args) > 0 {
fmt.Println("TODO: Implement filter.") filter = strings.Join(args, " ")
} }
sortBy := services.ListSort_NAME sortBy := services.ListSort_NAME
@ -50,14 +67,14 @@ func listCmd(cmd *cobra.Command, args []string) error {
sortBy = services.ListSort_LAST_RELEASED sortBy = services.ListSort_LAST_RELEASED
} }
res, err := helm.ListReleases(listMax, listOffset, sortBy) res, err := helm.ListReleases(listMax, listOffset, sortBy, filter)
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
rels := res.Releases rels := res.Releases
if res.Count+res.Offset < res.Total { if flagVerbose && res.Count+res.Offset < res.Total {
fmt.Println("Not all values were fetched.") fmt.Println("==> Not all values were fetched.")
} }
// Purty output, ya'll // Purty output, ya'll

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"regexp"
"sort" "sort"
"github.com/kubernetes/helm/cmd/tiller/environment" "github.com/kubernetes/helm/cmd/tiller/environment"
@ -48,11 +49,14 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
return err return err
} }
total := int64(len(rels)) if len(req.Filter) != 0 {
rels, err = filterReleases(req.Filter, rels)
if req.SortBy != services.ListSort_UNKNOWN { if err != nil {
log.Printf("Sorting by %q", req.SortBy) return err
} }
}
total := int64(len(rels))
switch req.SortBy { switch req.SortBy {
case services.ListSort_NAME: case services.ListSort_NAME:
@ -89,6 +93,20 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
return nil 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) { func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) {
if req.Name == "" { if req.Name == "" {
return nil, errMissingRelease return nil, errMissingRelease

@ -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 { func mockEnvironment() *environment.Environment {
e := environment.New() e := environment.New()
e.Releases = storage.NewMemory() e.Releases = storage.NewMemory()

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

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

Loading…
Cancel
Save