Commit 1be28d6f authored by Matt Butcher's avatar Matt Butcher

feat(*): add 'helm list --all' and related flags

This adds support for the following 'helm list' flags:

- all: show all release types
- deleted: show deleted releases
- deployed: show deployed releases
- failed: show failed releases

These flags can be toggled. Only '--deployed' is turned on by default.

On the server side, Tiller's list function can now filter based on a
slice of release.Status_Code filters. While the client only supports a
subset, the server supports all known release.Status_Code types.

Closes #973
parent 926d7931
......@@ -20,6 +20,7 @@ import "hapi/chart/chart.proto";
import "hapi/chart/config.proto";
import "hapi/release/release.proto";
import "hapi/release/info.proto";
import "hapi/release/status.proto";
option go_package = "services";
......@@ -90,7 +91,10 @@ message ListReleasesRequest {
// Anything that matches the regexp will be included in the results.
string filter = 4;
// SortOrder is the ordering directive used for sorting.
ListSort.SortOrder sort_order = 5;
repeated hapi.release.Status.Code status_codes = 6;
}
// ListSort defines sorting fields on a release list.
......
......@@ -49,6 +49,7 @@ type releaseOptions struct {
name string
version int32
chart *chart.Chart
statusCode release.Status_Code
}
func releaseMock(opts *releaseOptions) *release.Release {
......@@ -77,12 +78,17 @@ func releaseMock(opts *releaseOptions) *release.Release {
}
}
scode := release.Status_DEPLOYED
if opts.statusCode > 0 {
scode = opts.statusCode
}
return &release.Release{
Name: name,
Info: &release.Info{
FirstDeployed: &date,
LastDeployed: &date,
Status: &release.Status{Code: release.Status_DEPLOYED},
Status: &release.Status{Code: scode},
},
Chart: ch,
Config: &chart.Config{Raw: `name: "value"`},
......
......@@ -31,7 +31,10 @@ import (
)
var listHelp = `
This command lists all of the currently deployed releases.
This command lists all of the releases.
By default, it lists only releases that are deployed. Flags like '--delete' and
'--all' will alter this behavior. Such flags can be combined: '--deleted --failed'.
By default, items are sorted alphabetically. Use the '-d' flag to sort by
release date.
......@@ -61,6 +64,11 @@ type listCmd struct {
byDate bool
sortDesc bool
out io.Writer
all bool
deleted bool
deployed bool
failed bool
superseded bool
client helm.Interface
}
......@@ -91,6 +99,12 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order")
f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch")
f.StringVarP(&list.offset, "offset", "o", "", "the next release name in the list, used to offset from start value")
f.BoolVar(&list.all, "all", false, "show all releases, not just the ones marked DEPLOYED")
f.BoolVar(&list.deleted, "deleted", false, "show deleted releases")
f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
f.BoolVar(&list.failed, "failed", false, "show failed releases")
// TODO: Do we want this as a feature of 'helm list'?
//f.BoolVar(&list.superseded, "history", true, "show historical releases")
return cmd
}
......@@ -105,12 +119,15 @@ func (l *listCmd) run() error {
sortOrder = services.ListSort_DESC
}
stats := l.statusCodes()
res, err := l.client.ListReleases(
helm.ReleaseListLimit(l.limit),
helm.ReleaseListOffset(l.offset),
helm.ReleaseListFilter(l.filter),
helm.ReleaseListSort(int32(sortBy)),
helm.ReleaseListOrder(int32(sortOrder)),
helm.ReleaseListStatuses(stats),
)
if err != nil {
......@@ -138,6 +155,40 @@ func (l *listCmd) run() error {
return nil
}
// statusCodes gets the list of status codes that are to be included in the results.
func (l *listCmd) statusCodes() []release.Status_Code {
if l.all {
return []release.Status_Code{
release.Status_UNKNOWN,
release.Status_DEPLOYED,
release.Status_DELETED,
// TODO: Should we return superseded records? These are records
// that were replaced by an upgrade.
//release.Status_SUPERSEDED,
release.Status_FAILED,
}
}
status := []release.Status_Code{}
if l.deployed {
status = append(status, release.Status_DEPLOYED)
}
if l.deleted {
status = append(status, release.Status_DELETED)
}
if l.failed {
status = append(status, release.Status_FAILED)
}
if l.superseded {
status = append(status, release.Status_SUPERSEDED)
}
// Default case.
if len(status) == 0 {
status = append(status, release.Status_DEPLOYED)
}
return status
}
func formatList(rels []*release.Release) string {
table := uitable.New()
table.MaxColWidth = 30
......
......@@ -41,13 +41,33 @@ func TestListCmd(t *testing.T) {
},
{
name: "list --long",
//flags: map[string]string{"long": "1"},
args: []string{"--long"},
resp: []*release.Release{
releaseMock(&releaseOptions{name: "atlas"}),
},
expected: "NAME \tVERSION\tUPDATED \tSTATUS \tCHART \natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\n",
},
{
name: "with a release, multiple flags",
args: []string{"--deleted", "--deployed", "--failed"},
resp: []*release.Release{
releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_DELETED}),
releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}),
},
// Note: We're really only testing that the flags parsed correctly. Which results are returned
// depends on the backend. And until pkg/helm is done, we can't mock this.
expected: "thomas-guide\natlas-guide",
},
{
name: "with a release, multiple flags",
args: []string{"--all"},
resp: []*release.Release{
releaseMock(&releaseOptions{name: "thomas-guide", statusCode: release.Status_DELETED}),
releaseMock(&releaseOptions{name: "atlas-guide", statusCode: release.Status_DEPLOYED}),
},
// See note on previous test.
expected: "thomas-guide\natlas-guide",
},
}
var buf bytes.Buffer
......
......@@ -68,7 +68,20 @@ type releaseServer struct {
}
func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
rels, err := s.env.Releases.ListDeployed()
if len(req.StatusCodes) == 0 {
req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED}
}
//rels, err := s.env.Releases.ListDeployed()
rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool {
for _, sc := range req.StatusCodes {
if sc == r.Info.Status.Code {
return true
}
}
return false
})
if err != nil {
return err
}
......
......@@ -82,13 +82,17 @@ func chartStub() *chart.Chart {
// releaseStub creates a release stub, complete with the chartStub as its chart.
func releaseStub() *release.Release {
return namedReleaseStub("angry-panda", release.Status_DEPLOYED)
}
func namedReleaseStub(name string, status release.Status_Code) *release.Release {
date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
return &release.Release{
Name: "angry-panda",
Name: name,
Info: &release.Info{
FirstDeployed: &date,
LastDeployed: &date,
Status: &release.Status{Code: release.Status_DEPLOYED},
Status: &release.Status{Code: status},
},
Chart: chartStub(),
Config: &chart.Config{Raw: `name = "value"`},
......@@ -581,6 +585,71 @@ func TestListReleases(t *testing.T) {
}
}
func TestListReleasesByStatus(t *testing.T) {
rs := rsFixture()
stubs := []*release.Release{
namedReleaseStub("kamal", release.Status_DEPLOYED),
namedReleaseStub("astrolabe", release.Status_DELETED),
namedReleaseStub("octant", release.Status_FAILED),
namedReleaseStub("sextant", release.Status_UNKNOWN),
}
for _, stub := range stubs {
if err := rs.env.Releases.Create(stub); err != nil {
t.Fatalf("Could not create stub: %s", err)
}
}
tests := []struct {
statusCodes []release.Status_Code
names []string
}{
{
names: []string{"kamal"},
statusCodes: []release.Status_Code{release.Status_DEPLOYED},
},
{
names: []string{"astrolabe"},
statusCodes: []release.Status_Code{release.Status_DELETED},
},
{
names: []string{"kamal", "octant"},
statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED},
},
{
names: []string{"kamal", "astrolabe", "octant", "sextant"},
statusCodes: []release.Status_Code{
release.Status_DEPLOYED,
release.Status_DELETED,
release.Status_FAILED,
release.Status_UNKNOWN,
},
},
}
for i, tt := range tests {
mrs := &mockListServer{}
if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil {
t.Fatalf("Failed listing %d: %s", i, err)
}
if len(tt.names) != len(mrs.val.Releases) {
t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases))
}
for _, name := range tt.names {
found := false
for _, rel := range mrs.val.Releases {
if rel.Name == name {
found = true
}
}
if !found {
t.Errorf("%d: Did not find name %q", i, name)
}
}
}
}
func TestListReleasesSort(t *testing.T) {
rs := rsFixture()
......
......@@ -19,6 +19,7 @@ package helm
import (
"golang.org/x/net/context"
cpb "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
rls "k8s.io/helm/pkg/proto/hapi/services"
)
......@@ -105,6 +106,16 @@ func ReleaseListSort(sort int32) ReleaseListOption {
}
}
// ReleaseListStatuses specifies which status codes should be returned.
func ReleaseListStatuses(statuses []release.Status_Code) ReleaseListOption {
return func(opts *options) {
if len(statuses) == 0 {
statuses = []release.Status_Code{release.Status_DEPLOYED}
}
opts.listReq.StatusCodes = statuses
}
}
// InstallOption allows specifying various settings
// configurable by the helm client user for overriding
// the defaults used when running the `helm install` command.
......
......@@ -32,6 +32,7 @@ import hapi_chart3 "k8s.io/helm/pkg/proto/hapi/chart"
import hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"
import hapi_release3 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_release2 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_release1 "k8s.io/helm/pkg/proto/hapi/release"
import (
context "golang.org/x/net/context"
......@@ -114,7 +115,9 @@ type ListReleasesRequest struct {
//
// Anything that matches the regexp will be included in the results.
Filter string `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"`
// SortOrder is the ordering directive used for sorting.
SortOrder ListSort_SortOrder `protobuf:"varint,5,opt,name=sort_order,json=sortOrder,enum=hapi.services.tiller.ListSort_SortOrder" json:"sort_order,omitempty"`
StatusCodes []hapi_release1.Status_Code `protobuf:"varint,6,rep,name=status_codes,json=statusCodes,enum=hapi.release.Status_Code" json:"status_codes,omitempty"`
}
func (m *ListReleasesRequest) Reset() { *m = ListReleasesRequest{} }
......@@ -330,7 +333,7 @@ type UninstallReleaseRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// DisableHooks causes the server to skip running any hooks for the uninstall.
DisableHooks bool `protobuf:"varint,2,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
// Remove the release from the store and make its name free for later use.
// Purge removes the release from the store and make its name free for later use.
Purge bool `protobuf:"varint,3,opt,name=purge" json:"purge,omitempty"`
}
......@@ -628,54 +631,57 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
}
var fileDescriptor0 = []byte{
// 783 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xcb, 0x6e, 0xd3, 0x4c,
0x14, 0xae, 0x93, 0x34, 0x97, 0xd3, 0x8b, 0xd2, 0xf9, 0xdb, 0x26, 0xbf, 0x05, 0x08, 0x19, 0x01,
0xa5, 0x50, 0x07, 0xc2, 0x1e, 0x29, 0x6d, 0xa3, 0xb6, 0x6a, 0x48, 0x25, 0x87, 0x82, 0xc4, 0x82,
0xc8, 0x4d, 0x26, 0x8d, 0xc1, 0xb5, 0x83, 0x67, 0x52, 0xd1, 0x47, 0xe0, 0x8d, 0xd8, 0xf0, 0x36,
0xbc, 0x05, 0x1b, 0xe6, 0xe2, 0x31, 0x71, 0x62, 0x83, 0xe9, 0xc6, 0x99, 0x33, 0xe7, 0x9b, 0x73,
0xf9, 0xce, 0xa5, 0x05, 0x7d, 0x6c, 0x4f, 0x9c, 0x06, 0xc1, 0xc1, 0xb5, 0x33, 0xc0, 0xa4, 0x41,
0x1d, 0xd7, 0xc5, 0x81, 0x39, 0x09, 0x7c, 0xea, 0xa3, 0x4d, 0xae, 0x33, 0x95, 0xce, 0x94, 0x3a,
0x7d, 0x5b, 0xbc, 0x18, 0x8c, 0xed, 0x80, 0xca, 0xaf, 0x44, 0xeb, 0xb5, 0xd9, 0x7b, 0xdf, 0x1b,
0x39, 0x97, 0xa1, 0x42, 0xba, 0x08, 0xb0, 0x8b, 0x6d, 0x82, 0xd5, 0x6f, 0xec, 0x91, 0xd2, 0x39,
0xde, 0xc8, 0x97, 0x0a, 0xe3, 0x87, 0x06, 0xff, 0x75, 0x1c, 0x42, 0x2d, 0xa9, 0x22, 0x16, 0xfe,
0x3c, 0xc5, 0x84, 0xa2, 0x4d, 0x58, 0x76, 0x9d, 0x2b, 0x87, 0xd6, 0xb5, 0xfb, 0xda, 0x4e, 0xde,
0x92, 0x02, 0xda, 0x86, 0xa2, 0x3f, 0x1a, 0x11, 0x4c, 0xeb, 0x39, 0x76, 0x5d, 0xb1, 0x42, 0x09,
0xbd, 0x82, 0x12, 0xf1, 0x03, 0xda, 0xbf, 0xb8, 0xa9, 0xe7, 0x99, 0x62, 0xbd, 0xf9, 0xd0, 0x4c,
0xca, 0xc9, 0xe4, 0x9e, 0x7a, 0x0c, 0x68, 0xf2, 0xcf, 0xfe, 0x8d, 0x55, 0x24, 0xe2, 0x97, 0xdb,
0x1d, 0x39, 0x2e, 0xc5, 0x41, 0xbd, 0x20, 0xed, 0x4a, 0x09, 0x1d, 0x01, 0x08, 0xbb, 0x7e, 0x30,
0x64, 0xba, 0x65, 0x61, 0x7a, 0x27, 0x83, 0xe9, 0x33, 0x8e, 0xb7, 0x2a, 0x44, 0x1d, 0x8d, 0x0f,
0x50, 0x56, 0x00, 0xa3, 0x09, 0x45, 0xe9, 0x1e, 0xad, 0x40, 0xe9, 0xbc, 0x7b, 0xda, 0x3d, 0x7b,
0xd7, 0xad, 0x2e, 0xa1, 0x32, 0x14, 0xba, 0xad, 0xd7, 0xed, 0xaa, 0x86, 0x36, 0x60, 0xad, 0xd3,
0xea, 0xbd, 0xe9, 0x5b, 0xed, 0x4e, 0xbb, 0xd5, 0x6b, 0x1f, 0x56, 0x73, 0xc6, 0x3d, 0xa8, 0x44,
0x76, 0x51, 0x09, 0xf2, 0xad, 0xde, 0x81, 0x7c, 0x72, 0xd8, 0x66, 0x27, 0xcd, 0xf8, 0xaa, 0xc1,
0x66, 0x9c, 0x46, 0x32, 0xf1, 0x3d, 0x82, 0x39, 0x8f, 0x03, 0x7f, 0xea, 0x45, 0x3c, 0x0a, 0x01,
0x21, 0x28, 0x78, 0xf8, 0x8b, 0x62, 0x51, 0x9c, 0x39, 0x92, 0xfa, 0xd4, 0x76, 0x05, 0x83, 0x0c,
0x29, 0x04, 0xf4, 0x02, 0xca, 0x61, 0xd5, 0x08, 0xe3, 0x26, 0xbf, 0xb3, 0xd2, 0xdc, 0x92, 0xf9,
0xab, 0xfa, 0x86, 0x1e, 0xad, 0x08, 0x66, 0xec, 0x41, 0xed, 0x08, 0xab, 0x48, 0x7a, 0xd4, 0xa6,
0xd3, 0xa8, 0xaa, 0xdc, 0xaf, 0x7d, 0x85, 0x45, 0x30, 0xdc, 0x2f, 0x3b, 0x1b, 0x6f, 0xa1, 0xbe,
0x08, 0x0f, 0xa3, 0x4f, 0xc0, 0xa3, 0x47, 0x50, 0xe0, 0xfd, 0x23, 0x62, 0x5f, 0x69, 0xa2, 0x78,
0x34, 0x27, 0x4c, 0x63, 0x09, 0xbd, 0x61, 0xce, 0xda, 0x3d, 0xf0, 0x3d, 0x8a, 0x3d, 0xfa, 0xa7,
0x38, 0x3a, 0xf0, 0x7f, 0x02, 0x3e, 0x0c, 0xa4, 0x01, 0xa5, 0xd0, 0x85, 0x78, 0x93, 0xca, 0x82,
0x42, 0x19, 0xdf, 0x59, 0x41, 0xce, 0x27, 0x43, 0x9b, 0x62, 0xa5, 0x4a, 0x77, 0x8d, 0x1e, 0xb3,
0x22, 0xf1, 0x79, 0x0a, 0x73, 0xda, 0x90, 0xb6, 0xe5, 0xd0, 0x1d, 0xf0, 0xaf, 0x25, 0xf5, 0x68,
0x17, 0x8a, 0xd7, 0xb6, 0xcb, 0xec, 0x88, 0x22, 0x45, 0xd9, 0x87, 0x48, 0x31, 0x8c, 0x56, 0x88,
0x40, 0x35, 0x28, 0x0d, 0x83, 0x9b, 0x7e, 0x30, 0xf5, 0x44, 0x53, 0x97, 0xad, 0x22, 0x13, 0xad,
0xa9, 0x87, 0x1e, 0xc0, 0xda, 0xd0, 0x21, 0xf6, 0x85, 0x8b, 0xfb, 0x63, 0xdf, 0xff, 0x44, 0x44,
0x5f, 0x97, 0xad, 0xd5, 0xf0, 0xf2, 0x98, 0xdf, 0x19, 0xc7, 0xb0, 0x35, 0x17, 0xfe, 0x6d, 0x99,
0xf8, 0xa9, 0xc1, 0xd6, 0x89, 0x47, 0x58, 0x33, 0xb9, 0x73, 0x54, 0x44, 0x69, 0x6b, 0x99, 0xd3,
0xce, 0xfd, 0x4b, 0xda, 0xf9, 0x58, 0xda, 0x8a, 0xf8, 0xc2, 0x0c, 0xf1, 0x59, 0xa8, 0x40, 0x77,
0xa0, 0xc2, 0xc1, 0x64, 0x62, 0x0f, 0x70, 0xbd, 0x28, 0x5e, 0xff, 0xbe, 0x40, 0x77, 0x01, 0x02,
0x3c, 0x25, 0xb8, 0x2f, 0x8c, 0x97, 0xc4, 0xfb, 0x8a, 0xb8, 0xe9, 0xf2, 0xae, 0x3a, 0x81, 0xed,
0xf9, 0xe4, 0x6f, 0x4b, 0xe4, 0x18, 0x6a, 0xe7, 0x9e, 0x93, 0xc8, 0x64, 0x52, 0x53, 0x2d, 0xe4,
0x96, 0x4b, 0xc8, 0x8d, 0x0d, 0xfd, 0x64, 0x1a, 0x5c, 0xe2, 0x90, 0x2b, 0x29, 0x18, 0xa7, 0x50,
0x5f, 0xf4, 0x74, 0xcb, 0xb0, 0x9b, 0xdf, 0x96, 0x61, 0x5d, 0x4d, 0xb7, 0xdc, 0x99, 0xc8, 0x81,
0xd5, 0xd9, 0x65, 0x85, 0x9e, 0xa4, 0xaf, 0xd4, 0xb9, 0xbf, 0x0b, 0xfa, 0x6e, 0x16, 0xa8, 0x0c,
0xd5, 0x58, 0x7a, 0xae, 0x21, 0x02, 0xd5, 0xf9, 0xed, 0x82, 0xf6, 0x92, 0x6d, 0xa4, 0x2c, 0x2d,
0xdd, 0xcc, 0x0a, 0x57, 0x6e, 0xd1, 0x35, 0x6c, 0x2c, 0xac, 0x12, 0xf4, 0x57, 0x33, 0xf1, 0x1d,
0xa5, 0x37, 0x32, 0xe3, 0x23, 0xbf, 0x1f, 0x61, 0x2d, 0x36, 0xb4, 0x28, 0x85, 0xad, 0xa4, 0xc5,
0xa4, 0x3f, 0xcd, 0x84, 0x8d, 0x7c, 0x5d, 0xc1, 0x7a, 0xbc, 0xb1, 0x51, 0x8a, 0x81, 0xc4, 0xd9,
0xd7, 0x9f, 0x65, 0x03, 0x47, 0xee, 0x58, 0x1d, 0xe7, 0x5b, 0x32, 0xad, 0x8e, 0x29, 0x43, 0x92,
0x56, 0xc7, 0xb4, 0x4e, 0x37, 0x96, 0xf6, 0xe1, 0x7d, 0x59, 0xa1, 0x2f, 0x8a, 0xe2, 0xff, 0x95,
0x97, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x46, 0xba, 0xf9, 0x49, 0x09, 0x00, 0x00,
// 821 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x52, 0xd3, 0x40,
0x14, 0x26, 0x6d, 0xe9, 0xcf, 0x29, 0x30, 0x65, 0x05, 0x5a, 0x32, 0xea, 0x30, 0x71, 0x54, 0x44,
0x49, 0xb5, 0xde, 0x3a, 0xce, 0x94, 0xd2, 0x01, 0x86, 0x5a, 0x66, 0x52, 0xd1, 0x19, 0x2f, 0xec,
0x84, 0x76, 0x4b, 0xa3, 0x21, 0xa9, 0xd9, 0x2d, 0x23, 0x8f, 0xe0, 0x6b, 0xf8, 0x14, 0xde, 0xf8,
0x64, 0xde, 0xb8, 0x3f, 0x49, 0x6c, 0xda, 0x44, 0x23, 0x37, 0xe9, 0xee, 0x9e, 0x6f, 0xbf, 0x73,
0xce, 0x77, 0xf6, 0x1c, 0x00, 0x75, 0x6c, 0x4e, 0xac, 0x3a, 0xc1, 0xde, 0xb5, 0x35, 0xc0, 0xa4,
0x4e, 0x2d, 0xdb, 0xc6, 0x9e, 0x3e, 0xf1, 0x5c, 0xea, 0xa2, 0x0d, 0x6e, 0xd3, 0x03, 0x9b, 0x2e,
0x6d, 0xea, 0x96, 0xb8, 0x31, 0x18, 0x9b, 0x1e, 0x95, 0x5f, 0x89, 0x56, 0xab, 0xb3, 0xe7, 0xae,
0x33, 0xb2, 0x2e, 0x7d, 0x83, 0x74, 0xe1, 0x61, 0x1b, 0x9b, 0x04, 0x07, 0xbf, 0x91, 0x4b, 0x81,
0xcd, 0x72, 0x46, 0xae, 0x6f, 0xd8, 0x8e, 0x18, 0x08, 0x35, 0xe9, 0x94, 0x48, 0x93, 0xf6, 0x3d,
0x03, 0x77, 0x3a, 0x16, 0xa1, 0x86, 0x34, 0x12, 0x03, 0x7f, 0x99, 0x62, 0x42, 0xd1, 0x06, 0x2c,
0xdb, 0xd6, 0x95, 0x45, 0x6b, 0xca, 0x8e, 0xb2, 0x9b, 0x35, 0xe4, 0x06, 0x6d, 0x41, 0xde, 0x1d,
0x8d, 0x08, 0xa6, 0xb5, 0x0c, 0x3b, 0x2e, 0x19, 0xfe, 0x0e, 0xbd, 0x86, 0x02, 0x71, 0x3d, 0xda,
0xbf, 0xb8, 0xa9, 0x65, 0x99, 0x61, 0xad, 0xf1, 0x50, 0x8f, 0x4b, 0x57, 0xe7, 0x9e, 0x7a, 0x0c,
0xa8, 0xf3, 0xcf, 0xc1, 0x8d, 0x91, 0x27, 0xe2, 0x97, 0xf3, 0x8e, 0x2c, 0x9b, 0x62, 0xaf, 0x96,
0x93, 0xbc, 0x72, 0x87, 0x8e, 0x00, 0x04, 0xaf, 0xeb, 0x0d, 0x99, 0x6d, 0x59, 0x50, 0xef, 0xa6,
0xa0, 0x3e, 0xe3, 0x78, 0xa3, 0x44, 0x82, 0x25, 0x7a, 0x05, 0x2b, 0x32, 0xed, 0xfe, 0xc0, 0x1d,
0x62, 0x52, 0xcb, 0xef, 0x64, 0x19, 0xd5, 0xb6, 0xa4, 0x0a, 0x54, 0xec, 0x49, 0x61, 0x5a, 0x0c,
0x61, 0x94, 0x25, 0x9c, 0xaf, 0x89, 0xf6, 0x11, 0x8a, 0x01, 0xbd, 0xd6, 0x80, 0xbc, 0x0c, 0x1e,
0x95, 0xa1, 0x70, 0xde, 0x3d, 0xed, 0x9e, 0xbd, 0xef, 0x56, 0x96, 0x50, 0x11, 0x72, 0xdd, 0xe6,
0x9b, 0x76, 0x45, 0x41, 0xeb, 0xb0, 0xda, 0x69, 0xf6, 0xde, 0xf6, 0x8d, 0x76, 0xa7, 0xdd, 0xec,
0xb5, 0x0f, 0x2b, 0x19, 0xed, 0x3e, 0x94, 0xc2, 0xa8, 0x50, 0x01, 0xb2, 0xcd, 0x5e, 0x4b, 0x5e,
0x39, 0x6c, 0xb3, 0x95, 0xa2, 0x7d, 0x53, 0x60, 0x23, 0x5a, 0x04, 0x32, 0x71, 0x1d, 0x82, 0x79,
0x15, 0x06, 0xee, 0xd4, 0x09, 0xab, 0x20, 0x36, 0x08, 0x41, 0xce, 0xc1, 0x5f, 0x83, 0x1a, 0x88,
0x35, 0x47, 0x52, 0x97, 0x9a, 0xb6, 0xd0, 0x9f, 0x21, 0xc5, 0x06, 0xbd, 0x80, 0xa2, 0x9f, 0x1c,
0x61, 0xca, 0x66, 0x77, 0xcb, 0x8d, 0xcd, 0x68, 0xca, 0xbe, 0x47, 0x23, 0x84, 0x69, 0xfb, 0x50,
0x3d, 0xc2, 0x41, 0x24, 0x52, 0x91, 0xe0, 0x4d, 0x70, 0xbf, 0xe6, 0x15, 0x16, 0xc1, 0x70, 0xbf,
0x6c, 0xad, 0xbd, 0x83, 0xda, 0x22, 0xdc, 0x8f, 0x3e, 0x06, 0x8f, 0x1e, 0x41, 0x8e, 0x3f, 0x4c,
0x11, 0x7b, 0xb9, 0x81, 0xa2, 0xd1, 0x9c, 0x30, 0x8b, 0x21, 0xec, 0x9a, 0x3e, 0xcb, 0xdb, 0x72,
0x1d, 0x8a, 0x1d, 0xfa, 0xb7, 0x38, 0x3a, 0xb0, 0x1d, 0x83, 0xf7, 0x03, 0xa9, 0x43, 0xc1, 0x77,
0x21, 0xee, 0x24, 0xaa, 0x10, 0xa0, 0xb4, 0x9f, 0xac, 0x20, 0xe7, 0x93, 0xa1, 0x49, 0x71, 0x60,
0x4a, 0x76, 0x8d, 0x1e, 0xb3, 0x22, 0xf1, 0x46, 0xf5, 0x73, 0x5a, 0x97, 0xdc, 0xb2, 0x9b, 0x5b,
0xfc, 0x6b, 0x48, 0x3b, 0xda, 0x83, 0xfc, 0xb5, 0x69, 0x33, 0x1e, 0x51, 0xa4, 0x30, 0x7b, 0x1f,
0x29, 0xba, 0xdc, 0xf0, 0x11, 0xa8, 0x0a, 0x85, 0xa1, 0x77, 0xd3, 0xf7, 0xa6, 0x8e, 0x68, 0x89,
0xa2, 0x91, 0x67, 0x5b, 0x63, 0xea, 0xa0, 0x07, 0xb0, 0x3a, 0xb4, 0x88, 0x79, 0x61, 0xe3, 0xfe,
0xd8, 0x75, 0x3f, 0x13, 0xd1, 0x15, 0x45, 0x63, 0xc5, 0x3f, 0x3c, 0xe6, 0x67, 0xda, 0x31, 0x6c,
0xce, 0x85, 0x7f, 0x5b, 0x25, 0x7e, 0x29, 0xb0, 0x79, 0xe2, 0xb0, 0x66, 0xb0, 0xed, 0x39, 0x29,
0xc2, 0xb4, 0x95, 0xd4, 0x69, 0x67, 0xfe, 0x27, 0xed, 0x6c, 0x24, 0xed, 0x40, 0xf8, 0xdc, 0x8c,
0xf0, 0x69, 0xa4, 0x40, 0x77, 0xa1, 0xc4, 0xc1, 0x64, 0x62, 0x0e, 0x30, 0x6b, 0x7b, 0x7e, 0xfb,
0xcf, 0x01, 0xba, 0x07, 0xe0, 0xe1, 0x29, 0xc1, 0x7d, 0x41, 0x5e, 0x10, 0xf7, 0x4b, 0xe2, 0xa4,
0xcb, 0x5f, 0xd5, 0x09, 0x6c, 0xcd, 0x27, 0x7f, 0x5b, 0x21, 0xc7, 0x50, 0x3d, 0x77, 0xac, 0x58,
0x25, 0xe3, 0x1e, 0xd5, 0x42, 0x6e, 0x99, 0x98, 0xdc, 0x58, 0xd3, 0x4f, 0xa6, 0xde, 0x25, 0xf6,
0xb5, 0x92, 0x1b, 0xed, 0x14, 0x6a, 0x8b, 0x9e, 0x6e, 0x19, 0x76, 0xe3, 0xc7, 0x32, 0xac, 0x05,
0xdd, 0x2d, 0x27, 0x2e, 0xb2, 0x60, 0x65, 0x76, 0x58, 0xa1, 0x27, 0xc9, 0x03, 0x79, 0xee, 0xaf,
0x8a, 0xba, 0x97, 0x06, 0x2a, 0x43, 0xd5, 0x96, 0x9e, 0x2b, 0x88, 0x40, 0x65, 0x7e, 0xba, 0xa0,
0xfd, 0x78, 0x8e, 0x84, 0xa1, 0xa5, 0xea, 0x69, 0xe1, 0x81, 0x5b, 0x74, 0x0d, 0xeb, 0x0b, 0xa3,
0x04, 0xfd, 0x93, 0x26, 0x3a, 0xa3, 0xd4, 0x7a, 0x6a, 0x7c, 0xe8, 0xf7, 0x13, 0xac, 0x46, 0x9a,
0x16, 0x25, 0xa8, 0x15, 0x37, 0x98, 0xd4, 0xa7, 0xa9, 0xb0, 0xa1, 0xaf, 0x2b, 0x58, 0x8b, 0x3e,
0x6c, 0x94, 0x40, 0x10, 0xdb, 0xfb, 0xea, 0xb3, 0x74, 0xe0, 0xd0, 0x1d, 0xab, 0xe3, 0xfc, 0x93,
0x4c, 0xaa, 0x63, 0x42, 0x93, 0x24, 0xd5, 0x31, 0xe9, 0xa5, 0x6b, 0x4b, 0x07, 0xf0, 0xa1, 0x18,
0xa0, 0x2f, 0xf2, 0xe2, 0xbf, 0x9d, 0x97, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x44, 0xdd, 0xaf,
0x38, 0xa2, 0x09, 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