feat(tiller): implement get and status

pull/613/head
Matt Butcher 8 years ago
parent fe0dcd2d39
commit dd2ff4f916

@ -5,7 +5,7 @@ package hapi.services.tiller;
import "hapi/chart/chart.proto";
import "hapi/chart/config.proto";
import "hapi/release/release.proto";
import "hapi/release/status.proto";
import "hapi/release/info.proto";
option go_package = "services";
@ -89,55 +89,39 @@ message ListReleasesRequest {
message ListReleasesResponse {
// The expected total number of releases to be returned
int64 count = 1;
// The zero-based offset at which the list is positioned
int64 offset = 2;
// The total number of queryable releases
int64 total = 3;
// The resulting releases
repeated hapi.release.Release releases = 4;
}
//
// GetReleaseStatusRequest:
//
// TODO
//
// GetReleaseStatusRequest is a request to get the status of a release.
message GetReleaseStatusRequest {
// The name of the release
string release_name = 1;
// Name is the name of the release
string name = 1;
}
//
// GetReleaseStatusResponse:
//
// TODO
//
// GetReleaseStatusResponse is the response indicating the status of the named release.
message GetReleaseStatusResponse {
// The name of the release
string release_name = 1;
// The release status
hapi.release.Status release_status = 2;
// Name is the name of the release.
string name = 1;
// Info contains information about the release.
hapi.release.Info info = 2;
}
//
// GetReleaseContentRequest:
//
// TODO
//
// GetReleaseContentRequest is a request to get the contents of a release.
message GetReleaseContentRequest {
// The name of the release
string release_name = 1;
string name = 1;
}
//
// GetReleaseContentResponse:
//
// TODO
//
// GetReleaseContentResponse is a response containing the contents of a release.
message GetReleaseContentResponse {
// The release content
hapi.release.Release release = 1;

@ -0,0 +1,53 @@
package main
import (
"errors"
"fmt"
"github.com/deis/tiller/pkg/helm"
"github.com/spf13/cobra"
)
var getHelp = `
This command shows the details of a named release.
It can be used to get extended information about the release, including:
- The values used to generate the release
- The chart used to generate the release
- The generated manifest file
By default, this prints a human readable collection of information about the
chart, the supplied values, and the generated manifest file.
`
var errReleaseRequired = errors.New("release name is required")
var getCommand = &cobra.Command{
Use: "get [flags] RELEASE_NAME",
Short: "Download a named release",
Long: getHelp,
RunE: getCmd,
}
func init() {
RootCommand.AddCommand(getCommand)
}
func getCmd(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
}
res, err := helm.GetReleaseContent(args[0])
if err != nil {
return err
}
fmt.Printf("Chart/Version: %s %s\n", res.Release.Chart.Metadata.Name, res.Release.Chart.Metadata.Version)
fmt.Println("Config:")
fmt.Println(res.Release.Config)
fmt.Println("\nManifest:")
fmt.Println(res.Release.Manifest)
return nil
}

@ -0,0 +1,44 @@
package main
import (
"fmt"
"time"
"github.com/deis/tiller/pkg/helm"
"github.com/deis/tiller/pkg/timeconv"
"github.com/spf13/cobra"
)
var statusHelp = `
This command shows the status of a named release.
`
var statusCommand = &cobra.Command{
Use: "status [flags] RELEASE_NAME",
Short: "Displays the status of the named release",
Long: statusHelp,
RunE: status,
}
func init() {
RootCommand.AddCommand(statusCommand)
}
func status(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errReleaseRequired
}
res, err := helm.GetReleaseStatus(args[0])
if err != nil {
return err
}
fmt.Printf("Last Deployed: %s\n", timeconv.Format(res.Info.LastDeployed, time.ANSIC))
fmt.Printf("Status: %s\n", res.Info.Status.Code)
if res.Info.Status.Details != nil {
fmt.Printf("Details: %s\n", res.Info.Status.Details)
}
return nil
}

@ -4,12 +4,11 @@ import (
"bytes"
"errors"
"log"
"time"
"github.com/deis/tiller/cmd/tiller/environment"
"github.com/deis/tiller/pkg/proto/hapi/release"
"github.com/deis/tiller/pkg/proto/hapi/services"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/deis/tiller/pkg/timeconv"
"github.com/technosophos/moniker"
ctx "golang.org/x/net/context"
)
@ -39,11 +38,25 @@ func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream s
}
func (s *releaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) {
return nil, errNotImplemented
if req.Name == "" {
return nil, errMissingRelease
}
rel, err := s.env.Releases.Read(req.Name)
if err != nil {
return nil, err
}
if rel.Info == nil {
return nil, errors.New("release info is missing")
}
return &services.GetReleaseStatusResponse{Info: rel.Info}, nil
}
func (s *releaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleaseContentRequest) (*services.GetReleaseContentResponse, error) {
return nil, errNotImplemented
if req.Name == "" {
return nil, errMissingRelease
}
rel, err := s.env.Releases.Read(req.Name)
return &services.GetReleaseContentResponse{Release: rel}, err
}
func (s *releaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
@ -59,7 +72,7 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
namer := moniker.New()
// TODO: Make sure this is unique.
name := namer.NameSep("-")
ts := now()
ts := timeconv.Now()
// Render the templates
files, err := s.env.EngineYard.Default().Render(req.Chart, req.Values)
@ -102,15 +115,6 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
return &services.InstallReleaseResponse{Release: r}, nil
}
func now() *timestamp.Timestamp {
t := time.Now()
ts := &timestamp.Timestamp{
Seconds: t.Unix(),
Nanos: int32(t.Nanosecond()),
}
return ts
}
func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) {
if req.Name == "" {
log.Printf("uninstall: Release not found: %s", req.Name)
@ -125,7 +129,7 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
log.Printf("uninstall: Deleting %s", req.Name)
rel.Info.Status.Code = release.Status_DELETED
rel.Info.Deleted = now()
rel.Info.Deleted = timeconv.Now()
// TODO: Once KubeClient is ready, delete the resources.
log.Println("WARNING: Currently not deleting resources from k8s")

@ -9,6 +9,8 @@ import (
"github.com/deis/tiller/pkg/proto/hapi/release"
"github.com/deis/tiller/pkg/proto/hapi/services"
"github.com/deis/tiller/pkg/storage"
"github.com/deis/tiller/pkg/timeconv"
"github.com/golang/protobuf/ptypes/timestamp"
"golang.org/x/net/context"
)
@ -18,6 +20,28 @@ func rsFixture() *releaseServer {
}
}
func releaseMock() *release.Release {
date := timestamp.Timestamp{242085845, 0}
return &release.Release{
Name: "angry-panda",
Info: &release.Info{
FirstDeployed: &date,
LastDeployed: &date,
Status: &release.Status{Code: release.Status_DEPLOYED},
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "foo",
Version: "0.1.0-beta.1",
},
Templates: []*chart.Template{
{Name: "foo.tpl", Data: []byte("Hello")},
},
},
Config: &chart.Config{Raw: `name = "value"`},
}
}
func TestInstallRelease(t *testing.T) {
c := context.Background()
rs := rsFixture()
@ -104,7 +128,7 @@ func TestUninstallRelease(t *testing.T) {
rs.env.Releases.Create(&release.Release{
Name: "angry-panda",
Info: &release.Info{
FirstDeployed: now(),
FirstDeployed: timeconv.Now(),
Status: &release.Status{
Code: release.Status_DEPLOYED,
},
@ -133,6 +157,42 @@ func TestUninstallRelease(t *testing.T) {
}
}
func TestGetReleaseContent(t *testing.T) {
c := context.Background()
rs := rsFixture()
rel := releaseMock()
if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err)
}
res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name})
if err != nil {
t.Errorf("Error getting release content: %s", err)
}
if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name {
t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name)
}
}
func TestGetReleaseStatus(t *testing.T) {
c := context.Background()
rs := rsFixture()
rel := releaseMock()
if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err)
}
res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name})
if err != nil {
t.Errorf("Error getting release content: %s", err)
}
if res.Info.Status.Code != release.Status_DEPLOYED {
t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code)
}
}
func mockEnvironment() *environment.Environment {
e := environment.New()
e.Releases = storage.NewMemory()

@ -4,6 +4,7 @@ import (
"github.com/deis/tiller/pkg/chart"
chartpb "github.com/deis/tiller/pkg/proto/hapi/chart"
"github.com/deis/tiller/pkg/proto/hapi/services"
"golang.org/x/net/context"
)
// Config defines a gRPC client's configuration.
@ -19,12 +20,26 @@ func ListReleases(limit, offset int) (<-chan *services.ListReleasesResponse, err
// GetReleaseStatus returns the given release's status.
func GetReleaseStatus(name string) (*services.GetReleaseStatusResponse, error) {
return nil, errNotImplemented
c := Config.client()
if err := c.dial(); err != nil {
return nil, err
}
defer c.Close()
req := &services.GetReleaseStatusRequest{Name: name}
return c.impl.GetReleaseStatus(context.TODO(), req, c.cfg.CallOpts()...)
}
// GetReleaseContent returns the configuration for a given release.
func GetReleaseContent(name string) (*services.GetReleaseContentResponse, error) {
return nil, errNotImplemented
c := Config.client()
if err := c.dial(); err != nil {
return nil, err
}
defer c.Close()
req := &services.GetReleaseContentRequest{Name: name}
return c.impl.GetReleaseContent(context.TODO(), req, c.cfg.CallOpts()...)
}
// UpdateRelease updates a release to a new/different chart.

@ -10,7 +10,7 @@ import math "math"
import hapi_chart3 "github.com/deis/tiller/pkg/proto/hapi/chart"
import hapi_chart "github.com/deis/tiller/pkg/proto/hapi/chart"
import hapi_release2 "github.com/deis/tiller/pkg/proto/hapi/release"
import hapi_release "github.com/deis/tiller/pkg/proto/hapi/release"
import hapi_release1 "github.com/deis/tiller/pkg/proto/hapi/release"
import (
context "golang.org/x/net/context"
@ -67,14 +67,10 @@ func (m *ListReleasesResponse) GetReleases() []*hapi_release2.Release {
return nil
}
//
// GetReleaseStatusRequest:
//
// TODO
//
// GetReleaseStatusRequest is a request to get the status of a release.
type GetReleaseStatusRequest struct {
// The name of the release
ReleaseName string `protobuf:"bytes,1,opt,name=release_name,json=releaseName" json:"release_name,omitempty"`
// Name is the name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
func (m *GetReleaseStatusRequest) Reset() { *m = GetReleaseStatusRequest{} }
@ -82,16 +78,12 @@ func (m *GetReleaseStatusRequest) String() string { return proto.Comp
func (*GetReleaseStatusRequest) ProtoMessage() {}
func (*GetReleaseStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} }
//
// GetReleaseStatusResponse:
//
// TODO
//
// GetReleaseStatusResponse is the response indicating the status of the named release.
type GetReleaseStatusResponse struct {
// The name of the release
ReleaseName string `protobuf:"bytes,1,opt,name=release_name,json=releaseName" json:"release_name,omitempty"`
// The release status
ReleaseStatus *hapi_release.Status `protobuf:"bytes,2,opt,name=release_status,json=releaseStatus" json:"release_status,omitempty"`
// Name is the name of the release.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// Info contains information about the release.
Info *hapi_release1.Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
}
func (m *GetReleaseStatusResponse) Reset() { *m = GetReleaseStatusResponse{} }
@ -99,21 +91,17 @@ func (m *GetReleaseStatusResponse) String() string { return proto.Com
func (*GetReleaseStatusResponse) ProtoMessage() {}
func (*GetReleaseStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
func (m *GetReleaseStatusResponse) GetReleaseStatus() *hapi_release.Status {
func (m *GetReleaseStatusResponse) GetInfo() *hapi_release1.Info {
if m != nil {
return m.ReleaseStatus
return m.Info
}
return nil
}
//
// GetReleaseContentRequest:
//
// TODO
//
// GetReleaseContentRequest is a request to get the contents of a release.
type GetReleaseContentRequest struct {
// The name of the release
ReleaseName string `protobuf:"bytes,1,opt,name=release_name,json=releaseName" json:"release_name,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
func (m *GetReleaseContentRequest) Reset() { *m = GetReleaseContentRequest{} }
@ -121,11 +109,7 @@ func (m *GetReleaseContentRequest) String() string { return proto.Com
func (*GetReleaseContentRequest) ProtoMessage() {}
func (*GetReleaseContentRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
//
// GetReleaseContentResponse:
//
// TODO
//
// GetReleaseContentResponse is a response containing the contents of a release.
type GetReleaseContentResponse struct {
// The release content
Release *hapi_release2.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
@ -544,40 +528,39 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
}
var fileDescriptor1 = []byte{
// 558 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0x3d, 0x73, 0xd3, 0x40,
0x10, 0x8d, 0x71, 0xe2, 0x98, 0x75, 0xe2, 0x21, 0x8b, 0x6c, 0x29, 0xaa, 0xc2, 0x35, 0x84, 0x40,
0x64, 0x30, 0x25, 0xd0, 0xe0, 0x82, 0xc9, 0x90, 0xa1, 0x10, 0x93, 0x86, 0x26, 0x23, 0x9c, 0x33,
0x11, 0x23, 0x9f, 0x8c, 0xee, 0xe4, 0x19, 0xe8, 0x29, 0xf9, 0x3f, 0xfc, 0x3c, 0xa4, 0xfb, 0xd0,
0x48, 0xb6, 0x34, 0x11, 0x69, 0x64, 0xdf, 0xed, 0xdb, 0x7d, 0xfb, 0xf1, 0x56, 0x02, 0xf7, 0x36,
0x58, 0x85, 0x13, 0x4e, 0x93, 0x75, 0x38, 0xa7, 0x7c, 0x22, 0xc2, 0x28, 0xa2, 0x89, 0xb7, 0x4a,
0x62, 0x11, 0xa3, 0x95, 0xdb, 0x3c, 0x63, 0xf3, 0x94, 0xcd, 0x1d, 0x4b, 0x8f, 0xf9, 0x6d, 0x90,
0x08, 0xf5, 0x54, 0x68, 0xd7, 0x2e, 0xdf, 0xc7, 0x6c, 0x11, 0x7e, 0xd3, 0x06, 0x45, 0x91, 0xd0,
0x88, 0x06, 0x9c, 0x9a, 0x5f, 0x6d, 0x3b, 0xae, 0xd8, 0xb8, 0x08, 0x44, 0xca, 0x95, 0x89, 0xcc,
0xe0, 0xf1, 0x65, 0xc8, 0x85, 0xaf, 0x6c, 0xdc, 0xa7, 0x3f, 0x52, 0xca, 0x05, 0x5a, 0xb0, 0x17,
0x85, 0xcb, 0x50, 0x38, 0x9d, 0x93, 0xce, 0x69, 0xd7, 0x57, 0x07, 0x1c, 0x43, 0x2f, 0x5e, 0x2c,
0x38, 0x15, 0xce, 0x03, 0x79, 0xad, 0x4f, 0xe4, 0x4f, 0x07, 0xac, 0x6a, 0x14, 0xbe, 0x8a, 0x19,
0xa7, 0x79, 0x98, 0x79, 0x9c, 0xb2, 0x22, 0x8c, 0x3c, 0x34, 0x85, 0xc9, 0xd1, 0x22, 0x16, 0x41,
0xe4, 0x74, 0x15, 0x5a, 0x1e, 0xf0, 0x15, 0xf4, 0x75, 0xe6, 0xdc, 0xd9, 0x3d, 0xe9, 0x9e, 0x0e,
0xa6, 0x23, 0x4f, 0xb6, 0xcc, 0xd4, 0xa8, 0x59, 0xfd, 0x02, 0x46, 0xde, 0x82, 0xfd, 0x81, 0x9a,
0x6c, 0x3e, 0xcb, 0x72, 0x4d, 0x61, 0x4f, 0xe0, 0x40, 0xc3, 0xae, 0x59, 0xb0, 0xa4, 0x32, 0xb1,
0x87, 0xfe, 0x40, 0xdf, 0x7d, 0xca, 0xae, 0xc8, 0x2f, 0x70, 0xb6, 0xbd, 0x75, 0x41, 0x77, 0xbb,
0xe3, 0x1b, 0x18, 0x1a, 0x88, 0xea, 0xb4, 0xac, 0x72, 0x30, 0xb5, 0xaa, 0x59, 0xeb, 0xc0, 0x87,
0x49, 0x99, 0x87, 0xbc, 0x2b, 0x73, 0xcf, 0x62, 0x26, 0x28, 0x13, 0xff, 0x91, 0xfa, 0x25, 0x1c,
0xd7, 0xb8, 0xeb, 0xdc, 0x27, 0xb0, 0xaf, 0xb1, 0xd2, 0xb5, 0xb1, 0x8f, 0x06, 0x45, 0xc6, 0x60,
0x5d, 0xad, 0x6e, 0x02, 0x41, 0x8d, 0x45, 0x25, 0x42, 0x6c, 0x18, 0x6d, 0xdc, 0x2b, 0x06, 0xf2,
0xbb, 0x03, 0xa3, 0x0b, 0x96, 0x55, 0x1d, 0x45, 0x55, 0x17, 0x7c, 0x9a, 0x09, 0x21, 0xd7, 0xac,
0x66, 0x3e, 0x52, 0xcc, 0x4a, 0xd8, 0xb3, 0xfc, 0xe9, 0x2b, 0x3b, 0x9e, 0x41, 0x6f, 0x1d, 0x44,
0x99, 0x8f, 0xee, 0x1a, 0x56, 0x90, 0x52, 0xf0, 0xbe, 0x46, 0xa0, 0x0d, 0xfb, 0x37, 0xc9, 0xcf,
0xeb, 0x24, 0x65, 0x52, 0x31, 0x7d, 0xbf, 0x97, 0x1d, 0xfd, 0x94, 0x91, 0x0b, 0x18, 0x6f, 0xa6,
0x71, 0xdf, 0x1e, 0x9c, 0x83, 0x7d, 0xc5, 0xc2, 0xda, 0x9a, 0x10, 0x76, 0x4b, 0x73, 0x90, 0xff,
0xc9, 0x47, 0x70, 0xb6, 0xe1, 0xf7, 0xe4, 0x9e, 0xfe, 0xdd, 0x83, 0xa1, 0x91, 0xa1, 0x7a, 0x3d,
0x60, 0x08, 0x07, 0xe5, 0x45, 0xc3, 0x67, 0x5e, 0xdd, 0xdb, 0xc3, 0xab, 0x59, 0x69, 0xf7, 0xac,
0x0d, 0x54, 0x0f, 0x72, 0xe7, 0x65, 0x07, 0x39, 0x3c, 0xda, 0x5c, 0x03, 0x3c, 0xaf, 0x8f, 0xd1,
0xb0, 0x6c, 0xae, 0xd7, 0x16, 0x6e, 0x68, 0x71, 0x0d, 0x47, 0x5b, 0x02, 0xc6, 0x3b, 0xc3, 0x54,
0x17, 0xc5, 0x9d, 0xb4, 0xc6, 0x17, 0xbc, 0xdf, 0xe1, 0xb0, 0x22, 0x69, 0x6c, 0xe8, 0x56, 0xdd,
0x3e, 0xb8, 0xcf, 0x5b, 0x61, 0x0b, 0xae, 0x25, 0x0c, 0xab, 0xea, 0xc4, 0x86, 0x00, 0xb5, 0xab,
0xe4, 0xbe, 0x68, 0x07, 0x2e, 0xe8, 0xb2, 0x39, 0x6e, 0x4a, 0xb2, 0x69, 0x8e, 0x0d, 0x4a, 0x6f,
0x9a, 0x63, 0x93, 0xd2, 0xc9, 0xce, 0x7b, 0xf8, 0xd2, 0x37, 0xe8, 0xaf, 0x3d, 0xf9, 0xa5, 0x79,
0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x15, 0x68, 0xaf, 0x05, 0x07, 0x00, 0x00,
// 540 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x6e, 0xd3, 0x40,
0x10, 0xae, 0x49, 0x9a, 0x86, 0x29, 0x54, 0x74, 0xc8, 0x8f, 0xf1, 0xa9, 0xda, 0x03, 0x94, 0x42,
0x1d, 0x08, 0x6f, 0x40, 0x0e, 0x28, 0xa2, 0xa7, 0x45, 0xe5, 0xc0, 0x05, 0x99, 0x74, 0x43, 0x17,
0x39, 0xeb, 0xe0, 0x5d, 0x47, 0xe2, 0x01, 0x38, 0xf2, 0x3e, 0x3c, 0x1e, 0xf6, 0xfe, 0x58, 0x71,
0x62, 0x53, 0xab, 0x17, 0xb7, 0xbb, 0xdf, 0x37, 0xf3, 0xcd, 0xce, 0x7c, 0xa3, 0x40, 0x70, 0x1b,
0xad, 0xf9, 0x44, 0xb2, 0x74, 0xc3, 0x17, 0x4c, 0x4e, 0x14, 0x8f, 0x63, 0x96, 0x86, 0xeb, 0x34,
0x51, 0x09, 0x0e, 0x0a, 0x2c, 0x74, 0x58, 0x68, 0xb0, 0x60, 0xa4, 0x23, 0x16, 0xb7, 0x51, 0xaa,
0xcc, 0xd7, 0xb0, 0x83, 0xf1, 0xf6, 0x7d, 0x22, 0x96, 0xfc, 0xbb, 0x05, 0x8c, 0x44, 0xca, 0x62,
0x16, 0x49, 0xe6, 0xfe, 0x56, 0x82, 0x1c, 0xc6, 0xc5, 0x32, 0x31, 0x00, 0x99, 0xc1, 0xd3, 0x2b,
0x2e, 0x15, 0x35, 0x88, 0xa4, 0xec, 0x67, 0xc6, 0xa4, 0xc2, 0x01, 0x1c, 0xc6, 0x7c, 0xc5, 0x95,
0xef, 0x9d, 0x79, 0xe7, 0x1d, 0x6a, 0x0e, 0x38, 0x82, 0x5e, 0xb2, 0x5c, 0x4a, 0xa6, 0xfc, 0x07,
0xfa, 0xda, 0x9e, 0xc8, 0x1f, 0x0f, 0x06, 0xd5, 0x2c, 0x72, 0x9d, 0x08, 0xc9, 0x8a, 0x34, 0x8b,
0x24, 0x13, 0x65, 0x1a, 0x7d, 0x68, 0x4a, 0x53, 0xb0, 0x55, 0xa2, 0xa2, 0xd8, 0xef, 0x18, 0xb6,
0x3e, 0xe0, 0x5b, 0xe8, 0xdb, 0xba, 0xa5, 0xdf, 0x3d, 0xeb, 0x9c, 0x1f, 0x4f, 0x87, 0xa1, 0x6e,
0x98, 0x7b, 0xa1, 0x55, 0xa5, 0x25, 0x8d, 0x5c, 0xc2, 0xf8, 0x03, 0x73, 0xd5, 0x7c, 0x52, 0x91,
0xca, 0xca, 0x87, 0x21, 0x74, 0x45, 0xb4, 0x62, 0xba, 0xa0, 0x87, 0x54, 0xff, 0x4f, 0x3e, 0x83,
0xbf, 0x4f, 0xb7, 0x2f, 0xa8, 0xe1, 0xe3, 0x73, 0xe8, 0x16, 0x1d, 0xd4, 0xd5, 0x1f, 0x4f, 0xb1,
0x5a, 0xcd, 0x3c, 0x47, 0xa8, 0xc6, 0x49, 0xb8, 0x9d, 0x77, 0x96, 0x08, 0xc5, 0x84, 0xfa, 0x5f,
0x1d, 0x57, 0xf0, 0xac, 0x86, 0x6f, 0x0b, 0x99, 0xc0, 0x91, 0x95, 0xd0, 0x31, 0x8d, 0x5d, 0x70,
0x2c, 0x32, 0x82, 0xc1, 0xf5, 0xfa, 0x26, 0x52, 0xcc, 0x21, 0x46, 0x99, 0x8c, 0x61, 0xb8, 0x73,
0x6f, 0x14, 0xc8, 0x6f, 0x0f, 0x86, 0x73, 0x21, 0xf3, 0x9e, 0xc7, 0xd5, 0x10, 0x7c, 0x91, 0x8f,
0xb1, 0xf0, 0x9b, 0x55, 0x3e, 0x35, 0xca, 0xc6, 0x94, 0xb3, 0xe2, 0x4b, 0x0d, 0x8e, 0x17, 0xd0,
0xdb, 0x44, 0x71, 0x1e, 0x53, 0xed, 0x8d, 0x65, 0x6a, 0xb3, 0x52, 0xcb, 0xc0, 0x31, 0x1c, 0xdd,
0xa4, 0xbf, 0xbe, 0xa6, 0x99, 0xd0, 0xf3, 0xee, 0xd3, 0x5e, 0x7e, 0xa4, 0x99, 0x20, 0x73, 0x18,
0xed, 0x96, 0x71, 0xdf, 0x1e, 0xe4, 0x46, 0xb8, 0x16, 0xbc, 0xf6, 0x4d, 0x75, 0x03, 0xf8, 0x08,
0xfe, 0x3e, 0xfd, 0x9e, 0xda, 0xd3, 0xbf, 0x87, 0x70, 0xe2, 0x3c, 0x65, 0x56, 0x1b, 0x39, 0x3c,
0xda, 0x5e, 0x13, 0x7c, 0x19, 0xd6, 0x6d, 0x7e, 0x58, 0xb3, 0x90, 0xc1, 0x45, 0x1b, 0xaa, 0x1d,
0xe4, 0xc1, 0x1b, 0x0f, 0x25, 0x3c, 0xd9, 0xf5, 0x34, 0x5e, 0xd6, 0xe7, 0x68, 0x58, 0x95, 0x20,
0x6c, 0x4b, 0x77, 0xb2, 0xb8, 0x81, 0xd3, 0x3d, 0x03, 0xe3, 0x9d, 0x69, 0xaa, 0x9b, 0x11, 0x4c,
0x5a, 0xf3, 0x4b, 0xdd, 0x1f, 0xf0, 0xb8, 0x62, 0x69, 0x6c, 0xe8, 0x56, 0xdd, 0x3e, 0x04, 0xaf,
0x5a, 0x71, 0x4b, 0xad, 0x15, 0x9c, 0x54, 0xdd, 0x89, 0x0d, 0x09, 0x6a, 0x57, 0x29, 0x78, 0xdd,
0x8e, 0x5c, 0xca, 0xe5, 0x73, 0xdc, 0xb5, 0x64, 0xd3, 0x1c, 0x1b, 0x9c, 0xde, 0x34, 0xc7, 0x26,
0xa7, 0x93, 0x83, 0xf7, 0xf0, 0xa5, 0xef, 0xd8, 0xdf, 0x7a, 0xfa, 0x77, 0xe2, 0xdd, 0xbf, 0x00,
0x00, 0x00, 0xff, 0xff, 0x8c, 0xd6, 0xc7, 0x2c, 0xc1, 0x06, 0x00, 0x00,
}

@ -0,0 +1,7 @@
/*Package timeconv contains utilities for converting time.
The gRPC/Protobuf libraries contain time implementations that require conversion
to and from Go times. This library provides utilities and convenience functions
for performing conversions.
*/
package timeconv

@ -0,0 +1,32 @@
package timeconv
import (
"time"
"github.com/golang/protobuf/ptypes/timestamp"
)
// Now creates a timestamp.Timestamp representing the current time.
func Now() *timestamp.Timestamp {
return Timestamp(time.Now())
}
// Timestamp converts a time.Time to a protobuf *timestamp.Timestamp.
func Timestamp(t time.Time) *timestamp.Timestamp {
return &timestamp.Timestamp{
Seconds: t.Unix(),
Nanos: int32(t.Nanosecond()),
}
}
// Time converts a protobuf *timestamp.Timestamp to a time.Time.
func Time(ts *timestamp.Timestamp) time.Time {
return time.Unix(ts.Seconds, int64(ts.Nanos))
}
// Format formats a *timestamp.Timestamp into a string.
//
// This follows the rules for time.Time.Format().
func Format(ts *timestamp.Timestamp, layout string) string {
return Time(ts).Format(layout)
}

@ -0,0 +1,46 @@
package timeconv
import (
"testing"
"time"
)
func TestNow(t *testing.T) {
now := time.Now()
ts := Now()
var drift int64 = 5
if ts.Seconds < int64(now.Second())-drift {
t.Errorf("Unexpected time drift: %d", ts.Seconds)
}
}
func TestTimestamp(t *testing.T) {
now := time.Now()
ts := Timestamp(now)
if now.Unix() != ts.Seconds {
t.Errorf("Unexpected time drift: %d to %d", now.Second(), ts.Seconds)
}
if now.Nanosecond() != int(ts.Nanos) {
t.Errorf("Unexpected nano drift: %d to %d", now.Nanosecond(), ts.Nanos)
}
}
func TestTime(t *testing.T) {
nowts := Now()
now := Time(nowts)
if now.Unix() != nowts.Seconds {
t.Errorf("Unexpected time drift %d", now.Unix())
}
}
func TestFormat(t *testing.T) {
now := time.Now()
nowts := Timestamp(now)
if now.Format(time.ANSIC) != Format(nowts, time.ANSIC) {
t.Error("Format mismatch")
}
}
Loading…
Cancel
Save