Commit cc24e3f5 authored by Ville Aikas's avatar Ville Aikas Committed by GitHub

Merge pull request #1090 from vaikas-google/nodes.txt

First cut of handling NOTES.txt file
parents 27480470 7c4cad5c
......@@ -38,6 +38,10 @@ message Status {
Code code = 1;
google.protobuf.Any details = 2;
// Cluster resources as kubectl would print them.
string resources = 3;
// Contains the rendered templates/NOTES.txt if available
string notes = 4;
}
......@@ -142,6 +142,9 @@ message GetReleaseStatusResponse {
// Info contains information about the release.
hapi.release.Info info = 2;
// Namesapce the release was released into
string namespace = 3;
}
// GetReleaseContentRequest is a request to get the contents of a release.
......
......@@ -67,10 +67,15 @@ func (s *statusCmd) run() error {
}
fmt.Fprintf(s.out, "Last Deployed: %s\n", timeconv.String(res.Info.LastDeployed))
fmt.Fprintf(s.out, "Namespace: %s\n", res.Namespace)
fmt.Fprintf(s.out, "Status: %s\n", res.Info.Status.Code)
fmt.Fprintf(s.out, "Resources:\n%s\n", res.Info.Status.Resources)
if res.Info.Status.Details != nil {
fmt.Fprintf(s.out, "Details: %s\n", res.Info.Status.Details)
}
fmt.Fprintf(s.out, "\n")
fmt.Fprintf(s.out, "Resources:\n%s\n", res.Info.Status.Resources)
if len(res.Info.Status.Notes) > 0 {
fmt.Fprintf(s.out, "Notes:\n%s\n", res.Info.Status.Notes)
}
return nil
}
......@@ -23,6 +23,7 @@ import (
"log"
"regexp"
"sort"
"strings"
"github.com/ghodss/yaml"
"github.com/technosophos/moniker"
......@@ -47,6 +48,12 @@ var srv *releaseServer
// characters in length. See https://github.com/kubernetes/helm/issues/1071
const releaseNameMaxLen = 14
// NOTESFILE_SUFFIX that we want to treat special. It goes through the templating engine
// but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually
// wants to see this file after rendering in the status command. However, it must be a suffix
// since there can be filepath in front of it.
const notesFileSuffix = "NOTES.txt"
func init() {
srv = &releaseServer{
env: env,
......@@ -179,6 +186,9 @@ func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
if rel.Info == nil {
return nil, errors.New("release info is missing")
}
if rel.Chart == nil {
return nil, errors.New("release chart is missing")
}
// Ok, we got the status of the release as we had jotted down, now we need to match the
// manifest we stashed away with reality from the cluster.
......@@ -190,7 +200,7 @@ func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease
}
rel.Info.Status.Resources = resp
return &services.GetReleaseStatusResponse{Info: rel.Info}, nil
return &services.GetReleaseStatusResponse{Info: rel.Info, Namespace: rel.Namespace}, nil
}
func (s *releaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleaseContentRequest) (*services.GetReleaseContentResponse, error) {
......@@ -281,7 +291,7 @@ func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
return nil, nil, err
}
hooks, manifestDoc, err := s.renderResources(req.Chart, valuesToRender)
hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender)
if err != nil {
return nil, nil, err
}
......@@ -302,6 +312,9 @@ func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
Hooks: hooks,
}
if len(notesTxt) > 0 {
updatedRelease.Info.Status.Notes = notesTxt
}
return currentRelease, updatedRelease, nil
}
......@@ -389,7 +402,7 @@ func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
return nil, err
}
hooks, manifestDoc, err := s.renderResources(req.Chart, valuesToRender)
hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender)
if err != nil {
return nil, err
}
......@@ -409,6 +422,9 @@ func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
Hooks: hooks,
Version: 1,
}
if len(notesTxt) > 0 {
rel.Info.Status.Notes = notesTxt
}
return rel, nil
}
......@@ -437,11 +453,24 @@ func (s *releaseServer) getVersionSet() (versionSet, error) {
return newVersionSet(versions...), nil
}
func (s *releaseServer) renderResources(ch *chart.Chart, values chartutil.Values) ([]*release.Hook, *bytes.Buffer, error) {
func (s *releaseServer) renderResources(ch *chart.Chart, values chartutil.Values) ([]*release.Hook, *bytes.Buffer, string, error) {
renderer := s.engine(ch)
files, err := renderer.Render(ch, values)
if err != nil {
return nil, nil, err
return nil, nil, "", err
}
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
// pull it out of here into a separate file so that we can actually use the output of the rendered
// text file. We have to spin through this map because the file contains path information, so we
// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
// it in the sortHooks.
notes := ""
for k, v := range files {
if strings.HasSuffix(k, notesFileSuffix) {
notes = v
delete(files, k)
}
}
// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
......@@ -449,13 +478,13 @@ func (s *releaseServer) renderResources(ch *chart.Chart, values chartutil.Values
// removed here.
vs, err := s.getVersionSet()
if err != nil {
return nil, nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err)
return nil, nil, "", fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err)
}
hooks, manifests, err := sortManifests(files, vs)
if err != nil {
// By catching parse errors here, we can prevent bogus releases from going
// to Kubernetes.
return nil, nil, err
return nil, nil, "", err
}
// Aggregate all valid manifests into one big doc.
......@@ -465,7 +494,7 @@ func (s *releaseServer) renderResources(ch *chart.Chart, values chartutil.Values
b.WriteString(file)
}
return hooks, b, nil
return hooks, b, notes, nil
}
// validateYAML checks to see if YAML is well-formed.
......
......@@ -35,6 +35,8 @@ import (
"k8s.io/helm/pkg/storage/driver"
)
const notesText = "my notes here"
var manifestWithHook = `apiVersion: v1
kind: ConfigMap
metadata:
......@@ -230,6 +232,137 @@ func TestInstallRelease(t *testing.T) {
}
}
func TestInstallReleaseWithNotes(t *testing.T) {
c := context.Background()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "hello", Data: []byte("hello: world")},
{Name: "hooks", Data: []byte(manifestWithHook)},
{Name: "NOTES.txt", Data: []byte(notesText)},
},
},
}
res, err := rs.InstallRelease(c, req)
if err != nil {
t.Errorf("Failed install: %s", err)
}
if res.Release.Name == "" {
t.Errorf("Expected release name.")
}
if res.Release.Namespace != "spaced" {
t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace)
}
rel, err := rs.env.Releases.Get(res.Release.Name)
if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
}
t.Logf("rel: %v", rel)
if len(rel.Hooks) != 1 {
t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks))
}
if rel.Hooks[0].Manifest != manifestWithHook {
t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest)
}
if rel.Info.Status.Notes != notesText {
t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes)
}
if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL {
t.Errorf("Expected event 0 is post install")
}
if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE {
t.Errorf("Expected event 0 is pre-delete")
}
if len(res.Release.Manifest) == 0 {
t.Errorf("No manifest returned: %v", res.Release)
}
if len(rel.Manifest) == 0 {
t.Errorf("Expected manifest in %v", res)
}
if !strings.Contains(rel.Manifest, "---\n# Source: hello/hello\nhello: world") {
t.Errorf("unexpected output: %s", rel.Manifest)
}
}
func TestInstallReleaseWithNotesRendered(t *testing.T) {
c := context.Background()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "hello", Data: []byte("hello: world")},
{Name: "hooks", Data: []byte(manifestWithHook)},
{Name: "NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")},
},
},
}
res, err := rs.InstallRelease(c, req)
if err != nil {
t.Errorf("Failed install: %s", err)
}
if res.Release.Name == "" {
t.Errorf("Expected release name.")
}
if res.Release.Namespace != "spaced" {
t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace)
}
rel, err := rs.env.Releases.Get(res.Release.Name)
if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
}
t.Logf("rel: %v", rel)
if len(rel.Hooks) != 1 {
t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks))
}
if rel.Hooks[0].Manifest != manifestWithHook {
t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest)
}
expectedNotes := fmt.Sprintf("%s %s", notesText, res.Release.Name)
if rel.Info.Status.Notes != expectedNotes {
t.Fatalf("Expected '%s', got '%s'", expectedNotes, rel.Info.Status.Notes)
}
if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL {
t.Errorf("Expected event 0 is post install")
}
if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE {
t.Errorf("Expected event 0 is pre-delete")
}
if len(res.Release.Manifest) == 0 {
t.Errorf("No manifest returned: %v", res.Release)
}
if len(rel.Manifest) == 0 {
t.Errorf("Expected manifest in %v", res)
}
if !strings.Contains(rel.Manifest, "---\n# Source: hello/hello\nhello: world") {
t.Errorf("unexpected output: %s", rel.Manifest)
}
}
func TestInstallReleaseDryRun(t *testing.T) {
c := context.Background()
rs := rsFixture()
......
......@@ -54,7 +54,7 @@ func New() *Engine {
}
}
// Render takes a chart, optional values, and value overrids, and attempts to render the Go templates.
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
//
// Render can be called repeatedly on the same engine.
//
......
......@@ -55,6 +55,8 @@ type Status struct {
Details *google_protobuf1.Any `protobuf:"bytes,2,opt,name=details" json:"details,omitempty"`
// Cluster resources as kubectl would print them.
Resources string `protobuf:"bytes,3,opt,name=resources" json:"resources,omitempty"`
// Contains the rendered templates/NOTES.txt if available
Notes string `protobuf:"bytes,4,opt,name=notes" json:"notes,omitempty"`
}
func (m *Status) Reset() { *m = Status{} }
......@@ -75,21 +77,22 @@ func init() {
}
var fileDescriptor3 = []byte{
// 247 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0xcc, 0x48, 0x2c, 0xc8,
0xd4, 0x2f, 0x4a, 0xcd, 0x49, 0x4d, 0x2c, 0x4e, 0xd5, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0x49, 0xe9, 0x41, 0xa5, 0xa4, 0x24, 0xd3, 0xf3,
0xf3, 0xd3, 0x73, 0x52, 0xf5, 0xc1, 0x72, 0x49, 0xa5, 0x69, 0xfa, 0x89, 0x79, 0x95, 0x10, 0x85,
0x4a, 0x17, 0x19, 0xb9, 0xd8, 0x82, 0xc1, 0x3a, 0x85, 0x74, 0xb9, 0x58, 0x92, 0xf3, 0x53, 0x52,
0x25, 0x18, 0x15, 0x18, 0x35, 0xf8, 0x8c, 0x24, 0xf5, 0x90, 0x8d, 0xd0, 0x83, 0xa8, 0xd1, 0x73,
0x06, 0x2a, 0x08, 0x02, 0x2b, 0x13, 0xd2, 0xe3, 0x62, 0x4f, 0x49, 0x2d, 0x49, 0xcc, 0xcc, 0x29,
0x96, 0x60, 0x02, 0xea, 0xe0, 0x36, 0x12, 0xd1, 0x83, 0x58, 0xa3, 0x07, 0xb3, 0x46, 0xcf, 0x31,
0xaf, 0x32, 0x08, 0xa6, 0x48, 0x48, 0x86, 0x8b, 0xb3, 0x28, 0xb5, 0x38, 0xbf, 0xb4, 0x28, 0x39,
0xb5, 0x58, 0x82, 0x19, 0xa8, 0x83, 0x33, 0x08, 0x21, 0xa0, 0xe4, 0xc5, 0xc5, 0x02, 0x32, 0x5b,
0x88, 0x9b, 0x8b, 0x3d, 0xd4, 0xcf, 0xdb, 0xcf, 0x3f, 0xdc, 0x4f, 0x80, 0x41, 0x88, 0x87, 0x8b,
0xc3, 0xc5, 0x35, 0xc0, 0xc7, 0x3f, 0xd2, 0xd5, 0x45, 0x80, 0x11, 0x24, 0xe5, 0xe2, 0xea, 0xe3,
0x1a, 0x02, 0xe4, 0x30, 0x09, 0xf1, 0x71, 0x71, 0x05, 0x87, 0x06, 0xb8, 0x06, 0x05, 0xbb, 0xba,
0x00, 0xf9, 0xcc, 0x42, 0x5c, 0x5c, 0x6c, 0x6e, 0x8e, 0x9e, 0x3e, 0x40, 0x36, 0x8b, 0x13, 0x67,
0x14, 0x3b, 0xd4, 0xd9, 0x49, 0x6c, 0x60, 0xb7, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xd1,
0xc3, 0xbf, 0x50, 0x2b, 0x01, 0x00, 0x00,
// 259 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8f, 0xc1, 0x4e, 0xf2, 0x40,
0x14, 0x85, 0xff, 0x42, 0xff, 0xd6, 0x5e, 0x08, 0x21, 0x37, 0x2c, 0x5a, 0xe3, 0xc2, 0xb0, 0x72,
0xe3, 0x6d, 0x82, 0x4f, 0x80, 0x76, 0x4c, 0xd4, 0xa6, 0x90, 0x56, 0x62, 0x74, 0x37, 0xc0, 0x88,
0x24, 0x4d, 0x87, 0x74, 0xa6, 0x0b, 0x9e, 0xd8, 0xd7, 0x70, 0x3a, 0x85, 0xe8, 0xae, 0xa7, 0xdf,
0x77, 0xe6, 0xcc, 0x40, 0xf4, 0xc5, 0x0f, 0xfb, 0xb8, 0x16, 0xa5, 0xe0, 0x4a, 0xc4, 0x4a, 0x73,
0xdd, 0x28, 0x3a, 0xd4, 0x52, 0x4b, 0x1c, 0xb6, 0x88, 0x4e, 0xe8, 0x32, 0xda, 0x49, 0xb9, 0x2b,
0x45, 0x6c, 0xd9, 0xba, 0xf9, 0x8c, 0x79, 0x75, 0xec, 0xc4, 0xe9, 0xb7, 0x03, 0x5e, 0x61, 0x9b,
0x78, 0x0b, 0xee, 0x46, 0x6e, 0x45, 0xe8, 0x5c, 0x3b, 0x37, 0xa3, 0x59, 0x44, 0x7f, 0x8f, 0xa0,
0xce, 0xa1, 0x07, 0x23, 0xe4, 0x56, 0x43, 0x02, 0x7f, 0x2b, 0x34, 0xdf, 0x97, 0x2a, 0xec, 0x99,
0xc6, 0x60, 0x36, 0xa1, 0x6e, 0x86, 0xce, 0x33, 0x34, 0xaf, 0x8e, 0xf9, 0x59, 0xc2, 0x2b, 0x08,
0x6a, 0xa1, 0x64, 0x53, 0x6f, 0x84, 0x0a, 0xfb, 0xa6, 0x11, 0xe4, 0xbf, 0x3f, 0x70, 0x02, 0xff,
0x2b, 0xa9, 0x0d, 0x71, 0x2d, 0xe9, 0xc2, 0xf4, 0x19, 0xdc, 0x76, 0x11, 0x07, 0xe0, 0xaf, 0xb2,
0x97, 0x6c, 0xf1, 0x96, 0x8d, 0xff, 0xe1, 0x10, 0x2e, 0x12, 0xb6, 0x4c, 0x17, 0xef, 0x2c, 0x19,
0x3b, 0x2d, 0x4a, 0x58, 0xca, 0x5e, 0x4d, 0xe8, 0xe1, 0x08, 0xa0, 0x58, 0x2d, 0x59, 0x5e, 0xb0,
0xc4, 0xe4, 0x3e, 0x02, 0x78, 0x8f, 0xf3, 0xa7, 0xd4, 0x7c, 0xbb, 0xf7, 0xc1, 0x87, 0x7f, 0x7a,
0xcc, 0xda, 0xb3, 0x37, 0xbc, 0xfb, 0x09, 0x00, 0x00, 0xff, 0xff, 0xae, 0x07, 0x47, 0x1f, 0x41,
0x01, 0x00, 0x00,
}
......@@ -176,6 +176,8 @@ type GetReleaseStatusResponse struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Info contains information about the release.
Info *hapi_release2.Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
// Namesapce the release was released into
Namespace string `protobuf:"bytes,3,opt,name=namespace" json:"namespace,omitempty"`
}
func (m *GetReleaseStatusResponse) Reset() { *m = GetReleaseStatusResponse{} }
......@@ -631,57 +633,57 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
}
var fileDescriptor0 = []byte{
// 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,
// 829 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x4e, 0xe3, 0x46,
0x14, 0xc6, 0x49, 0x70, 0x92, 0x13, 0x40, 0x61, 0x0a, 0x24, 0x58, 0x6d, 0x85, 0x5c, 0xb5, 0xa5,
0xb4, 0x38, 0x6d, 0x7a, 0x5b, 0x55, 0x0a, 0x21, 0x02, 0x44, 0x1a, 0xa4, 0x49, 0x51, 0xa5, 0x5e,
0x34, 0x32, 0xc9, 0x84, 0xb8, 0x6b, 0xec, 0xac, 0x67, 0x82, 0x96, 0x47, 0xd8, 0xd7, 0xd8, 0xa7,
0xd8, 0x9b, 0x7d, 0xb2, 0xbd, 0xd9, 0xf9, 0xb1, 0xbd, 0x71, 0x62, 0xef, 0x7a, 0xb9, 0x71, 0xe6,
0xcc, 0xf9, 0xe6, 0xfc, 0x7c, 0xe7, 0x07, 0xc0, 0x98, 0xd9, 0x73, 0xa7, 0x45, 0x49, 0xf0, 0xe8,
0x8c, 0x09, 0x6d, 0x31, 0xc7, 0x75, 0x49, 0x60, 0xcd, 0x03, 0x9f, 0xf9, 0x68, 0x4f, 0xe8, 0xac,
0x48, 0x67, 0x29, 0x9d, 0x71, 0x20, 0x5f, 0x8c, 0x67, 0x76, 0xc0, 0xd4, 0x57, 0xa1, 0x8d, 0xc6,
0xf2, 0xbd, 0xef, 0x4d, 0x9d, 0xfb, 0x50, 0xa1, 0x5c, 0x04, 0xc4, 0x25, 0x36, 0x25, 0xd1, 0x6f,
0xe2, 0x51, 0xa4, 0x73, 0xbc, 0xa9, 0x1f, 0x2a, 0x0e, 0x13, 0x0a, 0xca, 0x6c, 0xb6, 0xa0, 0x4a,
0x65, 0xbe, 0x29, 0xc0, 0x57, 0x7d, 0x87, 0x32, 0xac, 0x94, 0x14, 0x93, 0x97, 0x0b, 0x42, 0x19,
0xda, 0x83, 0x4d, 0xd7, 0x79, 0x70, 0x58, 0x53, 0x3b, 0xd2, 0x8e, 0x8b, 0x58, 0x09, 0xe8, 0x00,
0x74, 0x7f, 0x3a, 0xa5, 0x84, 0x35, 0x0b, 0xfc, 0xba, 0x8a, 0x43, 0x09, 0xfd, 0x09, 0x65, 0xea,
0x07, 0x6c, 0x74, 0xf7, 0xd4, 0x2c, 0x72, 0xc5, 0x4e, 0xfb, 0x7b, 0x2b, 0x2d, 0x5d, 0x4b, 0x78,
0x1a, 0x72, 0xa0, 0x25, 0x3e, 0x67, 0x4f, 0x58, 0xa7, 0xf2, 0x57, 0xd8, 0x9d, 0x3a, 0x2e, 0x23,
0x41, 0xb3, 0xa4, 0xec, 0x2a, 0x09, 0x5d, 0x00, 0x48, 0xbb, 0x7e, 0x30, 0xe1, 0xba, 0x4d, 0x69,
0xfa, 0x38, 0x87, 0xe9, 0x1b, 0x81, 0xc7, 0x55, 0x1a, 0x1d, 0xd1, 0x1f, 0xb0, 0xa5, 0xd2, 0x1e,
0x8d, 0xfd, 0x09, 0xa1, 0x4d, 0xfd, 0xa8, 0xc8, 0x4d, 0x1d, 0x2a, 0x53, 0x11, 0x8b, 0x43, 0x45,
0x4c, 0x97, 0x23, 0x70, 0x4d, 0xc1, 0xc5, 0x99, 0x9a, 0xff, 0x41, 0x25, 0x32, 0x6f, 0xb6, 0x41,
0x57, 0xc1, 0xa3, 0x1a, 0x94, 0x6f, 0x07, 0xd7, 0x83, 0x9b, 0x7f, 0x06, 0xf5, 0x0d, 0x54, 0x81,
0xd2, 0xa0, 0xf3, 0x57, 0xaf, 0xae, 0xa1, 0x5d, 0xd8, 0xee, 0x77, 0x86, 0x7f, 0x8f, 0x70, 0xaf,
0xdf, 0xeb, 0x0c, 0x7b, 0xe7, 0xf5, 0x82, 0xf9, 0x2d, 0x54, 0xe3, 0xa8, 0x50, 0x19, 0x8a, 0x9d,
0x61, 0x57, 0x3d, 0x39, 0xef, 0xf1, 0x93, 0x66, 0xbe, 0xd6, 0x60, 0x2f, 0x59, 0x04, 0x3a, 0xf7,
0x3d, 0x4a, 0x44, 0x15, 0xc6, 0xfe, 0xc2, 0x8b, 0xab, 0x20, 0x05, 0x84, 0xa0, 0xe4, 0x91, 0x57,
0x51, 0x0d, 0xe4, 0x59, 0x20, 0x99, 0xcf, 0x6c, 0x57, 0xf2, 0xcf, 0x91, 0x52, 0x40, 0xbf, 0x41,
0x25, 0x4c, 0x8e, 0x72, 0x66, 0x8b, 0xc7, 0xb5, 0xf6, 0x7e, 0x32, 0xe5, 0xd0, 0x23, 0x8e, 0x61,
0xe6, 0x29, 0x34, 0x2e, 0x48, 0x14, 0x89, 0x62, 0x24, 0xea, 0x09, 0xe1, 0xd7, 0x7e, 0x20, 0x32,
0x18, 0xe1, 0x97, 0x9f, 0x4d, 0x06, 0xcd, 0x75, 0x78, 0x18, 0x7d, 0x0a, 0x1e, 0xfd, 0x00, 0x25,
0xd1, 0x98, 0x32, 0xf6, 0x5a, 0x1b, 0x25, 0xa3, 0xb9, 0xe2, 0x1a, 0x2c, 0xf5, 0xe8, 0x6b, 0xa8,
0x0a, 0x3c, 0x9d, 0xdb, 0x63, 0x22, 0x73, 0xaa, 0xe2, 0x8f, 0x17, 0xa6, 0xb5, 0xec, 0xb5, 0xeb,
0x7b, 0x8c, 0x78, 0xec, 0x53, 0x51, 0xf6, 0xe1, 0x30, 0x05, 0x1f, 0x86, 0xd9, 0x82, 0x72, 0x18,
0x80, 0x7c, 0x93, 0xc9, 0x51, 0x84, 0x32, 0xdf, 0xf1, 0x72, 0xdd, 0xce, 0x27, 0x36, 0x23, 0x91,
0x2a, 0xdb, 0x35, 0xfa, 0x91, 0x97, 0x50, 0x8c, 0x71, 0x98, 0xf1, 0xae, 0xb2, 0xad, 0x66, 0xbd,
0x2b, 0xbe, 0x58, 0xe9, 0xd1, 0x09, 0xe8, 0x8f, 0xb6, 0xcb, 0xed, 0xc8, 0x74, 0x63, 0x6e, 0x42,
0xa4, 0xdc, 0x01, 0x38, 0x44, 0xa0, 0x06, 0x94, 0x27, 0xc1, 0xd3, 0x28, 0x58, 0x78, 0x72, 0x60,
0x2a, 0x58, 0xe7, 0x22, 0x5e, 0x78, 0xe8, 0x3b, 0xd8, 0x9e, 0x38, 0xd4, 0xbe, 0x73, 0xc9, 0x68,
0xe6, 0xfb, 0x2f, 0xa8, 0x9c, 0x99, 0x0a, 0xde, 0x0a, 0x2f, 0x2f, 0xc5, 0x9d, 0x79, 0x09, 0xfb,
0x2b, 0xe1, 0x3f, 0x97, 0x89, 0xf7, 0x1a, 0xec, 0x5f, 0x79, 0x7c, 0x54, 0x5c, 0x77, 0x85, 0x8a,
0x38, 0x6d, 0x2d, 0x77, 0xda, 0x85, 0x2f, 0x49, 0xbb, 0x98, 0x48, 0x3b, 0x22, 0xbe, 0xb4, 0x44,
0x7c, 0x1e, 0x2a, 0x92, 0x6d, 0xa6, 0xaf, 0xb4, 0x19, 0xfa, 0x06, 0x20, 0x20, 0x0b, 0x4a, 0x46,
0xd2, 0x78, 0x59, 0xbe, 0xaf, 0xca, 0x9b, 0x81, 0xe8, 0xaa, 0x2b, 0x38, 0x58, 0x4d, 0xfe, 0xb9,
0x44, 0xce, 0xa0, 0x71, 0xeb, 0x39, 0xa9, 0x4c, 0xa6, 0x35, 0xd5, 0x5a, 0x6e, 0x85, 0x94, 0xdc,
0xf8, 0x4a, 0x98, 0x2f, 0x82, 0x7b, 0x12, 0x72, 0xa5, 0x04, 0xf3, 0x1a, 0x9a, 0xeb, 0x9e, 0x9e,
0x19, 0x76, 0xfb, 0xed, 0x26, 0xec, 0x44, 0xb3, 0xaf, 0xf6, 0x31, 0x72, 0x60, 0x6b, 0x79, 0x95,
0xa1, 0x9f, 0xb2, 0xd7, 0xf5, 0xca, 0xdf, 0x1c, 0xe3, 0x24, 0x0f, 0x54, 0x85, 0x6a, 0x6e, 0xfc,
0xaa, 0x21, 0x0a, 0xf5, 0xd5, 0xdd, 0x83, 0x4e, 0xd3, 0x6d, 0x64, 0xac, 0x34, 0xc3, 0xca, 0x0b,
0x8f, 0xdc, 0xa2, 0x47, 0xd8, 0x5d, 0x5b, 0x25, 0xe8, 0xb3, 0x66, 0x92, 0x3b, 0xca, 0x68, 0xe5,
0xc6, 0xc7, 0x7e, 0xff, 0x87, 0xed, 0xc4, 0xd0, 0xa2, 0x0c, 0xb6, 0xd2, 0x16, 0x93, 0xf1, 0x73,
0x2e, 0x6c, 0xec, 0xeb, 0x01, 0x76, 0x92, 0x8d, 0x8d, 0x32, 0x0c, 0xa4, 0xce, 0xbe, 0xf1, 0x4b,
0x3e, 0x70, 0xec, 0x8e, 0xd7, 0x71, 0xb5, 0x25, 0xb3, 0xea, 0x98, 0x31, 0x24, 0x59, 0x75, 0xcc,
0xea, 0x74, 0x73, 0xe3, 0x0c, 0xfe, 0xad, 0x44, 0xe8, 0x3b, 0x5d, 0xfe, 0x2f, 0xf4, 0xfb, 0x87,
0x00, 0x00, 0x00, 0xff, 0xff, 0xde, 0x47, 0x5d, 0x04, 0xc0, 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