Commit 6844d3b4 authored by Matt Butcher's avatar Matt Butcher

feat(helm,tiller): add list reverse, string offset

parent dfc9693a
...@@ -61,39 +61,56 @@ message ListReleasesRequest { ...@@ -61,39 +61,56 @@ message ListReleasesRequest {
// Limit is the maximum number of releases to be returned. // Limit is the maximum number of releases to be returned.
int64 limit = 1; int64 limit = 1;
// Offset is the zero-based offset at which the returned release list begins. // Offset is the last release name that was seen. The next listing
int64 offset = 2; // operation will start with the name after this one.
// Example: If list one returns albert, bernie, carl and we supply
// SortBy is the sort field that the ListReleases server should sort data before returning. // carl as the offset, the next one should begin with the next release name
ListSort.SortBy sort_by = 3; // after carl (e.g. dennis).
string offset = 2;
// Filter is a regular expression used to filter which releases should be listed.
// // SortBy is the sort field that the ListReleases server should sort data before returning.
// Anything that matches the regexp will be included in the results. ListSort.SortBy sort_by = 3;
string filter = 4;
// 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;
ListSort.SortOrder sort_order = 5;
} }
// ListSort defines sorting fields on a release list.
message ListSort{ message ListSort{
enum SortBy { // SortBy defines sort operations.
UNKNOWN = 0; enum SortBy {
NAME = 1; UNKNOWN = 0;
LAST_RELEASED = 2; NAME = 1;
} LAST_RELEASED = 2;
}
// SortOrder defines sort orders to augment sorting operations.
enum SortOrder {
ASC = 0;
DESC = 1;
}
} }
// ListReleasesResponse is a list of releases. // ListReleasesResponse is a list of releases.
message ListReleasesResponse { message ListReleasesResponse {
// The expected total number of releases to be returned // Count is the expected total number of releases to be returned.
int64 count = 1; int64 count = 1;
// The zero-based offset at which the list is positioned // Offset is the last-seen release name, used to offset the next set.
int64 offset = 2; string offset = 2;
// The total number of queryable releases // Total is the total number of queryable releases.
int64 total = 3; int64 total = 3;
// The resulting releases // Releases is the list of found release objects.
repeated hapi.release.Release releases = 4; repeated hapi.release.Release releases = 4;
// More indicates whether there are more objects to retrieve.
bool more = 5;
} }
// GetReleaseStatusRequest is a request to get the status of a release. // GetReleaseStatusRequest is a request to get the status of a release.
......
...@@ -43,16 +43,22 @@ var listCommand = &cobra.Command{ ...@@ -43,16 +43,22 @@ var listCommand = &cobra.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
} }
var listLong bool var (
var listMax int listLong bool
var listOffset int listMax int
var listByDate bool listOffset string
listByDate bool
listSortDesc bool
)
func init() { func init() {
listCommand.Flags().BoolVarP(&listLong, "long", "l", false, "output long listing format") f := listCommand.Flags()
listCommand.Flags().BoolVarP(&listByDate, "date", "d", false, "sort by release date") f.BoolVarP(&listLong, "long", "l", false, "output long listing format")
listCommand.Flags().IntVarP(&listMax, "max", "m", 256, "maximum number of releases to fetch") f.BoolVarP(&listByDate, "date", "d", false, "sort by release date")
listCommand.Flags().IntVarP(&listOffset, "offset", "o", 0, "offset from start value (zero-indexed)") f.BoolVarP(&listSortDesc, "reverse", "r", false, "reverse the sort order")
f.IntVarP(&listMax, "max", "m", 256, "maximum number of releases to fetch")
f.StringVarP(&listOffset, "offset", "o", "", "the last seen release name, used to offset from start value")
RootCommand.AddCommand(listCommand) RootCommand.AddCommand(listCommand)
} }
...@@ -67,17 +73,17 @@ func listCmd(cmd *cobra.Command, args []string) error { ...@@ -67,17 +73,17 @@ 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, filter) sortOrder := services.ListSort_ASC
if listSortDesc {
sortOrder = services.ListSort_DESC
}
res, err := helm.ListReleases(listMax, listOffset, sortBy, sortOrder, filter)
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
rels := res.Releases rels := res.Releases
if flagVerbose && res.Count+res.Offset < res.Total {
fmt.Println("==> Not all values were fetched.")
}
// Purty output, ya'll
if listLong { if listLong {
return formatList(rels) return formatList(rels)
} }
......
...@@ -44,6 +44,7 @@ var ( ...@@ -44,6 +44,7 @@ var (
var ListDefaultLimit int64 = 512 var ListDefaultLimit int64 = 512
func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error { func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
more := false
rels, err := s.env.Releases.List() rels, err := s.env.Releases.List()
if err != nil { if err != nil {
return err return err
...@@ -65,12 +66,33 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s ...@@ -65,12 +66,33 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
sort.Sort(byDate(rels)) sort.Sort(byDate(rels))
} }
if req.SortOrder == services.ListSort_DESC {
ll := len(rels)
rr := make([]*release.Release, ll)
for i, item := range rels {
rr[ll-i-1] = item
}
rels = rr
}
l := int64(len(rels)) l := int64(len(rels))
if req.Offset > 0 { if req.Offset != "" {
if req.Offset >= l {
return fmt.Errorf("offset %d is outside of range %d", req.Offset, l) i := -1
for ii, cur := range rels {
if cur.Name == req.Offset {
i = ii + 1
}
}
if i == -1 {
return fmt.Errorf("offset %q not found", req.Offset)
} }
rels = rels[req.Offset:]
if len(rels) < i {
return fmt.Errorf("no items after %q", req.Offset)
}
rels = rels[i:]
l = int64(len(rels)) l = int64(len(rels))
} }
...@@ -79,15 +101,21 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s ...@@ -79,15 +101,21 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
} }
if l > req.Limit { if l > req.Limit {
more = true
rels = rels[0:req.Limit] rels = rels[0:req.Limit]
l = int64(len(rels)) l = int64(len(rels))
} }
last := ""
if l > 0 {
last = rels[l-1].Name
}
res := &services.ListReleasesResponse{ res := &services.ListReleasesResponse{
Offset: 0, Offset: last,
Count: l, Count: l,
Total: total, Total: total,
Releases: rels, Releases: rels,
More: more,
} }
stream.Send(res) stream.Send(res)
return nil return nil
......
...@@ -208,7 +208,7 @@ func TestListReleases(t *testing.T) { ...@@ -208,7 +208,7 @@ func TestListReleases(t *testing.T) {
} }
mrs := &mockListServer{} mrs := &mockListServer{}
if err := rs.ListReleases(&services.ListReleasesRequest{Offset: 0, Limit: 64}, mrs); err != nil { if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil {
t.Fatalf("Failed listing: %s", err) t.Fatalf("Failed listing: %s", err)
} }
...@@ -234,7 +234,7 @@ func TestListReleasesSort(t *testing.T) { ...@@ -234,7 +234,7 @@ func TestListReleasesSort(t *testing.T) {
limit := 6 limit := 6
mrs := &mockListServer{} mrs := &mockListServer{}
req := &services.ListReleasesRequest{ req := &services.ListReleasesRequest{
Offset: 0, Offset: "",
Limit: int64(limit), Limit: int64(limit),
SortBy: services.ListSort_NAME, SortBy: services.ListSort_NAME,
} }
...@@ -276,7 +276,7 @@ func TestListReleasesFilter(t *testing.T) { ...@@ -276,7 +276,7 @@ func TestListReleasesFilter(t *testing.T) {
mrs := &mockListServer{} mrs := &mockListServer{}
req := &services.ListReleasesRequest{ req := &services.ListReleasesRequest{
Offset: 0, Offset: "",
Limit: 64, Limit: 64,
Filter: "neuro[a-z]+", Filter: "neuro[a-z]+",
SortBy: services.ListSort_NAME, SortBy: services.ListSort_NAME,
......
...@@ -13,7 +13,7 @@ var Config = &config{ ...@@ -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, filter string) (*services.ListReleasesResponse, error) { func ListReleases(limit int, offset string, sort services.ListSort_SortBy, order services.ListSort_SortOrder, 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
...@@ -21,10 +21,11 @@ func ListReleases(limit, offset int, sort services.ListSort_SortBy, filter strin ...@@ -21,10 +21,11 @@ func ListReleases(limit, offset int, sort services.ListSort_SortBy, filter strin
defer c.Close() defer c.Close()
req := &services.ListReleasesRequest{ req := &services.ListReleasesRequest{
Limit: int64(limit), Limit: int64(limit),
Offset: int64(offset), Offset: offset,
SortBy: sort, SortBy: sort,
Filter: filter, SortOrder: order,
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 {
......
...@@ -47,6 +47,7 @@ var _ = math.Inf ...@@ -47,6 +47,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
const _ = proto.ProtoPackageIsVersion1 const _ = proto.ProtoPackageIsVersion1
// SortBy defines sort operations.
type ListSort_SortBy int32 type ListSort_SortBy int32
const ( const (
...@@ -71,6 +72,28 @@ func (x ListSort_SortBy) String() string { ...@@ -71,6 +72,28 @@ func (x ListSort_SortBy) String() string {
} }
func (ListSort_SortBy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } func (ListSort_SortBy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
// SortOrder defines sort orders to augment sorting operations.
type ListSort_SortOrder int32
const (
ListSort_ASC ListSort_SortOrder = 0
ListSort_DESC ListSort_SortOrder = 1
)
var ListSort_SortOrder_name = map[int32]string{
0: "ASC",
1: "DESC",
}
var ListSort_SortOrder_value = map[string]int32{
"ASC": 0,
"DESC": 1,
}
func (x ListSort_SortOrder) String() string {
return proto.EnumName(ListSort_SortOrder_name, int32(x))
}
func (ListSort_SortOrder) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} }
// ListReleasesRequest requests a list of releases. // ListReleasesRequest requests a list of releases.
// //
// Releases can be retrieved in chunks by setting limit and offset. // Releases can be retrieved in chunks by setting limit and offset.
...@@ -79,14 +102,19 @@ func (ListSort_SortBy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0 ...@@ -79,14 +102,19 @@ func (ListSort_SortBy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0
type ListReleasesRequest struct { type ListReleasesRequest struct {
// Limit is the maximum number of releases to be returned. // Limit is the maximum number of releases to be returned.
Limit int64 `protobuf:"varint,1,opt,name=limit" json:"limit,omitempty"` Limit int64 `protobuf:"varint,1,opt,name=limit" json:"limit,omitempty"`
// Offset is the zero-based offset at which the returned release list begins. // Offset is the last release name that was seen. The next listing
Offset int64 `protobuf:"varint,2,opt,name=offset" json:"offset,omitempty"` // operation will start with the name after this one.
// Example: If list one returns albert, bernie, carl and we supply
// carl as the offset, the next one should begin with the next release name
// after carl (e.g. dennis).
Offset string `protobuf:"bytes,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. // Filter is a regular expression used to filter which releases should be listed.
// //
// Anything that matches the regexp will be included in the results. // Anything that matches the regexp will be included in the results.
Filter string `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"` Filter string `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"`
SortOrder ListSort_SortOrder `protobuf:"varint,5,opt,name=sort_order,json=sortOrder,enum=hapi.services.tiller.ListSort_SortOrder" json:"sort_order,omitempty"`
} }
func (m *ListReleasesRequest) Reset() { *m = ListReleasesRequest{} } func (m *ListReleasesRequest) Reset() { *m = ListReleasesRequest{} }
...@@ -94,6 +122,7 @@ func (m *ListReleasesRequest) String() string { return proto.CompactT ...@@ -94,6 +122,7 @@ func (m *ListReleasesRequest) String() string { return proto.CompactT
func (*ListReleasesRequest) ProtoMessage() {} func (*ListReleasesRequest) ProtoMessage() {}
func (*ListReleasesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } func (*ListReleasesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// ListSort defines sorting fields on a release list.
type ListSort struct { type ListSort struct {
} }
...@@ -104,14 +133,16 @@ func (*ListSort) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} ...@@ -104,14 +133,16 @@ func (*ListSort) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1}
// ListReleasesResponse is a list of releases. // ListReleasesResponse is a list of releases.
type ListReleasesResponse struct { type ListReleasesResponse struct {
// The expected total number of releases to be returned // Count is the expected total number of releases to be returned.
Count int64 `protobuf:"varint,1,opt,name=count" json:"count,omitempty"` Count int64 `protobuf:"varint,1,opt,name=count" json:"count,omitempty"`
// The zero-based offset at which the list is positioned // Offset is the last-seen release name, used to offset the next set.
Offset int64 `protobuf:"varint,2,opt,name=offset" json:"offset,omitempty"` Offset string `protobuf:"bytes,2,opt,name=offset" json:"offset,omitempty"`
// The total number of queryable releases // Total is the total number of queryable releases.
Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"` Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"`
// The resulting releases // Releases is the list of found release objects.
Releases []*hapi_release2.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"` Releases []*hapi_release2.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"`
// More indicates whether there are more objects to retrieve.
More bool `protobuf:"varint,5,opt,name=more" json:"more,omitempty"`
} }
func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} } func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} }
...@@ -296,6 +327,7 @@ func init() { ...@@ -296,6 +327,7 @@ func init() {
proto.RegisterType((*UninstallReleaseRequest)(nil), "hapi.services.tiller.UninstallReleaseRequest") proto.RegisterType((*UninstallReleaseRequest)(nil), "hapi.services.tiller.UninstallReleaseRequest")
proto.RegisterType((*UninstallReleaseResponse)(nil), "hapi.services.tiller.UninstallReleaseResponse") proto.RegisterType((*UninstallReleaseResponse)(nil), "hapi.services.tiller.UninstallReleaseResponse")
proto.RegisterEnum("hapi.services.tiller.ListSort_SortBy", ListSort_SortBy_name, ListSort_SortBy_value) proto.RegisterEnum("hapi.services.tiller.ListSort_SortBy", ListSort_SortBy_name, ListSort_SortBy_value)
proto.RegisterEnum("hapi.services.tiller.ListSort_SortOrder", ListSort_SortOrder_name, ListSort_SortOrder_value)
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
...@@ -551,45 +583,49 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ ...@@ -551,45 +583,49 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
} }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 637 bytes of a gzipped FileDescriptorProto // 689 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xdf, 0x6e, 0xd3, 0x3e, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x6e, 0xd3, 0x4c,
0x14, 0x5e, 0xb6, 0x2e, 0xed, 0xce, 0x7e, 0x9b, 0x3a, 0xff, 0xba, 0x36, 0xe4, 0x0a, 0x59, 0x02, 0x10, 0xad, 0x9b, 0x34, 0x3f, 0xd3, 0xaf, 0x55, 0x3a, 0x5f, 0x9a, 0x04, 0x5f, 0x20, 0xb4, 0x12,
0xc6, 0x60, 0x29, 0x94, 0xfb, 0x49, 0xdd, 0xa8, 0x50, 0xb5, 0x52, 0x24, 0x97, 0x82, 0xc4, 0x4d, 0x50, 0x0a, 0x75, 0x20, 0xdc, 0x23, 0xa5, 0x6d, 0x54, 0x55, 0x0d, 0xa9, 0xb4, 0xa1, 0x20, 0x71,
0x95, 0x75, 0x2e, 0x0b, 0x4a, 0x93, 0x12, 0xbb, 0x95, 0xfa, 0x00, 0x5c, 0xf2, 0x04, 0xbc, 0x08, 0x41, 0xe5, 0xb6, 0x1b, 0x6a, 0xe4, 0xd8, 0xc1, 0xbb, 0xa9, 0xd4, 0x07, 0xe0, 0x41, 0x78, 0x0b,
0x8f, 0x87, 0x63, 0x3b, 0x51, 0xd3, 0x26, 0x50, 0xed, 0xc6, 0xd9, 0xf1, 0xf7, 0x1d, 0x7f, 0xe7, 0x1e, 0x88, 0x07, 0x61, 0xbd, 0xeb, 0xb5, 0xea, 0xd4, 0x86, 0xa8, 0x37, 0xf6, 0xae, 0xcf, 0xd9,
0xef, 0x0a, 0xf6, 0xbd, 0x3b, 0xf3, 0x9a, 0x8c, 0x46, 0x0b, 0x6f, 0x4c, 0x59, 0x93, 0x7b, 0xbe, 0x39, 0xb3, 0x67, 0x66, 0x12, 0xb0, 0xaf, 0xdd, 0x99, 0xd7, 0xe5, 0x2c, 0xba, 0xf1, 0x2e, 0x19,
0x4f, 0x23, 0x67, 0x16, 0x85, 0x3c, 0x44, 0xb5, 0x18, 0x73, 0x12, 0xcc, 0x51, 0x98, 0x5d, 0x97, 0xef, 0x0a, 0xcf, 0xf7, 0x59, 0xe4, 0xcc, 0xa2, 0x50, 0x84, 0xd8, 0x8c, 0x31, 0xc7, 0x60, 0x8e,
0x1e, 0xe3, 0x7b, 0x37, 0xe2, 0xea, 0x54, 0x6c, 0xbb, 0xb1, 0x7a, 0x1f, 0x06, 0x13, 0xef, 0xab, 0xc6, 0xec, 0x96, 0x3a, 0x71, 0x79, 0xed, 0x46, 0x42, 0x3f, 0x35, 0xdb, 0x6e, 0xdf, 0xfd, 0x1e,
0x06, 0x94, 0x44, 0x44, 0x7d, 0xea, 0x32, 0x9a, 0x7c, 0x33, 0x4e, 0x09, 0xe6, 0x05, 0x93, 0x50, 0x06, 0x13, 0xef, 0x6b, 0x02, 0x68, 0x89, 0x88, 0xf9, 0xcc, 0xe5, 0xcc, 0xbc, 0x33, 0x87, 0x0c,
0x01, 0xf8, 0x97, 0x01, 0xff, 0xf7, 0x3c, 0xc6, 0x89, 0x82, 0x18, 0xa1, 0xdf, 0xe7, 0x94, 0x71, 0xe6, 0x05, 0x93, 0x50, 0x03, 0xe4, 0xb7, 0x05, 0xff, 0x0f, 0x3d, 0x2e, 0xa8, 0x86, 0x38, 0x65,
0x54, 0x83, 0x7d, 0xdf, 0x9b, 0x7a, 0xdc, 0x32, 0x1e, 0x1b, 0x67, 0x7b, 0x44, 0x19, 0xa8, 0x0e, 0xdf, 0xe7, 0x8c, 0x0b, 0x6c, 0xc2, 0x9a, 0xef, 0x4d, 0x3d, 0xd1, 0xb1, 0x9e, 0x58, 0x3b, 0x25,
0x66, 0x38, 0x99, 0x30, 0xca, 0xad, 0x5d, 0x79, 0xad, 0x2d, 0x74, 0x09, 0x65, 0x16, 0x46, 0x7c, 0xaa, 0x37, 0xd8, 0x82, 0x4a, 0x38, 0x99, 0x70, 0x26, 0x3a, 0xab, 0xf2, 0x73, 0x9d, 0x26, 0x3b,
0x74, 0xbb, 0xb4, 0xf6, 0x04, 0x70, 0xdc, 0x7a, 0xe2, 0xe4, 0xe5, 0xe4, 0xc4, 0x4a, 0x03, 0x41, 0x7c, 0x07, 0x55, 0x1e, 0x46, 0xe2, 0xfc, 0xe2, 0xb6, 0x53, 0x92, 0xc0, 0x66, 0xef, 0xa9, 0x93,
0x74, 0xe2, 0xe3, 0x6a, 0x49, 0x4c, 0x26, 0xbf, 0xf1, 0xbb, 0x13, 0xcf, 0xe7, 0x34, 0xb2, 0x4a, 0x77, 0x27, 0x27, 0x56, 0x1a, 0x4b, 0xa2, 0x13, 0x3f, 0xf6, 0x6f, 0x69, 0x85, 0xab, 0x77, 0x1c,
0xc2, 0xfd, 0x80, 0x68, 0x0b, 0x5f, 0x42, 0x25, 0x71, 0xc1, 0x2d, 0x30, 0x95, 0x17, 0x3a, 0x84, 0x77, 0xe2, 0xf9, 0x82, 0x45, 0x9d, 0xb2, 0x8e, 0xab, 0x77, 0x78, 0x04, 0xa0, 0xe2, 0x86, 0xd1,
0xf2, 0xb0, 0x7f, 0xd3, 0xff, 0xf0, 0xb9, 0x5f, 0xdd, 0x41, 0x15, 0x28, 0xf5, 0xdb, 0xef, 0x3b, 0x95, 0xc4, 0xd6, 0x54, 0xe8, 0x9d, 0x25, 0x42, 0x9f, 0xc6, 0x7c, 0x5a, 0xe7, 0x66, 0x49, 0xbe,
0x55, 0x03, 0x9d, 0xc0, 0x51, 0xaf, 0x3d, 0xf8, 0x38, 0x22, 0x9d, 0x5e, 0xa7, 0x3d, 0xe8, 0xbc, 0x40, 0xcd, 0x10, 0x48, 0x0f, 0x2a, 0x5a, 0x1e, 0xd7, 0xa1, 0x7a, 0x36, 0x3a, 0x19, 0x9d, 0x7e,
0xad, 0xee, 0xe2, 0x9f, 0x06, 0xd4, 0xb2, 0xd9, 0xb1, 0x59, 0x18, 0x30, 0x1a, 0xa7, 0x37, 0x0e, 0x1a, 0x35, 0x56, 0xb0, 0x06, 0xe5, 0x51, 0xff, 0xfd, 0xa0, 0x61, 0xe1, 0x16, 0x6c, 0x0c, 0xfb,
0xe7, 0x41, 0x9a, 0x9e, 0x34, 0x0a, 0xd3, 0x13, 0x6c, 0x1e, 0x72, 0xd7, 0x97, 0xc9, 0x09, 0xb6, 0xe3, 0x0f, 0xe7, 0x74, 0x30, 0x1c, 0xf4, 0xc7, 0x83, 0xc3, 0xc6, 0x2a, 0x79, 0x0c, 0xf5, 0x34,
0x34, 0xd0, 0x6b, 0xa8, 0xe8, 0x82, 0x32, 0x11, 0xf6, 0xde, 0xd9, 0x61, 0xeb, 0x54, 0x65, 0x9d, 0x2e, 0x56, 0xa1, 0xd4, 0x1f, 0x1f, 0xe8, 0x23, 0x87, 0x03, 0xb9, 0xb2, 0xc8, 0x4f, 0x0b, 0x9a,
0x94, 0x5e, 0xab, 0x92, 0x94, 0x86, 0x2f, 0xa0, 0xf1, 0x8e, 0x26, 0xd1, 0x0c, 0xb8, 0xcb, 0xe7, 0x59, 0x1b, 0xf9, 0x2c, 0x0c, 0x38, 0x8b, 0x7d, 0xbc, 0x0c, 0xe7, 0x41, 0xea, 0xa3, 0xda, 0x14,
0x69, 0xc1, 0x11, 0x94, 0x02, 0x77, 0x4a, 0x65, 0x40, 0x07, 0x44, 0xfe, 0x8d, 0x3f, 0x81, 0xb5, 0xfa, 0x28, 0xd9, 0x22, 0x14, 0xae, 0xaf, 0x5c, 0x94, 0x6c, 0xb5, 0xc1, 0x37, 0x50, 0x4b, 0x2a,
0x49, 0xd7, 0x19, 0xe4, 0xf0, 0xd1, 0x53, 0x28, 0xc5, 0xad, 0x95, 0xd1, 0x1f, 0xb6, 0x50, 0x36, 0xc7, 0xa5, 0x3f, 0xa5, 0x9d, 0xf5, 0xde, 0xb6, 0xf6, 0xc0, 0xd4, 0x38, 0x51, 0xa5, 0x29, 0x0d,
0x9a, 0xae, 0x40, 0x88, 0xc4, 0xb1, 0xb3, 0xfa, 0xee, 0x75, 0x18, 0x70, 0x1a, 0xf0, 0xbf, 0xc5, 0x11, 0xca, 0xd3, 0x30, 0x62, 0xca, 0xb2, 0x1a, 0x55, 0x6b, 0xb2, 0x07, 0xed, 0x23, 0x66, 0x32,
0xd1, 0x83, 0x47, 0x39, 0x7c, 0x1d, 0x48, 0x13, 0xca, 0x5a, 0x42, 0xfa, 0x14, 0x56, 0x21, 0x61, 0x1c, 0x0b, 0x57, 0xcc, 0xd3, 0x6a, 0x4b, 0x7a, 0xe0, 0x4e, 0x99, 0x4a, 0xb2, 0x4e, 0xd5, 0x9a,
0xe1, 0x3a, 0xd4, 0x86, 0xb3, 0x3b, 0x97, 0xd3, 0x04, 0x51, 0xca, 0xb8, 0x01, 0xa7, 0x6b, 0xf7, 0x7c, 0x84, 0xce, 0x7d, 0x7a, 0x72, 0xab, 0x1c, 0x3e, 0x3e, 0x83, 0x72, 0xdc, 0x57, 0xea, 0x46,
0x4a, 0x01, 0xff, 0x30, 0xe0, 0xb4, 0x1b, 0x30, 0x51, 0x73, 0x3f, 0xeb, 0x82, 0x9e, 0x89, 0x36, 0xeb, 0x3d, 0xcc, 0x66, 0x78, 0x2c, 0x11, 0xaa, 0x70, 0xe2, 0xdc, 0x8d, 0x7b, 0x10, 0x06, 0x82,
0xc6, 0x8b, 0xa0, 0x95, 0x4f, 0x94, 0xb2, 0xda, 0x96, 0xeb, 0xf8, 0x24, 0x0a, 0x47, 0xe7, 0x60, 0x05, 0xe2, 0x6f, 0x79, 0x0c, 0xe1, 0x51, 0x0e, 0x3f, 0x49, 0xa4, 0x0b, 0xd5, 0x44, 0x42, 0x9d,
0x2e, 0x5c, 0x5f, 0xf8, 0x64, 0x6b, 0xa3, 0x99, 0x72, 0x8b, 0x88, 0x66, 0xa0, 0x06, 0x94, 0xef, 0x29, 0x74, 0xc6, 0xb0, 0x48, 0x0b, 0x9a, 0x67, 0xb3, 0x2b, 0x57, 0x30, 0x83, 0x68, 0x65, 0xd2,
0xa2, 0xe5, 0x28, 0x9a, 0x07, 0xb2, 0xdf, 0x15, 0x62, 0x0a, 0x93, 0xcc, 0x03, 0xdc, 0x85, 0xfa, 0x86, 0xed, 0x85, 0xef, 0x5a, 0x81, 0xfc, 0xb0, 0x60, 0xfb, 0x38, 0xe0, 0xb2, 0x0e, 0x7e, 0xf6,
0x7a, 0x18, 0x0f, 0xad, 0x81, 0x18, 0x84, 0x61, 0xe0, 0xe5, 0xe6, 0x94, 0xd7, 0x80, 0x1b, 0xb0, 0x08, 0x3e, 0x97, 0xa5, 0x8d, 0xa7, 0x30, 0x51, 0xde, 0xd2, 0xca, 0x7a, 0x54, 0x0f, 0xe2, 0x27,
0x36, 0xe9, 0x0f, 0xd4, 0x6e, 0xfd, 0xde, 0x87, 0xe3, 0x64, 0xa6, 0xd4, 0x7e, 0x22, 0x0f, 0xfe, 0xd5, 0x38, 0xee, 0x42, 0xe5, 0xc6, 0xf5, 0xe5, 0x99, 0xac, 0x37, 0x09, 0x53, 0x8d, 0x30, 0x4d,
0x5b, 0x5d, 0x13, 0xf4, 0xbc, 0x78, 0x7d, 0xd7, 0xfe, 0x51, 0xd8, 0xe7, 0xdb, 0x50, 0x75, 0x23, 0x18, 0xd8, 0x86, 0xea, 0x55, 0x74, 0x7b, 0x1e, 0xcd, 0x03, 0xd5, 0x03, 0x35, 0x5a, 0x91, 0x5b,
0x77, 0x5e, 0x19, 0x88, 0x41, 0x75, 0x7d, 0xa6, 0xd1, 0x45, 0xfe, 0x1b, 0x05, 0xab, 0x62, 0x3b, 0x3a, 0x0f, 0xc8, 0x31, 0xb4, 0x16, 0xd3, 0x78, 0xa8, 0x07, 0xb2, 0x11, 0xce, 0x02, 0x2f, 0xf7,
0xdb, 0xd2, 0x13, 0x59, 0xb4, 0x80, 0x93, 0x8d, 0x01, 0x46, 0xff, 0x7c, 0x26, 0xbb, 0x19, 0x76, 0x4e, 0x79, 0x05, 0x38, 0x81, 0xce, 0x7d, 0xfa, 0x03, 0xb5, 0x7b, 0xbf, 0xd6, 0x60, 0xd3, 0xf4,
0x73, 0x6b, 0x7e, 0xaa, 0xfb, 0x0d, 0x8e, 0x32, 0x23, 0x8d, 0x0a, 0xaa, 0x95, 0xb7, 0x0f, 0xf6, 0x94, 0x9e, 0x60, 0xf4, 0xe0, 0xbf, 0xbb, 0xa3, 0x83, 0x2f, 0x8a, 0x07, 0x7c, 0xe1, 0x57, 0xca,
0x8b, 0xad, 0xb8, 0xa9, 0xd6, 0x14, 0x8e, 0xb3, 0xd3, 0x89, 0x0a, 0x1e, 0xc8, 0x5d, 0x25, 0xfb, 0xde, 0x5d, 0x86, 0x9a, 0x14, 0x72, 0xe5, 0xb5, 0x85, 0x1c, 0x1a, 0x8b, 0x3d, 0x8d, 0x7b, 0xf9,
0xe5, 0x76, 0xe4, 0x54, 0x4e, 0xf4, 0x71, 0x7d, 0x24, 0x8b, 0xfa, 0x58, 0x30, 0xe9, 0x45, 0x7d, 0x31, 0x0a, 0x46, 0xc5, 0x76, 0x96, 0xa5, 0x1b, 0x59, 0xbc, 0x81, 0xad, 0x7b, 0x0d, 0x8c, 0xff,
0x2c, 0x9a, 0x74, 0xbc, 0x73, 0x05, 0x5f, 0x2a, 0x09, 0xfb, 0xd6, 0x94, 0x3f, 0x60, 0x6f, 0xfe, 0x0c, 0x93, 0x9d, 0x0c, 0xbb, 0xbb, 0x34, 0x3f, 0xd5, 0xfd, 0x06, 0x1b, 0x99, 0x96, 0xc6, 0x02,
0x04, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x16, 0x42, 0xc7, 0x5a, 0x07, 0x00, 0x00, 0xb7, 0xf2, 0xe6, 0xc1, 0x7e, 0xb9, 0x14, 0x37, 0xd5, 0x9a, 0xc2, 0x66, 0xb6, 0x3b, 0xb1, 0x20,
0x40, 0xee, 0x28, 0xd9, 0xaf, 0x96, 0x23, 0xa7, 0x72, 0xb2, 0x8e, 0x8b, 0x2d, 0x59, 0x54, 0xc7,
0x82, 0x4e, 0x2f, 0xaa, 0x63, 0x51, 0xa7, 0x93, 0x95, 0x7d, 0xf8, 0x5c, 0x33, 0xec, 0x8b, 0x8a,
0xfa, 0xf7, 0x7c, 0xfb, 0x27, 0x00, 0x00, 0xff, 0xff, 0x81, 0xdd, 0x53, 0x62, 0xd7, 0x07, 0x00,
0x00,
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment