diff --git a/CHANGELOG.md b/CHANGELOG.md index 664b9b01..7dd37f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [UNRELEASED] - 0000-00-00 +## [1.0.0-alpha.13] - 2018-04-21 +### Fixes +- [CLI] Remove code and description from GRCP errors +- [API] Skipping ephemeral pods while log streaming +- [CLI] `ps` should list recent pods with tabular format (Added cpu, memory fields) +- [API] Don't stream logs from crashed/failed pods +- Sort environment variables in API response +- AWS nginx ingress controller to respect `Path: /` +- Async support for streaming logs from mutiple processes. +- Making `datacol run` independent of shell +### Added +- [CLI] Added domains:{add, remove} API +- [CLI] Renaming command `ps scale` to `scale` +- [CLI] Renaming command `build list` to `builds` +- [API] version label into k8s deployments +- Paging for `GET /v1/builds` API +- [CLI] Tabular output for listing apps and builds +- [CLI] `STACK` env var for `datacol env`, `datacol infra` +- [CLI] Number of logs lines for process logs (`--lines 10`) + ## [1.0.0-alpha.12] - 2018-03-27 ### Added - [CLI] `ps` to support container status @@ -14,7 +34,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - AWS elasticsearch support - Websocket connection for streaming logs and Running one-off commands - Added `--ref` flag into deploy cmd -- Proxy support through bastion Host +- Proxy support through bastion Host ### Fixed - CLI improvements - [CLI] Bump default version of GCP cluster to `1.7.14-gke.1` diff --git a/Gopkg.lock b/Gopkg.lock index 9199d007..2b75b733 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -343,6 +343,12 @@ packages = ["."] revision = "57fdcb988a5c543893cc61bce354a6e24ab70022" +[[projects]] + name = "github.com/mattn/go-runewidth" + packages = ["."] + revision = "9e777a8366cce605130a531d2cd6363d07ad7317" + version = "v0.0.2" + [[projects]] name = "github.com/mholt/archiver" packages = ["."] @@ -367,6 +373,12 @@ packages = ["."] revision = "f22b7ef81a0afac9ce1447d37e5ab8e99fbd2f73" +[[projects]] + branch = "master" + name = "github.com/olekukonko/tablewriter" + packages = ["."] + revision = "b8a9be070da40449e501c3c4730a889e42d87a9e" + [[projects]] name = "github.com/pelletier/go-toml" packages = ["."] @@ -562,6 +574,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "46839c89b027427ff758080f2a6d231f96ae66876d4d84c45f630b36ef9275f2" + inputs-digest = "d19ecfd1dcb05bb021b32d5e90162154643244b212751b86e9657be4c803a0e6" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 87921ff4..1f7ccc45 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -74,4 +74,4 @@ required = [ version = "~1.13.0" [[constraint]] - name = "github.com/mholt/archiver" + name = "github.com/mholt/archiver" \ No newline at end of file diff --git a/Makefile b/Makefile index 57bc31e4..c572d86b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -version=1.0.0-alpha.12 +version=1.0.0-alpha.13 MODEL_PROTO_DIR=./api/models SERVICE_PROTO_DIR=./api/controller VEDNOR_GOOGLE_APIS=./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis diff --git a/api/controller/services.pb.go b/api/controller/services.pb.go index b0b53ee2..3a3479ce 100644 --- a/api/controller/services.pb.go +++ b/api/controller/services.pb.go @@ -10,6 +10,7 @@ It is generated from these files: It has these top-level messages: ListRequest + ListResponse AppRequest AppListResponse AuthRequest @@ -20,6 +21,7 @@ It has these top-level messages: BuildLogResponse EnvSetRequest ResourceListResponse + AppListRequest BuildListResponse ProcessListResponse CreateResourceRequest @@ -31,6 +33,7 @@ It has these top-level messages: StreamMsg ReleaseListResponse CreateReleaseRequest + ListDomainReqponse */ package controller @@ -83,6 +86,22 @@ func (m *ListRequest) GetLimit() int32 { return 0 } +type ListResponse struct { + Items []string `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (m *ListResponse) String() string { return proto.CompactTextString(m) } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *ListResponse) GetItems() []string { + if m != nil { + return m.Items + } + return nil +} + type AppRequest struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` RepoUrl string `protobuf:"bytes,2,opt,name=repo_url,json=repoUrl" json:"repo_url,omitempty"` @@ -91,7 +110,7 @@ type AppRequest struct { func (m *AppRequest) Reset() { *m = AppRequest{} } func (m *AppRequest) String() string { return proto.CompactTextString(m) } func (*AppRequest) ProtoMessage() {} -func (*AppRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (*AppRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func (m *AppRequest) GetName() string { if m != nil { @@ -116,7 +135,7 @@ type AppListResponse struct { func (m *AppListResponse) Reset() { *m = AppListResponse{} } func (m *AppListResponse) String() string { return proto.CompactTextString(m) } func (*AppListResponse) ProtoMessage() {} -func (*AppListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*AppListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } func (m *AppListResponse) GetApps() []*models.App { if m != nil { @@ -146,7 +165,7 @@ type AuthRequest struct { func (m *AuthRequest) Reset() { *m = AuthRequest{} } func (m *AuthRequest) String() string { return proto.CompactTextString(m) } func (*AuthRequest) ProtoMessage() {} -func (*AuthRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +func (*AuthRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } func (m *AuthRequest) GetPassword() string { if m != nil { @@ -166,7 +185,7 @@ type AuthResponse struct { func (m *AuthResponse) Reset() { *m = AuthResponse{} } func (m *AuthResponse) String() string { return proto.CompactTextString(m) } func (*AuthResponse) ProtoMessage() {} -func (*AuthResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +func (*AuthResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } func (m *AuthResponse) GetHost() string { if m != nil { @@ -212,7 +231,7 @@ type CreateBuildRequest struct { func (m *CreateBuildRequest) Reset() { *m = CreateBuildRequest{} } func (m *CreateBuildRequest) String() string { return proto.CompactTextString(m) } func (*CreateBuildRequest) ProtoMessage() {} -func (*CreateBuildRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (*CreateBuildRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } func (m *CreateBuildRequest) GetApp() string { if m != nil { @@ -243,7 +262,7 @@ type AppIdRequest struct { func (m *AppIdRequest) Reset() { *m = AppIdRequest{} } func (m *AppIdRequest) String() string { return proto.CompactTextString(m) } func (*AppIdRequest) ProtoMessage() {} -func (*AppIdRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +func (*AppIdRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } func (m *AppIdRequest) GetApp() string { if m != nil { @@ -268,7 +287,7 @@ type BuildLogRequest struct { func (m *BuildLogRequest) Reset() { *m = BuildLogRequest{} } func (m *BuildLogRequest) String() string { return proto.CompactTextString(m) } func (*BuildLogRequest) ProtoMessage() {} -func (*BuildLogRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } +func (*BuildLogRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } func (m *BuildLogRequest) GetApp() string { if m != nil { @@ -299,7 +318,7 @@ type BuildLogResponse struct { func (m *BuildLogResponse) Reset() { *m = BuildLogResponse{} } func (m *BuildLogResponse) String() string { return proto.CompactTextString(m) } func (*BuildLogResponse) ProtoMessage() {} -func (*BuildLogResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } +func (*BuildLogResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } func (m *BuildLogResponse) GetPos() int32 { if m != nil { @@ -323,7 +342,7 @@ type EnvSetRequest struct { func (m *EnvSetRequest) Reset() { *m = EnvSetRequest{} } func (m *EnvSetRequest) String() string { return proto.CompactTextString(m) } func (*EnvSetRequest) ProtoMessage() {} -func (*EnvSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } +func (*EnvSetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } func (m *EnvSetRequest) GetName() string { if m != nil { @@ -346,7 +365,7 @@ type ResourceListResponse struct { func (m *ResourceListResponse) Reset() { *m = ResourceListResponse{} } func (m *ResourceListResponse) String() string { return proto.CompactTextString(m) } func (*ResourceListResponse) ProtoMessage() {} -func (*ResourceListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } +func (*ResourceListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } func (m *ResourceListResponse) GetResources() []*models.Resource { if m != nil { @@ -355,6 +374,30 @@ func (m *ResourceListResponse) GetResources() []*models.Resource { return nil } +type AppListRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit" json:"limit,omitempty"` +} + +func (m *AppListRequest) Reset() { *m = AppListRequest{} } +func (m *AppListRequest) String() string { return proto.CompactTextString(m) } +func (*AppListRequest) ProtoMessage() {} +func (*AppListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *AppListRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *AppListRequest) GetLimit() int32 { + if m != nil { + return m.Limit + } + return 0 +} + type BuildListResponse struct { Builds []*models.Build `protobuf:"bytes,1,rep,name=builds" json:"builds,omitempty"` } @@ -362,7 +405,7 @@ type BuildListResponse struct { func (m *BuildListResponse) Reset() { *m = BuildListResponse{} } func (m *BuildListResponse) String() string { return proto.CompactTextString(m) } func (*BuildListResponse) ProtoMessage() {} -func (*BuildListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } +func (*BuildListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } func (m *BuildListResponse) GetBuilds() []*models.Build { if m != nil { @@ -378,7 +421,7 @@ type ProcessListResponse struct { func (m *ProcessListResponse) Reset() { *m = ProcessListResponse{} } func (m *ProcessListResponse) String() string { return proto.CompactTextString(m) } func (*ProcessListResponse) ProtoMessage() {} -func (*ProcessListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } +func (*ProcessListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } func (m *ProcessListResponse) GetItems() []*models.Process { if m != nil { @@ -396,7 +439,7 @@ type CreateResourceRequest struct { func (m *CreateResourceRequest) Reset() { *m = CreateResourceRequest{} } func (m *CreateResourceRequest) String() string { return proto.CompactTextString(m) } func (*CreateResourceRequest) ProtoMessage() {} -func (*CreateResourceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } +func (*CreateResourceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } func (m *CreateResourceRequest) GetName() string { if m != nil { @@ -420,14 +463,14 @@ func (m *CreateResourceRequest) GetParams() map[string]string { } type AppResourceReq struct { - App string `protobuf:"bytes,1,opt,name=app" json:"app,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + App string `protobuf:"bytes,1,opt,name=app" json:"app,omitempty"` + Resource string `protobuf:"bytes,2,opt,name=resource" json:"resource,omitempty"` } func (m *AppResourceReq) Reset() { *m = AppResourceReq{} } func (m *AppResourceReq) String() string { return proto.CompactTextString(m) } func (*AppResourceReq) ProtoMessage() {} -func (*AppResourceReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } +func (*AppResourceReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } func (m *AppResourceReq) GetApp() string { if m != nil { @@ -436,9 +479,9 @@ func (m *AppResourceReq) GetApp() string { return "" } -func (m *AppResourceReq) GetName() string { +func (m *AppResourceReq) GetResource() string { if m != nil { - return m.Name + return m.Resource } return "" } @@ -450,7 +493,7 @@ type KubectlReq struct { func (m *KubectlReq) Reset() { *m = KubectlReq{} } func (m *KubectlReq) String() string { return proto.CompactTextString(m) } func (*KubectlReq) ProtoMessage() {} -func (*KubectlReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } +func (*KubectlReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } func (m *KubectlReq) GetArgs() []string { if m != nil { @@ -469,7 +512,7 @@ type CmdResponse struct { func (m *CmdResponse) Reset() { *m = CmdResponse{} } func (m *CmdResponse) String() string { return proto.CompactTextString(m) } func (*CmdResponse) ProtoMessage() {} -func (*CmdResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } +func (*CmdResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } func (m *CmdResponse) GetExitCode() int32 { if m != nil { @@ -509,7 +552,7 @@ type LogStreamReq struct { func (m *LogStreamReq) Reset() { *m = LogStreamReq{} } func (m *LogStreamReq) String() string { return proto.CompactTextString(m) } func (*LogStreamReq) ProtoMessage() {} -func (*LogStreamReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } +func (*LogStreamReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } func (m *LogStreamReq) GetName() string { if m != nil { @@ -547,7 +590,7 @@ type ProcessRunReq struct { func (m *ProcessRunReq) Reset() { *m = ProcessRunReq{} } func (m *ProcessRunReq) String() string { return proto.CompactTextString(m) } func (*ProcessRunReq) ProtoMessage() {} -func (*ProcessRunReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } +func (*ProcessRunReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } func (m *ProcessRunReq) GetName() string { if m != nil { @@ -570,7 +613,7 @@ type StreamMsg struct { func (m *StreamMsg) Reset() { *m = StreamMsg{} } func (m *StreamMsg) String() string { return proto.CompactTextString(m) } func (*StreamMsg) ProtoMessage() {} -func (*StreamMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } +func (*StreamMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } func (m *StreamMsg) GetData() []byte { if m != nil { @@ -586,7 +629,7 @@ type ReleaseListResponse struct { func (m *ReleaseListResponse) Reset() { *m = ReleaseListResponse{} } func (m *ReleaseListResponse) String() string { return proto.CompactTextString(m) } func (*ReleaseListResponse) ProtoMessage() {} -func (*ReleaseListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } +func (*ReleaseListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } func (m *ReleaseListResponse) GetReleases() []*models.Release { if m != nil { @@ -603,7 +646,7 @@ type CreateReleaseRequest struct { func (m *CreateReleaseRequest) Reset() { *m = CreateReleaseRequest{} } func (m *CreateReleaseRequest) String() string { return proto.CompactTextString(m) } func (*CreateReleaseRequest) ProtoMessage() {} -func (*CreateReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } +func (*CreateReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } func (m *CreateReleaseRequest) GetBuild() *models.Build { if m != nil { @@ -619,8 +662,25 @@ func (m *CreateReleaseRequest) GetDomain() string { return "" } +type ListDomainReqponse struct { + Url []string `protobuf:"bytes,1,rep,name=url" json:"url,omitempty"` +} + +func (m *ListDomainReqponse) Reset() { *m = ListDomainReqponse{} } +func (m *ListDomainReqponse) String() string { return proto.CompactTextString(m) } +func (*ListDomainReqponse) ProtoMessage() {} +func (*ListDomainReqponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } + +func (m *ListDomainReqponse) GetUrl() []string { + if m != nil { + return m.Url + } + return nil +} + func init() { proto.RegisterType((*ListRequest)(nil), "controller.ListRequest") + proto.RegisterType((*ListResponse)(nil), "controller.ListResponse") proto.RegisterType((*AppRequest)(nil), "controller.AppRequest") proto.RegisterType((*AppListResponse)(nil), "controller.AppListResponse") proto.RegisterType((*AuthRequest)(nil), "controller.AuthRequest") @@ -631,6 +691,7 @@ func init() { proto.RegisterType((*BuildLogResponse)(nil), "controller.BuildLogResponse") proto.RegisterType((*EnvSetRequest)(nil), "controller.EnvSetRequest") proto.RegisterType((*ResourceListResponse)(nil), "controller.ResourceListResponse") + proto.RegisterType((*AppListRequest)(nil), "controller.AppListRequest") proto.RegisterType((*BuildListResponse)(nil), "controller.BuildListResponse") proto.RegisterType((*ProcessListResponse)(nil), "controller.ProcessListResponse") proto.RegisterType((*CreateResourceRequest)(nil), "controller.CreateResourceRequest") @@ -642,6 +703,7 @@ func init() { proto.RegisterType((*StreamMsg)(nil), "controller.StreamMsg") proto.RegisterType((*ReleaseListResponse)(nil), "controller.ReleaseListResponse") proto.RegisterType((*CreateReleaseRequest)(nil), "controller.CreateReleaseRequest") + proto.RegisterType((*ListDomainReqponse)(nil), "controller.ListDomainReqponse") } // Reference imports to suppress errors if they are not otherwise used. @@ -662,13 +724,15 @@ type ProviderServiceClient interface { AppGet(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*models.App, error) AppRestart(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) AppDelete(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + // Update Domain API + AppUpdateDomain(ctx context.Context, in *AppResourceReq, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) // Build Api BuildCreate(ctx context.Context, in *CreateBuildRequest, opts ...grpc.CallOption) (*models.Build, error) BuildImport(ctx context.Context, opts ...grpc.CallOption) (ProviderService_BuildImportClient, error) BuildRelease(ctx context.Context, in *CreateReleaseRequest, opts ...grpc.CallOption) (*models.Release, error) BuildGet(ctx context.Context, in *AppIdRequest, opts ...grpc.CallOption) (*models.Build, error) BuildDelete(ctx context.Context, in *AppIdRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) - BuildList(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*BuildListResponse, error) + BuildList(ctx context.Context, in *AppListRequest, opts ...grpc.CallOption) (*BuildListResponse, error) BuildLogs(ctx context.Context, in *BuildLogRequest, opts ...grpc.CallOption) (*BuildLogResponse, error) EnvironmentGet(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*models.EnvConfig, error) EnvironmentSet(ctx context.Context, in *EnvSetRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) @@ -754,6 +818,15 @@ func (c *providerServiceClient) AppDelete(ctx context.Context, in *AppRequest, o return out, nil } +func (c *providerServiceClient) AppUpdateDomain(ctx context.Context, in *AppResourceReq, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/controller.ProviderService/AppUpdateDomain", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *providerServiceClient) BuildCreate(ctx context.Context, in *CreateBuildRequest, opts ...grpc.CallOption) (*models.Build, error) { out := new(models.Build) err := grpc.Invoke(ctx, "/controller.ProviderService/BuildCreate", in, out, c.cc, opts...) @@ -824,7 +897,7 @@ func (c *providerServiceClient) BuildDelete(ctx context.Context, in *AppIdReques return out, nil } -func (c *providerServiceClient) BuildList(ctx context.Context, in *AppRequest, opts ...grpc.CallOption) (*BuildListResponse, error) { +func (c *providerServiceClient) BuildList(ctx context.Context, in *AppListRequest, opts ...grpc.CallOption) (*BuildListResponse, error) { out := new(BuildListResponse) err := grpc.Invoke(ctx, "/controller.ProviderService/BuildList", in, out, c.cc, opts...) if err != nil { @@ -1032,13 +1105,15 @@ type ProviderServiceServer interface { AppGet(context.Context, *AppRequest) (*models.App, error) AppRestart(context.Context, *AppRequest) (*google_protobuf1.Empty, error) AppDelete(context.Context, *AppRequest) (*google_protobuf1.Empty, error) + // Update Domain API + AppUpdateDomain(context.Context, *AppResourceReq) (*google_protobuf1.Empty, error) // Build Api BuildCreate(context.Context, *CreateBuildRequest) (*models.Build, error) BuildImport(ProviderService_BuildImportServer) error BuildRelease(context.Context, *CreateReleaseRequest) (*models.Release, error) BuildGet(context.Context, *AppIdRequest) (*models.Build, error) BuildDelete(context.Context, *AppIdRequest) (*google_protobuf1.Empty, error) - BuildList(context.Context, *AppRequest) (*BuildListResponse, error) + BuildList(context.Context, *AppListRequest) (*BuildListResponse, error) BuildLogs(context.Context, *BuildLogRequest) (*BuildLogResponse, error) EnvironmentGet(context.Context, *AppRequest) (*models.EnvConfig, error) EnvironmentSet(context.Context, *EnvSetRequest) (*google_protobuf1.Empty, error) @@ -1174,6 +1249,24 @@ func _ProviderService_AppDelete_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _ProviderService_AppUpdateDomain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AppResourceReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServiceServer).AppUpdateDomain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.ProviderService/AppUpdateDomain", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServiceServer).AppUpdateDomain(ctx, req.(*AppResourceReq)) + } + return interceptor(ctx, in, info, handler) +} + func _ProviderService_BuildCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateBuildRequest) if err := dec(in); err != nil { @@ -1273,7 +1366,7 @@ func _ProviderService_BuildDelete_Handler(srv interface{}, ctx context.Context, } func _ProviderService_BuildList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AppRequest) + in := new(AppListRequest) if err := dec(in); err != nil { return nil, err } @@ -1285,7 +1378,7 @@ func _ProviderService_BuildList_Handler(srv interface{}, ctx context.Context, de FullMethod: "/controller.ProviderService/BuildList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServiceServer).BuildList(ctx, req.(*AppRequest)) + return srv.(ProviderServiceServer).BuildList(ctx, req.(*AppListRequest)) } return interceptor(ctx, in, info, handler) } @@ -1617,6 +1710,10 @@ var _ProviderService_serviceDesc = grpc.ServiceDesc{ MethodName: "AppDelete", Handler: _ProviderService_AppDelete_Handler, }, + { + MethodName: "AppUpdateDomain", + Handler: _ProviderService_AppUpdateDomain_Handler, + }, { MethodName: "BuildCreate", Handler: _ProviderService_BuildCreate_Handler, @@ -1718,104 +1815,108 @@ var _ProviderService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("services.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1571 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0x5b, 0x6f, 0xdb, 0x46, - 0x16, 0x5e, 0xda, 0xf2, 0x45, 0x47, 0xf2, 0x6d, 0x7c, 0x93, 0xe5, 0xc4, 0x76, 0x26, 0x58, 0xac, - 0xe3, 0x45, 0xc4, 0x6c, 0x76, 0xb1, 0x17, 0x67, 0xf7, 0xc1, 0x76, 0x94, 0x85, 0x51, 0xb7, 0x71, - 0xe9, 0x06, 0xee, 0x25, 0x69, 0x42, 0x8b, 0x23, 0x99, 0x35, 0xc9, 0x99, 0x0e, 0x49, 0xa5, 0x46, - 0x90, 0x97, 0xa2, 0x40, 0x7f, 0x40, 0x7f, 0x51, 0xdf, 0xf2, 0x58, 0xa0, 0xef, 0x45, 0x11, 0xf4, - 0xb5, 0xff, 0xa1, 0x98, 0x1b, 0x45, 0xea, 0xe2, 0xba, 0x79, 0x31, 0xe6, 0xcc, 0xe5, 0x3b, 0x1f, - 0xcf, 0x7c, 0xe7, 0xcc, 0x91, 0x61, 0x36, 0x26, 0xbc, 0xeb, 0xb7, 0x48, 0xdc, 0x60, 0x9c, 0x26, - 0x14, 0x41, 0x8b, 0x46, 0x09, 0xa7, 0x41, 0x40, 0x78, 0xfd, 0x46, 0x87, 0xd2, 0x4e, 0x40, 0x6c, - 0x97, 0xf9, 0xb6, 0x1b, 0x45, 0x34, 0x71, 0x13, 0x9f, 0x46, 0x7a, 0x67, 0xbd, 0xc6, 0x92, 0x4b, - 0x46, 0x62, 0x9b, 0x84, 0x2c, 0xb9, 0x54, 0x7f, 0xf5, 0xca, 0x86, 0x5e, 0xf1, 0x52, 0x2e, 0x4f, - 0x64, 0x03, 0xbd, 0x7e, 0xb7, 0xe3, 0x27, 0xe7, 0xe9, 0x59, 0xa3, 0x45, 0x43, 0xbb, 0x43, 0x3b, - 0xd4, 0x96, 0xd3, 0x67, 0x69, 0x5b, 0x5a, 0xd2, 0x90, 0x23, 0xbd, 0xfd, 0x1f, 0xb9, 0xed, 0x9e, - 0x9b, 0xb8, 0x2d, 0x1a, 0xdc, 0xf5, 0xa9, 0x19, 0x4a, 0x76, 0x21, 0xf5, 0x48, 0x10, 0xdb, 0xd2, - 0xad, 0x3a, 0x85, 0x1f, 0x40, 0xe5, 0xc8, 0x8f, 0x13, 0x87, 0x7c, 0x99, 0x92, 0x38, 0x41, 0x2b, - 0x30, 0x49, 0xdb, 0xed, 0x98, 0x24, 0x35, 0x6b, 0xcb, 0xda, 0x9e, 0x70, 0xb4, 0x85, 0x96, 0x60, - 0x22, 0xf0, 0x43, 0x3f, 0xa9, 0x8d, 0xc9, 0x69, 0x65, 0xe0, 0x07, 0x00, 0x7b, 0x8c, 0x99, 0xb3, - 0x08, 0x4a, 0x91, 0x1b, 0x12, 0x79, 0xb2, 0xec, 0xc8, 0x31, 0x5a, 0x83, 0x69, 0x4e, 0x18, 0x7d, - 0x9e, 0xf2, 0x40, 0x1e, 0x2d, 0x3b, 0x53, 0xc2, 0x7e, 0xc2, 0x03, 0xdc, 0x86, 0xb9, 0x3d, 0xc6, - 0x94, 0xf3, 0x98, 0xd1, 0x28, 0x26, 0xe8, 0xcf, 0x50, 0x72, 0x19, 0x8b, 0x6b, 0xd6, 0xd6, 0xf8, - 0x76, 0xe5, 0x7e, 0xa5, 0xa1, 0xf8, 0x36, 0xf6, 0x18, 0xdb, 0x2f, 0xbd, 0xf9, 0x69, 0xf3, 0x4f, - 0x8e, 0x5c, 0x1e, 0x4e, 0x26, 0x47, 0x7d, 0x3c, 0x4f, 0x1d, 0xdf, 0x81, 0xca, 0x5e, 0x9a, 0x9c, - 0x1b, 0x96, 0x75, 0x98, 0x66, 0x6e, 0x1c, 0xbf, 0xa4, 0xdc, 0xd3, 0x4c, 0x33, 0x1b, 0x7f, 0x63, - 0x41, 0x55, 0xed, 0xd5, 0x84, 0x10, 0x94, 0xce, 0x69, 0x9c, 0x98, 0x4f, 0x12, 0xe3, 0xec, 0x33, - 0xc7, 0x72, 0x9f, 0x59, 0x83, 0x29, 0xc6, 0xe9, 0x17, 0xa4, 0xa5, 0x9c, 0x97, 0x1d, 0x63, 0x0a, - 0x56, 0x9c, 0x74, 0x7c, 0x1a, 0xd5, 0x4a, 0x72, 0x41, 0x5b, 0x92, 0x06, 0xa7, 0x5d, 0xdf, 0x23, - 0xbc, 0x36, 0xa1, 0x69, 0x68, 0x1b, 0x3f, 0x05, 0x74, 0xc0, 0x89, 0x9b, 0x90, 0xfd, 0xd4, 0x0f, - 0x3c, 0x43, 0x7c, 0x1e, 0xc6, 0x5d, 0xc6, 0x34, 0x15, 0x31, 0x14, 0x5e, 0xbb, 0x84, 0xc7, 0x02, - 0x5c, 0xc7, 0x56, 0x9b, 0x1a, 0xbd, 0xd5, 0xf6, 0x03, 0x22, 0x09, 0x55, 0x9d, 0xcc, 0xc6, 0xf7, - 0xa0, 0xba, 0xc7, 0xd8, 0xe1, 0x15, 0xb8, 0xb3, 0x30, 0xe6, 0x7b, 0x1a, 0x72, 0xcc, 0xf7, 0x70, - 0x13, 0xe6, 0x24, 0x93, 0x23, 0xda, 0xb9, 0xf6, 0x21, 0xb1, 0x83, 0xd1, 0x58, 0xdf, 0x85, 0x18, - 0xe2, 0x5d, 0x98, 0xef, 0xc1, 0xe8, 0x00, 0xeb, 0x5d, 0x56, 0xb6, 0x4b, 0x5d, 0x6e, 0x44, 0xe2, - 0xda, 0xd8, 0xd6, 0xf8, 0x76, 0xd9, 0x51, 0x06, 0xfe, 0x17, 0xcc, 0x34, 0xa3, 0xee, 0x09, 0x49, - 0xae, 0x12, 0x1b, 0x82, 0x92, 0x50, 0xbb, 0xb9, 0x19, 0x31, 0xc6, 0x8f, 0x60, 0xc9, 0x21, 0x31, - 0x4d, 0x79, 0x8b, 0x14, 0xa4, 0xd6, 0x80, 0x32, 0xd7, 0xf3, 0x46, 0x6f, 0xf3, 0x46, 0x6f, 0xe6, - 0x80, 0xd3, 0xdb, 0x82, 0x77, 0x61, 0x41, 0x91, 0x2f, 0xea, 0x75, 0xf2, 0x4c, 0x4c, 0x1a, 0x84, - 0x19, 0x83, 0xa0, 0x2e, 0x4e, 0x2f, 0xe2, 0xff, 0xc2, 0xe2, 0x31, 0xa7, 0x2d, 0x12, 0xc7, 0x7d, - 0xa7, 0x27, 0x0e, 0x13, 0x12, 0x9a, 0xc3, 0x73, 0xe6, 0xb0, 0xde, 0xeb, 0xa8, 0x55, 0xfc, 0xbd, - 0x05, 0xcb, 0x4a, 0x0e, 0x19, 0xaf, 0xab, 0x63, 0x70, 0xe1, 0x47, 0xe6, 0x22, 0xe4, 0x18, 0x35, - 0x61, 0x92, 0xb9, 0xdc, 0x0d, 0xc5, 0x6d, 0x08, 0x4f, 0x77, 0x1b, 0xbd, 0xea, 0xd5, 0x18, 0x0a, - 0xdd, 0x38, 0x96, 0xfb, 0x9b, 0x51, 0xc2, 0x2f, 0x1d, 0x7d, 0xb8, 0xfe, 0x1f, 0xa8, 0xe4, 0xa6, - 0xc5, 0xd5, 0x5d, 0x90, 0x4b, 0x23, 0x81, 0x0b, 0x72, 0x29, 0xae, 0xae, 0xeb, 0x06, 0xa9, 0x49, - 0x0d, 0x65, 0xec, 0x8e, 0xfd, 0xdb, 0xc2, 0xff, 0x84, 0x59, 0x59, 0x28, 0x32, 0x27, 0x43, 0x04, - 0x34, 0x24, 0xaf, 0xf0, 0x16, 0xc0, 0x7b, 0xe9, 0x19, 0x69, 0x25, 0x81, 0x38, 0x83, 0xa0, 0xe4, - 0xf2, 0x8e, 0x8a, 0x57, 0xd9, 0x91, 0x63, 0x1c, 0x40, 0xe5, 0x20, 0xf4, 0xb2, 0x98, 0xae, 0x43, - 0x99, 0x7c, 0xe5, 0x27, 0xcf, 0x5b, 0xd4, 0x23, 0x5a, 0x55, 0xd3, 0x62, 0xe2, 0x80, 0x7a, 0x52, - 0x6c, 0x84, 0x73, 0xed, 0x40, 0x0c, 0x45, 0x76, 0xc6, 0x89, 0xf7, 0x38, 0x35, 0x69, 0xab, 0x2d, - 0x3d, 0xdf, 0xe4, 0xdc, 0x64, 0xad, 0xb2, 0xf0, 0xb7, 0x16, 0x54, 0x8f, 0x68, 0xe7, 0x24, 0xe1, - 0xc4, 0x0d, 0x35, 0xa5, 0x81, 0x2b, 0x58, 0x81, 0xc9, 0x36, 0x0d, 0x02, 0xfa, 0x52, 0x7a, 0x9a, - 0x76, 0xb4, 0x85, 0x6c, 0x98, 0x88, 0xfd, 0xa8, 0xa5, 0x32, 0xb2, 0x72, 0x7f, 0xad, 0xa1, 0xde, - 0x8d, 0x86, 0x29, 0xeb, 0x8d, 0x87, 0xba, 0xfe, 0x3b, 0x6a, 0x9f, 0xc9, 0x62, 0x51, 0xae, 0x35, - 0x8f, 0xcc, 0xc6, 0xff, 0x83, 0x19, 0xa3, 0x93, 0x34, 0x1a, 0xc5, 0xa4, 0x06, 0x53, 0x2d, 0x1a, - 0x86, 0xae, 0xd4, 0x83, 0x88, 0x99, 0x31, 0xf1, 0x26, 0x94, 0xd5, 0x47, 0xbc, 0x1f, 0x77, 0xb2, - 0xbc, 0xb1, 0x64, 0xa5, 0x50, 0x79, 0xb3, 0x0f, 0x8b, 0x0e, 0x09, 0x88, 0x1b, 0x17, 0xd3, 0xe6, - 0xaf, 0xa2, 0x9e, 0xcb, 0xe9, 0x01, 0xd9, 0xea, 0xed, 0x4e, 0xb6, 0x01, 0x9f, 0xc0, 0x92, 0x51, - 0x97, 0x5a, 0xd2, 0xba, 0xbd, 0x0d, 0x13, 0x32, 0x33, 0xa4, 0xc3, 0x81, 0xac, 0x51, 0x6b, 0x22, - 0x8a, 0x1e, 0x0d, 0x5d, 0xdf, 0xd4, 0x36, 0x6d, 0xdd, 0xff, 0x75, 0x11, 0xe6, 0x8e, 0x75, 0xa5, - 0x3c, 0x51, 0x8f, 0x32, 0xfa, 0x00, 0x4a, 0xa2, 0x6c, 0xa3, 0xd5, 0xbc, 0xb0, 0x73, 0x45, 0xbf, - 0x5e, 0x1b, 0x5c, 0x50, 0x1f, 0x84, 0x17, 0xbf, 0xfe, 0xf1, 0x97, 0xef, 0xc6, 0x66, 0xf0, 0xb4, - 0xdd, 0xfd, 0x9b, 0xed, 0xa6, 0xc9, 0xf9, 0xae, 0xb5, 0x83, 0x3e, 0x84, 0x29, 0xfd, 0x34, 0x15, - 0x21, 0x73, 0x2f, 0x65, 0x7d, 0xbd, 0x00, 0x59, 0x7c, 0xc8, 0xf0, 0xbc, 0x44, 0x05, 0xa4, 0x50, - 0xc5, 0x9b, 0xd5, 0x84, 0xf2, 0x1e, 0x63, 0x2a, 0x1c, 0x68, 0xa5, 0xef, 0xac, 0xc1, 0xcc, 0xbf, - 0x78, 0x7d, 0xcc, 0x18, 0x8b, 0x05, 0xb3, 0x47, 0x30, 0xb9, 0xc7, 0xd8, 0xff, 0x49, 0x72, 0x3d, - 0x8c, 0x55, 0x89, 0xb1, 0x80, 0xe6, 0x0c, 0x86, 0xfd, 0x4a, 0x08, 0xe3, 0x35, 0x7a, 0xa6, 0x5f, - 0xee, 0x38, 0x71, 0xf9, 0x68, 0xac, 0x95, 0x01, 0x89, 0x36, 0x45, 0xff, 0x82, 0x37, 0x25, 0xec, - 0x1a, 0x5a, 0xed, 0x83, 0xb5, 0xb9, 0x06, 0xfc, 0x48, 0x7e, 0xed, 0x43, 0x12, 0x90, 0x2b, 0xbe, - 0x76, 0x14, 0xba, 0x26, 0xbd, 0x33, 0x40, 0xfa, 0x18, 0x2a, 0x52, 0x22, 0x3a, 0x8a, 0x1b, 0x83, - 0x65, 0x2c, 0xff, 0x60, 0xd6, 0x8b, 0xba, 0xc2, 0x48, 0xc2, 0x56, 0x31, 0x08, 0x58, 0x55, 0x99, - 0xd1, 0x27, 0x1a, 0xf1, 0x30, 0x64, 0x94, 0x27, 0x68, 0x39, 0x8f, 0x98, 0xe5, 0xc7, 0x48, 0xa2, - 0x6b, 0x12, 0x71, 0x11, 0x2f, 0xf4, 0x10, 0x6d, 0x5f, 0x22, 0x6d, 0x5b, 0xe8, 0x14, 0xaa, 0x9a, - 0x8d, 0xd4, 0x3e, 0xda, 0x1a, 0x56, 0x74, 0xf3, 0x69, 0x51, 0xef, 0xcf, 0x24, 0xbc, 0x24, 0xf1, - 0x67, 0x71, 0x55, 0xe0, 0x9b, 0xac, 0x42, 0x47, 0x30, 0x2d, 0x81, 0x85, 0x08, 0x6a, 0x7d, 0xa1, - 0x3d, 0x1c, 0xf5, 0xf1, 0x05, 0x21, 0x68, 0xaa, 0xaf, 0x7c, 0xef, 0x35, 0xfa, 0x58, 0x47, 0x40, - 0xdf, 0xd5, 0x68, 0xc0, 0x6b, 0xdd, 0x56, 0x1e, 0xf9, 0x19, 0x94, 0xb3, 0x17, 0x73, 0xa4, 0x06, - 0x6e, 0xe6, 0xe7, 0x07, 0x1e, 0x58, 0x13, 0x61, 0x94, 0x8f, 0xb0, 0x16, 0x83, 0x67, 0xe0, 0x69, - 0x27, 0x46, 0xeb, 0x83, 0x30, 0x59, 0xaf, 0x52, 0xbf, 0x31, 0x7c, 0x51, 0xbb, 0xb8, 0x21, 0x5d, - 0xac, 0xa0, 0xa5, 0x3e, 0xfe, 0x76, 0x20, 0x80, 0x4f, 0x60, 0xb6, 0x19, 0x75, 0x7d, 0x4e, 0xa3, - 0x90, 0x44, 0xc9, 0x55, 0x79, 0xb7, 0x60, 0x02, 0xde, 0x8c, 0xba, 0x07, 0x34, 0x6a, 0xfb, 0x1d, - 0xbc, 0x22, 0xa1, 0xe7, 0xd1, 0xac, 0x80, 0x26, 0x51, 0xd7, 0x50, 0xff, 0xac, 0x00, 0x7a, 0x42, - 0x12, 0xb4, 0x96, 0x07, 0x2d, 0x34, 0x3a, 0x23, 0xe3, 0xae, 0xc1, 0x71, 0x3f, 0x78, 0x0b, 0x2a, - 0xb9, 0xc2, 0x3d, 0x92, 0xee, 0x66, 0x7e, 0x7e, 0x48, 0xa5, 0xc7, 0xeb, 0x12, 0x7f, 0x19, 0x2d, - 0xe6, 0xc5, 0x67, 0x9c, 0x9c, 0xc1, 0x8c, 0x3e, 0xf3, 0xce, 0xba, 0xd1, 0x35, 0x64, 0x67, 0xb5, - 0x88, 0xef, 0x32, 0xf6, 0x5a, 0xe9, 0xe7, 0x05, 0x54, 0xf3, 0x9d, 0xdb, 0xe8, 0x4a, 0xbc, 0x55, - 0xfc, 0x94, 0xc1, 0x66, 0x0f, 0x2f, 0x4b, 0x5f, 0x73, 0x68, 0x46, 0xf9, 0xd2, 0x3d, 0x1d, 0x7a, - 0x22, 0x42, 0xa5, 0x8c, 0xab, 0x6e, 0x76, 0xa0, 0x2f, 0x2c, 0x6a, 0x26, 0xc3, 0x33, 0xc1, 0x79, - 0x0a, 0xb3, 0x66, 0xa7, 0xae, 0x54, 0xb7, 0x7e, 0xb7, 0xe1, 0x1a, 0xe2, 0x44, 0x93, 0xc6, 0x7d, - 0xa4, 0x3f, 0xef, 0xa1, 0xbf, 0x63, 0x7d, 0xd5, 0xec, 0x77, 0x86, 0xb3, 0x27, 0xf9, 0xb0, 0x47, - 0x17, 0xa8, 0x3e, 0x80, 0x9e, 0x11, 0x1f, 0x42, 0xfa, 0x2f, 0x12, 0xfb, 0x56, 0x7d, 0x73, 0x18, - 0xb6, 0x1d, 0xf8, 0xd1, 0x85, 0xba, 0x62, 0xe4, 0xf7, 0x3e, 0xe3, 0x49, 0x14, 0xfc, 0x71, 0x47, - 0x77, 0xa4, 0xa3, 0xdb, 0xf5, 0x5b, 0x43, 0x1d, 0xa5, 0x51, 0xce, 0xd5, 0x63, 0x98, 0xd2, 0x4d, - 0x64, 0x31, 0x54, 0xbd, 0xce, 0xb2, 0x5e, 0xd0, 0x56, 0xae, 0x9f, 0x34, 0x8f, 0x30, 0xaa, 0x08, - 0x37, 0x17, 0x1a, 0xe5, 0x14, 0xa0, 0xd7, 0x7b, 0x8d, 0x7a, 0x34, 0x86, 0x4f, 0x17, 0x0b, 0x31, - 0x53, 0x28, 0x36, 0x4f, 0xa3, 0x6d, 0xeb, 0x9e, 0x85, 0x5e, 0x40, 0x25, 0xf7, 0x43, 0xe1, 0x7a, - 0xb9, 0x3b, 0xe4, 0x97, 0x05, 0xae, 0x49, 0x27, 0x08, 0xcd, 0xe7, 0x9d, 0x04, 0x02, 0xf2, 0x34, - 0xf3, 0x70, 0xe2, 0x76, 0x09, 0xca, 0x8a, 0xd6, 0x23, 0xca, 0x43, 0xd9, 0x7b, 0x8e, 0x54, 0xcd, - 0x4d, 0x89, 0xb9, 0x8a, 0x96, 0xf3, 0x98, 0x6d, 0x73, 0x0c, 0x9d, 0x42, 0x39, 0x6b, 0x8c, 0x8b, - 0xd5, 0x20, 0xdf, 0x2f, 0x5f, 0x2b, 0x2a, 0xa2, 0xf0, 0xea, 0x8b, 0xbc, 0x67, 0xed, 0xd7, 0xde, - 0xbc, 0xdd, 0xb0, 0x7e, 0x78, 0xbb, 0x61, 0xfd, 0xfc, 0x76, 0xc3, 0xfa, 0x34, 0xf7, 0x7f, 0x97, - 0xb3, 0x49, 0xc9, 0xf0, 0xef, 0xbf, 0x05, 0x00, 0x00, 0xff, 0xff, 0x67, 0xc1, 0x55, 0x4a, 0x9c, - 0x11, 0x00, 0x00, + // 1644 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0x5b, 0x6f, 0x1b, 0xc7, + 0x15, 0xee, 0xca, 0xd4, 0x85, 0x87, 0xd4, 0x6d, 0x74, 0xa3, 0x28, 0x5b, 0x52, 0xc7, 0x75, 0xab, + 0xca, 0x10, 0xd7, 0x55, 0x0b, 0xb4, 0x95, 0xdb, 0x02, 0x92, 0x4c, 0x17, 0x42, 0xd5, 0x5a, 0x5d, + 0x55, 0x50, 0x13, 0x3b, 0xb1, 0x57, 0xdc, 0x21, 0xb5, 0xd1, 0xee, 0xce, 0x78, 0x76, 0x49, 0x47, + 0x10, 0xf4, 0x12, 0x04, 0xc8, 0x0f, 0xc8, 0x2f, 0xca, 0x9b, 0x1f, 0x03, 0x04, 0xc8, 0x63, 0x10, + 0x18, 0xf9, 0x21, 0xc1, 0xdc, 0x96, 0xbb, 0xbc, 0xc8, 0x8a, 0x5f, 0x84, 0x39, 0x3b, 0x33, 0xdf, + 0xf9, 0xe6, 0xcc, 0x77, 0xce, 0x19, 0x0a, 0xa6, 0x62, 0xc2, 0x3b, 0x7e, 0x83, 0xc4, 0x35, 0xc6, + 0x69, 0x42, 0x11, 0x34, 0x68, 0x94, 0x70, 0x1a, 0x04, 0x84, 0x57, 0xef, 0xb6, 0x28, 0x6d, 0x05, + 0xc4, 0x76, 0x99, 0x6f, 0xbb, 0x51, 0x44, 0x13, 0x37, 0xf1, 0x69, 0xa4, 0x57, 0x56, 0x2b, 0x2c, + 0xb9, 0x64, 0x24, 0xb6, 0x49, 0xc8, 0x92, 0x4b, 0xf5, 0x57, 0xcf, 0xac, 0xea, 0x19, 0xaf, 0xcd, + 0xe5, 0x8e, 0x74, 0xa0, 0xe7, 0xb7, 0x5a, 0x7e, 0x72, 0xde, 0x3e, 0xab, 0x35, 0x68, 0x68, 0xb7, + 0x68, 0x8b, 0xda, 0xf2, 0xf3, 0x59, 0xbb, 0x29, 0x2d, 0x69, 0xc8, 0x91, 0x5e, 0xfe, 0xa7, 0xcc, + 0x72, 0xcf, 0x4d, 0xdc, 0x06, 0x0d, 0xb6, 0x7c, 0x6a, 0x86, 0x92, 0x5d, 0x48, 0x3d, 0x12, 0xc4, + 0xb6, 0x74, 0xab, 0x76, 0xe1, 0xc7, 0x50, 0x3a, 0xf4, 0xe3, 0xc4, 0x21, 0xaf, 0xdb, 0x24, 0x4e, + 0xd0, 0x22, 0x8c, 0xd1, 0x66, 0x33, 0x26, 0x49, 0xc5, 0x5a, 0xb7, 0x36, 0x46, 0x1d, 0x6d, 0xa1, + 0x79, 0x18, 0x0d, 0xfc, 0xd0, 0x4f, 0x2a, 0x23, 0xf2, 0xb3, 0x32, 0xf0, 0x6f, 0xa0, 0xac, 0x36, + 0xc7, 0x8c, 0x46, 0x31, 0x11, 0xab, 0xfc, 0x84, 0x84, 0x71, 0xc5, 0x5a, 0xbf, 0xb3, 0x51, 0x74, + 0x94, 0x81, 0x1f, 0x03, 0xec, 0x32, 0x66, 0x3c, 0x20, 0x28, 0x44, 0x6e, 0x48, 0x24, 0x7e, 0xd1, + 0x91, 0x63, 0xb4, 0x0c, 0x13, 0x9c, 0x30, 0xfa, 0xb2, 0xcd, 0x03, 0xe9, 0xa0, 0xe8, 0x8c, 0x0b, + 0xfb, 0x84, 0x07, 0xb8, 0x09, 0xd3, 0xbb, 0x8c, 0xe5, 0xbc, 0x3c, 0x80, 0x82, 0xcb, 0x98, 0x72, + 0x52, 0xda, 0x2e, 0xd5, 0xd4, 0xa9, 0x6a, 0xbb, 0x8c, 0xed, 0x15, 0xde, 0xfe, 0xb0, 0xf6, 0x2b, + 0x47, 0x4e, 0x0f, 0xa6, 0x9c, 0x39, 0xe0, 0x9d, 0xec, 0x01, 0xf1, 0xef, 0xa1, 0xb4, 0xdb, 0x4e, + 0xce, 0x0d, 0xcb, 0x2a, 0x4c, 0x30, 0x37, 0x8e, 0xdf, 0x50, 0xee, 0x69, 0xa6, 0xa9, 0x8d, 0xbf, + 0xb4, 0xa0, 0xac, 0xd6, 0x6a, 0x42, 0x08, 0x0a, 0xe7, 0x34, 0x4e, 0xcc, 0x91, 0xc4, 0x38, 0x3d, + 0xe6, 0x48, 0xe6, 0x98, 0x15, 0x18, 0x67, 0x9c, 0x7e, 0x46, 0x1a, 0xca, 0x79, 0xd1, 0x31, 0xa6, + 0x60, 0xc5, 0x49, 0xcb, 0xa7, 0x51, 0xa5, 0x20, 0x27, 0xb4, 0x25, 0x69, 0x70, 0xda, 0xf1, 0x3d, + 0xc2, 0x2b, 0xa3, 0x9a, 0x86, 0xb6, 0xf1, 0x0b, 0x40, 0xfb, 0x9c, 0xb8, 0x09, 0xd9, 0x6b, 0xfb, + 0x81, 0x67, 0x88, 0xcf, 0xc0, 0x1d, 0x97, 0x31, 0x4d, 0x45, 0x0c, 0x85, 0xd7, 0x0e, 0xe1, 0xb1, + 0x00, 0xd7, 0xb1, 0xd5, 0xa6, 0x46, 0x6f, 0x34, 0xfd, 0x80, 0x48, 0x42, 0x65, 0x27, 0xb5, 0xf1, + 0x23, 0x28, 0xef, 0x32, 0x76, 0x70, 0x03, 0xee, 0x14, 0x8c, 0xf8, 0x9e, 0x86, 0x1c, 0xf1, 0x3d, + 0x5c, 0x87, 0x69, 0xc9, 0xe4, 0x90, 0xb6, 0x6e, 0xbd, 0x49, 0xac, 0x60, 0x34, 0xd6, 0x77, 0x21, + 0x86, 0x78, 0x07, 0x66, 0xba, 0x30, 0x3a, 0xc0, 0x7a, 0x95, 0x95, 0xae, 0x52, 0x97, 0x1b, 0x91, + 0xb8, 0x32, 0xa2, 0x94, 0x26, 0x0d, 0xfc, 0x67, 0x98, 0xac, 0x47, 0x9d, 0x63, 0x92, 0xdc, 0x24, + 0x36, 0x04, 0x05, 0x91, 0x13, 0xe6, 0x66, 0xc4, 0x18, 0x3f, 0x85, 0x79, 0x87, 0xc4, 0xb4, 0xcd, + 0x1b, 0x24, 0x27, 0xb5, 0x1a, 0x14, 0xb9, 0xfe, 0x6e, 0xf4, 0x36, 0x63, 0xf4, 0x66, 0x36, 0x38, + 0xdd, 0x25, 0x78, 0x07, 0xa6, 0x52, 0xb5, 0x0e, 0x67, 0x30, 0x38, 0x99, 0x76, 0x60, 0x56, 0x1d, + 0x3c, 0xaf, 0xf5, 0xb1, 0x33, 0xf1, 0xd1, 0x78, 0x9f, 0x34, 0xde, 0xd5, 0xa5, 0xeb, 0x49, 0xfc, + 0x37, 0x98, 0x3b, 0xe2, 0xb4, 0x41, 0xe2, 0xb8, 0x67, 0xf7, 0xe8, 0x41, 0x9a, 0x8f, 0xa5, 0xed, + 0x69, 0xb3, 0x59, 0xaf, 0x75, 0xd4, 0x2c, 0xfe, 0xc6, 0x82, 0x05, 0x25, 0xa5, 0xf4, 0x4c, 0x37, + 0xc7, 0xef, 0xc2, 0x8f, 0xcc, 0x25, 0xca, 0x31, 0xaa, 0xc3, 0x18, 0x73, 0xb9, 0x1b, 0x8a, 0x9b, + 0x14, 0x9e, 0xb6, 0x6a, 0xdd, 0xfa, 0x58, 0x1b, 0x08, 0x5d, 0x3b, 0x92, 0xeb, 0xeb, 0x51, 0xc2, + 0x2f, 0x1d, 0xbd, 0xb9, 0xfa, 0x57, 0x28, 0x65, 0x3e, 0x8b, 0x6b, 0xbf, 0x20, 0x97, 0x46, 0x3e, + 0x17, 0xe4, 0x52, 0x44, 0xae, 0xe3, 0x06, 0x6d, 0x93, 0x56, 0xca, 0xd8, 0x19, 0xf9, 0x8b, 0x85, + 0xff, 0x21, 0x23, 0x9f, 0x71, 0x32, 0x40, 0x7c, 0x55, 0x51, 0x66, 0xd4, 0x02, 0x0d, 0x90, 0xda, + 0x78, 0x1d, 0xe0, 0x5f, 0xed, 0x33, 0xd2, 0x48, 0x02, 0xb1, 0x17, 0x41, 0xc1, 0xe5, 0x2d, 0x53, + 0xc7, 0xe4, 0x18, 0x07, 0x50, 0xda, 0x0f, 0xbd, 0x34, 0xb6, 0x2b, 0x50, 0x24, 0x9f, 0xfb, 0xc9, + 0xcb, 0x06, 0xf5, 0x88, 0x56, 0xe6, 0x84, 0xf8, 0xb0, 0x4f, 0x3d, 0x29, 0x58, 0xc2, 0xb9, 0x76, + 0x22, 0x86, 0x22, 0xc3, 0xe3, 0xc4, 0x7b, 0xd6, 0x36, 0xa9, 0xaf, 0x2d, 0xfd, 0xbd, 0xce, 0xb9, + 0xc9, 0x7c, 0x65, 0xe1, 0xaf, 0x2c, 0x28, 0x1f, 0xd2, 0xd6, 0x71, 0xc2, 0x89, 0x1b, 0x6a, 0x4a, + 0x7d, 0x57, 0xb1, 0x08, 0x63, 0x4d, 0x1a, 0x04, 0xf4, 0x8d, 0xf4, 0x34, 0xe1, 0x68, 0x0b, 0xd9, + 0x30, 0x1a, 0xfb, 0x51, 0x43, 0x65, 0x75, 0x69, 0x7b, 0xb9, 0xa6, 0x3a, 0x54, 0xcd, 0x34, 0x90, + 0xda, 0x13, 0xdd, 0x69, 0x1c, 0xb5, 0xce, 0x54, 0x02, 0xd1, 0x18, 0x34, 0x8f, 0xd4, 0xc6, 0x7f, + 0x87, 0x49, 0xa3, 0x97, 0x76, 0x34, 0x8c, 0x49, 0x05, 0xc6, 0x1b, 0x34, 0x0c, 0x5d, 0xa9, 0x0b, + 0x11, 0x33, 0x63, 0xe2, 0x35, 0x28, 0xaa, 0x43, 0xfc, 0x3b, 0x6e, 0xa5, 0xb9, 0x67, 0xc9, 0x6a, + 0xa3, 0x72, 0x6f, 0x0f, 0xe6, 0x1c, 0x12, 0x10, 0x37, 0xce, 0xa7, 0xde, 0x43, 0x71, 0x59, 0xf2, + 0x73, 0x9f, 0x7c, 0xf5, 0x72, 0x27, 0x5d, 0x80, 0x8f, 0x61, 0xde, 0xa8, 0x4c, 0x4d, 0x69, 0xfd, + 0xde, 0x87, 0x51, 0x99, 0x21, 0xd2, 0x61, 0x5f, 0xf6, 0xa8, 0x39, 0x11, 0x45, 0x8f, 0x86, 0xae, + 0x6f, 0xea, 0xa3, 0xb6, 0xf0, 0x6f, 0x01, 0x09, 0x46, 0x4f, 0xa4, 0xe5, 0x90, 0xd7, 0x69, 0x2d, + 0x12, 0x6d, 0x4a, 0x29, 0x43, 0x0c, 0xb7, 0xbf, 0x9f, 0x87, 0xe9, 0x23, 0x5d, 0x95, 0x8f, 0xd5, + 0x33, 0x01, 0xfd, 0x07, 0x0a, 0xa2, 0x45, 0xa0, 0xa5, 0x6c, 0x22, 0x64, 0x1a, 0x4c, 0xb5, 0xd2, + 0x3f, 0xa1, 0x0e, 0x8e, 0xe7, 0xbe, 0xf8, 0xee, 0xa7, 0xaf, 0x47, 0x26, 0xf1, 0x84, 0xdd, 0xf9, + 0x83, 0xed, 0xb6, 0x93, 0xf3, 0x1d, 0x6b, 0x13, 0xfd, 0x17, 0xc6, 0x75, 0x61, 0xc9, 0x43, 0x66, + 0x4a, 0x4d, 0x75, 0x25, 0x07, 0x99, 0x6f, 0x9a, 0x78, 0x46, 0xa2, 0x02, 0x52, 0xa8, 0xa2, 0x3f, + 0xd6, 0xa1, 0xb8, 0xcb, 0x98, 0x0a, 0x1b, 0x5a, 0xec, 0xd9, 0x6b, 0x30, 0xb3, 0xdd, 0xb5, 0x87, + 0x19, 0x63, 0xb1, 0x60, 0xf6, 0x14, 0xc6, 0x76, 0x19, 0xfb, 0x27, 0x49, 0x6e, 0x87, 0xb1, 0x24, + 0x31, 0x66, 0xd1, 0xb4, 0xc1, 0xb0, 0xaf, 0x84, 0x80, 0xae, 0xd1, 0x27, 0xfa, 0x95, 0x10, 0x27, + 0x2e, 0x1f, 0x8e, 0xb5, 0xd8, 0x27, 0xe5, 0xba, 0x78, 0x51, 0xe1, 0x35, 0x09, 0xbb, 0x8c, 0x96, + 0x7a, 0x60, 0x6d, 0xae, 0x01, 0xff, 0x27, 0x4f, 0xfb, 0x84, 0x04, 0xe4, 0x86, 0xd3, 0x0e, 0x43, + 0xd7, 0xa4, 0x37, 0xfb, 0x48, 0xbf, 0x92, 0xaf, 0x93, 0x13, 0xe6, 0xb9, 0x09, 0x51, 0x3a, 0x41, + 0xd5, 0x3e, 0xec, 0xb4, 0x24, 0x0d, 0xc5, 0x5f, 0x96, 0xf8, 0x73, 0xd5, 0x59, 0x81, 0xaf, 0xd4, + 0x17, 0xdb, 0x57, 0x2e, 0x63, 0xd7, 0xe8, 0x08, 0x4a, 0x52, 0xac, 0xfa, 0x9e, 0x56, 0xfb, 0x0b, + 0x6b, 0xb6, 0xfd, 0x57, 0xf3, 0x0a, 0xc7, 0x48, 0x02, 0x97, 0x31, 0x08, 0x60, 0xd5, 0x2b, 0xd0, + 0x47, 0x1a, 0xf1, 0x20, 0x64, 0x94, 0x27, 0x68, 0x21, 0x8b, 0x98, 0x66, 0xea, 0xfb, 0xa8, 0xe2, + 0xd9, 0x2e, 0xa2, 0xed, 0x4b, 0xa4, 0x0d, 0x0b, 0x9d, 0x42, 0x59, 0xb3, 0x91, 0x59, 0x88, 0xd6, + 0x07, 0xb5, 0x81, 0x6c, 0x82, 0x56, 0x7b, 0x73, 0x1a, 0xcf, 0x4b, 0xfc, 0x29, 0x5c, 0x16, 0xf8, + 0x26, 0xbf, 0xd1, 0x21, 0x4c, 0x48, 0x60, 0x21, 0xb3, 0x4a, 0x4f, 0x80, 0x0f, 0x86, 0x1d, 0x3e, + 0x27, 0x35, 0x4d, 0xf5, 0xca, 0xf7, 0xae, 0xd1, 0xff, 0x75, 0x04, 0xb4, 0x1a, 0x86, 0x03, 0xde, + 0x4a, 0x0f, 0x59, 0x64, 0x17, 0x8a, 0x69, 0x0f, 0xef, 0x53, 0x42, 0x36, 0x57, 0xef, 0x65, 0xe7, + 0xfa, 0xda, 0xbe, 0x89, 0x32, 0xca, 0x46, 0x59, 0x4b, 0xce, 0x33, 0x2e, 0x68, 0x2b, 0x46, 0x2b, + 0xfd, 0x30, 0xe9, 0xeb, 0xab, 0x7a, 0x77, 0xf0, 0xa4, 0x76, 0x71, 0x57, 0xba, 0x58, 0x44, 0xf3, + 0x3d, 0x67, 0xb0, 0x03, 0x01, 0x7c, 0x0c, 0x53, 0xf5, 0xa8, 0xe3, 0x73, 0x1a, 0x85, 0x24, 0x4a, + 0x6e, 0xca, 0xee, 0x59, 0x13, 0xf4, 0x7a, 0xd4, 0xd9, 0xa7, 0x51, 0xd3, 0x6f, 0xe1, 0x45, 0x09, + 0x3d, 0x83, 0xa6, 0x04, 0x34, 0x89, 0x3a, 0x86, 0xfa, 0xf3, 0x1c, 0xe8, 0x31, 0x49, 0xd0, 0x72, + 0x16, 0x34, 0xf7, 0x74, 0x1b, 0x1a, 0x7b, 0x0d, 0x8e, 0x7b, 0xc1, 0x1b, 0x50, 0xca, 0xb4, 0x91, + 0xa1, 0x74, 0xd7, 0xb2, 0xdf, 0x07, 0xf4, 0x1d, 0xbc, 0x22, 0xf1, 0x17, 0xd0, 0x5c, 0x56, 0x80, + 0xc6, 0xc9, 0x19, 0x4c, 0xea, 0x3d, 0x1f, 0xac, 0x1d, 0x5d, 0xa9, 0x36, 0x97, 0xf2, 0xf8, 0x22, + 0xd9, 0x95, 0x86, 0x5e, 0x41, 0x39, 0xfb, 0x16, 0x1d, 0x5e, 0xef, 0xd7, 0xf3, 0x47, 0xe9, 0x7f, + 0xbe, 0xe2, 0x05, 0xe9, 0x6b, 0x1a, 0x4d, 0x2a, 0x5f, 0xfa, 0x95, 0x8a, 0x4e, 0x44, 0xa8, 0x94, + 0x71, 0xd3, 0xcd, 0xf6, 0xbd, 0x74, 0xf3, 0x9a, 0x49, 0xf1, 0x4c, 0x70, 0x5e, 0xc0, 0x94, 0x59, + 0xa9, 0xab, 0xd5, 0xaf, 0xdf, 0xfb, 0x0c, 0x1c, 0xe0, 0x44, 0x93, 0xc6, 0x3d, 0xa4, 0x3f, 0xed, + 0xa2, 0x7f, 0x60, 0x15, 0xd7, 0xec, 0x37, 0x07, 0xb3, 0xf7, 0xb3, 0x61, 0x8f, 0x2e, 0x6e, 0xac, + 0xe3, 0xfd, 0xa4, 0x1f, 0x4a, 0xec, 0x07, 0xd5, 0xfb, 0x3d, 0xd8, 0xf2, 0x5a, 0x03, 0x3f, 0xba, + 0xb0, 0xaf, 0xcc, 0xd7, 0x6b, 0x14, 0x76, 0x8f, 0x72, 0x12, 0x05, 0xbf, 0xdc, 0xd9, 0x96, 0x74, + 0xf6, 0xbb, 0xea, 0x83, 0x41, 0xce, 0xda, 0x51, 0xaf, 0xbb, 0x67, 0x30, 0xae, 0x9f, 0xb6, 0xf9, + 0x90, 0x75, 0xdf, 0xbb, 0xd5, 0x9c, 0xc6, 0x32, 0xaf, 0x5c, 0xd3, 0xf2, 0x51, 0x49, 0xb8, 0xba, + 0xd0, 0x28, 0xa7, 0x00, 0xdd, 0x17, 0xe1, 0xb0, 0x06, 0x32, 0xf8, 0x73, 0xbe, 0x28, 0x33, 0x85, + 0x62, 0xf3, 0x76, 0xb4, 0x61, 0x3d, 0xb2, 0xd0, 0x73, 0x28, 0x65, 0x7e, 0xc6, 0xdc, 0x2e, 0x87, + 0x07, 0xfc, 0xee, 0xc9, 0xb3, 0xd6, 0x4e, 0xd0, 0x69, 0x0a, 0x7e, 0xec, 0x76, 0x08, 0x4a, 0xeb, + 0xd6, 0x53, 0xca, 0x43, 0xf9, 0x18, 0x1e, 0x2a, 0x9c, 0x7b, 0x12, 0x6e, 0x09, 0x2d, 0x64, 0x39, + 0x37, 0xcd, 0x36, 0x74, 0x0a, 0xc5, 0xf4, 0xa5, 0x9e, 0x2f, 0x08, 0xd9, 0x07, 0xfc, 0xad, 0x02, + 0x22, 0x6a, 0xaf, 0x16, 0xe4, 0x23, 0x6b, 0xaf, 0xf2, 0xf6, 0xdd, 0xaa, 0xf5, 0xed, 0xbb, 0x55, + 0xeb, 0xc7, 0x77, 0xab, 0xd6, 0xc7, 0x99, 0x7f, 0x39, 0x9d, 0x8d, 0x49, 0x86, 0x7f, 0xfc, 0x39, + 0x00, 0x00, 0xff, 0xff, 0x5e, 0xa0, 0x75, 0x51, 0x97, 0x12, 0x00, 0x00, } diff --git a/api/controller/services.pb.gw.go b/api/controller/services.pb.gw.go index fd17f66c..897ebca3 100644 --- a/api/controller/services.pb.gw.go +++ b/api/controller/services.pb.gw.go @@ -177,6 +177,41 @@ func request_ProviderService_AppDelete_0(ctx context.Context, marshaler runtime. } +var ( + filter_ProviderService_AppUpdateDomain_0 = &utilities.DoubleArray{Encoding: map[string]int{"app": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_ProviderService_AppUpdateDomain_0(ctx context.Context, marshaler runtime.Marshaler, client ProviderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AppResourceReq + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["app"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "app") + } + + protoReq.App, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "app", err) + } + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ProviderService_AppUpdateDomain_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.AppUpdateDomain(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + var ( filter_ProviderService_BuildCreate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -327,7 +362,7 @@ var ( ) func request_ProviderService_BuildList_0(ctx context.Context, marshaler runtime.Marshaler, client ProviderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AppRequest + var protoReq AppListRequest var metadata runtime.ServerMetadata var ( @@ -650,26 +685,26 @@ func request_ProviderService_ResourceLink_0(ctx context.Context, marshaler runti _ = err ) - val, ok = pathParams["name"] + val, ok = pathParams["app"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "app") } - protoReq.Name, err = runtime.String(val) + protoReq.App, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "app", err) } - val, ok = pathParams["app"] + val, ok = pathParams["resource"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "app") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource") } - protoReq.App, err = runtime.String(val) + protoReq.Resource, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "app", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource", err) } msg, err := client.ResourceLink(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -688,26 +723,26 @@ func request_ProviderService_ResourceUnlink_0(ctx context.Context, marshaler run _ = err ) - val, ok = pathParams["name"] + val, ok = pathParams["app"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "app") } - protoReq.Name, err = runtime.String(val) + protoReq.App, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "app", err) } - val, ok = pathParams["app"] + val, ok = pathParams["resource"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "app") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource") } - protoReq.App, err = runtime.String(val) + protoReq.Resource, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "app", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource", err) } msg, err := client.ResourceUnlink(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -1073,6 +1108,35 @@ func RegisterProviderServiceHandlerClient(ctx context.Context, mux *runtime.Serv }) + mux.Handle("PUT", pattern_ProviderService_AppUpdateDomain_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ProviderService_AppUpdateDomain_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ProviderService_AppUpdateDomain_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_ProviderService_BuildCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -1727,6 +1791,8 @@ var ( pattern_ProviderService_AppDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "apps", "name"}, "")) + pattern_ProviderService_AppUpdateDomain_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "domains", "app"}, "")) + pattern_ProviderService_BuildCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "builds"}, "")) pattern_ProviderService_BuildImport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "builds", "import"}, "")) @@ -1757,15 +1823,15 @@ var ( pattern_ProviderService_ResourceDelete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "resources", "name"}, "")) - pattern_ProviderService_ResourceLink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "resources", "name", "link", "app"}, "")) + pattern_ProviderService_ResourceLink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "resources", "app", "link", "resource"}, "")) - pattern_ProviderService_ResourceUnlink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "resources", "name", "unlink", "app"}, "")) + pattern_ProviderService_ResourceUnlink_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "resources", "app", "unlink", "resource"}, "")) pattern_ProviderService_Kubectl_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "kubectl"}, "")) pattern_ProviderService_ProcessRun_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "process", "run"}, "")) - pattern_ProviderService_ProcessList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "process", "list"}, "")) + pattern_ProviderService_ProcessList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "process"}, "")) pattern_ProviderService_ProcessSave_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "process", "formation"}, "")) @@ -1785,6 +1851,8 @@ var ( forward_ProviderService_AppDelete_0 = runtime.ForwardResponseMessage + forward_ProviderService_AppUpdateDomain_0 = runtime.ForwardResponseMessage + forward_ProviderService_BuildCreate_0 = runtime.ForwardResponseMessage forward_ProviderService_BuildImport_0 = runtime.ForwardResponseMessage diff --git a/api/controller/services.proto b/api/controller/services.proto index 95ae5dce..a6668156 100644 --- a/api/controller/services.proto +++ b/api/controller/services.proto @@ -17,6 +17,10 @@ message ListRequest { int32 limit = 2; } +message ListResponse { + repeated string items = 1; +} + message AppRequest { string name = 1; string repo_url = 2; @@ -71,6 +75,11 @@ message ResourceListResponse { repeated models.Resource resources = 1; } +message AppListRequest { + string name = 1; + int32 limit = 2; +} + message BuildListResponse { repeated models.Build builds = 1; } @@ -87,7 +96,7 @@ message CreateResourceRequest { message AppResourceReq { string app = 1; - string name = 2; + string resource = 2; } message KubectlReq { @@ -126,6 +135,10 @@ message CreateReleaseRequest { string domain = 2; } +message ListDomainReqponse { + repeated string url = 1; +} + service ProviderService { rpc Auth(AuthRequest) returns (AuthResponse) { option (google.api.http) = { @@ -166,6 +179,13 @@ service ProviderService { }; } + //Update Domain API + rpc AppUpdateDomain(AppResourceReq) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/v1/domains/{app}" + }; + } + //Build Api rpc BuildCreate(CreateBuildRequest) returns (models.Build) { option (google.api.http) = { @@ -197,7 +217,7 @@ service ProviderService { }; } - rpc BuildList(AppRequest) returns (BuildListResponse) { + rpc BuildList(AppListRequest) returns (BuildListResponse) { option (google.api.http) = { get: "/v1/builds/{name}" }; @@ -261,13 +281,13 @@ service ProviderService { rpc ResourceLink(AppResourceReq) returns (models.Resource) { option (google.api.http) = { - put: "/v1/resources/{name}/link/{app}" + put: "/v1/resources/{app}/link/{resource}" }; } rpc ResourceUnlink(AppResourceReq) returns (models.Resource) { option (google.api.http) = { - put: "/v1/resources/{name}/unlink/{app}" + put: "/v1/resources/{app}/unlink/{resource}" }; } @@ -287,7 +307,7 @@ service ProviderService { rpc ProcessList(AppRequest) returns (ProcessListResponse) { option (google.api.http) = { - get: "/v1/process/list" + get: "/v1/process" }; } diff --git a/api/controller/services.swagger.json b/api/controller/services.swagger.json index bae34437..4d90c143 100644 --- a/api/controller/services.swagger.json +++ b/api/controller/services.swagger.json @@ -321,9 +321,35 @@ "type": "string" }, { - "name": "repo_url", + "name": "limit", "in": "query", "required": false, + "type": "integer", + "format": "int32" + } + ], + "tags": [ + "ProviderService" + ] + } + }, + "/v1/domains/{app}": { + "put": { + "summary": "Update Domain API", + "operationId": "AppUpdateDomain", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/protobufEmpty" + } + } + }, + "parameters": [ + { + "name": "app", + "in": "path", + "required": true, "type": "string" } ], @@ -466,20 +492,26 @@ ] } }, - "/v1/process/formation": { + "/v1/process": { "get": { - "operationId": "ProcessSave", + "operationId": "ProcessList", "responses": { "200": { "description": "", "schema": { - "$ref": "#/definitions/protobufEmpty" + "$ref": "#/definitions/controllerProcessListResponse" } } }, "parameters": [ { - "name": "app", + "name": "name", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "repo_url", "in": "query", "required": false, "type": "string" @@ -490,26 +522,20 @@ ] } }, - "/v1/process/list": { + "/v1/process/formation": { "get": { - "operationId": "ProcessList", + "operationId": "ProcessSave", "responses": { "200": { "description": "", "schema": { - "$ref": "#/definitions/controllerProcessListResponse" + "$ref": "#/definitions/protobufEmpty" } } }, "parameters": [ { - "name": "name", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "repo_url", + "name": "app", "in": "query", "required": false, "type": "string" @@ -670,9 +696,9 @@ ] } }, - "/v1/resources/{name}": { - "get": { - "operationId": "ResourceGet", + "/v1/resources/{app}/link/{resource}": { + "put": { + "operationId": "ResourceLink", "responses": { "200": { "description": "", @@ -683,35 +709,43 @@ }, "parameters": [ { - "name": "name", + "name": "app", "in": "path", "required": true, "type": "string" }, { - "name": "repo_url", - "in": "query", - "required": false, + "name": "resource", + "in": "path", + "required": true, "type": "string" } ], "tags": [ "ProviderService" ] - }, - "delete": { - "operationId": "ResourceDelete", + } + }, + "/v1/resources/{app}/unlink/{resource}": { + "put": { + "operationId": "ResourceUnlink", "responses": { "200": { "description": "", "schema": { - "$ref": "#/definitions/protobufEmpty" + "$ref": "#/definitions/modelsResource" } } }, "parameters": [ { - "name": "name", + "name": "app", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "resource", "in": "path", "required": true, "type": "string" @@ -722,9 +756,9 @@ ] } }, - "/v1/resources/{name}/link/{app}": { - "put": { - "operationId": "ResourceLink", + "/v1/resources/{name}": { + "get": { + "operationId": "ResourceGet", "responses": { "200": { "description": "", @@ -741,25 +775,23 @@ "type": "string" }, { - "name": "app", - "in": "path", - "required": true, + "name": "repo_url", + "in": "query", + "required": false, "type": "string" } ], "tags": [ "ProviderService" ] - } - }, - "/v1/resources/{name}/unlink/{app}": { - "put": { - "operationId": "ResourceUnlink", + }, + "delete": { + "operationId": "ResourceDelete", "responses": { "200": { "description": "", "schema": { - "$ref": "#/definitions/modelsResource" + "$ref": "#/definitions/protobufEmpty" } } }, @@ -769,12 +801,6 @@ "in": "path", "required": true, "type": "string" - }, - { - "name": "app", - "in": "path", - "required": true, - "type": "string" } ], "tags": [ @@ -1002,18 +1028,27 @@ "modelsProcess": { "type": "object", "properties": { - "name": { - "type": "string" - }, "proctype": { "type": "string" }, - "workers": { + "count": { "type": "integer", "format": "int32" }, "status": { "type": "string" + }, + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "command": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -1035,6 +1070,10 @@ "created_at": { "type": "integer", "format": "int32" + }, + "version": { + "type": "string", + "format": "int64" } } }, diff --git a/api/domains.go b/api/domains.go new file mode 100644 index 00000000..62e98e2f --- /dev/null +++ b/api/domains.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + + pbs "github.com/datacol-io/datacol/api/controller" + pb "github.com/datacol-io/datacol/api/models" + "github.com/golang/protobuf/ptypes/empty" + "golang.org/x/net/context" +) + +func (s *Server) AppUpdateDomain(ctx context.Context, req *pbs.AppResourceReq) (*empty.Empty, error) { + empty := &empty.Empty{} + if err := s.Provider.AppUpdateDomain(req.App, req.Resource); err != nil { + return nil, internalError(err, "Failed to add domain") + } + + // kick-off a new release to make it real into ingress + + app, err := s.Provider.AppGet(req.App) + if err != nil { + return nil, internalError(err, "Failed to fetch app") + } + + if app.BuildId == "" { + return empty, nil + } + + b, err := s.Provider.BuildGet(app.Name, app.BuildId) + if err != nil { + return nil, internalError(err, fmt.Sprintf("Failed to fetch build by %s", app.BuildId)) + } + + _, err = s.Provider.BuildRelease(b, pb.ReleaseOptions{}) + + return empty, err +} diff --git a/api/helper.go b/api/helper.go index 510d40b9..7173aafa 100644 --- a/api/helper.go +++ b/api/helper.go @@ -128,7 +128,7 @@ func ws(at string, handler websocketFunc) websocket.Handler { err := handler(ws) if err != nil { - log.Error(err) + log.Errorf("ws %s: %v", at, err) ws.Write([]byte(fmt.Sprintf("ERROR: %v\n", err))) return } diff --git a/api/logs.go b/api/logs.go index 42980934..cb460513 100644 --- a/api/logs.go +++ b/api/logs.go @@ -40,9 +40,10 @@ func (s *Server) LogStreamWs(ws *websocket.Conn) error { } return s.Provider.LogStream(app, ws, pb.LogStreamOptions{ - Since: since, - Follow: follow, - Proctype: headers.Get("process"), + Since: since, + Follow: follow, + Proctype: headers.Get("process"), + TailLines: headers.Get("lines"), }) } diff --git a/api/models/extra.go b/api/models/extra.go index 55469be2..795366f4 100644 --- a/api/models/extra.go +++ b/api/models/extra.go @@ -36,6 +36,7 @@ type Apps []*App type Builds []*Build type Releases []*Release type Resources []*Resource +type AppDomains []string type ReleaseOptions struct { Port int @@ -46,9 +47,10 @@ type ReleaseOptions struct { } type LogStreamOptions struct { - Follow bool - Proctype string - Since time.Duration + Follow bool + Proctype string + Since time.Duration + TailLines string // number of recent lines to show for streaming (string format) } type AppCreateOptions struct { diff --git a/api/models/types.pb.go b/api/models/types.pb.go index 9c67a5e5..544e1ed4 100644 --- a/api/models/types.pb.go +++ b/api/models/types.pb.go @@ -172,6 +172,7 @@ type Release struct { BuildId string `protobuf:"bytes,3,opt,name=build_id,json=buildId,proto3" json:"build_id,omitempty" datastore:"build_id,noindex"` Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty" datastore:"status,noindex"` CreatedAt int32 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty" datastore:"created_at,noindex"` + Version int64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty" datastore:"version,noindex"` } func (m *Release) Reset() { *m = Release{} } @@ -214,6 +215,13 @@ func (m *Release) GetCreatedAt() int32 { return 0 } +func (m *Release) GetVersion() int64 { + if m != nil { + return m.Version + } + return 0 +} + type Resource struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty" datastore:"name"` Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty" datastore:"kind"` @@ -351,10 +359,12 @@ func (m *EnvConfig) GetData() map[string]string { } type Process struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Proctype string `protobuf:"bytes,2,opt,name=proctype,proto3" json:"proctype,omitempty"` - Workers int32 `protobuf:"varint,3,opt,name=workers,proto3" json:"workers,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` + Proctype string `protobuf:"bytes,1,opt,name=proctype,proto3" json:"proctype,omitempty"` + Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + Cpu string `protobuf:"bytes,4,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory string `protobuf:"bytes,5,opt,name=memory,proto3" json:"memory,omitempty"` + Command []string `protobuf:"bytes,6,rep,name=command" json:"command,omitempty"` } func (m *Process) Reset() { *m = Process{} } @@ -362,13 +372,6 @@ func (m *Process) String() string { return proto.CompactTextString(m) func (*Process) ProtoMessage() {} func (*Process) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{6} } -func (m *Process) GetName() string { - if m != nil { - return m.Name - } - return "" -} - func (m *Process) GetProctype() string { if m != nil { return m.Proctype @@ -376,9 +379,9 @@ func (m *Process) GetProctype() string { return "" } -func (m *Process) GetWorkers() int32 { +func (m *Process) GetCount() int32 { if m != nil { - return m.Workers + return m.Count } return 0 } @@ -390,6 +393,27 @@ func (m *Process) GetStatus() string { return "" } +func (m *Process) GetCpu() string { + if m != nil { + return m.Cpu + } + return "" +} + +func (m *Process) GetMemory() string { + if m != nil { + return m.Memory + } + return "" +} + +func (m *Process) GetCommand() []string { + if m != nil { + return m.Command + } + return nil +} + type Formation struct { App string `protobuf:"bytes,1,opt,name=app,proto3" json:"app,omitempty"` Structure map[string]int32 `protobuf:"bytes,2,rep,name=structure" json:"structure,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` @@ -605,6 +629,11 @@ func (m *Release) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.CreatedAt)) } + if m.Version != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.Version)) + } return i, nil } @@ -825,29 +854,50 @@ func (m *Process) MarshalTo(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } if len(m.Proctype) > 0 { - dAtA[i] = 0x12 + dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(len(m.Proctype))) i += copy(dAtA[i:], m.Proctype) } - if m.Workers != 0 { - dAtA[i] = 0x18 + if m.Count != 0 { + dAtA[i] = 0x10 i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Workers)) + i = encodeVarintTypes(dAtA, i, uint64(m.Count)) } if len(m.Status) > 0 { - dAtA[i] = 0x22 + dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(len(m.Status))) i += copy(dAtA[i:], m.Status) } + if len(m.Cpu) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.Cpu))) + i += copy(dAtA[i:], m.Cpu) + } + if len(m.Memory) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.Memory))) + i += copy(dAtA[i:], m.Memory) + } + if len(m.Command) > 0 { + for _, s := range m.Command { + dAtA[i] = 0x32 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } return i, nil } @@ -993,6 +1043,9 @@ func (m *Release) Size() (n int) { if m.CreatedAt != 0 { n += 1 + sovTypes(uint64(m.CreatedAt)) } + if m.Version != 0 { + n += 1 + sovTypes(uint64(m.Version)) + } return n } @@ -1095,21 +1148,31 @@ func (m *EnvConfig) Size() (n int) { func (m *Process) Size() (n int) { var l int _ = l - l = len(m.Name) + l = len(m.Proctype) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = len(m.Proctype) + if m.Count != 0 { + n += 1 + sovTypes(uint64(m.Count)) + } + l = len(m.Status) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - if m.Workers != 0 { - n += 1 + sovTypes(uint64(m.Workers)) + l = len(m.Cpu) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) } - l = len(m.Status) + l = len(m.Memory) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if len(m.Command) > 0 { + for _, s := range m.Command { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } return n } @@ -1806,6 +1869,25 @@ func (m *Release) Unmarshal(dAtA []byte) error { break } } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -2859,7 +2941,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Proctype", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2884,11 +2966,30 @@ func (m *Process) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Name = string(dAtA[iNdEx:postIndex]) + m.Proctype = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + } + m.Count = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Count |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Proctype", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2913,13 +3014,13 @@ func (m *Process) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Proctype = string(dAtA[iNdEx:postIndex]) + m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Workers", wireType) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Cpu", wireType) } - m.Workers = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -2929,14 +3030,24 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Workers |= (int32(b) & 0x7F) << shift + stringLen |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } - case 4: + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Cpu = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -2961,7 +3072,36 @@ func (m *Process) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Status = string(dAtA[iNdEx:postIndex]) + m.Memory = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Command = append(m.Command, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -3278,64 +3418,66 @@ var ( func init() { proto.RegisterFile("types.proto", fileDescriptorTypes) } var fileDescriptorTypes = []byte{ - // 938 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xdf, 0x6e, 0xe3, 0xc4, - 0x17, 0x96, 0xe3, 0x24, 0xae, 0x4f, 0xbb, 0x7f, 0x7e, 0xf3, 0xab, 0x16, 0x2b, 0xed, 0xc6, 0x59, - 0x4b, 0x2b, 0x72, 0x41, 0x13, 0x04, 0xe2, 0x5f, 0x59, 0x40, 0x0d, 0x14, 0xa9, 0xab, 0x95, 0x58, - 0x19, 0x16, 0x21, 0x6e, 0xaa, 0x69, 0x3c, 0x0d, 0x56, 0x12, 0x8f, 0x35, 0x33, 0x2e, 0xdb, 0x17, - 0xe0, 0x8a, 0x17, 0x40, 0x3c, 0x02, 0x2f, 0xc2, 0x25, 0x4f, 0x60, 0xa1, 0x3e, 0x82, 0x9f, 0x00, - 0xcd, 0x1f, 0xc7, 0x93, 0x66, 0x57, 0x10, 0x09, 0x89, 0xbb, 0xcc, 0x39, 0xdf, 0xf7, 0xf9, 0xcc, - 0x39, 0x5f, 0x8e, 0x0d, 0xbb, 0xe2, 0x3a, 0x27, 0x7c, 0x94, 0x33, 0x2a, 0x28, 0xea, 0x2e, 0x69, - 0x42, 0x16, 0xbc, 0x77, 0x38, 0xa3, 0x74, 0xb6, 0x20, 0x63, 0x9c, 0xa7, 0x63, 0x9c, 0x65, 0x54, - 0x60, 0x91, 0xd2, 0xcc, 0xa0, 0x7a, 0x47, 0xb3, 0x54, 0xfc, 0x50, 0x5c, 0x8c, 0xa6, 0x74, 0x39, - 0x9e, 0xd1, 0x19, 0x1d, 0xab, 0xf0, 0x45, 0x71, 0xa9, 0x4e, 0xea, 0xa0, 0x7e, 0x69, 0x78, 0xf4, - 0x8b, 0x0b, 0xee, 0x49, 0x9e, 0xa3, 0x21, 0xb4, 0x33, 0xbc, 0x24, 0x81, 0x33, 0x70, 0x86, 0xfe, - 0x64, 0xbf, 0x2a, 0xc3, 0xfb, 0x09, 0x16, 0x98, 0x0b, 0xca, 0xc8, 0x71, 0x24, 0x53, 0x51, 0xac, - 0x10, 0x68, 0x04, 0x5d, 0x2e, 0xb0, 0x28, 0x78, 0xd0, 0x52, 0xd8, 0x07, 0x55, 0x19, 0x22, 0x0b, - 0xab, 0x93, 0x51, 0x6c, 0x50, 0xe8, 0x04, 0x80, 0x91, 0x05, 0xc1, 0x9c, 0x9c, 0xa7, 0x49, 0xe0, - 0x2a, 0x4e, 0x54, 0x95, 0x61, 0xdf, 0xe2, 0x34, 0x80, 0xb7, 0x32, 0x9a, 0x66, 0x09, 0x79, 0x19, - 0xc5, 0xbe, 0x09, 0x9e, 0x25, 0xe8, 0x09, 0xec, 0x90, 0x2c, 0xc9, 0x69, 0x9a, 0x89, 0xa0, 0xad, - 0x04, 0x06, 0x55, 0x19, 0x1e, 0x5a, 0x02, 0x75, 0xba, 0xa1, 0xaf, 0x18, 0xe8, 0x63, 0xd8, 0xb9, - 0x28, 0xd2, 0x45, 0x22, 0x1f, 0xdf, 0x79, 0x25, 0xbb, 0x4e, 0x37, 0x6c, 0x4f, 0x85, 0xce, 0x12, - 0xf4, 0x11, 0x78, 0x09, 0x5d, 0xe2, 0x34, 0xe3, 0x41, 0x77, 0xe0, 0x0e, 0xfd, 0x49, 0x58, 0x95, - 0xe1, 0x81, 0xc5, 0x35, 0x59, 0x8b, 0x6a, 0x22, 0xe8, 0x33, 0xf0, 0x19, 0xe1, 0xb4, 0x60, 0x53, - 0xc2, 0x03, 0x4f, 0x91, 0x1f, 0x55, 0x65, 0xf8, 0x70, 0xed, 0xde, 0x26, 0xbf, 0x76, 0x6d, 0x13, - 0x8b, 0x7e, 0x72, 0xa1, 0x33, 0x91, 0x75, 0xa0, 0x08, 0x5a, 0x69, 0x62, 0x66, 0x83, 0xaa, 0x32, - 0xbc, 0x6b, 0x69, 0xa4, 0x49, 0x14, 0xb7, 0xd2, 0x04, 0x3d, 0x06, 0x17, 0xe7, 0xb9, 0x19, 0xca, - 0xff, 0xab, 0x32, 0xbc, 0x67, 0x81, 0x70, 0x9e, 0x47, 0xb1, 0xcc, 0xa3, 0x4f, 0x65, 0x55, 0x4b, - 0x2a, 0xac, 0x69, 0x6c, 0x56, 0x65, 0xf2, 0x56, 0x37, 0x75, 0xec, 0x2c, 0x41, 0xef, 0xaf, 0xc6, - 0xaf, 0x27, 0xd1, 0xaf, 0xca, 0xb0, 0xb7, 0x31, 0xfe, 0x86, 0x69, 0xd9, 0x60, 0xca, 0x08, 0x16, - 0x24, 0x39, 0xc7, 0x42, 0xcd, 0xa1, 0xb3, 0x61, 0x83, 0x06, 0x60, 0xf5, 0xc3, 0x04, 0x4f, 0x84, - 0xb4, 0x41, 0xce, 0xe8, 0xf4, 0x32, 0x5d, 0x90, 0xa0, 0x3b, 0x70, 0x86, 0x7b, 0x1b, 0x83, 0xac, - 0xd3, 0x56, 0xe1, 0x75, 0x48, 0x4e, 0xf2, 0x8a, 0x30, 0x9e, 0xd2, 0x2c, 0xf0, 0x54, 0xe5, 0xb7, - 0x27, 0x69, 0xb2, 0xd6, 0x24, 0x4d, 0x24, 0xfa, 0xb9, 0x05, 0x5e, 0xac, 0xdd, 0xf8, 0x6f, 0x8e, - 0xc2, 0x36, 0xa6, 0xbb, 0xad, 0x31, 0xff, 0xbb, 0x39, 0x44, 0xbf, 0x79, 0xb0, 0x13, 0x1b, 0x97, - 0x6e, 0xb1, 0x38, 0x86, 0xd0, 0x9e, 0xa7, 0x59, 0x62, 0xda, 0x72, 0x1b, 0x29, 0x53, 0x51, 0xac, - 0x10, 0xd6, 0xdd, 0xdc, 0xad, 0xee, 0xf6, 0x14, 0xee, 0xe8, 0x5f, 0xe7, 0x8c, 0x60, 0x4e, 0x33, - 0xd3, 0x9a, 0xc7, 0x55, 0x19, 0x3e, 0xda, 0xa0, 0x1b, 0x4c, 0xa3, 0xb2, 0xa7, 0xe3, 0xb1, 0x0a, - 0xa3, 0x23, 0x70, 0x5f, 0xc4, 0xcf, 0xcc, 0xc2, 0x38, 0xa8, 0xca, 0xf0, 0x0d, 0x4b, 0xe1, 0x45, - 0xfc, 0xac, 0xe1, 0x49, 0x1c, 0x7a, 0x13, 0x3a, 0x5c, 0xe0, 0xe9, 0x5c, 0x19, 0xd3, 0x9f, 0xfc, - 0xaf, 0x2a, 0xc3, 0x3b, 0x16, 0xe1, 0x28, 0x8a, 0x75, 0x1e, 0xbd, 0x0d, 0x6d, 0x9c, 0xe7, 0xf5, - 0x42, 0x38, 0xac, 0xca, 0x30, 0x58, 0x37, 0x87, 0x75, 0x2f, 0x85, 0x44, 0xdf, 0x81, 0x47, 0x5e, - 0xe6, 0x94, 0x09, 0x1e, 0xec, 0x0c, 0xdc, 0xe1, 0xee, 0x3b, 0x0f, 0x47, 0xfa, 0x4d, 0x30, 0xaa, - 0x87, 0x30, 0x3a, 0xd5, 0xf9, 0xd3, 0x4c, 0xb0, 0xeb, 0x0d, 0x5f, 0x1b, 0xb2, 0xe5, 0x21, 0x13, - 0x41, 0x09, 0x40, 0x8e, 0x19, 0x5e, 0x12, 0x41, 0x18, 0x0f, 0x7c, 0x25, 0x3e, 0xd8, 0x10, 0x7f, - 0xbe, 0x82, 0x68, 0xfd, 0xdb, 0x6e, 0x69, 0x24, 0x9a, 0x47, 0x58, 0xba, 0xe8, 0x29, 0x78, 0xb4, - 0x10, 0x79, 0x21, 0x78, 0x00, 0xaf, 0xa9, 0xff, 0x2b, 0x9d, 0xd7, 0xfa, 0xaf, 0xe8, 0x5d, 0x2d, - 0x80, 0x4e, 0xa0, 0x2d, 0xf0, 0x8c, 0x07, 0xbb, 0x4a, 0xa8, 0xb7, 0x21, 0xf4, 0x0d, 0x9e, 0xbd, - 0x5e, 0x45, 0x51, 0x7b, 0xc7, 0xb0, 0x67, 0xb7, 0x0b, 0xdd, 0x07, 0x77, 0x4e, 0xae, 0xb5, 0x7f, - 0x63, 0xf9, 0x13, 0xed, 0x43, 0xe7, 0x0a, 0x2f, 0x0a, 0xa2, 0x9d, 0x1a, 0xeb, 0xc3, 0x71, 0xeb, - 0x43, 0xa7, 0xf7, 0x09, 0xdc, 0xbb, 0xd5, 0x8d, 0xad, 0xe8, 0xc7, 0xb0, 0x67, 0xdf, 0x74, 0x2b, - 0xee, 0x07, 0xe0, 0xaf, 0x2e, 0xb7, 0x0d, 0x31, 0x7a, 0x0f, 0x76, 0xeb, 0xf6, 0x7c, 0x8b, 0xd9, - 0x3f, 0xa5, 0x46, 0x05, 0xf8, 0xa7, 0xd9, 0xd5, 0xe7, 0x34, 0xbb, 0x4c, 0x67, 0x68, 0x0c, 0x6d, - 0xd9, 0xca, 0xc0, 0x51, 0x6d, 0x3f, 0xa8, 0xdb, 0xbe, 0x02, 0x8c, 0xbe, 0xc0, 0x02, 0xab, 0xd2, - 0x62, 0x05, 0x94, 0xd5, 0xae, 0x42, 0x5b, 0x55, 0x3b, 0x07, 0xef, 0x39, 0xa3, 0x53, 0xc2, 0x39, - 0x42, 0xf6, 0x66, 0x31, 0x3b, 0xa4, 0xa7, 0x5f, 0x01, 0xf2, 0xb3, 0xc8, 0x70, 0x57, 0x67, 0x14, - 0x80, 0xf7, 0x23, 0x65, 0x73, 0x69, 0x65, 0xb9, 0x36, 0x3a, 0x71, 0x7d, 0x44, 0x0f, 0xd6, 0x77, - 0x65, 0xbd, 0x2f, 0xa2, 0x5f, 0x1d, 0xf0, 0xbf, 0xa4, 0x6c, 0xa9, 0x3e, 0xa0, 0x64, 0x99, 0x72, - 0x6b, 0x9b, 0x32, 0xcd, 0xbb, 0x92, 0x0b, 0x56, 0x4c, 0x45, 0xc1, 0xe4, 0xe3, 0xd6, 0xfe, 0x1e, - 0x2b, 0xde, 0xe8, 0xeb, 0x1a, 0xa2, 0x1b, 0xd0, 0x50, 0x7a, 0x4f, 0xe0, 0xee, 0x7a, 0xf2, 0xef, - 0x5a, 0xd1, 0xb1, 0x5a, 0x31, 0xd9, 0xff, 0xfd, 0xa6, 0xef, 0xfc, 0x71, 0xd3, 0x77, 0xfe, 0xbc, - 0xe9, 0x3b, 0xdf, 0x9b, 0xaf, 0xbf, 0x8b, 0xae, 0xfa, 0x6e, 0x7b, 0xf7, 0xaf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x83, 0x0d, 0x5c, 0x58, 0x1b, 0x0a, 0x00, 0x00, + // 967 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0x96, 0xeb, 0xa4, 0x6e, 0x4e, 0xbb, 0xbb, 0x65, 0xa8, 0x16, 0x2b, 0xed, 0xc6, 0x59, 0x4b, + 0x2b, 0x72, 0x41, 0x13, 0x04, 0xe2, 0xaf, 0x2c, 0xa0, 0x06, 0x8a, 0xd4, 0xd5, 0x4a, 0xac, 0x0c, + 0x8b, 0x10, 0x37, 0xd5, 0xd4, 0x9e, 0x06, 0xab, 0xb1, 0xc7, 0x9a, 0x19, 0x57, 0xdb, 0x17, 0xe0, + 0x15, 0x10, 0xe2, 0x11, 0x78, 0x10, 0xb8, 0xe4, 0x09, 0x2c, 0xd4, 0x47, 0xf0, 0x13, 0xa0, 0xf9, + 0x71, 0x3c, 0x69, 0x76, 0x05, 0x91, 0x90, 0xf6, 0xce, 0x73, 0xce, 0xf7, 0x7d, 0x39, 0xf3, 0x9d, + 0x93, 0x99, 0x81, 0x6d, 0x71, 0x5d, 0x10, 0x3e, 0x2e, 0x18, 0x15, 0x14, 0x6d, 0x66, 0x34, 0x21, + 0x73, 0xde, 0x3f, 0x98, 0x51, 0x3a, 0x9b, 0x93, 0x09, 0x2e, 0xd2, 0x09, 0xce, 0x73, 0x2a, 0xb0, + 0x48, 0x69, 0x6e, 0x50, 0xfd, 0xc3, 0x59, 0x2a, 0x7e, 0x2a, 0xcf, 0xc7, 0x31, 0xcd, 0x26, 0x33, + 0x3a, 0xa3, 0x13, 0x15, 0x3e, 0x2f, 0x2f, 0xd4, 0x4a, 0x2d, 0xd4, 0x97, 0x86, 0x87, 0xbf, 0xba, + 0xe0, 0x1e, 0x17, 0x05, 0x1a, 0x41, 0x27, 0xc7, 0x19, 0xf1, 0x9d, 0xa1, 0x33, 0xea, 0x4d, 0xf7, + 0xea, 0x2a, 0xd8, 0x4d, 0xb0, 0xc0, 0x5c, 0x50, 0x46, 0x8e, 0x42, 0x99, 0x0a, 0x23, 0x85, 0x40, + 0x63, 0xd8, 0xe4, 0x02, 0x8b, 0x92, 0xfb, 0x1b, 0x0a, 0x7b, 0xbf, 0xae, 0x02, 0x64, 0x61, 0x75, + 0x32, 0x8c, 0x0c, 0x0a, 0x1d, 0x03, 0x30, 0x32, 0x27, 0x98, 0x93, 0xb3, 0x34, 0xf1, 0x5d, 0xc5, + 0x09, 0xeb, 0x2a, 0x18, 0x58, 0x9c, 0x16, 0xf0, 0x4e, 0x4e, 0xd3, 0x3c, 0x21, 0x2f, 0xc2, 0xa8, + 0x67, 0x82, 0xa7, 0x09, 0x7a, 0x0c, 0x5b, 0x24, 0x4f, 0x0a, 0x9a, 0xe6, 0xc2, 0xef, 0x28, 0x81, + 0x61, 0x5d, 0x05, 0x07, 0x96, 0x40, 0x93, 0x6e, 0xe9, 0x0b, 0x06, 0xfa, 0x14, 0xb6, 0xce, 0xcb, + 0x74, 0x9e, 0xc8, 0x9f, 0xef, 0xbe, 0x94, 0xdd, 0xa4, 0x5b, 0xb6, 0xa7, 0x42, 0xa7, 0x09, 0xfa, + 0x04, 0xbc, 0x84, 0x66, 0x38, 0xcd, 0xb9, 0xbf, 0x39, 0x74, 0x47, 0xbd, 0x69, 0x50, 0x57, 0xc1, + 0xbe, 0xc5, 0x35, 0x59, 0x8b, 0x6a, 0x22, 0xe8, 0x0b, 0xe8, 0x31, 0xc2, 0x69, 0xc9, 0x62, 0xc2, + 0x7d, 0x4f, 0x91, 0x1f, 0xd6, 0x55, 0xf0, 0x60, 0x69, 0xdf, 0x26, 0xbf, 0xb4, 0x6d, 0x13, 0x0b, + 0x7f, 0x76, 0xa1, 0x3b, 0x95, 0x75, 0xa0, 0x10, 0x36, 0xd2, 0xc4, 0xf4, 0x06, 0xd5, 0x55, 0x70, + 0xd7, 0xd2, 0x48, 0x93, 0x30, 0xda, 0x48, 0x13, 0xf4, 0x08, 0x5c, 0x5c, 0x14, 0xa6, 0x29, 0x6f, + 0xd6, 0x55, 0x70, 0xcf, 0x02, 0xe1, 0xa2, 0x08, 0x23, 0x99, 0x47, 0x9f, 0xcb, 0xaa, 0x32, 0x2a, + 0xac, 0x6e, 0xac, 0x56, 0x65, 0xf2, 0x96, 0x9b, 0x3a, 0x76, 0x9a, 0xa0, 0x0f, 0x17, 0xed, 0xd7, + 0x9d, 0x18, 0xd4, 0x55, 0xd0, 0x5f, 0x69, 0x7f, 0xcb, 0xb4, 0xc6, 0x20, 0x66, 0x04, 0x0b, 0x92, + 0x9c, 0x61, 0xa1, 0xfa, 0xd0, 0x5d, 0x19, 0x83, 0x16, 0x60, 0xf9, 0x61, 0x82, 0xc7, 0x42, 0x8e, + 0x41, 0xc1, 0x68, 0x7c, 0x91, 0xce, 0x89, 0xbf, 0x39, 0x74, 0x46, 0x3b, 0x2b, 0x8d, 0x6c, 0xd2, + 0x56, 0xe1, 0x4d, 0x48, 0x76, 0xf2, 0x8a, 0x30, 0x9e, 0xd2, 0xdc, 0xf7, 0x54, 0xe5, 0xb7, 0x3b, + 0x69, 0xb2, 0x56, 0x27, 0x4d, 0x24, 0xfc, 0x63, 0x03, 0xbc, 0x48, 0x4f, 0xe3, 0xff, 0xd9, 0x0a, + 0x7b, 0x30, 0xdd, 0x75, 0x07, 0xf3, 0x35, 0xf6, 0xc1, 0x72, 0x52, 0xb6, 0xc1, 0x5d, 0xc3, 0xc9, + 0xdf, 0x3d, 0xd8, 0x8a, 0xcc, 0x80, 0xaf, 0x71, 0xe6, 0x8c, 0xa0, 0x73, 0x99, 0xe6, 0x89, 0x71, + 0xf4, 0x36, 0x52, 0xa6, 0xc2, 0x48, 0x21, 0x2c, 0x5b, 0xdc, 0xb5, 0x6c, 0x79, 0x02, 0x77, 0xf4, + 0xd7, 0x19, 0x23, 0x98, 0xd3, 0xdc, 0xb8, 0xfa, 0xa8, 0xae, 0x82, 0x87, 0x2b, 0x74, 0x83, 0x69, + 0x55, 0x76, 0x74, 0x3c, 0x52, 0x61, 0x74, 0x08, 0xee, 0xf3, 0xe8, 0xa9, 0x39, 0x6b, 0xf6, 0xeb, + 0x2a, 0x78, 0xcb, 0x52, 0x78, 0x1e, 0x3d, 0x6d, 0x79, 0x12, 0x87, 0xde, 0x86, 0x2e, 0x17, 0x38, + 0xbe, 0x54, 0x66, 0xf6, 0xa6, 0x6f, 0xd4, 0x55, 0x70, 0xc7, 0x22, 0x1c, 0x86, 0x91, 0xce, 0xa3, + 0x77, 0xa1, 0x83, 0x8b, 0xa2, 0x39, 0x4b, 0x0e, 0xea, 0x2a, 0xf0, 0x97, 0xe7, 0xca, 0xda, 0x97, + 0x42, 0xa2, 0x1f, 0xc0, 0x23, 0x2f, 0x0a, 0xca, 0x04, 0xf7, 0xb7, 0x86, 0xee, 0x68, 0xfb, 0xbd, + 0x07, 0x63, 0x7d, 0x89, 0x8c, 0x9b, 0x26, 0x8c, 0x4f, 0x74, 0xfe, 0x24, 0x17, 0xec, 0x7a, 0xa5, + 0x91, 0x86, 0x6c, 0x35, 0xd2, 0x44, 0x50, 0x02, 0x50, 0x60, 0x86, 0x33, 0x22, 0x08, 0xe3, 0x7e, + 0x4f, 0x89, 0x0f, 0x57, 0xc4, 0x9f, 0x2d, 0x20, 0x5a, 0xff, 0xf6, 0xa0, 0xb5, 0x12, 0xed, 0x4f, + 0x58, 0xba, 0xe8, 0x09, 0x78, 0xb4, 0x14, 0x45, 0x29, 0xb8, 0x0f, 0xaf, 0xa8, 0xff, 0x1b, 0x9d, + 0xd7, 0xfa, 0x2f, 0xf1, 0xae, 0x11, 0x40, 0xc7, 0xd0, 0x11, 0x78, 0xc6, 0xfd, 0x6d, 0x25, 0xd4, + 0x5f, 0x11, 0xfa, 0x0e, 0xcf, 0x5e, 0xad, 0xa2, 0xa8, 0xfd, 0x23, 0xd8, 0xb1, 0xed, 0x42, 0xbb, + 0xe0, 0x5e, 0x92, 0x6b, 0x3d, 0xbf, 0x91, 0xfc, 0x44, 0x7b, 0xd0, 0xbd, 0xc2, 0xf3, 0x92, 0xe8, + 0x49, 0x8d, 0xf4, 0xe2, 0x68, 0xe3, 0x63, 0xa7, 0xff, 0x19, 0xdc, 0xbb, 0xe5, 0xc6, 0x5a, 0xf4, + 0x23, 0xd8, 0xb1, 0x77, 0xba, 0x16, 0xf7, 0x23, 0xe8, 0x2d, 0x36, 0xb7, 0x0e, 0x31, 0xfc, 0x00, + 0xb6, 0x1b, 0x7b, 0xbe, 0xc7, 0xec, 0xbf, 0x52, 0xc3, 0x12, 0x7a, 0x27, 0xf9, 0xd5, 0x97, 0x34, + 0xbf, 0x48, 0x67, 0x68, 0x02, 0x1d, 0x69, 0xa5, 0xef, 0x28, 0xdb, 0xf7, 0x1b, 0xdb, 0x17, 0x80, + 0xf1, 0x57, 0x58, 0x60, 0x55, 0x5a, 0xa4, 0x80, 0xb2, 0xda, 0x45, 0x68, 0xad, 0x6a, 0x7f, 0x71, + 0xc0, 0x7b, 0xc6, 0x68, 0x4c, 0x38, 0x47, 0x7d, 0x7d, 0x55, 0xc8, 0xe7, 0x93, 0x21, 0x2f, 0xd6, + 0x52, 0x21, 0xa6, 0x65, 0x2e, 0x94, 0x42, 0x37, 0xd2, 0x0b, 0x74, 0x7f, 0xf9, 0xe0, 0x58, 0x1c, + 0x0c, 0xbb, 0xe0, 0xc6, 0x45, 0xa9, 0x8f, 0x83, 0x48, 0x7e, 0x4a, 0x64, 0x46, 0x32, 0xca, 0xae, + 0xf5, 0x3f, 0x3c, 0x32, 0x2b, 0xe4, 0x83, 0x17, 0xd3, 0x2c, 0xc3, 0x79, 0xa2, 0x9f, 0x0a, 0x51, + 0xb3, 0x0c, 0x7f, 0x73, 0xa0, 0xf7, 0x35, 0x65, 0x99, 0x7a, 0xa8, 0x49, 0x45, 0x79, 0x3b, 0x98, + 0x3d, 0x99, 0x3b, 0x99, 0x0b, 0x56, 0xc6, 0xa2, 0x64, 0x72, 0x5f, 0x4b, 0xff, 0xa5, 0x05, 0x6f, + 0xfc, 0x6d, 0x03, 0xd1, 0x6e, 0xb5, 0x94, 0xfe, 0x63, 0xb8, 0xbb, 0x9c, 0xfc, 0x37, 0xdf, 0xba, + 0x96, 0x6f, 0xd3, 0xbd, 0x3f, 0x6f, 0x06, 0xce, 0x5f, 0x37, 0x03, 0xe7, 0xef, 0x9b, 0x81, 0xf3, + 0xa3, 0x79, 0x65, 0x9e, 0x6f, 0xaa, 0xf7, 0xe1, 0xfb, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x1c, + 0x7b, 0xad, 0x60, 0x83, 0x0a, 0x00, 0x00, } diff --git a/api/models/types.proto b/api/models/types.proto index 81b7a4bc..84bf8c51 100644 --- a/api/models/types.proto +++ b/api/models/types.proto @@ -35,6 +35,7 @@ message Release { string build_id = 3 [(gogoproto.moretags) = "datastore:\"build_id,noindex\""]; string status = 4 [(gogoproto.moretags) = "datastore:\"status,noindex\""]; int32 created_at = 5 [(gogoproto.moretags) = "datastore:\"created_at,noindex\""]; + int64 version = 6 [(gogoproto.moretags) = "datastore:\"version,noindex\""]; } message Resource { @@ -61,10 +62,12 @@ message EnvConfig { } message Process { - string name = 1; - string proctype = 2; - int32 workers = 3; - string status = 4; + string proctype = 1; + int32 count = 2; + string status = 3; + string cpu = 4; + string memory = 5; + repeated string command = 6; } message Formation { diff --git a/api/process.go b/api/process.go index c2af92c6..0a8f9b90 100644 --- a/api/process.go +++ b/api/process.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net" + "strings" "sync" "time" @@ -57,7 +58,8 @@ func (s *Server) ProcessRunWs(ws *websocket.Conn) error { return fmt.Errorf("Missing require param: app") } - return s.Provider.ProcessRun(app, ws, headers.Get("command")) + command := strings.Split(headers.Get("command"), "#") + return s.Provider.ProcessRun(app, ws, command) } func (s *Server) ProcessRun(srv pbs.ProviderService_ProcessRunServer) error { @@ -65,7 +67,8 @@ func (s *Server) ProcessRun(srv pbs.ProviderService_ProcessRunServer) error { app, command := md["app"][0], md["command"][0] stream := &runStreamRW{srv} - if err := s.Provider.ProcessRun(app, stream, command); err != nil { + commandParts := strings.Split(command, "#") + if err := s.Provider.ProcessRun(app, stream, commandParts); err != nil { log.Errorf("failed to run the process: %v", err) return err } diff --git a/api/server.go b/api/server.go index 1b9930d9..5bbc8776 100644 --- a/api/server.go +++ b/api/server.go @@ -242,8 +242,8 @@ func (s *Server) BuildImport(stream pbs.ProviderService_BuildImportServer) error return stream.SendAndClose(emptyMsg) } -func (s *Server) BuildList(ctx context.Context, req *pbs.AppRequest) (*pbs.BuildListResponse, error) { - items, err := s.Provider.BuildList(req.Name, 100) +func (s *Server) BuildList(ctx context.Context, req *pbs.AppListRequest) (*pbs.BuildListResponse, error) { + items, err := s.Provider.BuildList(req.Name, int(req.Limit)) if err != nil { return nil, err } @@ -297,7 +297,11 @@ func (s *Server) BuildLogStreamReq(ws *websocket.Conn) error { return err } - _, err = io.Copy(ws, r) + //FIXME: r can be nil for minikube-based environment. Should return io.Reader from docker daemon + if r != nil { + _, err = io.Copy(ws, r) + } + return err } @@ -358,17 +362,17 @@ func (s *Server) ResourceDelete(ctx context.Context, req *pbs.AppRequest) (*empt } func (s *Server) ResourceLink(ctx context.Context, req *pbs.AppResourceReq) (*pb.Resource, error) { - ret, err := s.Provider.ResourceLink(req.App, req.Name) + ret, err := s.Provider.ResourceLink(req.App, req.Resource) if err != nil { - return nil, internalError(err, fmt.Sprintf("failed to link resource %s", req.Name)) + return nil, internalError(err, fmt.Sprintf("failed to link resource %s", req.Resource)) } return ret, nil } func (s *Server) ResourceUnlink(ctx context.Context, req *pbs.AppResourceReq) (*pb.Resource, error) { - ret, err := s.Provider.ResourceUnlink(req.App, req.Name) + ret, err := s.Provider.ResourceUnlink(req.App, req.Resource) if err != nil { - return nil, internalError(err, fmt.Sprintf("failed to unlink resource %s", req.Name)) + return nil, internalError(err, fmt.Sprintf("failed to unlink resource %s", req.Resource)) } return ret, nil } diff --git a/client/apps.go b/client/apps.go index 5eb67254..c25848ee 100644 --- a/client/apps.go +++ b/client/apps.go @@ -37,18 +37,24 @@ func (c *Client) DeleteApp(name string) error { return err } +func (c *Client) AppDomainUpdate(name, domain string) error { + _, err := c.ProviderServiceClient.AppUpdateDomain(ctx, &pbs.AppResourceReq{App: name, Resource: domain}) + return err +} + func (c *Client) RestartApp(name string) error { _, err := c.ProviderServiceClient.AppRestart(ctx, &pbs.AppRequest{Name: name}) return err } -func (c *Client) StreamAppLogs(name string, follow bool, since time.Duration, process string, out io.Writer) error { +func (c *Client) StreamAppLogs(name string, follow bool, since time.Duration, process string, lines int, out io.Writer) error { in, out := os.Stdin, os.Stdout return c.Stream("/ws/v1/logs", map[string]string{ "app": name, "since": since.String(), "follow": strconv.FormatBool(follow), "Process": process, + "lines": strconv.Itoa(lines), }, in, out) } diff --git a/client/build.go b/client/build.go index e7cca526..f5cd46ea 100644 --- a/client/build.go +++ b/client/build.go @@ -70,8 +70,12 @@ func (c *Client) CreateBuildGit(app *pb.App, version string, procfile []byte) (* }) } -func (c *Client) GetBuilds(app string) (pb.Builds, error) { - ret, err := c.ProviderServiceClient.BuildList(ctx, &pbs.AppRequest{Name: app}) +func (c *Client) GetBuilds(app string, limit int) (pb.Builds, error) { + ret, err := c.ProviderServiceClient.BuildList(ctx, &pbs.AppListRequest{ + Name: app, + Limit: int32(limit), + }) + if err != nil { return nil, err } diff --git a/client/process.go b/client/process.go index 442727b4..99f507c8 100644 --- a/client/process.go +++ b/client/process.go @@ -44,6 +44,6 @@ func (c *Client) SaveProcess(name string, options map[string]string) error { func (c *Client) RunProcess(name string, args []string) error { return c.Stream("/ws/v1/exec", map[string]string{ "app": name, - "command": strings.Join(args, " "), + "command": strings.Join(args, "#"), }, os.Stdin, os.Stdout) } diff --git a/client/resource.go b/client/resource.go index 9de02ff9..4bcba22f 100644 --- a/client/resource.go +++ b/client/resource.go @@ -31,11 +31,11 @@ func (c *Client) DeleteResource(name string) error { } func (c *Client) CreateResourceLink(app, name string) error { - _, err := c.ProviderServiceClient.ResourceLink(ctx, &pbs.AppResourceReq{App: app, Name: name}) + _, err := c.ProviderServiceClient.ResourceLink(ctx, &pbs.AppResourceReq{App: app, Resource: name}) return err } func (c *Client) DeleteResourceLink(app, name string) error { - _, err := c.ProviderServiceClient.ResourceUnlink(ctx, &pbs.AppResourceReq{App: app, Name: name}) + _, err := c.ProviderServiceClient.ResourceUnlink(ctx, &pbs.AppResourceReq{App: app, Resource: name}) return err } diff --git a/cloud/aws/apps.go b/cloud/aws/apps.go index de5f7f50..7ee19a95 100644 --- a/cloud/aws/apps.go +++ b/cloud/aws/apps.go @@ -12,8 +12,8 @@ import ( log "github.com/Sirupsen/logrus" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - sched "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + sched "github.com/datacol-io/datacol/k8s" ) func (a *AwsCloud) AppList() (pb.Apps, error) { @@ -126,6 +126,19 @@ func (a *AwsCloud) AppDelete(name string) error { return nil } +// DomainUpdate updates list of Domains for an app +// domain can be example.com if you want to add or :example.com if you want to delete +func (p *AwsCloud) AppUpdateDomain(name, domain string) error { + app, err := p.AppGet(name) + if err != nil { + return err + } + + app.Domains = common.MergeAppDomains(app.Domains, domain) + + return p.saveApp(app) +} + func (p *AwsCloud) deleteAppResources(name string) error { svc := p.ecr() diff --git a/cloud/aws/builder.go b/cloud/aws/builder.go index e553ca67..ddd37320 100644 --- a/cloud/aws/builder.go +++ b/cloud/aws/builder.go @@ -96,10 +96,10 @@ func (a *AwsCloud) BuildList(app string, limit int) (pb.Builds, error) { } sort.Slice(builds, func(i, j int) bool { - return builds[i].CreatedAt < builds[j].CreatedAt + return builds[i].CreatedAt > builds[j].CreatedAt }) - return builds, nil + return builds[0:limit], nil } func (a *AwsCloud) BuildImport(id, gzipPath string) error { diff --git a/cloud/aws/environment.go b/cloud/aws/environment.go index f606008c..b11e6b98 100644 --- a/cloud/aws/environment.go +++ b/cloud/aws/environment.go @@ -11,7 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/s3" pb "github.com/datacol-io/datacol/api/models" - "github.com/datacol-io/datacol/cloud/common" + "github.com/datacol-io/datacol/common" ) func (a *AwsCloud) s3KeyForEnv(name string) string { diff --git a/cloud/aws/kubeconfig.go b/cloud/aws/kubeconfig.go index d77cf712..519f19e6 100644 --- a/cloud/aws/kubeconfig.go +++ b/cloud/aws/kubeconfig.go @@ -2,12 +2,18 @@ package aws import ( "fmt" + "io/ioutil" "os" "os/exec" "path/filepath" + "regexp" "sync" log "github.com/Sirupsen/logrus" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -18,12 +24,13 @@ var cacheClientsetOnce sync.Once var cacheConfigPathOnce sync.Once var ( - rootPath = "/opt/datacol" - kcpath = filepath.Join(rootPath, "kubeconfig") - pemPathRE = filepath.Join(rootPath, "%s.pem") - privateIpAttr = "MasterPrivateIp" - bastionIpAttr = "BastionHostPublicIp" - scpCmd = "scp -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@%s:~/kubeconfig %s" + rootPath = "/opt/datacol" + kcpath = filepath.Join(rootPath, "kubeconfig") + pemPathRE = filepath.Join(rootPath, "%s.pem") + privateIpAttr = "MasterPrivateIp" + bastionIpAttr = "BastionHostPublicIp" + scpCmd = "scp -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu@%s:~/kubeconfig %s" + s3StateStoreMatcher = regexp.MustCompile(`s3://(\S+)/cluster-info.yaml`) ) func (p *AwsCloud) kubeClient() *kubernetes.Clientset { @@ -43,9 +50,46 @@ func (p *AwsCloud) kubeClient() *kubernetes.Clientset { return kubeClient } +// fetchClusterInfoFromS3 tries to fetch cluster state from s3. Currently not being used +// since the cluster-info.yaml doesn't contain user credentials +func (p *AwsCloud) fetchClusterInfoFromS3(path string) (cached bool, err error) { + value, err := p.stackOutputValue("JoinNodes") + log.Warn(err) + + if value != "" { + log.Debugf("Found JoinNodes Output: %s", value) + + if match := s3StateStoreMatcher.FindStringSubmatch(value); len(match) == 2 { + s3bucket := match[1] + buff := &aws.WriteAtBuffer{} + s3dl := s3manager.NewDownloader(session.New()) + + if _, err = s3dl.Download(buff, &s3.GetObjectInput{ + Bucket: aws.String(s3bucket), + Key: aws.String("cluster-info.yaml"), + }); err != nil { + return + } + + err = ioutil.WriteFile(path, buff.Bytes(), 0644) + if err == nil { + cached = true + } + } + } + + return +} + func (p *AwsCloud) K8sConfigPath() (string, error) { if _, err := os.Stat(kcpath); err != nil { if os.IsNotExist(err) { + + // if fetched, err := p.fetchClusterInfoFromS3(kcpath); fetched { + // log.Infof("cached the %s from s3 state store. err:%v", kcpath, err) + // return kcpath, nil + // } + ipAddr, err := p.masterPrivateIp() if err != nil { return ipAddr, err diff --git a/cloud/aws/logs.go b/cloud/aws/logs.go index 178ef4ec..50f145a4 100644 --- a/cloud/aws/logs.go +++ b/cloud/aws/logs.go @@ -4,12 +4,9 @@ import ( "io" pb "github.com/datacol-io/datacol/api/models" - sched "github.com/datacol-io/datacol/cloud/kube" + sched "github.com/datacol-io/datacol/k8s" ) func (a *AwsCloud) LogStream(app string, w io.Writer, opts pb.LogStreamOptions) error { - ns := a.DeploymentName - c := a.kubeClient() - - return sched.LogStreamReq(c, w, ns, app, opts) + return sched.LogStreamReq(a.kubeClient(), w, a.DeploymentName, app, opts) } diff --git a/cloud/aws/process.go b/cloud/aws/process.go index bcd9abfa..15380b1d 100644 --- a/cloud/aws/process.go +++ b/cloud/aws/process.go @@ -7,22 +7,21 @@ import ( pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + kube "github.com/datacol-io/datacol/k8s" ) func (p *AwsCloud) ProcessList(app string) ([]*pb.Process, error) { return kube.ProcessList(p.kubeClient(), p.DeploymentName, app) } -func (p *AwsCloud) ProcessRun(name string, r io.ReadWriter, command string) error { +func (p *AwsCloud) ProcessRun(name string, r io.ReadWriter, command []string) error { ns := p.DeploymentName cfg, _ := getKubeClientConfig(ns) - envVars, _ := p.EnvironmentGet(name) app, _ := p.AppGet(name) - return kube.ProcessExec(p.kubeClient(), cfg, ns, name, p.latestImage(app), command, envVars, false, r, cloud.AwsProvider) + return kube.ProcessRun(p.kubeClient(), cfg, ns, name, p.latestImage(app), command, envVars, false, r, cloud.AwsProvider) } func (p *AwsCloud) ProcessSave(name string, structure map[string]int32) error { diff --git a/cloud/aws/provider.go b/cloud/aws/provider.go index 3eae68eb..c62bdea1 100644 --- a/cloud/aws/provider.go +++ b/cloud/aws/provider.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "sync" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" @@ -17,6 +18,8 @@ type AwsCloud struct { DeploymentName string Region, SettingBucket string Access, Secret, Token string + + lock sync.Mutex } func (p *AwsCloud) config() *aws.Config { diff --git a/cloud/aws/releases.go b/cloud/aws/releases.go index 299dc90b..5a303973 100644 --- a/cloud/aws/releases.go +++ b/cloud/aws/releases.go @@ -3,16 +3,13 @@ package aws import ( "fmt" "os" - "sort" - "strings" log "github.com/Sirupsen/logrus" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/dynamodb" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" ) func (a *AwsCloud) dynamoReleases() string { @@ -43,10 +40,6 @@ func (a *AwsCloud) ReleaseList(app string, limit int) (pb.Releases, error) { return releases, nil } -func (a *AwsCloud) ReleaseDelete(app, id string) error { - return nil -} - func (a *AwsCloud) BuildRelease(b *pb.Build, options pb.ReleaseOptions) (*pb.Release, error) { image := fmt.Sprintf("%s.dkr.ecr.%s.amazonaws.com/%s:%s", os.Getenv("AWS_ACCOUNT_ID"), a.Region, a.ecrRepository(b.App), b.Id, @@ -69,27 +62,21 @@ func (a *AwsCloud) BuildRelease(b *pb.Build, options pb.ReleaseOptions) (*pb.Rel BuildId: b.Id, Status: pb.StatusCreated, CreatedAt: timestampNow(), + Version: a.releaseCount(b.App) + 1, } if err = a.releaseSave(r); err != nil { return r, err } - domains := app.Domains - for _, domain := range strings.Split(options.Domain, ",") { - domains = kube.MergeAppDomains(domains, domain) - } - - if len(app.Domains) != len(domains) { - app.Domains = domains - } - app.BuildId = b.Id app.ReleaseId = r.Id + rversion := fmt.Sprintf("%d", r.Version) log.Debugf("Saving app state: %s err:%v", toJson(app), a.saveApp(app)) // note the mutate function - if err := common.UpdateApp(a.kubeClient(), b, a.DeploymentName, image, false, domains, envVars, cloud.AwsProvider); err != nil { + if err := common.UpdateApp(a.kubeClient(), b, a.DeploymentName, + image, false, app.Domains, envVars, cloud.AwsProvider, rversion); err != nil { return nil, err } @@ -113,6 +100,10 @@ func (a *AwsCloud) releaseSave(r *pb.Release) error { req.Item["status"] = &dynamodb.AttributeValue{S: aws.String(r.Status)} } + if r.Version > 0 { + req.Item["version"] = &dynamodb.AttributeValue{N: aws.String(fmt.Sprintf("%d", r.Version))} + } + _, err := a.dynamodb().PutItem(req) return err } @@ -124,21 +115,33 @@ func (a *AwsCloud) releaseFromItem(item map[string]*dynamodb.AttributeValue) *pb BuildId: coalesce(item["build_id"], ""), Status: coalesce(item["status"], ""), CreatedAt: int32(coalesceInt(item["created_at"], 0)), + Version: int64(coalesceInt(item["version"], 0)), } } -func (a *AwsCloud) latestRelease(app string) *pb.Release { - allReleases, err := a.ReleaseList(app, 100) - if err != nil { - return nil +func (a *AwsCloud) ReleaseDelete(app, id string) error { + return notImplemented +} + +func (a *AwsCloud) releaseCount(app string) (version int64) { + a.lock.Lock() + defer a.lock.Unlock() + + queryInput := &dynamodb.ScanInput{ + TableName: aws.String(a.dynamoReleases()), + Select: aws.String("COUNT"), + ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ + ":app": {S: aws.String(app)}, + }, + FilterExpression: aws.String("app=:app"), } - if len(allReleases) > 0 { - sort.Slice(allReleases, func(i, j int) bool { - return allReleases[i].CreatedAt > allReleases[j].CreatedAt - }) - return allReleases[0] + res, err := a.dynamodb().Scan(queryInput) + if err != nil { + log.Warnf("Fetching release count: %v", err) } else { - return nil + version = *res.ScannedCount } + + return version } diff --git a/cloud/google/apps.go b/cloud/google/apps.go index 787f59b1..8716c485 100644 --- a/cloud/google/apps.go +++ b/cloud/google/apps.go @@ -9,8 +9,8 @@ import ( log "github.com/Sirupsen/logrus" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - sched "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + sched "github.com/datacol-io/datacol/k8s" ) const appKind = "App" @@ -81,6 +81,19 @@ func (g *GCPCloud) AppDelete(name string) error { return g.deleteAppFromDatastore(name) } +// DomainUpdate updates list of Domains for an app +// domain can be example.com if you want to add or :example.com if you want to delete +func (g *GCPCloud) AppUpdateDomain(name, domain string) error { + app, err := g.AppGet(name) + if err != nil { + return err + } + + app.Domains = common.MergeAppDomains(app.Domains, domain) + + return g.saveApp(app) +} + func (g *GCPCloud) saveApp(app *pb.App) error { ctx, key := g.nestedKey(appKind, app.Name) _, err := g.datastore().Put(ctx, key, app) diff --git a/cloud/google/builder.go b/cloud/google/builder.go index aaf692d6..52608bed 100644 --- a/cloud/google/builder.go +++ b/cloud/google/builder.go @@ -15,9 +15,6 @@ import ( "cloud.google.com/go/datastore" log "github.com/Sirupsen/logrus" pb "github.com/datacol-io/datacol/api/models" - "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - sched "github.com/datacol-io/datacol/cloud/kube" "google.golang.org/api/cloudbuild/v1" "google.golang.org/api/googleapi" "google.golang.org/api/storage/v1" @@ -58,7 +55,10 @@ func (g *GCPCloud) BuildDelete(app, id string) error { } func (g *GCPCloud) BuildList(app string, limit int) (pb.Builds, error) { - q := datastore.NewQuery(buildKind).Namespace(g.DeploymentName).Filter("app = ", app).Limit(limit) + q := datastore.NewQuery(buildKind).Namespace(g.DeploymentName). + Filter("app = ", app). + Limit(limit). + Order("-" + "created_at") // descending order var builds pb.Builds _, err := g.datastore().GetAll(context.Background(), q, &builds) @@ -66,20 +66,6 @@ func (g *GCPCloud) BuildList(app string, limit int) (pb.Builds, error) { return builds, err } -func (g *GCPCloud) ReleaseList(app string, limit int) (pb.Releases, error) { - q := datastore.NewQuery(releaseKind).Namespace(g.DeploymentName).Filter("app = ", app).Limit(limit) - - var rs pb.Releases - _, err := g.datastore().GetAll(context.Background(), q, &rs) - - return rs, err -} - -func (g *GCPCloud) ReleaseDelete(app, id string) error { - ctx, key := g.nestedKey(buildKind, id) - return g.datastore().Delete(ctx, key) -} - func (g *GCPCloud) BuildCreate(app string, req *pb.CreateBuildOptions) (*pb.Build, error) { id := generateId("B", 5) @@ -195,58 +181,6 @@ func (g *GCPCloud) BuildLogsStream(id string) (io.Reader, error) { return nil, fmt.Errorf("Not supported on GCP.") } -func (g *GCPCloud) BuildRelease(b *pb.Build, options pb.ReleaseOptions) (*pb.Release, error) { - image := fmt.Sprintf("gcr.io/%v/%v:%v", g.Project, b.App, b.Id) - log.Debugf("---- Docker Image: %s", image) - - envVars, err := g.EnvironmentGet(b.App) - if err != nil { - return nil, err - } - - app, err := g.AppGet(b.App) - if err != nil { - return nil, err - } - - domains := app.Domains - for _, domain := range strings.Split(options.Domain, ",") { - domains = sched.MergeAppDomains(domains, domain) - } - - if err := common.UpdateApp(g.kubeClient(), b, g.DeploymentName, image, g.appLinkedDB(app), domains, envVars, cloud.GCPProvider); err != nil { - return nil, err - } - - if len(app.Domains) != len(domains) { - app.Domains = domains - - if err = g.saveApp(app); err != nil { - log.Warnf("datastore put failed: %v", err) - } - } - - r := &pb.Release{ - Id: generateId("R", 5), - App: b.App, - BuildId: b.Id, - Status: pb.StatusCreated, - CreatedAt: timestampNow(), - } - - ctx, key := g.nestedKey(releaseKind, r.Id) - _, err = g.datastore().Put(ctx, key, r) - - if err != nil { - return r, err - } - - app.ReleaseId = r.Id - app.BuildId = b.Id - - return r, g.saveApp(app) -} - func getBuildID(op *cloudbuild.Operation) (string, error) { if len(op.Metadata) == 0 { return "", fmt.Errorf("missing Metadata in operation") diff --git a/cloud/google/cloudsql.go b/cloud/google/cloudsql.go index dba86ed8..a5f0bc71 100644 --- a/cloud/google/cloudsql.go +++ b/cloud/google/cloudsql.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/kube" + kube "github.com/datacol-io/datacol/k8s" "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/cloud/google/environment.go b/cloud/google/environment.go index db1a4bd0..ae31ce0f 100644 --- a/cloud/google/environment.go +++ b/cloud/google/environment.go @@ -5,7 +5,7 @@ import ( "io" pb "github.com/datacol-io/datacol/api/models" - "github.com/datacol-io/datacol/cloud/common" + "github.com/datacol-io/datacol/common" "google.golang.org/api/googleapi" ) diff --git a/cloud/google/kubeconfig.go b/cloud/google/kubeconfig.go index dc332b0f..38370ec9 100644 --- a/cloud/google/kubeconfig.go +++ b/cloud/google/kubeconfig.go @@ -111,7 +111,6 @@ func generateClusterConfig(rackName, baseDir string, c *container.Cluster) error User: rackName, Context: rackName, Cluster: rackName, - // TokenFile: getTokenFile(rackName), } var kubeconfig bytes.Buffer diff --git a/cloud/google/logs.go b/cloud/google/logs.go index 32eab010..d226f644 100644 --- a/cloud/google/logs.go +++ b/cloud/google/logs.go @@ -4,7 +4,7 @@ import ( "io" pb "github.com/datacol-io/datacol/api/models" - sched "github.com/datacol-io/datacol/cloud/kube" + sched "github.com/datacol-io/datacol/k8s" ) func (g *GCPCloud) LogStream(app string, w io.Writer, opts pb.LogStreamOptions) error { diff --git a/cloud/google/process.go b/cloud/google/process.go index 768a092e..51fe230c 100644 --- a/cloud/google/process.go +++ b/cloud/google/process.go @@ -6,11 +6,11 @@ import ( pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + kube "github.com/datacol-io/datacol/k8s" ) -func (g *GCPCloud) ProcessRun(name string, stream io.ReadWriter, command string) error { +func (g *GCPCloud) ProcessRun(name string, stream io.ReadWriter, command []string) error { ns := g.DeploymentName cfg, err := getKubeClientConfig(ns) if err != nil { @@ -21,7 +21,7 @@ func (g *GCPCloud) ProcessRun(name string, stream io.ReadWriter, command string) envVars, _ := g.EnvironmentGet(name) sqlproxy := g.appLinkedDB(app) - return kube.ProcessExec(g.kubeClient(), cfg, ns, name, g.latestImage(app), command, envVars, sqlproxy, stream, cloud.GCPProvider) + return kube.ProcessRun(g.kubeClient(), cfg, ns, name, g.latestImage(app), command, envVars, sqlproxy, stream, cloud.GCPProvider) } func (g *GCPCloud) ProcessList(app string) ([]*pb.Process, error) { diff --git a/cloud/google/releases.go b/cloud/google/releases.go new file mode 100644 index 00000000..3754abeb --- /dev/null +++ b/cloud/google/releases.go @@ -0,0 +1,83 @@ +package google + +import ( + "context" + "fmt" + + "cloud.google.com/go/datastore" + log "github.com/Sirupsen/logrus" + pb "github.com/datacol-io/datacol/api/models" + "github.com/datacol-io/datacol/cloud" + "github.com/datacol-io/datacol/common" +) + +func (g *GCPCloud) ReleaseList(app string, limit int) (pb.Releases, error) { + q := datastore.NewQuery(releaseKind).Namespace(g.DeploymentName).Filter("app = ", app).Limit(limit) + + var rs pb.Releases + _, err := g.datastore().GetAll(context.Background(), q, &rs) + + return rs, err +} + +func (g *GCPCloud) ReleaseDelete(app, id string) error { + ctx, key := g.nestedKey(buildKind, id) + return g.datastore().Delete(ctx, key) +} + +func (g *GCPCloud) BuildRelease(b *pb.Build, options pb.ReleaseOptions) (*pb.Release, error) { + image := fmt.Sprintf("gcr.io/%v/%v:%v", g.Project, b.App, b.Id) + log.Debugf("---- Docker Image: %s", image) + + envVars, err := g.EnvironmentGet(b.App) + if err != nil { + return nil, err + } + + app, err := g.AppGet(b.App) + if err != nil { + return nil, err + } + + r := &pb.Release{ + Id: generateId("R", 5), + App: b.App, + BuildId: b.Id, + Status: pb.StatusCreated, + CreatedAt: timestampNow(), + Version: g.releaseCount(b.App) + 1, + } + + rversion := fmt.Sprintf("%d", r.Version) + + if err := common.UpdateApp(g.kubeClient(), b, g.DeploymentName, image, g.appLinkedDB(app), + app.Domains, envVars, cloud.GCPProvider, rversion); err != nil { + return nil, err + } + + ctx, key := g.nestedKey(releaseKind, r.Id) + _, err = g.datastore().Put(ctx, key, r) + + if err != nil { + return r, err + } + + app.ReleaseId = r.Id + app.BuildId = b.Id + + return r, g.saveApp(app) +} + +func (g *GCPCloud) releaseCount(app string) (version int64) { + q := datastore.NewQuery(releaseKind). + Namespace(g.DeploymentName). + Filter("app = ", app) + + total, err := g.datastore().Count(context.Background(), q) + if err != nil { + log.Warnf("fetching release total: %v", err) + } + + version = int64(total) + return +} diff --git a/cloud/google/templates.go b/cloud/google/templates.go index f8745714..1161baea 100644 --- a/cloud/google/templates.go +++ b/cloud/google/templates.go @@ -84,7 +84,7 @@ func cloudGoogleTemplatesMysqlTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cloud/google/templates/mysql.tmpl", size: 1392, mode: os.FileMode(436), modTime: time.Unix(1520015640, 0)} + info := bindataFileInfo{name: "cloud/google/templates/mysql.tmpl", size: 1392, mode: os.FileMode(436), modTime: time.Unix(1521875529, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/cloud/kube/helper.go b/cloud/kube/helper.go deleted file mode 100644 index b96b6df2..00000000 --- a/cloud/kube/helper.go +++ /dev/null @@ -1,75 +0,0 @@ -package kube - -import ( - "encoding/json" - "strings" - - log "github.com/Sirupsen/logrus" - "k8s.io/api/extensions/v1beta1" -) - -func ingressNamespace(parentNS string) string { - // return ingressDefaultNamespace - return parentNS -} - -func toJson(object interface{}) string { - dump, err := json.MarshalIndent(object, " ", " ") - if err != nil { - log.Warnf("dumping json: %v", err) - } - - return string(dump) -} - -func MergeAppDomains(domains []string, item string) []string { - if item == "" { - return domains - } - - itemIndex := -1 - dotted := strings.HasPrefix(item, ":") - - if dotted { - item = item[1:] - } - - for i, d := range domains { - if d == item { - itemIndex = i - break - } - } - - if dotted && itemIndex >= 0 { - return append(domains[0:itemIndex], domains[itemIndex+1:]...) - } - - if !dotted && itemIndex == -1 { - return append(domains, item) - } - - return domains -} - -func mergeIngressRules(dest *v1beta1.Ingress, source *v1beta1.Ingress) *v1beta1.Ingress { - for _, r := range source.Spec.Rules { - foundAt := -1 - for i, rr := range dest.Spec.Rules { - if rr.Host == r.Host { - foundAt = i - break - } - } - - if foundAt >= 0 { - dest.Spec.Rules[foundAt] = r - } else { - dest.Spec.Rules = append(dest.Spec.Rules, r) - } - } - - log.Debugf("ingress rules %s", toJson(dest.Spec)) - - return dest -} diff --git a/cloud/local/apps.go b/cloud/local/apps.go index 42047ab8..c45531f2 100644 --- a/cloud/local/apps.go +++ b/cloud/local/apps.go @@ -2,15 +2,13 @@ package local import ( "fmt" - "io" - "io/ioutil" "time" "github.com/appscode/go/log" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - sched "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + sched "github.com/datacol-io/datacol/k8s" ) func (l *LocalCloud) AppList() (pb.Apps, error) { @@ -78,15 +76,15 @@ func (g *LocalCloud) AppDelete(name string) error { return nil } -func (g *LocalCloud) EnvironmentGet(name string) (pb.Environment, error) { - return g.EnvMap[name], nil -} - -func (g *LocalCloud) EnvironmentSet(name string, body io.Reader) error { - data, err := ioutil.ReadAll(body) +// DomainUpdate updates list of Domains for an app +// domain can be example.com if you want to add or :example.com if you want to delete +func (g *LocalCloud) AppUpdateDomain(name, domain string) error { + app, err := g.AppGet(name) if err != nil { return err } - g.EnvMap[name] = common.LoadEnvironment(data) - return nil + + app.Domains = common.MergeAppDomains(app.Domains, domain) + + return g.saveApp(app) } diff --git a/cloud/local/build.go b/cloud/local/build.go index 3492099c..ab6e35bd 100644 --- a/cloud/local/build.go +++ b/cloud/local/build.go @@ -10,7 +10,7 @@ import ( "github.com/appscode/go/crypto/rand" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" + "github.com/datacol-io/datacol/common" docker "github.com/fsouza/go-dockerclient" ) @@ -119,15 +119,17 @@ func (g *LocalCloud) BuildRelease(b *pb.Build, options pb.ReleaseOptions) (*pb.R return nil, err } - if err := common.UpdateApp(g.kubeClient(), b, g.Name, image, false, []string{}, envVars, cloud.LocalProvider); err != nil { - return nil, err - } - r := &pb.Release{ Id: common.GenerateId("R", 5), App: b.App, BuildId: b.Id, Status: pb.StatusCreated, + Version: int64(len(g.Releases) + 1), + } + + if err := common.UpdateApp(g.kubeClient(), b, g.Name, image, false, + []string{}, envVars, cloud.LocalProvider, fmt.Sprintf("%d", r.Version)); err != nil { + return nil, err } g.Releases = append(g.Releases, r) diff --git a/cloud/local/deploy.go b/cloud/local/deploy.go index 195e5243..eed27bde 100644 --- a/cloud/local/deploy.go +++ b/cloud/local/deploy.go @@ -7,8 +7,8 @@ import ( pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/common" - sched "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" + sched "github.com/datacol-io/datacol/k8s" ) func (g *LocalCloud) K8sConfigPath() (string, error) { @@ -19,7 +19,7 @@ func (g *LocalCloud) LogStream(app string, w io.Writer, opts pb.LogStreamOptions return sched.LogStreamReq(g.kubeClient(), w, g.Name, app, opts) } -func (g *LocalCloud) ProcessRun(name string, stream io.ReadWriter, command string) error { +func (g *LocalCloud) ProcessRun(name string, stream io.ReadWriter, command []string) error { ns := g.Name cfg, err := getKubeClientConfig(ns) if err != nil { @@ -29,7 +29,7 @@ func (g *LocalCloud) ProcessRun(name string, stream io.ReadWriter, command strin app, _ := g.AppGet(name) envVars, _ := g.EnvironmentGet(name) - return sched.ProcessExec(g.kubeClient(), cfg, ns, name, g.latestImage(app), command, envVars, false, stream, cloud.LocalProvider) + return sched.ProcessRun(g.kubeClient(), cfg, ns, name, g.latestImage(app), command, envVars, false, stream, cloud.LocalProvider) } func (g *LocalCloud) ProcessList(app string) ([]*pb.Process, error) { diff --git a/cloud/local/environ.go b/cloud/local/environ.go new file mode 100644 index 00000000..489ea0e9 --- /dev/null +++ b/cloud/local/environ.go @@ -0,0 +1,22 @@ +package local + +import ( + "io" + "io/ioutil" + + pb "github.com/datacol-io/datacol/api/models" + "github.com/datacol-io/datacol/common" +) + +func (g *LocalCloud) EnvironmentGet(name string) (pb.Environment, error) { + return common.SortEnvironment(g.EnvMap[name]), nil +} + +func (g *LocalCloud) EnvironmentSet(name string, body io.Reader) error { + data, err := ioutil.ReadAll(body) + if err != nil { + return err + } + g.EnvMap[name] = common.LoadEnvironment(data) + return nil +} diff --git a/cloud/provider.go b/cloud/provider.go index 6fcd469f..be6fc763 100644 --- a/cloud/provider.go +++ b/cloud/provider.go @@ -12,6 +12,7 @@ type Provider interface { AppDelete(string) error AppRestart(string) error AppList() (pb.Apps, error) + AppUpdateDomain(string, string) error EnvironmentGet(app string) (pb.Environment, error) EnvironmentSet(app string, body io.Reader) error @@ -31,7 +32,7 @@ type Provider interface { LogStream(app string, w io.Writer, opts pb.LogStreamOptions) error ProcessList(app string) ([]*pb.Process, error) - ProcessRun(app string, r io.ReadWriter, command string) error + ProcessRun(app string, r io.ReadWriter, command []string) error ProcessSave(app string, formation map[string]int32) error ResourceList() (pb.Resources, error) diff --git a/cmd/apps.go b/cmd/apps.go index b9d3155d..40c0c69c 100644 --- a/cmd/apps.go +++ b/cmd/apps.go @@ -3,6 +3,9 @@ package cmd import ( "fmt" "os" + "strings" + + "github.com/olekukonko/tablewriter" log "github.com/Sirupsen/logrus" term "github.com/appscode/go/term" @@ -16,6 +19,7 @@ func init() { Name: "apps", Usage: "Manage your apps in a stack", Action: cmdAppsList, + Flags: []cli.Flag{&stackFlag}, Subcommands: []cli.Command{ cli.Command{ Name: "create", @@ -75,7 +79,14 @@ func cmdAppsList(c *cli.Context) error { if len(apps) == 0 { fmt.Println("No apps found.") } else { - fmt.Println(toJson(apps)) + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"NAME", "BUILD", "RELEASE", "DOMAINS"}) + for _, a := range apps { + table.Append([]string{a.Name, a.BuildId, a.ReleaseId, + strings.Join(a.Domains, "\n"), + }) + } + table.Render() } return nil } diff --git a/cmd/build.go b/cmd/build.go index a56079d6..5ccf4f45 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -10,6 +10,7 @@ import ( "time" log "github.com/Sirupsen/logrus" + term "github.com/appscode/go/term" pbs "github.com/datacol-io/datacol/api/controller" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/client" @@ -18,6 +19,7 @@ import ( "github.com/docker/docker/builder/dockerignore" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/fileutils" + "github.com/olekukonko/tablewriter" "github.com/urfave/cli" "golang.org/x/net/context" "golang.org/x/net/websocket" @@ -26,7 +28,7 @@ import ( func init() { stdcli.AddCommand(cli.Command{ Name: "build", - Usage: "build an app from Dockerfile or app.yaml (App-Engine)", + Usage: "build an app from Dockerfile", Action: cmdBuild, Flags: []cli.Flag{ &cli.StringFlag{ @@ -34,16 +36,25 @@ func init() { Usage: "branch or commit Id of git repository", }, }, - Subcommands: []cli.Command{ - { - Name: "list", - Usage: "get builds for an app", - Action: cmdBuildList, + }) + + stdcli.AddCommand(cli.Command{ + Name: "builds", + Usage: "manage the builds for an app", + Action: cmdBuildList, + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "limit, n", + Usage: "Limit the number of recent builds to fetch", + Value: 5, }, + }, + Subcommands: []cli.Command{ { - Name: "delete", - Usage: "delete a build", - Action: cmdBuildDelete, + Name: "delete", + Usage: "delete a build", + ArgsUsage: "", + Action: cmdBuildDelete, }, }, }) @@ -57,10 +68,17 @@ func cmdBuildList(c *cli.Context) error { api, close := getApiClient(c) defer close() - builds, err := api.GetBuilds(name) + builds, err := api.GetBuilds(name, c.Int("limit")) stdcli.ExitOnError(err) - fmt.Println(toJson(builds)) + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"ID", "COMMIT", "STATUS", "CREATED"}) + for _, b := range builds { + delta := elaspedDuration(time.Unix(int64(b.CreatedAt), 0)) + table.Append([]string{b.Id, b.Version, b.Status, delta}) + } + + table.Render() return nil } @@ -72,12 +90,11 @@ func cmdBuildDelete(c *cli.Context) error { defer close() if c.NArg() == 0 { - stdcli.ExitOnError(fmt.Errorf("Please provide id of the build")) + term.Warningln("No build Id provided") + stdcli.Usage(c) } - bid := c.Args().First() - - stdcli.ExitOnError(api.DeleteBuild(name, bid)) + stdcli.ExitOnError(api.DeleteBuild(name, c.Args().First())) fmt.Println("DONE") return nil diff --git a/cmd/deploy.go b/cmd/deploy.go index dbcf298a..ec8e9cc6 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -41,10 +41,6 @@ func init() { Name: "file, f", Usage: "path of Dockerfile or app.yaml", }, - &cli.StringFlag{ - Name: "domain, d", - Usage: "domain(s) to use with this app", - }, &cli.BoolTFlag{ //TODO: support expose in API Name: "expose", @@ -98,7 +94,6 @@ func cmdDeploy(c *cli.Context) error { fmt.Printf("Deploying build %s\n", build.Id) _, err = client.ReleaseBuild(build, pb.ReleaseOptions{ - Domain: c.String("domain"), Wait: c.Bool("wait"), Expose: c.BoolT("expose"), }) diff --git a/cmd/domains.go b/cmd/domains.go new file mode 100644 index 00000000..5aa7749a --- /dev/null +++ b/cmd/domains.go @@ -0,0 +1,78 @@ +package cmd + +import ( + "fmt" + + term "github.com/appscode/go/term" + "github.com/datacol-io/datacol/cmd/stdcli" + "github.com/urfave/cli" +) + +func init() { + stdcli.AddCommand(cli.Command{ + Name: "domains", + Usage: "Manage your domains for an app", + Action: cmdDomainsList, + Flags: []cli.Flag{&stackFlag}, + Subcommands: []cli.Command{ + { + Name: "add", + ArgsUsage: "", + Action: cmdAddDomain, + Flags: []cli.Flag{appFlag}, + }, + { + Name: "remove", + ArgsUsage: "", + Action: cmdRemoveDomain, + Flags: []cli.Flag{appFlag}, + }, + }, + }) +} + +func cmdDomainsList(c *cli.Context) error { + _, name, err := getDirApp(".") + stdcli.ExitOnError(err) + + api, close := getApiClient(c) + defer close() + + app, err := api.GetApp(name) + stdcli.ExitOnError(err) + + fmt.Println(toJson(app.Domains)) + return nil +} + +func cmdAddDomain(c *cli.Context) error { + stdcli.ExitOnError(modifyDomain(c, false)) + term.Infoln("DONE") + return nil +} + +func cmdRemoveDomain(c *cli.Context) error { + stdcli.ExitOnError(modifyDomain(c, true)) + term.Infoln("DONE") + return nil +} + +func modifyDomain(c *cli.Context, delete bool) error { + _, name, err := getDirApp(".") + stdcli.ExitOnError(err) + + api, close := getApiClient(c) + defer close() + + if c.NArg() < 1 { + term.Warningln("No domain provided.") + stdcli.Usage(c) + } + + domain := c.Args().First() + if delete { + domain = fmt.Sprintf(":%s", domain) + } + + return api.AppDomainUpdate(name, domain) +} diff --git a/cmd/env.go b/cmd/env.go index 8487e56f..1a1429be 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -13,7 +13,7 @@ func init() { Name: "env", Usage: "manage environment variables for an app", Action: cmdConfigList, - Flags: []cli.Flag{&appFlag}, + Flags: []cli.Flag{&appFlag, &stackFlag}, Subcommands: []cli.Command{ { Name: "set", diff --git a/cmd/helper.go b/cmd/helper.go index 5435090b..5b28a9f5 100644 --- a/cmd/helper.go +++ b/cmd/helper.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io/ioutil" + "math" "math/rand" "os" "regexp" @@ -109,81 +110,6 @@ type appYAMLConfig struct { RuntimeSteps []string } -func parseAppYAML() (*appYAMLConfig, error) { - data, err := ioutil.ReadFile("app.yaml") - if err != nil { - return nil, err - } - - var appyaml appYAMLConfig - if err := yaml.Unmarshal(data, &appyaml); err != nil { - return nil, err - } - - if !(appyaml.Env == "flex" || appyaml.Env == "custom") { - fmt.Printf("\nignoring %s env", appyaml.Env) - return &appyaml, nil - } - - return nil, fmt.Errorf("invalid app.yaml file.") -} - -func gnDockerFromGAE(filename string) error { - appyaml, err := parseAppYAML() - if err != nil { - return err - } - - if len(appyaml.Entrypoint) > 0 { - appyaml.Entrypoint = entrypoint(appyaml.Entrypoint) - } else { - appyaml.Entrypoint = "/bin/sh -c" - } - - appyaml.RuntimeSteps = getruntimeSteps(appyaml) - - log.Debugf(toJson(appyaml)) - tmpl, err := template.New("ct").Parse(dkrYAML) - if err != nil { - return err - } - - var doc bytes.Buffer - if err := tmpl.Execute(&doc, appyaml); err != nil { - return err - } - - err = ioutil.WriteFile(filename, doc.Bytes(), 0700) - if err != nil { - return err - } - - return nil -} - -func entrypoint(cmd string) string { - parts := strings.Split(cmd, " ") - return `["` + strings.Join(parts, `", "`) + `"]` -} - -func getruntimeSteps(spec *appYAMLConfig) []string { - steps := []string{} - switch spec.Runtime { - case "python": - steps = append(steps, []string{ - "RUN virtualenv /env", - "ENV VIRTUAL_ENV /env", - "ENV PATH /env/bin:$PATH", - "ADD requirements.txt /app/requirements.txt", - "RUN pip install -r /app/requirements.txt", - }...) - default: - log.Fatal(fmt.Errorf("unsupported runtime %s", spec.Runtime)) - } - - return steps -} - func toJson(object interface{}) string { dump, err := json.MarshalIndent(object, " ", " ") if err != nil { @@ -215,3 +141,32 @@ func unmarshalProcfile(procfile []byte) (map[string]string, error) { procfileMap := make(map[string]string) return procfileMap, yaml.Unmarshal(procfile, &procfileMap) } + +func elaspedDuration(t time.Time) string { + duration := time.Since(t) + days := int64(duration.Hours() / 24) + hours := int64(math.Mod(duration.Hours(), 24)) + minutes := int64(math.Mod(duration.Minutes(), 60)) + + chunks := []struct { + singularName string + amount int64 + }{ + {"d", days}, + {"h", hours}, + {"m", minutes}, + } + + parts := []string{} + + for _, chunk := range chunks { + switch chunk.amount { + case 0: + continue + default: + parts = append(parts, fmt.Sprintf("%d%s", chunk.amount, chunk.singularName)) + } + } + + return strings.Join(parts, " ") +} diff --git a/cmd/infra.go b/cmd/infra.go index f7446f67..044b9f0f 100644 --- a/cmd/infra.go +++ b/cmd/infra.go @@ -47,6 +47,7 @@ func init() { Name: "infra", Usage: "Managed GCP stack resources and infrastructure", Action: cmdResourceList, + Flags: []cli.Flag{&stackFlag}, Subcommands: []cli.Command{ { Name: "create", diff --git a/cmd/logs.go b/cmd/logs.go index b8cc1002..6fd4c832 100644 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -2,7 +2,6 @@ package cmd import ( "os" - "time" "github.com/datacol-io/datacol/cmd/stdcli" "github.com/urfave/cli" @@ -22,7 +21,11 @@ func init() { &cli.DurationFlag{ Name: "since", Usage: "show logs since a duration (e.g. 10m or 1h2m10s)", - Value: 2 * time.Minute, + }, + &cli.IntFlag{ + Name: "lines, l", + Usage: "Number of lines of recent log file to display", + Value: 10, }, &cli.StringFlag{ Name: "process, p", @@ -46,7 +49,7 @@ func cmdAppLogStream(c *cli.Context) error { name = c.Args().Get(0) } - err = client.StreamAppLogs(name, c.Bool("follow"), c.Duration("since"), c.String("process"), os.Stdout) + err = client.StreamAppLogs(name, c.Bool("follow"), c.Duration("since"), c.String("process"), c.Int("lines"), os.Stdout) stdcli.ExitOnError(err) return err diff --git a/cmd/provider/aws/templates.go b/cmd/provider/aws/templates.go index fc036238..c353406f 100644 --- a/cmd/provider/aws/templates.go +++ b/cmd/provider/aws/templates.go @@ -83,7 +83,7 @@ func cmdProviderAwsTemplatesFormationYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "cmd/provider/aws/templates/formation.yaml", size: 22170, mode: os.FileMode(436), modTime: time.Unix(1521183917, 0)} + info := bindataFileInfo{name: "cmd/provider/aws/templates/formation.yaml", size: 22170, mode: os.FileMode(436), modTime: time.Unix(1522155621, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/cmd/ps.go b/cmd/ps.go index 9a431f4f..32d31407 100644 --- a/cmd/ps.go +++ b/cmd/ps.go @@ -3,9 +3,12 @@ package cmd import ( "errors" "fmt" + "os" + "strings" "github.com/appscode/go/term" "github.com/datacol-io/datacol/cmd/stdcli" + "github.com/olekukonko/tablewriter" "github.com/urfave/cli" ) @@ -16,11 +19,6 @@ func init() { Action: cmdAppPS, Flags: []cli.Flag{&appFlag}, Subcommands: []cli.Command{ - { - Name: "scale", - Usage: "scale the process", - Action: cmdAppScale, - }, { Name: "start", Usage: "start a process", @@ -33,6 +31,14 @@ func init() { }, }, }) + + stdcli.AddCommand(cli.Command{ + Name: "scale", + Usage: "scale processes in an app", + Action: cmdAppScale, + ArgsUsage: ": ...", + Flags: []cli.Flag{&appFlag}, + }) } func cmdAppPS(c *cli.Context) error { @@ -49,7 +55,13 @@ func cmdAppPS(c *cli.Context) error { stdcli.ExitOnError(err) if len(items) > 0 { - term.Println(toJson(items)) + table := tablewriter.NewWriter(os.Stdout) + table.SetColWidth(100) + table.SetHeader([]string{"PROCESS", "REPLICAS", "STATUS", "COMMAND"}) + for _, item := range items { + table.Append([]string{item.Proctype, fmt.Sprintf("%d", item.Count), item.Status, strings.Join(item.Command, " ")}) + } + table.Render() } else { term.Println("No process running") } diff --git a/cmd/run.go b/cmd/run.go index cb8f7add..4d8d27c5 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -36,10 +36,15 @@ func cmdAppRun(c *cli.Context) error { func prepareCommand(c *cli.Context) []string { args := c.Args() + + return append([]string{"env", fmt.Sprintf("TERM=%s", getTermEnv())}, args...) +} + +func getTermEnv() string { term := os.Getenv("TERM") if term == "" { term = "xterm" } - return append([]string{"env", fmt.Sprintf("TERM=%s", term)}, args...) + return term } diff --git a/cmd/stdcli/apprc.go b/cmd/stdcli/apprc.go index bc9b3b0c..7eead5d9 100644 --- a/cmd/stdcli/apprc.go +++ b/cmd/stdcli/apprc.go @@ -1,6 +1,7 @@ package stdcli import ( + "fmt" "os" "github.com/appscode/go/io" @@ -94,36 +95,23 @@ func LoadApprc() (*Apprc, error) { /* Exits if there is any error.*/ func GetAuthContextOrDie(stack string) (*Auth, *Apprc) { - rc, err := LoadApprc() - if err != nil { - term.Fatalln("Command requires authentication, please run `datacol login`") - } - - a := rc.GetAuth(stack) - - if a == nil { - term.Fatalln("Command requires authentication, please run `datacol login`") - } - return a, rc + return loadAuthForStack(stack) } /* Exits if there is any error.*/ func GetAuthOrDie(c *cli.Context) (*Auth, *Apprc) { - rc, err := LoadApprc() - if err != nil { - term.Fatalln("Command requires authentication, please run `datacol login`") + stack := c.String("stack") + + if stack == "" { + // Some commands might not parse arguments + stack = os.Getenv("STACK") } - stack := c.String("stack") if stack == "" { stack = GetAppSetting("stack") } - a := rc.GetAuth(stack) - if a == nil { - term.Fatalln("Command requires authentication, please run `datacol login`") - } - return a, rc + return loadAuthForStack(stack) } /* Exits if there is any error.*/ @@ -153,3 +141,28 @@ func NewAnonAUth() *Auth { a := &Auth{ApiServer: "localhost"} return a } + +func loadAuthForStack(stack string) (*Auth, *Apprc) { + rc, err := LoadApprc() + if err != nil { + exitWithLoginError("failed to load config file.") + } + + if stack == "" { + term.Fatalln("No stack found. Please provide `STACK` environment variable or --stack flag") + } + + a := rc.GetAuth(stack) + + if a == nil { + exitWithLoginError(fmt.Sprintf("No stack found for `%s`.", stack)) + } + return a, rc +} + +func exitWithLoginError(msg ...string) { + for _, m := range msg { + term.Println(m) + } + term.Fatalln("Since the command requires authentication, please run `datacol login` if you haven't logged in.") +} diff --git a/cmd/stdcli/cli.go b/cmd/stdcli/cli.go index a12bc162..39bf574c 100644 --- a/cmd/stdcli/cli.go +++ b/cmd/stdcli/cli.go @@ -14,6 +14,7 @@ import ( pb "github.com/datacol-io/datacol/api/models" rollbarAPI "github.com/stvp/rollbar" "github.com/urfave/cli" + "google.golang.org/grpc/status" ) var ( @@ -25,7 +26,7 @@ var ( ) func init() { - Version = "1.0.0-alpha.12" + Version = "1.0.0-alpha.13" LocalAppDir = ".datacol" Binary = filepath.Base(os.Args[0]) Commands = []cli.Command{} @@ -137,6 +138,9 @@ func CheckFlagsPresence(c *cli.Context, flags ...string) { func ExitOnError(err error) { if err != nil { + if gerr, ok := status.FromError(err); ok { + term.Fatalln(gerr.Message()) + } term.Fatalln(err) } } diff --git a/cloud/common/apps.go b/common/apps.go similarity index 69% rename from cloud/common/apps.go rename to common/apps.go index bfd65689..2c09ea0b 100644 --- a/cloud/common/apps.go +++ b/common/apps.go @@ -2,18 +2,21 @@ package common import ( "strconv" + "strings" log "github.com/Sirupsen/logrus" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - "github.com/datacol-io/datacol/cloud/kube" + kube "github.com/datacol-io/datacol/k8s" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" ) func UpdateApp(c *kubernetes.Clientset, build *pb.Build, ns, image string, sqlProxy bool, - domains []string, envVars map[string]string, provider cloud.CloudProvider) error { + domains []string, envVars map[string]string, + provider cloud.CloudProvider, + version string) error { deployer, err := kube.NewDeployer(c) if err != nil { @@ -34,7 +37,7 @@ func UpdateApp(c *kubernetes.Clientset, build *pb.Build, defaultProctype := GetDefaultProctype(build) procesess = append(procesess, &pb.Process{ Proctype: defaultProctype, - Workers: 1, + Count: 1, }) runningProcesses, err := kube.ProcessList(c, ns, build.App) @@ -44,7 +47,7 @@ func UpdateApp(c *kubernetes.Clientset, build *pb.Build, for _, rp := range runningProcesses { if rp.Proctype == defaultProctype { - procesess[0].Workers = rp.Workers // set the current worker similar to whatever running currently + procesess[0].Count = rp.Count // set the current worker similar to whatever running currently } // Only append non-default proceses @@ -73,6 +76,7 @@ func UpdateApp(c *kubernetes.Clientset, build *pb.Build, Provider: provider, ServiceID: GetJobID(build.App, proctype), EnableCloudSqlProxy: sqlProxy, + Version: version, } if proctype == WebProcessKind || proctype == CmdProcessKind { @@ -84,5 +88,36 @@ func UpdateApp(c *kubernetes.Clientset, build *pb.Build, } } + // TODO: cleanup old resource based on req.Version return nil } + +func MergeAppDomains(domains []string, item string) []string { + if item == "" { + return domains + } + + itemIndex := -1 + dotted := strings.HasPrefix(item, ":") + + if dotted { + item = item[1:] + } + + for i, d := range domains { + if d == item { + itemIndex = i + break + } + } + + if dotted && itemIndex >= 0 { + return append(domains[0:itemIndex], domains[itemIndex+1:]...) + } + + if !dotted && itemIndex == -1 { + return append(domains, item) + } + + return domains +} diff --git a/cloud/kube/helper_test.go b/common/apps_test.go similarity index 74% rename from cloud/kube/helper_test.go rename to common/apps_test.go index 0627a419..96c91d98 100644 --- a/cloud/kube/helper_test.go +++ b/common/apps_test.go @@ -1,9 +1,9 @@ -package kube_test +package common_test import ( "testing" - "github.com/datacol-io/datacol/cloud/kube" + "github.com/datacol-io/datacol/common" "github.com/stretchr/testify/assert" ) @@ -21,7 +21,7 @@ func TestMergeAppDomains(t *testing.T) { for _, tc := range testcases { t.Run(tc.item, func(t *testing.T) { - assert.Equal(t, kube.MergeAppDomains(tc.domains, tc.item), tc.expected, "Should get correct domains") + assert.Equal(t, common.MergeAppDomains(tc.domains, tc.item), tc.expected, "Should get correct domains") }) } } diff --git a/cloud/common/procfile.go b/common/procfile.go similarity index 98% rename from cloud/common/procfile.go rename to common/procfile.go index ea253eea..5650cddf 100644 --- a/cloud/common/procfile.go +++ b/common/procfile.go @@ -12,6 +12,7 @@ const ( WebProcessKind = "web" CmdProcessKind = "cmd" + RunProcessKind = "run" defaultShell = "sh" ) diff --git a/cloud/common/procfile_test.go b/common/procfile_test.go similarity index 100% rename from cloud/common/procfile_test.go rename to common/procfile_test.go diff --git a/cloud/common/util.go b/common/util.go similarity index 85% rename from cloud/common/util.go rename to common/util.go index 16cbae82..0b870745 100644 --- a/cloud/common/util.go +++ b/common/util.go @@ -5,13 +5,14 @@ import ( "bytes" "errors" "fmt" + "sort" "strings" log "github.com/Sirupsen/logrus" "github.com/appscode/go/crypto/rand" pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" - sched "github.com/datacol-io/datacol/cloud/kube" + sched "github.com/datacol-io/datacol/k8s" "k8s.io/client-go/kubernetes" ) @@ -29,7 +30,7 @@ func LoadEnvironment(data []byte) pb.Environment { } } - return e + return SortEnvironment(e) } func GenerateId(prefix string, size int) string { @@ -119,3 +120,21 @@ func GetProcessCommand(proctype string, b *pb.Build) (command []string, err erro return } + +func SortEnvironment(current pb.Environment) pb.Environment { + sorted := make(pb.Environment) + keys := make([]string, 0, len(current)) + for key := range current { + keys = append(keys, key) + } + + sort.Slice(keys, func(i, j int) bool { + return keys[i] < keys[j] + }) + + for _, k := range keys { + sorted[k] = current[k] + } + + return sorted +} diff --git a/hack/make.rb b/hack/make.rb index a36a067e..8aaa77f6 100644 --- a/hack/make.rb +++ b/hack/make.rb @@ -1,6 +1,6 @@ require 'rubygems' -$version = ENV.fetch('VERSION', "1.0.0-alpha.12") +$version = ENV.fetch('VERSION', "1.0.0-alpha.13") $env = ENV.fetch('DATACOL_ENV') # dev or prod $cgo = ENV.fetch("CGO_ENABLED", "0") diff --git a/cloud/kube/common.go b/k8s/common.go similarity index 83% rename from cloud/kube/common.go rename to k8s/common.go index f5a81ffe..f85f54cf 100644 --- a/cloud/kube/common.go +++ b/k8s/common.go @@ -2,15 +2,9 @@ package kube import ( "fmt" - "io" - "math" - "strconv" "strings" - "time" log "github.com/Sirupsen/logrus" - "github.com/bjaglin/multiplexio" - pb "github.com/datacol-io/datacol/api/models" "github.com/datacol-io/datacol/cloud" core_v1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -207,58 +201,6 @@ func GetServiceEndpoint(c *kubernetes.Clientset, ns, name string) (string, error return endpoint, nil } -func LogStreamReq(c *kubernetes.Clientset, w io.Writer, ns, app string, opts pb.LogStreamOptions) error { - pods, err := GetAllPods(c, ns, app) - if err != nil { - return err - } - - log.Debugf("Got %d pods for app=%s", len(pods), app) - - //TODO: consider using https://github.com/djherbis/stream for reading multiple streams - var sources []multiplexio.Source - - for _, pod := range pods { - if opts.Proctype != "" && opts.Proctype != pod.ObjectMeta.Labels[typeLabel] { - continue - } - - name := pod.Name - req := c.Core().RESTClient().Get(). - Namespace(ns). - Name(name). - Resource("pods"). - SubResource("log"). - Param("follow", strconv.FormatBool(opts.Follow)) - - var cntName string - if len(pod.Spec.Containers) > 0 { - cntName = pod.Spec.Containers[0].Name - } - - req = req.Param("container", cntName) - log.Debugf("streaming logs from pod:%v container:%s", name, cntName) - - if opts.Since > 0 { - sec := int64(math.Ceil(float64(opts.Since) / float64(time.Second))) - req = req.Param("sinceSeconds", strconv.FormatInt(sec, 10)) - } - - if r, err := req.Stream(); err == nil { - prefix := fmt.Sprintf("[%s] ", strings.TrimPrefix(name, app+"-")) - sources = append(sources, multiplexio.Source{ - Reader: r, - Write: func(dest io.Writer, token []byte) (int, error) { - return multiplexio.WriteNewLine(dest, append([]byte(prefix), token...)) - }, - }) - } - } - - _, err = io.Copy(w, multiplexio.NewReader(multiplexio.Options{}, sources...)) - return err -} - func PruneCloudSQLManifest(spec *core_v1.PodSpec) { containers := spec.Containers diff --git a/cloud/kube/deployer.go b/k8s/deployer.go similarity index 98% rename from cloud/kube/deployer.go rename to k8s/deployer.go index 4e5a661b..f38ae46a 100644 --- a/cloud/kube/deployer.go +++ b/k8s/deployer.go @@ -21,6 +21,8 @@ const ( heritage string = "datacol" appLabel string = "app" typeLabel string = "type" + versionLabel string = "version" + runProcessKind string = "run" ) type Deployer struct { @@ -229,8 +231,7 @@ func (r *Deployer) CreateOrUpdateDeployment(payload *DeployRequest) (*v1beta1.De if err == nil { found = true - i, _ := findContainer(d, payload.ServiceID) - if i >= 0 { + if i, _ := findContainer(d, payload.ServiceID); i >= 0 { d.Spec.Template.Spec.Containers[i] = newContainer(payload) //TODO: we are only updating containers schema for existing deployment. Add support for updating any any schema change //Below is one workaround of it. diff --git a/cloud/kube/deployment.go b/k8s/deployment.go similarity index 91% rename from cloud/kube/deployment.go rename to k8s/deployment.go index 816f93e1..7ffaa33a 100644 --- a/cloud/kube/deployment.go +++ b/k8s/deployment.go @@ -16,8 +16,8 @@ import ( ) func newDeployment(payload *DeployRequest) *v1beta1.Deployment { - maxunavailable := intstr.FromString("25%") - maxsurge := intstr.FromString("25%") + maxunavailable := intstr.FromInt(0) + maxsurge := intstr.FromInt(1) labels := map[string]string{ appLabel: payload.App, @@ -55,9 +55,8 @@ func newPodMetadata(req *DeployRequest) metav1.ObjectMeta { return metav1.ObjectMeta{ Annotations: req.Tags, Labels: map[string]string{ - "app": req.App, - "version": req.Version, - "type": req.Proctype, + appLabel: req.App, + typeLabel: req.Proctype, managedBy: heritage, }, Name: req.ServiceID, @@ -128,30 +127,41 @@ func newContainer(payload *DeployRequest) v1.Container { return container } -func newIngress(payload *DeployResponse, domains []string) *v1beta1.Ingress { - r := payload.Request - - if len(domains) == 0 { - domains = []string{fmt.Sprintf("%s.%s", r.ServiceID, defaultIngressDomain)} - } - +func ingressRulesManifest(service, path string, port intstr.IntOrString, domains []string) []v1beta1.IngressRule { rules := make([]v1beta1.IngressRule, len(domains)) for i, domain := range domains { rules[i] = v1beta1.IngressRule{ Host: domain, IngressRuleValue: v1beta1.IngressRuleValue{HTTP: &v1beta1.HTTPIngressRuleValue{ Paths: []v1beta1.HTTPIngressPath{{ - // It's important to have * after / since GCP GLBC load balancer doesn't support subresources automatically. - Path: "/*", + Path: path, Backend: v1beta1.IngressBackend{ - ServiceName: r.ServiceID, - ServicePort: r.ContainerPort, + ServiceName: service, + ServicePort: port, }, }}, }}, } } + return rules +} + +func newIngress(payload *DeployResponse, domains []string) *v1beta1.Ingress { + r := payload.Request + + if len(domains) == 0 { + domains = []string{fmt.Sprintf("%s.%s", r.ServiceID, defaultIngressDomain)} + } + + ingressPath := "/" + if payload.Request.Provider == cloud.GCPProvider { + // It's important to have * after / since GCP GLBC load balancer doesn't support subresources automatically. + ingressPath = "/*" + } + + rules := ingressRulesManifest(r.ServiceID, ingressPath, r.ContainerPort, domains) + //Note: making name dependent on namespace i.e. stackName will only provision one load-balancer per stack // change this if you want to allocate individual load balanacer for each app and use Name = payload.Request.ServiceID // Also if you change this remember to chage in AppGet to fetch IP of load balancer code diff --git a/k8s/helper.go b/k8s/helper.go new file mode 100644 index 00000000..433e9c22 --- /dev/null +++ b/k8s/helper.go @@ -0,0 +1,73 @@ +package kube + +import ( + "encoding/json" + "fmt" + + log "github.com/Sirupsen/logrus" + "k8s.io/api/extensions/v1beta1" +) + +func ingressNamespace(parentNS string) string { + // return ingressDefaultNamespace + return parentNS +} + +func toJson(object interface{}) string { + dump, err := json.MarshalIndent(object, " ", " ") + if err != nil { + log.Warnf("dumping json: %v", err) + } + + return string(dump) +} + +// Merge ingress rules from different applications since we share an ingress controller among +// different services. `source` will represent the rules for only an app +func mergeIngressRules(dest *v1beta1.Ingress, source *v1beta1.Ingress) *v1beta1.Ingress { + // check if the app domains can be added + for _, r := range source.Spec.Rules { + foundAt := -1 + for i, rr := range dest.Spec.Rules { + if rr.Host == r.Host { + foundAt = i + break + } + } + + if foundAt >= 0 { + dest.Spec.Rules[foundAt] = r + } else { + dest.Spec.Rules = append(dest.Spec.Rules, r) + } + } + + // check if the app domains should be removed + for i, r := range dest.Spec.Rules { + backend := r.IngressRuleValue.HTTP.Paths[0].Backend + serviceID := fmt.Sprintf("%s:%v", backend.ServiceName, backend.ServicePort) + related, found := false, false + + for _, rr := range source.Spec.Rules { + bk := rr.IngressRuleValue.HTTP.Paths[0].Backend + sid := fmt.Sprintf("%s:%v", bk.ServiceName, bk.ServicePort) + + if serviceID == sid { + related = true + } + + if rr.Host == r.Host { + found = true + break + } + } + + if related && !found { + dest.Spec.Rules = append(dest.Spec.Rules[:i], dest.Spec.Rules[i+1:]...) + } + } + + log.Debugf("final ingress rules %s", toJson(dest.Spec.Rules)) + + return dest +} diff --git a/k8s/helper_test.go b/k8s/helper_test.go new file mode 100644 index 00000000..0de44c6f --- /dev/null +++ b/k8s/helper_test.go @@ -0,0 +1,57 @@ +package kube + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func TestMergeIngressRules(t *testing.T) { + { + ing1 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app1", "", intstr.FromInt(80), []string{"a1.com"}), + }} + + ing2 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app2", "", intstr.FromInt(80), []string{"a2.com"}), + }} + + ing := mergeIngressRules(&ing1, &ing2) + assert.Len(t, ing.Spec.Rules, 2) + + ing3 := mergeIngressRules(ing, &ing2) + assert.Len(t, ing3.Spec.Rules, 2) + + ing2.Spec.Rules[0].Host = "b1.com" + ing4 := mergeIngressRules(ing3, &ing2) + assert.Len(t, ing4.Spec.Rules, 2) + } + + { + ing1 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app1", "", intstr.FromInt(80), []string{"a1.com", "a2.com"}), + }} + + ing2 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app1", "", intstr.FromInt(80), []string{"a1.com", "a2.com", "b1.com"}), + }} + + ing := mergeIngressRules(&ing1, &ing2) + assert.Len(t, ing.Spec.Rules, 3) + } + + { + ing1 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app1", "", intstr.FromInt(80), []string{"a1.com", "a2.com"}), + }} + + ing2 := v1beta1.Ingress{Spec: v1beta1.IngressSpec{ + Rules: ingressRulesManifest("app1", "", intstr.FromInt(80), []string{"a1.com"}), + }} + + ing := mergeIngressRules(&ing1, &ing2) + assert.Len(t, ing.Spec.Rules, 1) + } +} diff --git a/cloud/kube/ingress.go b/k8s/ingress.go similarity index 100% rename from cloud/kube/ingress.go rename to k8s/ingress.go diff --git a/k8s/logs.go b/k8s/logs.go new file mode 100644 index 00000000..18b1ada0 --- /dev/null +++ b/k8s/logs.go @@ -0,0 +1,99 @@ +package kube + +import ( + "fmt" + "io" + "math" + "strconv" + "strings" + "sync" + "time" + + log "github.com/Sirupsen/logrus" + "github.com/bjaglin/multiplexio" + pb "github.com/datacol-io/datacol/api/models" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +func LogStreamReq(c *kubernetes.Clientset, w io.Writer, ns, app string, opts pb.LogStreamOptions) error { + pods, err := GetAllRunningPods(c, ns, app) + if err != nil { + return err + } + + log.Debugf("Got %d pods for app=%s", len(pods), app) + + //TODO: consider using https://github.com/djherbis/stream for reading multiple streams + var sources []multiplexio.Source + type reqQueue struct { + name string + request *rest.Request + } + + var requests []reqQueue + + for _, pod := range pods { + if opts.Proctype != "" && opts.Proctype != pod.ObjectMeta.Labels[typeLabel] { + continue + } + + // Don't collect logs from ephemeral pods + if proctype := pod.ObjectMeta.Labels[typeLabel]; proctype == runProcessKind { + continue + } + + name := pod.Name + req := c.Core().RESTClient().Get(). + Namespace(ns). + Name(name). + Resource("pods"). + SubResource("log"). + Param("follow", strconv.FormatBool(opts.Follow)). + Param("tailLines", opts.TailLines) + + var cntName string + if len(pod.Spec.Containers) > 0 { + cntName = pod.Spec.Containers[0].Name + } + + req = req.Param("container", cntName) + log.Debugf("will stream logs from %v/%s", name, cntName) + + if opts.Since > 0 { + sec := int64(math.Ceil(float64(opts.Since) / float64(time.Second))) + req = req.Param("sinceSeconds", strconv.FormatInt(sec, 10)) + } + + requests = append(requests, reqQueue{name: name, request: req}) + } + + var wg sync.WaitGroup + + for _, rq := range requests { + wg.Add(1) + + go func(rq reqQueue) { + defer wg.Done() + r, err := rq.request.Stream() + if err != nil { + log.Errorf("creating log stream: %v", err) + } + + prefix := fmt.Sprintf("[%s] ", strings.TrimPrefix(rq.name, app+"-")) + sources = append(sources, multiplexio.Source{ + Reader: r, + Write: func(dest io.Writer, token []byte) (int, error) { + return multiplexio.WriteNewLine(dest, append([]byte(prefix), token...)) + }, + }) + }(rq) + } + + log.Infof("waiting for stream handlers ...") + wg.Wait() + log.Debugf("Done. Got %d streams", len(sources)) + + _, err = io.Copy(w, multiplexio.NewReader(multiplexio.Options{}, sources...)) + return err +} diff --git a/cloud/kube/pod.go b/k8s/pod.go similarity index 89% rename from cloud/kube/pod.go rename to k8s/pod.go index 0acab836..35b0b74f 100644 --- a/cloud/kube/pod.go +++ b/k8s/pod.go @@ -47,9 +47,35 @@ func GetAllPods(c *kubernetes.Clientset, ns, app string) ([]v1.Pod, error) { return nil, err } + sort.Slice(res.Items, func(i, j int) bool { + return res.Items[i].Status.StartTime.Before(res.Items[j].Status.StartTime) + }) + return res.Items, nil } +// Return the pods which seems to be up and running. +func GetAllRunningPods(c *kubernetes.Clientset, ns, app string) ([]v1.Pod, error) { + labels := map[string]string{appLabel: app, managedBy: heritage} + selector := klabels.Set(labels).AsSelector() + res, err := c.Core().Pods(ns).List(metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return nil, err + } + + items := make([]v1.Pod, 0, len(res.Items)) + + for _, item := range res.Items { + status := getPodStatus(c, &item) + if status == podDown || status == podCrashed { + continue + } + items = append(items, item) + } + + return items, nil +} + func GetAllPodNames(c *kubernetes.Clientset, ns, app string) ([]string, error) { pods, err := GetAllPods(c, ns, app) if err != nil { @@ -85,6 +111,25 @@ func getPodsForDeployment(c *kubernetes.Clientset, dp *v1beta1.Deployment) ([]v1 return res.Items, nil } +// Will return the pods in ordered by status.StartTime, Ideally we should filter the pods by the app's +// release version +func getLatestPodsForDeployment(c *kubernetes.Clientset, dp *v1beta1.Deployment) ([]v1.Pod, error) { + selector := klabels.Set(dp.Spec.Selector.MatchLabels).AsSelector() + res, err := c.Core().Pods(dp.Namespace).List(metav1.ListOptions{ + LabelSelector: selector.String(), + }) + + if err != nil { + return nil, err + } + + sort.Slice(res.Items, func(i, j int) bool { + return res.Items[i].Status.StartTime.Before(res.Items[j].Status.StartTime) + }) + + return res.Items, nil +} + func getPodByName(c *kubernetes.Clientset, ns, app string) (*v1.Pod, error) { pods, err := GetAllPods(c, ns, app) if err != nil { @@ -107,7 +152,6 @@ func podEvents(c *kubernetes.Clientset, pod *v1.Pod) (*v1.EventList, error) { res, err := c.Core().Events(pod.Namespace).List(metav1.ListOptions{ FieldSelector: klabels.Set(fields).AsSelector().String(), - // ResourceVersion: pod.ObjectMeta.ResourceVersion, }) if err != nil { return res, err diff --git a/cloud/kube/process.go b/k8s/process.go similarity index 83% rename from cloud/kube/process.go rename to k8s/process.go index 8382d8c2..e2f758b2 100644 --- a/cloud/kube/process.go +++ b/k8s/process.go @@ -89,10 +89,7 @@ func (p *ExecOptions) Run() error { Name(pod.Name). Namespace(pod.Namespace). SubResource("exec"). - Param("container", containerName). - Param("command", "/bin/sh"). - Param("command", "-c"). - Param("tty", "true") + Param("container", containerName) req.VersionedParams(&corev1.PodExecOptions{ Container: containerName, @@ -120,44 +117,49 @@ func ProcessList(c *kubernetes.Clientset, ns, app string) ([]*pb.Process, error) var items []*pb.Process for _, dp := range deployments { - pods, err := getPodsForDeployment(c, &dp) + pods, err := getLatestPodsForDeployment(c, &dp) if err != nil { return nil, err } - var status string - if len(pods) > 0 { - //FIXME: ideally should report status of all pods - status = getPodStatusStr(c, &pods[len(pods)-1]) + log.Debugf("got %d pods for %s", len(pods), app) + + if len(pods) == 0 { + continue } + //FIXME: ideally should report status of all pods for latest release/version + targetPod := pods[len(pods)-1] + status := getPodStatusStr(c, &targetPod) + items = append(items, &pb.Process{ Proctype: dp.ObjectMeta.Labels[typeLabel], - Workers: *dp.Spec.Replicas, - Name: dp.Name, + Count: *dp.Spec.Replicas, Status: status, + Command: targetPod.Spec.Containers[0].Args, }) } return items, nil } -func ProcessExec( +func ProcessRun( c *kubernetes.Clientset, cfg *rest.Config, - ns, name, image, command string, + ns, name, image string, + command []string, envVars map[string]string, sqlproxy bool, stream io.ReadWriter, provider cloud.CloudProvider, ) error { - proctype := rand.Characters(6) - podName := fmt.Sprintf("%s-%s", name, proctype) + proctype := runProcessKind + podName := fmt.Sprintf("%s-%s-%s", name, proctype, rand.Characters(6)) + req := &DeployRequest{ ServiceID: podName, Image: image, - Entrypoint: []string{"/bin/bash", "-c"}, - Args: []string{"trap : TERM INT; sleep infinity & wait"}, //FIXME: To let the pod stay around until being told to exit + Args: []string{"sleep", "infinity"}, //FIXME: To let the pod stay around until being told to exit EnvVars: envVars, App: name, Proctype: proctype, @@ -168,19 +170,18 @@ func ProcessExec( // Delete the pod sunce it's ephemeral defer deletePodByName(c, ns, podName) - return processExec(c, cfg, ns, command, req, stream) + return processRun(c, cfg, ns, command, req, stream) } func deletePodByName(c *kubernetes.Clientset, ns, name string) error { return c.Core().Pods(ns).Delete(name, &metav1.DeleteOptions{}) } -func processExec(c *kubernetes.Clientset, cfg *rest.Config, ns, command string, req *DeployRequest, stream io.ReadWriter) error { +func processRun(c *kubernetes.Clientset, cfg *rest.Config, ns string, command []string, req *DeployRequest, stream io.ReadWriter) error { spec := newPodSpec(req) spec.Spec.RestartPolicy = corev1.RestartPolicyNever log.Debugf("creating pod with spec %s", toJson(spec)) - pod, err := c.Core().Pods(ns).Create(spec) if err != nil { return err @@ -195,7 +196,7 @@ func processExec(c *kubernetes.Clientset, cfg *rest.Config, ns, command string, executer := &ExecOptions{ Namespace: ns, PodName: req.ServiceID, - Command: []string{command}, + Command: command, Stdin: true, In: stream, Out: stream, diff --git a/mini.json b/mini.json deleted file mode 100644 index e79c6f09..00000000 --- a/mini.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "steps": [ - { - "name": "gcr.io/cloud-builders/go", - "env":[ - "GOOS=linux", - "GOARCH=amd64" - ], - "args": [ "build", "-o", "apictl", "-ldflags=\"-s -w\"", "./api"] - }, { - "name": "zip", - "args": ["apictl.zip", "apictl"] - }, { - "name": "gcr.io/cloud-builders/gsutil", - "args": ["cp", "apictl.zip", "datacol-dev/binaries/${_DATACOL_VERSION}/apictl.zip"] - }, { - "name": "gcr.io/cloud-builders/gsutil", - "args": ["acl", "ch", "-u", "allUsers:R", "gs://datacol-dev/binaries/${_DATACOL_VERSION}/apictl.zip"] - }] -} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 00000000..5c9c2a30 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 00000000..91b5cef3 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-runewidth/README.mkd b/vendor/github.com/mattn/go-runewidth/README.mkd new file mode 100644 index 00000000..66663a94 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/README.mkd @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Coverage Status](https://coveralls.io/repos/mattn/go-runewidth/badge.png?branch=HEAD)](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 00000000..2164497a --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,1223 @@ +package runewidth + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth = IsEastAsian() + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{EastAsianWidth} +) + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + // func (t table) IncludesRune(r rune) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) / 2 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return true + } + } + + return false +} + +var private = table{ + {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, +} + +var nonprint = table{ + {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, + {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, + {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, + {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F}, + {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, + {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711}, + {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, + {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, + {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1}, + {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F}, + {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983}, + {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, + {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83}, + {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03}, + {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63}, + {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03}, + {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83}, + {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, + {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3}, + {0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63}, + {0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, + {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3}, + {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, + {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, + {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F}, + {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E}, + {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, + {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, + {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F}, + {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, + {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, + {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, + {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B}, + {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F}, + {0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, + {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, + {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2}, + {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4}, + {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, + {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, + {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, + {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881}, + {0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D}, + {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, + {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, + {0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, + {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, + {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, + {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E}, + {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, + {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03}, + {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, + {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002}, + {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, + {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173}, + {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC}, + {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA}, + {0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, + {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5}, + {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, + {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36}, + {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, + {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E}, + {0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169}, + {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, + {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36}, + {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, + {0xE0100, 0xE01EF}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, + {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, + {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, + {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, + {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, + {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, + {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC}, + {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004}, + {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, + {0x1F250, 0x1F251}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, + {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, + {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, + {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, + {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, + {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, + {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, + {0x1F6D0, 0x1F6D2}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6}, + {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930}, + {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E}, + {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD}, + {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} + +var emoji = table{ + {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F321}, {0x1F324, 0x1F32C}, + {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F396, 0x1F397}, + {0x1F399, 0x1F39B}, {0x1F39E, 0x1F39F}, {0x1F3CB, 0x1F3CE}, + {0x1F3D4, 0x1F3DF}, {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3F7}, + {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FD}, + {0x1F549, 0x1F54A}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F579}, + {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, + {0x1F5A5, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, + {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, + {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, + {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, + {0x1F5FA, 0x1F5FA}, {0x1F6CB, 0x1F6CF}, {0x1F6E0, 0x1F6E5}, + {0x1F6E9, 0x1F6E9}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F3}, +} + +var notassigned = table{ + {0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B}, + {0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530}, + {0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588}, + {0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF}, + {0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D}, + {0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF}, + {0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F}, + {0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5}, + {0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E}, + {0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1}, + {0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6}, + {0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB}, + {0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00}, + {0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12}, + {0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34}, + {0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D}, + {0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50}, + {0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65}, + {0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E}, + {0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1}, + {0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6}, + {0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF}, + {0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00}, + {0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12}, + {0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34}, + {0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A}, + {0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E}, + {0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84}, + {0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98}, + {0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2}, + {0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD}, + {0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF}, + {0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF}, + {0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11}, + {0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45}, + {0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57}, + {0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77}, + {0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91}, + {0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB}, + {0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4}, + {0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5}, + {0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04}, + {0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C}, + {0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53}, + {0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84}, + {0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC}, + {0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE}, + {0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5}, + {0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E}, + {0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86}, + {0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93}, + {0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4}, + {0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC}, + {0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5}, + {0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB}, + {0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70}, + {0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD}, + {0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC}, + {0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F}, + {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F}, + {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1}, + {0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1}, + {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311}, + {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F}, + {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF}, + {0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D}, + {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F}, + {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F}, + {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF}, + {0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F}, + {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F}, + {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943}, + {0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF}, + {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D}, + {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F}, + {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF}, + {0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB}, + {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF}, + {0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF}, + {0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F}, + {0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58}, + {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E}, + {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5}, + {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1}, + {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065}, + {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F}, + {0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F}, + {0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F}, + {0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC}, + {0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF}, + {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8}, + {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F}, + {0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F}, + {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7}, + {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF}, + {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F}, + {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF}, + {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098}, + {0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F}, + {0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F}, + {0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF}, + {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F}, + {0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6}, + {0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F}, + {0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF}, + {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE}, + {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F}, + {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA}, + {0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10}, + {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F}, + {0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF}, + {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF}, + {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12}, + {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D}, + {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45}, + {0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91}, + {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F}, + {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F}, + {0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00}, + {0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1}, + {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7}, + {0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C}, + {0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E}, + {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF}, + {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F}, + {0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F}, + {0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF}, + {0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F}, + {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF}, + {0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7}, + {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E}, + {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F}, + {0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809}, + {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E}, + {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF}, + {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E}, + {0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB}, + {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, + {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37}, + {0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F}, + {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF}, + {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77}, + {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF}, + {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9}, + {0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051}, + {0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF}, + {0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F}, + {0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0}, + {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F}, + {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E}, + {0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF}, + {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E}, + {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331}, + {0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346}, + {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356}, + {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F}, + {0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C}, + {0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F}, + {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F}, + {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF}, + {0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F}, + {0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF}, + {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37}, + {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91}, + {0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF}, + {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF}, + {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F}, + {0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF}, + {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F}, + {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C}, + {0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E}, + {0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF}, + {0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F}, + {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B}, + {0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128}, + {0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F}, + {0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D}, + {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8}, + {0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC}, + {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C}, + {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A}, + {0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549}, + {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD}, + {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF}, + {0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022}, + {0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6}, + {0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D}, + {0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20}, + {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28}, + {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A}, + {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48}, + {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50}, + {0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58}, + {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E}, + {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66}, + {0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78}, + {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A}, + {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA}, + {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F}, + {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, + {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F}, + {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5}, + {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F}, + {0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF}, + {0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, + {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, + {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F}, + {0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F}, + {0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF}, + {0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F}, + {0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000}, + {0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF}, + {0xFFFFE, 0xFFFFF}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x007F}, {0x0080, 0x009F}, + {0x00A0, 0x00A0}, {0x00A9, 0x00A9}, {0x00AB, 0x00AB}, + {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, {0x00C0, 0x00C5}, + {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, {0x00D9, 0x00DD}, + {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, {0x00EB, 0x00EB}, + {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, {0x00F4, 0x00F6}, + {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, {0x00FF, 0x00FF}, + {0x0100, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, + {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, + {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, + {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, + {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, + {0x016C, 0x017F}, {0x0180, 0x01BA}, {0x01BB, 0x01BB}, + {0x01BC, 0x01BF}, {0x01C0, 0x01C3}, {0x01C4, 0x01CD}, + {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, + {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, + {0x01DB, 0x01DB}, {0x01DD, 0x024F}, {0x0250, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x0293}, {0x0294, 0x0294}, + {0x0295, 0x02AF}, {0x02B0, 0x02C1}, {0x02C2, 0x02C3}, + {0x02C5, 0x02C5}, {0x02C6, 0x02C6}, {0x02C8, 0x02C8}, + {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D1}, + {0x02D2, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02E4}, {0x02E5, 0x02EB}, {0x02EC, 0x02EC}, + {0x02ED, 0x02ED}, {0x02EE, 0x02EE}, {0x02EF, 0x02FF}, + {0x0370, 0x0373}, {0x0374, 0x0374}, {0x0375, 0x0375}, + {0x0376, 0x0377}, {0x037A, 0x037A}, {0x037B, 0x037D}, + {0x037E, 0x037E}, {0x037F, 0x037F}, {0x0384, 0x0385}, + {0x0386, 0x0386}, {0x0387, 0x0387}, {0x0388, 0x038A}, + {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, + {0x03C2, 0x03C2}, {0x03CA, 0x03F5}, {0x03F6, 0x03F6}, + {0x03F7, 0x03FF}, {0x0400, 0x0400}, {0x0402, 0x040F}, + {0x0450, 0x0450}, {0x0452, 0x0481}, {0x0482, 0x0482}, + {0x0483, 0x0487}, {0x0488, 0x0489}, {0x048A, 0x04FF}, + {0x0500, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x0559}, + {0x055A, 0x055F}, {0x0561, 0x0587}, {0x0589, 0x0589}, + {0x058A, 0x058A}, {0x058D, 0x058E}, {0x058F, 0x058F}, + {0x0591, 0x05BD}, {0x05BE, 0x05BE}, {0x05BF, 0x05BF}, + {0x05C0, 0x05C0}, {0x05C1, 0x05C2}, {0x05C3, 0x05C3}, + {0x05C4, 0x05C5}, {0x05C6, 0x05C6}, {0x05C7, 0x05C7}, + {0x05D0, 0x05EA}, {0x05F0, 0x05F2}, {0x05F3, 0x05F4}, + {0x0600, 0x0605}, {0x0606, 0x0608}, {0x0609, 0x060A}, + {0x060B, 0x060B}, {0x060C, 0x060D}, {0x060E, 0x060F}, + {0x0610, 0x061A}, {0x061B, 0x061B}, {0x061C, 0x061C}, + {0x061E, 0x061F}, {0x0620, 0x063F}, {0x0640, 0x0640}, + {0x0641, 0x064A}, {0x064B, 0x065F}, {0x0660, 0x0669}, + {0x066A, 0x066D}, {0x066E, 0x066F}, {0x0670, 0x0670}, + {0x0671, 0x06D3}, {0x06D4, 0x06D4}, {0x06D5, 0x06D5}, + {0x06D6, 0x06DC}, {0x06DD, 0x06DD}, {0x06DE, 0x06DE}, + {0x06DF, 0x06E4}, {0x06E5, 0x06E6}, {0x06E7, 0x06E8}, + {0x06E9, 0x06E9}, {0x06EA, 0x06ED}, {0x06EE, 0x06EF}, + {0x06F0, 0x06F9}, {0x06FA, 0x06FC}, {0x06FD, 0x06FE}, + {0x06FF, 0x06FF}, {0x0700, 0x070D}, {0x070F, 0x070F}, + {0x0710, 0x0710}, {0x0711, 0x0711}, {0x0712, 0x072F}, + {0x0730, 0x074A}, {0x074D, 0x074F}, {0x0750, 0x077F}, + {0x0780, 0x07A5}, {0x07A6, 0x07B0}, {0x07B1, 0x07B1}, + {0x07C0, 0x07C9}, {0x07CA, 0x07EA}, {0x07EB, 0x07F3}, + {0x07F4, 0x07F5}, {0x07F6, 0x07F6}, {0x07F7, 0x07F9}, + {0x07FA, 0x07FA}, {0x0800, 0x0815}, {0x0816, 0x0819}, + {0x081A, 0x081A}, {0x081B, 0x0823}, {0x0824, 0x0824}, + {0x0825, 0x0827}, {0x0828, 0x0828}, {0x0829, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x0858}, {0x0859, 0x085B}, + {0x085E, 0x085E}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, + {0x08D4, 0x08E1}, {0x08E2, 0x08E2}, {0x08E3, 0x08FF}, + {0x0900, 0x0902}, {0x0903, 0x0903}, {0x0904, 0x0939}, + {0x093A, 0x093A}, {0x093B, 0x093B}, {0x093C, 0x093C}, + {0x093D, 0x093D}, {0x093E, 0x0940}, {0x0941, 0x0948}, + {0x0949, 0x094C}, {0x094D, 0x094D}, {0x094E, 0x094F}, + {0x0950, 0x0950}, {0x0951, 0x0957}, {0x0958, 0x0961}, + {0x0962, 0x0963}, {0x0964, 0x0965}, {0x0966, 0x096F}, + {0x0970, 0x0970}, {0x0971, 0x0971}, {0x0972, 0x097F}, + {0x0980, 0x0980}, {0x0981, 0x0981}, {0x0982, 0x0983}, + {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, + {0x09BC, 0x09BC}, {0x09BD, 0x09BD}, {0x09BE, 0x09C0}, + {0x09C1, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CC}, + {0x09CD, 0x09CD}, {0x09CE, 0x09CE}, {0x09D7, 0x09D7}, + {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09E2, 0x09E3}, + {0x09E6, 0x09EF}, {0x09F0, 0x09F1}, {0x09F2, 0x09F3}, + {0x09F4, 0x09F9}, {0x09FA, 0x09FA}, {0x09FB, 0x09FB}, + {0x0A01, 0x0A02}, {0x0A03, 0x0A03}, {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, + {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A40}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A6F}, + {0x0A70, 0x0A71}, {0x0A72, 0x0A74}, {0x0A75, 0x0A75}, + {0x0A81, 0x0A82}, {0x0A83, 0x0A83}, {0x0A85, 0x0A8D}, + {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, + {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0ABC}, + {0x0ABD, 0x0ABD}, {0x0ABE, 0x0AC0}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0AC9, 0x0AC9}, {0x0ACB, 0x0ACC}, + {0x0ACD, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE1}, + {0x0AE2, 0x0AE3}, {0x0AE6, 0x0AEF}, {0x0AF0, 0x0AF0}, + {0x0AF1, 0x0AF1}, {0x0AF9, 0x0AF9}, {0x0B01, 0x0B01}, + {0x0B02, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, + {0x0B35, 0x0B39}, {0x0B3C, 0x0B3C}, {0x0B3D, 0x0B3D}, + {0x0B3E, 0x0B3E}, {0x0B3F, 0x0B3F}, {0x0B40, 0x0B40}, + {0x0B41, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4C}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B57, 0x0B57}, + {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B62, 0x0B63}, + {0x0B66, 0x0B6F}, {0x0B70, 0x0B70}, {0x0B71, 0x0B71}, + {0x0B72, 0x0B77}, {0x0B82, 0x0B82}, {0x0B83, 0x0B83}, + {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, + {0x0BBE, 0x0BBF}, {0x0BC0, 0x0BC0}, {0x0BC1, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCC}, {0x0BCD, 0x0BCD}, + {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BEF}, + {0x0BF0, 0x0BF2}, {0x0BF3, 0x0BF8}, {0x0BF9, 0x0BF9}, + {0x0BFA, 0x0BFA}, {0x0C00, 0x0C00}, {0x0C01, 0x0C03}, + {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, + {0x0C2A, 0x0C39}, {0x0C3D, 0x0C3D}, {0x0C3E, 0x0C40}, + {0x0C41, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C61}, + {0x0C62, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C78, 0x0C7E}, + {0x0C7F, 0x0C7F}, {0x0C80, 0x0C80}, {0x0C81, 0x0C81}, + {0x0C82, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CBC}, {0x0CBD, 0x0CBD}, {0x0CBE, 0x0CBE}, + {0x0CBF, 0x0CBF}, {0x0CC0, 0x0CC4}, {0x0CC6, 0x0CC6}, + {0x0CC7, 0x0CC8}, {0x0CCA, 0x0CCB}, {0x0CCC, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, + {0x0CE2, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, + {0x0D01, 0x0D01}, {0x0D02, 0x0D03}, {0x0D05, 0x0D0C}, + {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A}, {0x0D3D, 0x0D3D}, + {0x0D3E, 0x0D40}, {0x0D41, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4C}, {0x0D4D, 0x0D4D}, {0x0D4E, 0x0D4E}, + {0x0D4F, 0x0D4F}, {0x0D54, 0x0D56}, {0x0D57, 0x0D57}, + {0x0D58, 0x0D5E}, {0x0D5F, 0x0D61}, {0x0D62, 0x0D63}, + {0x0D66, 0x0D6F}, {0x0D70, 0x0D78}, {0x0D79, 0x0D79}, + {0x0D7A, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, + {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, + {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD1}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, + {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF3}, {0x0DF4, 0x0DF4}, + {0x0E01, 0x0E30}, {0x0E31, 0x0E31}, {0x0E32, 0x0E33}, + {0x0E34, 0x0E3A}, {0x0E3F, 0x0E3F}, {0x0E40, 0x0E45}, + {0x0E46, 0x0E46}, {0x0E47, 0x0E4E}, {0x0E4F, 0x0E4F}, + {0x0E50, 0x0E59}, {0x0E5A, 0x0E5B}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, + {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, + {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, + {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EB0}, {0x0EB1, 0x0EB1}, + {0x0EB2, 0x0EB3}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, + {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, + {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, + {0x0F00, 0x0F00}, {0x0F01, 0x0F03}, {0x0F04, 0x0F12}, + {0x0F13, 0x0F13}, {0x0F14, 0x0F14}, {0x0F15, 0x0F17}, + {0x0F18, 0x0F19}, {0x0F1A, 0x0F1F}, {0x0F20, 0x0F29}, + {0x0F2A, 0x0F33}, {0x0F34, 0x0F34}, {0x0F35, 0x0F35}, + {0x0F36, 0x0F36}, {0x0F37, 0x0F37}, {0x0F38, 0x0F38}, + {0x0F39, 0x0F39}, {0x0F3A, 0x0F3A}, {0x0F3B, 0x0F3B}, + {0x0F3C, 0x0F3C}, {0x0F3D, 0x0F3D}, {0x0F3E, 0x0F3F}, + {0x0F40, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F7E}, + {0x0F7F, 0x0F7F}, {0x0F80, 0x0F84}, {0x0F85, 0x0F85}, + {0x0F86, 0x0F87}, {0x0F88, 0x0F8C}, {0x0F8D, 0x0F97}, + {0x0F99, 0x0FBC}, {0x0FBE, 0x0FC5}, {0x0FC6, 0x0FC6}, + {0x0FC7, 0x0FCC}, {0x0FCE, 0x0FCF}, {0x0FD0, 0x0FD4}, + {0x0FD5, 0x0FD8}, {0x0FD9, 0x0FDA}, {0x1000, 0x102A}, + {0x102B, 0x102C}, {0x102D, 0x1030}, {0x1031, 0x1031}, + {0x1032, 0x1037}, {0x1038, 0x1038}, {0x1039, 0x103A}, + {0x103B, 0x103C}, {0x103D, 0x103E}, {0x103F, 0x103F}, + {0x1040, 0x1049}, {0x104A, 0x104F}, {0x1050, 0x1055}, + {0x1056, 0x1057}, {0x1058, 0x1059}, {0x105A, 0x105D}, + {0x105E, 0x1060}, {0x1061, 0x1061}, {0x1062, 0x1064}, + {0x1065, 0x1066}, {0x1067, 0x106D}, {0x106E, 0x1070}, + {0x1071, 0x1074}, {0x1075, 0x1081}, {0x1082, 0x1082}, + {0x1083, 0x1084}, {0x1085, 0x1086}, {0x1087, 0x108C}, + {0x108D, 0x108D}, {0x108E, 0x108E}, {0x108F, 0x108F}, + {0x1090, 0x1099}, {0x109A, 0x109C}, {0x109D, 0x109D}, + {0x109E, 0x109F}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7}, + {0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FB, 0x10FB}, + {0x10FC, 0x10FC}, {0x10FD, 0x10FF}, {0x1160, 0x11FF}, + {0x1200, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, + {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, + {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, + {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, + {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, + {0x1318, 0x135A}, {0x135D, 0x135F}, {0x1360, 0x1368}, + {0x1369, 0x137C}, {0x1380, 0x138F}, {0x1390, 0x1399}, + {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x1400}, + {0x1401, 0x166C}, {0x166D, 0x166E}, {0x166F, 0x167F}, + {0x1680, 0x1680}, {0x1681, 0x169A}, {0x169B, 0x169B}, + {0x169C, 0x169C}, {0x16A0, 0x16EA}, {0x16EB, 0x16ED}, + {0x16EE, 0x16F0}, {0x16F1, 0x16F8}, {0x1700, 0x170C}, + {0x170E, 0x1711}, {0x1712, 0x1714}, {0x1720, 0x1731}, + {0x1732, 0x1734}, {0x1735, 0x1736}, {0x1740, 0x1751}, + {0x1752, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, + {0x1772, 0x1773}, {0x1780, 0x17B3}, {0x17B4, 0x17B5}, + {0x17B6, 0x17B6}, {0x17B7, 0x17BD}, {0x17BE, 0x17C5}, + {0x17C6, 0x17C6}, {0x17C7, 0x17C8}, {0x17C9, 0x17D3}, + {0x17D4, 0x17D6}, {0x17D7, 0x17D7}, {0x17D8, 0x17DA}, + {0x17DB, 0x17DB}, {0x17DC, 0x17DC}, {0x17DD, 0x17DD}, + {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1805}, + {0x1806, 0x1806}, {0x1807, 0x180A}, {0x180B, 0x180D}, + {0x180E, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1842}, + {0x1843, 0x1843}, {0x1844, 0x1877}, {0x1880, 0x1884}, + {0x1885, 0x1886}, {0x1887, 0x18A8}, {0x18A9, 0x18A9}, + {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, + {0x1920, 0x1922}, {0x1923, 0x1926}, {0x1927, 0x1928}, + {0x1929, 0x192B}, {0x1930, 0x1931}, {0x1932, 0x1932}, + {0x1933, 0x1938}, {0x1939, 0x193B}, {0x1940, 0x1940}, + {0x1944, 0x1945}, {0x1946, 0x194F}, {0x1950, 0x196D}, + {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, + {0x19D0, 0x19D9}, {0x19DA, 0x19DA}, {0x19DE, 0x19DF}, + {0x19E0, 0x19FF}, {0x1A00, 0x1A16}, {0x1A17, 0x1A18}, + {0x1A19, 0x1A1A}, {0x1A1B, 0x1A1B}, {0x1A1E, 0x1A1F}, + {0x1A20, 0x1A54}, {0x1A55, 0x1A55}, {0x1A56, 0x1A56}, + {0x1A57, 0x1A57}, {0x1A58, 0x1A5E}, {0x1A60, 0x1A60}, + {0x1A61, 0x1A61}, {0x1A62, 0x1A62}, {0x1A63, 0x1A64}, + {0x1A65, 0x1A6C}, {0x1A6D, 0x1A72}, {0x1A73, 0x1A7C}, + {0x1A7F, 0x1A7F}, {0x1A80, 0x1A89}, {0x1A90, 0x1A99}, + {0x1AA0, 0x1AA6}, {0x1AA7, 0x1AA7}, {0x1AA8, 0x1AAD}, + {0x1AB0, 0x1ABD}, {0x1ABE, 0x1ABE}, {0x1B00, 0x1B03}, + {0x1B04, 0x1B04}, {0x1B05, 0x1B33}, {0x1B34, 0x1B34}, + {0x1B35, 0x1B35}, {0x1B36, 0x1B3A}, {0x1B3B, 0x1B3B}, + {0x1B3C, 0x1B3C}, {0x1B3D, 0x1B41}, {0x1B42, 0x1B42}, + {0x1B43, 0x1B44}, {0x1B45, 0x1B4B}, {0x1B50, 0x1B59}, + {0x1B5A, 0x1B60}, {0x1B61, 0x1B6A}, {0x1B6B, 0x1B73}, + {0x1B74, 0x1B7C}, {0x1B80, 0x1B81}, {0x1B82, 0x1B82}, + {0x1B83, 0x1BA0}, {0x1BA1, 0x1BA1}, {0x1BA2, 0x1BA5}, + {0x1BA6, 0x1BA7}, {0x1BA8, 0x1BA9}, {0x1BAA, 0x1BAA}, + {0x1BAB, 0x1BAD}, {0x1BAE, 0x1BAF}, {0x1BB0, 0x1BB9}, + {0x1BBA, 0x1BBF}, {0x1BC0, 0x1BE5}, {0x1BE6, 0x1BE6}, + {0x1BE7, 0x1BE7}, {0x1BE8, 0x1BE9}, {0x1BEA, 0x1BEC}, + {0x1BED, 0x1BED}, {0x1BEE, 0x1BEE}, {0x1BEF, 0x1BF1}, + {0x1BF2, 0x1BF3}, {0x1BFC, 0x1BFF}, {0x1C00, 0x1C23}, + {0x1C24, 0x1C2B}, {0x1C2C, 0x1C33}, {0x1C34, 0x1C35}, + {0x1C36, 0x1C37}, {0x1C3B, 0x1C3F}, {0x1C40, 0x1C49}, + {0x1C4D, 0x1C4F}, {0x1C50, 0x1C59}, {0x1C5A, 0x1C77}, + {0x1C78, 0x1C7D}, {0x1C7E, 0x1C7F}, {0x1C80, 0x1C88}, + {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CD2}, {0x1CD3, 0x1CD3}, + {0x1CD4, 0x1CE0}, {0x1CE1, 0x1CE1}, {0x1CE2, 0x1CE8}, + {0x1CE9, 0x1CEC}, {0x1CED, 0x1CED}, {0x1CEE, 0x1CF1}, + {0x1CF2, 0x1CF3}, {0x1CF4, 0x1CF4}, {0x1CF5, 0x1CF6}, + {0x1CF8, 0x1CF9}, {0x1D00, 0x1D2B}, {0x1D2C, 0x1D6A}, + {0x1D6B, 0x1D77}, {0x1D78, 0x1D78}, {0x1D79, 0x1D7F}, + {0x1D80, 0x1D9A}, {0x1D9B, 0x1DBF}, {0x1DC0, 0x1DF5}, + {0x1DFB, 0x1DFF}, {0x1E00, 0x1EFF}, {0x1F00, 0x1F15}, + {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, {0x1FBD, 0x1FBD}, {0x1FBE, 0x1FBE}, + {0x1FBF, 0x1FC1}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, + {0x1FCD, 0x1FCF}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, + {0x1FDD, 0x1FDF}, {0x1FE0, 0x1FEC}, {0x1FED, 0x1FEF}, + {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x1FFD, 0x1FFE}, + {0x2000, 0x200A}, {0x200B, 0x200F}, {0x2011, 0x2012}, + {0x2017, 0x2017}, {0x201A, 0x201A}, {0x201B, 0x201B}, + {0x201E, 0x201E}, {0x201F, 0x201F}, {0x2023, 0x2023}, + {0x2028, 0x2028}, {0x2029, 0x2029}, {0x202A, 0x202E}, + {0x202F, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, + {0x2036, 0x2038}, {0x2039, 0x2039}, {0x203A, 0x203A}, + {0x203C, 0x203D}, {0x203F, 0x2040}, {0x2041, 0x2043}, + {0x2044, 0x2044}, {0x2045, 0x2045}, {0x2046, 0x2046}, + {0x2047, 0x2051}, {0x2052, 0x2052}, {0x2053, 0x2053}, + {0x2054, 0x2054}, {0x2055, 0x205E}, {0x205F, 0x205F}, + {0x2060, 0x2064}, {0x2066, 0x206F}, {0x2070, 0x2070}, + {0x2071, 0x2071}, {0x2075, 0x2079}, {0x207A, 0x207C}, + {0x207D, 0x207D}, {0x207E, 0x207E}, {0x2080, 0x2080}, + {0x2085, 0x2089}, {0x208A, 0x208C}, {0x208D, 0x208D}, + {0x208E, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, + {0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20DC}, + {0x20DD, 0x20E0}, {0x20E1, 0x20E1}, {0x20E2, 0x20E4}, + {0x20E5, 0x20F0}, {0x2100, 0x2101}, {0x2102, 0x2102}, + {0x2104, 0x2104}, {0x2106, 0x2106}, {0x2107, 0x2107}, + {0x2108, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2114}, + {0x2115, 0x2115}, {0x2117, 0x2117}, {0x2118, 0x2118}, + {0x2119, 0x211D}, {0x211E, 0x2120}, {0x2123, 0x2123}, + {0x2124, 0x2124}, {0x2125, 0x2125}, {0x2127, 0x2127}, + {0x2128, 0x2128}, {0x2129, 0x2129}, {0x212A, 0x212A}, + {0x212C, 0x212D}, {0x212E, 0x212E}, {0x212F, 0x2134}, + {0x2135, 0x2138}, {0x2139, 0x2139}, {0x213A, 0x213B}, + {0x213C, 0x213F}, {0x2140, 0x2144}, {0x2145, 0x2149}, + {0x214A, 0x214A}, {0x214B, 0x214B}, {0x214C, 0x214D}, + {0x214E, 0x214E}, {0x214F, 0x214F}, {0x2150, 0x2152}, + {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, + {0x217A, 0x2182}, {0x2183, 0x2184}, {0x2185, 0x2188}, + {0x218A, 0x218B}, {0x219A, 0x219B}, {0x219C, 0x219F}, + {0x21A0, 0x21A0}, {0x21A1, 0x21A2}, {0x21A3, 0x21A3}, + {0x21A4, 0x21A5}, {0x21A6, 0x21A6}, {0x21A7, 0x21AD}, + {0x21AE, 0x21AE}, {0x21AF, 0x21B7}, {0x21BA, 0x21CD}, + {0x21CE, 0x21CF}, {0x21D0, 0x21D1}, {0x21D3, 0x21D3}, + {0x21D5, 0x21E6}, {0x21E8, 0x21F3}, {0x21F4, 0x21FF}, + {0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A}, + {0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214}, + {0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222}, + {0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D}, + {0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247}, + {0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F}, + {0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D}, + {0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294}, + {0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE}, + {0x22C0, 0x22FF}, {0x2300, 0x2307}, {0x2308, 0x2308}, + {0x2309, 0x2309}, {0x230A, 0x230A}, {0x230B, 0x230B}, + {0x230C, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x231F}, + {0x2320, 0x2321}, {0x2322, 0x2328}, {0x232B, 0x237B}, + {0x237C, 0x237C}, {0x237D, 0x239A}, {0x239B, 0x23B3}, + {0x23B4, 0x23DB}, {0x23DC, 0x23E1}, {0x23E2, 0x23E8}, + {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x23FE}, + {0x2400, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, + {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, + {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, + {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, + {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, + {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25F7}, + {0x25F8, 0x25FC}, {0x25FF, 0x25FF}, {0x2600, 0x2604}, + {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, + {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, + {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, + {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, + {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, + {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, + {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, + {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, + {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, + {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, + {0x2758, 0x2767}, {0x2768, 0x2768}, {0x2769, 0x2769}, + {0x276A, 0x276A}, {0x276B, 0x276B}, {0x276C, 0x276C}, + {0x276D, 0x276D}, {0x276E, 0x276E}, {0x276F, 0x276F}, + {0x2770, 0x2770}, {0x2771, 0x2771}, {0x2772, 0x2772}, + {0x2773, 0x2773}, {0x2774, 0x2774}, {0x2775, 0x2775}, + {0x2780, 0x2793}, {0x2794, 0x2794}, {0x2798, 0x27AF}, + {0x27B1, 0x27BE}, {0x27C0, 0x27C4}, {0x27C5, 0x27C5}, + {0x27C6, 0x27C6}, {0x27C7, 0x27E5}, {0x27EE, 0x27EE}, + {0x27EF, 0x27EF}, {0x27F0, 0x27FF}, {0x2800, 0x28FF}, + {0x2900, 0x297F}, {0x2980, 0x2982}, {0x2983, 0x2983}, + {0x2984, 0x2984}, {0x2987, 0x2987}, {0x2988, 0x2988}, + {0x2989, 0x2989}, {0x298A, 0x298A}, {0x298B, 0x298B}, + {0x298C, 0x298C}, {0x298D, 0x298D}, {0x298E, 0x298E}, + {0x298F, 0x298F}, {0x2990, 0x2990}, {0x2991, 0x2991}, + {0x2992, 0x2992}, {0x2993, 0x2993}, {0x2994, 0x2994}, + {0x2995, 0x2995}, {0x2996, 0x2996}, {0x2997, 0x2997}, + {0x2998, 0x2998}, {0x2999, 0x29D7}, {0x29D8, 0x29D8}, + {0x29D9, 0x29D9}, {0x29DA, 0x29DA}, {0x29DB, 0x29DB}, + {0x29DC, 0x29FB}, {0x29FC, 0x29FC}, {0x29FD, 0x29FD}, + {0x29FE, 0x29FF}, {0x2A00, 0x2AFF}, {0x2B00, 0x2B1A}, + {0x2B1D, 0x2B2F}, {0x2B30, 0x2B44}, {0x2B45, 0x2B46}, + {0x2B47, 0x2B4C}, {0x2B4D, 0x2B4F}, {0x2B51, 0x2B54}, + {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9}, + {0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF}, + {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2C7B}, + {0x2C7C, 0x2C7D}, {0x2C7E, 0x2C7F}, {0x2C80, 0x2CE4}, + {0x2CE5, 0x2CEA}, {0x2CEB, 0x2CEE}, {0x2CEF, 0x2CF1}, + {0x2CF2, 0x2CF3}, {0x2CF9, 0x2CFC}, {0x2CFD, 0x2CFD}, + {0x2CFE, 0x2CFF}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F}, + {0x2D70, 0x2D70}, {0x2D7F, 0x2D7F}, {0x2D80, 0x2D96}, + {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, + {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, + {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2DFF}, + {0x2E00, 0x2E01}, {0x2E02, 0x2E02}, {0x2E03, 0x2E03}, + {0x2E04, 0x2E04}, {0x2E05, 0x2E05}, {0x2E06, 0x2E08}, + {0x2E09, 0x2E09}, {0x2E0A, 0x2E0A}, {0x2E0B, 0x2E0B}, + {0x2E0C, 0x2E0C}, {0x2E0D, 0x2E0D}, {0x2E0E, 0x2E16}, + {0x2E17, 0x2E17}, {0x2E18, 0x2E19}, {0x2E1A, 0x2E1A}, + {0x2E1B, 0x2E1B}, {0x2E1C, 0x2E1C}, {0x2E1D, 0x2E1D}, + {0x2E1E, 0x2E1F}, {0x2E20, 0x2E20}, {0x2E21, 0x2E21}, + {0x2E22, 0x2E22}, {0x2E23, 0x2E23}, {0x2E24, 0x2E24}, + {0x2E25, 0x2E25}, {0x2E26, 0x2E26}, {0x2E27, 0x2E27}, + {0x2E28, 0x2E28}, {0x2E29, 0x2E29}, {0x2E2A, 0x2E2E}, + {0x2E2F, 0x2E2F}, {0x2E30, 0x2E39}, {0x2E3A, 0x2E3B}, + {0x2E3C, 0x2E3F}, {0x2E40, 0x2E40}, {0x2E41, 0x2E41}, + {0x2E42, 0x2E42}, {0x2E43, 0x2E44}, {0x303F, 0x303F}, + {0x4DC0, 0x4DFF}, {0xA4D0, 0xA4F7}, {0xA4F8, 0xA4FD}, + {0xA4FE, 0xA4FF}, {0xA500, 0xA60B}, {0xA60C, 0xA60C}, + {0xA60D, 0xA60F}, {0xA610, 0xA61F}, {0xA620, 0xA629}, + {0xA62A, 0xA62B}, {0xA640, 0xA66D}, {0xA66E, 0xA66E}, + {0xA66F, 0xA66F}, {0xA670, 0xA672}, {0xA673, 0xA673}, + {0xA674, 0xA67D}, {0xA67E, 0xA67E}, {0xA67F, 0xA67F}, + {0xA680, 0xA69B}, {0xA69C, 0xA69D}, {0xA69E, 0xA69F}, + {0xA6A0, 0xA6E5}, {0xA6E6, 0xA6EF}, {0xA6F0, 0xA6F1}, + {0xA6F2, 0xA6F7}, {0xA700, 0xA716}, {0xA717, 0xA71F}, + {0xA720, 0xA721}, {0xA722, 0xA76F}, {0xA770, 0xA770}, + {0xA771, 0xA787}, {0xA788, 0xA788}, {0xA789, 0xA78A}, + {0xA78B, 0xA78E}, {0xA78F, 0xA78F}, {0xA790, 0xA7AE}, + {0xA7B0, 0xA7B7}, {0xA7F7, 0xA7F7}, {0xA7F8, 0xA7F9}, + {0xA7FA, 0xA7FA}, {0xA7FB, 0xA7FF}, {0xA800, 0xA801}, + {0xA802, 0xA802}, {0xA803, 0xA805}, {0xA806, 0xA806}, + {0xA807, 0xA80A}, {0xA80B, 0xA80B}, {0xA80C, 0xA822}, + {0xA823, 0xA824}, {0xA825, 0xA826}, {0xA827, 0xA827}, + {0xA828, 0xA82B}, {0xA830, 0xA835}, {0xA836, 0xA837}, + {0xA838, 0xA838}, {0xA839, 0xA839}, {0xA840, 0xA873}, + {0xA874, 0xA877}, {0xA880, 0xA881}, {0xA882, 0xA8B3}, + {0xA8B4, 0xA8C3}, {0xA8C4, 0xA8C5}, {0xA8CE, 0xA8CF}, + {0xA8D0, 0xA8D9}, {0xA8E0, 0xA8F1}, {0xA8F2, 0xA8F7}, + {0xA8F8, 0xA8FA}, {0xA8FB, 0xA8FB}, {0xA8FC, 0xA8FC}, + {0xA8FD, 0xA8FD}, {0xA900, 0xA909}, {0xA90A, 0xA925}, + {0xA926, 0xA92D}, {0xA92E, 0xA92F}, {0xA930, 0xA946}, + {0xA947, 0xA951}, {0xA952, 0xA953}, {0xA95F, 0xA95F}, + {0xA980, 0xA982}, {0xA983, 0xA983}, {0xA984, 0xA9B2}, + {0xA9B3, 0xA9B3}, {0xA9B4, 0xA9B5}, {0xA9B6, 0xA9B9}, + {0xA9BA, 0xA9BB}, {0xA9BC, 0xA9BC}, {0xA9BD, 0xA9C0}, + {0xA9C1, 0xA9CD}, {0xA9CF, 0xA9CF}, {0xA9D0, 0xA9D9}, + {0xA9DE, 0xA9DF}, {0xA9E0, 0xA9E4}, {0xA9E5, 0xA9E5}, + {0xA9E6, 0xA9E6}, {0xA9E7, 0xA9EF}, {0xA9F0, 0xA9F9}, + {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA29, 0xAA2E}, + {0xAA2F, 0xAA30}, {0xAA31, 0xAA32}, {0xAA33, 0xAA34}, + {0xAA35, 0xAA36}, {0xAA40, 0xAA42}, {0xAA43, 0xAA43}, + {0xAA44, 0xAA4B}, {0xAA4C, 0xAA4C}, {0xAA4D, 0xAA4D}, + {0xAA50, 0xAA59}, {0xAA5C, 0xAA5F}, {0xAA60, 0xAA6F}, + {0xAA70, 0xAA70}, {0xAA71, 0xAA76}, {0xAA77, 0xAA79}, + {0xAA7A, 0xAA7A}, {0xAA7B, 0xAA7B}, {0xAA7C, 0xAA7C}, + {0xAA7D, 0xAA7D}, {0xAA7E, 0xAA7F}, {0xAA80, 0xAAAF}, + {0xAAB0, 0xAAB0}, {0xAAB1, 0xAAB1}, {0xAAB2, 0xAAB4}, + {0xAAB5, 0xAAB6}, {0xAAB7, 0xAAB8}, {0xAAB9, 0xAABD}, + {0xAABE, 0xAABF}, {0xAAC0, 0xAAC0}, {0xAAC1, 0xAAC1}, + {0xAAC2, 0xAAC2}, {0xAADB, 0xAADC}, {0xAADD, 0xAADD}, + {0xAADE, 0xAADF}, {0xAAE0, 0xAAEA}, {0xAAEB, 0xAAEB}, + {0xAAEC, 0xAAED}, {0xAAEE, 0xAAEF}, {0xAAF0, 0xAAF1}, + {0xAAF2, 0xAAF2}, {0xAAF3, 0xAAF4}, {0xAAF5, 0xAAF5}, + {0xAAF6, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, + {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, + {0xAB30, 0xAB5A}, {0xAB5B, 0xAB5B}, {0xAB5C, 0xAB5F}, + {0xAB60, 0xAB65}, {0xAB70, 0xABBF}, {0xABC0, 0xABE2}, + {0xABE3, 0xABE4}, {0xABE5, 0xABE5}, {0xABE6, 0xABE7}, + {0xABE8, 0xABE8}, {0xABE9, 0xABEA}, {0xABEB, 0xABEB}, + {0xABEC, 0xABEC}, {0xABED, 0xABED}, {0xABF0, 0xABF9}, + {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDB7F}, + {0xDB80, 0xDBFF}, {0xDC00, 0xDFFF}, {0xFB00, 0xFB06}, + {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1E, 0xFB1E}, + {0xFB1F, 0xFB28}, {0xFB29, 0xFB29}, {0xFB2A, 0xFB36}, + {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, {0xFB46, 0xFB4F}, {0xFB50, 0xFBB1}, + {0xFBB2, 0xFBC1}, {0xFBD3, 0xFD3D}, {0xFD3E, 0xFD3E}, + {0xFD3F, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, + {0xFDF0, 0xFDFB}, {0xFDFC, 0xFDFC}, {0xFDFD, 0xFDFD}, + {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFC, 0xFFFC}, + {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, + {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, + {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, + {0x10137, 0x1013F}, {0x10140, 0x10174}, {0x10175, 0x10178}, + {0x10179, 0x10189}, {0x1018A, 0x1018B}, {0x1018C, 0x1018E}, + {0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FC}, + {0x101FD, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, + {0x102E0, 0x102E0}, {0x102E1, 0x102FB}, {0x10300, 0x1031F}, + {0x10320, 0x10323}, {0x10330, 0x10340}, {0x10341, 0x10341}, + {0x10342, 0x10349}, {0x1034A, 0x1034A}, {0x10350, 0x10375}, + {0x10376, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x1039F}, + {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x103D0, 0x103D0}, + {0x103D1, 0x103D5}, {0x10400, 0x1044F}, {0x10450, 0x1047F}, + {0x10480, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, + {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, + {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, + {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, + {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, + {0x1083F, 0x1083F}, {0x10840, 0x10855}, {0x10857, 0x10857}, + {0x10858, 0x1085F}, {0x10860, 0x10876}, {0x10877, 0x10878}, + {0x10879, 0x1087F}, {0x10880, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x108FF}, + {0x10900, 0x10915}, {0x10916, 0x1091B}, {0x1091F, 0x1091F}, + {0x10920, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x1099F}, + {0x109A0, 0x109B7}, {0x109BC, 0x109BD}, {0x109BE, 0x109BF}, + {0x109C0, 0x109CF}, {0x109D2, 0x109FF}, {0x10A00, 0x10A00}, + {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, + {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x10A40, 0x10A47}, + {0x10A50, 0x10A58}, {0x10A60, 0x10A7C}, {0x10A7D, 0x10A7E}, + {0x10A7F, 0x10A7F}, {0x10A80, 0x10A9C}, {0x10A9D, 0x10A9F}, + {0x10AC0, 0x10AC7}, {0x10AC8, 0x10AC8}, {0x10AC9, 0x10AE4}, + {0x10AE5, 0x10AE6}, {0x10AEB, 0x10AEF}, {0x10AF0, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B3F}, {0x10B40, 0x10B55}, + {0x10B58, 0x10B5F}, {0x10B60, 0x10B72}, {0x10B78, 0x10B7F}, + {0x10B80, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x11000}, + {0x11001, 0x11001}, {0x11002, 0x11002}, {0x11003, 0x11037}, + {0x11038, 0x11046}, {0x11047, 0x1104D}, {0x11052, 0x11065}, + {0x11066, 0x1106F}, {0x1107F, 0x1107F}, {0x11080, 0x11081}, + {0x11082, 0x11082}, {0x11083, 0x110AF}, {0x110B0, 0x110B2}, + {0x110B3, 0x110B6}, {0x110B7, 0x110B8}, {0x110B9, 0x110BA}, + {0x110BB, 0x110BC}, {0x110BD, 0x110BD}, {0x110BE, 0x110C1}, + {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11102}, + {0x11103, 0x11126}, {0x11127, 0x1112B}, {0x1112C, 0x1112C}, + {0x1112D, 0x11134}, {0x11136, 0x1113F}, {0x11140, 0x11143}, + {0x11150, 0x11172}, {0x11173, 0x11173}, {0x11174, 0x11175}, + {0x11176, 0x11176}, {0x11180, 0x11181}, {0x11182, 0x11182}, + {0x11183, 0x111B2}, {0x111B3, 0x111B5}, {0x111B6, 0x111BE}, + {0x111BF, 0x111C0}, {0x111C1, 0x111C4}, {0x111C5, 0x111C9}, + {0x111CA, 0x111CC}, {0x111CD, 0x111CD}, {0x111D0, 0x111D9}, + {0x111DA, 0x111DA}, {0x111DB, 0x111DB}, {0x111DC, 0x111DC}, + {0x111DD, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, + {0x11213, 0x1122B}, {0x1122C, 0x1122E}, {0x1122F, 0x11231}, + {0x11232, 0x11233}, {0x11234, 0x11234}, {0x11235, 0x11235}, + {0x11236, 0x11237}, {0x11238, 0x1123D}, {0x1123E, 0x1123E}, + {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, + {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112A9, 0x112A9}, + {0x112B0, 0x112DE}, {0x112DF, 0x112DF}, {0x112E0, 0x112E2}, + {0x112E3, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11301}, + {0x11302, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133C, 0x1133C}, {0x1133D, 0x1133D}, + {0x1133E, 0x1133F}, {0x11340, 0x11340}, {0x11341, 0x11344}, + {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, + {0x11357, 0x11357}, {0x1135D, 0x11361}, {0x11362, 0x11363}, + {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x11434}, + {0x11435, 0x11437}, {0x11438, 0x1143F}, {0x11440, 0x11441}, + {0x11442, 0x11444}, {0x11445, 0x11445}, {0x11446, 0x11446}, + {0x11447, 0x1144A}, {0x1144B, 0x1144F}, {0x11450, 0x11459}, + {0x1145B, 0x1145B}, {0x1145D, 0x1145D}, {0x11480, 0x114AF}, + {0x114B0, 0x114B2}, {0x114B3, 0x114B8}, {0x114B9, 0x114B9}, + {0x114BA, 0x114BA}, {0x114BB, 0x114BE}, {0x114BF, 0x114C0}, + {0x114C1, 0x114C1}, {0x114C2, 0x114C3}, {0x114C4, 0x114C5}, + {0x114C6, 0x114C6}, {0x114C7, 0x114C7}, {0x114D0, 0x114D9}, + {0x11580, 0x115AE}, {0x115AF, 0x115B1}, {0x115B2, 0x115B5}, + {0x115B8, 0x115BB}, {0x115BC, 0x115BD}, {0x115BE, 0x115BE}, + {0x115BF, 0x115C0}, {0x115C1, 0x115D7}, {0x115D8, 0x115DB}, + {0x115DC, 0x115DD}, {0x11600, 0x1162F}, {0x11630, 0x11632}, + {0x11633, 0x1163A}, {0x1163B, 0x1163C}, {0x1163D, 0x1163D}, + {0x1163E, 0x1163E}, {0x1163F, 0x11640}, {0x11641, 0x11643}, + {0x11644, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, + {0x11680, 0x116AA}, {0x116AB, 0x116AB}, {0x116AC, 0x116AC}, + {0x116AD, 0x116AD}, {0x116AE, 0x116AF}, {0x116B0, 0x116B5}, + {0x116B6, 0x116B6}, {0x116B7, 0x116B7}, {0x116C0, 0x116C9}, + {0x11700, 0x11719}, {0x1171D, 0x1171F}, {0x11720, 0x11721}, + {0x11722, 0x11725}, {0x11726, 0x11726}, {0x11727, 0x1172B}, + {0x11730, 0x11739}, {0x1173A, 0x1173B}, {0x1173C, 0x1173E}, + {0x1173F, 0x1173F}, {0x118A0, 0x118DF}, {0x118E0, 0x118E9}, + {0x118EA, 0x118F2}, {0x118FF, 0x118FF}, {0x11AC0, 0x11AF8}, + {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C2F, 0x11C2F}, + {0x11C30, 0x11C36}, {0x11C38, 0x11C3D}, {0x11C3E, 0x11C3E}, + {0x11C3F, 0x11C3F}, {0x11C40, 0x11C40}, {0x11C41, 0x11C45}, + {0x11C50, 0x11C59}, {0x11C5A, 0x11C6C}, {0x11C70, 0x11C71}, + {0x11C72, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CA9}, + {0x11CAA, 0x11CB0}, {0x11CB1, 0x11CB1}, {0x11CB2, 0x11CB3}, + {0x11CB4, 0x11CB4}, {0x11CB5, 0x11CB6}, {0x12000, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, + {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F}, + {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF4}, {0x16AF5, 0x16AF5}, + {0x16B00, 0x16B2F}, {0x16B30, 0x16B36}, {0x16B37, 0x16B3B}, + {0x16B3C, 0x16B3F}, {0x16B40, 0x16B43}, {0x16B44, 0x16B44}, + {0x16B45, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, + {0x16F50, 0x16F50}, {0x16F51, 0x16F7E}, {0x16F8F, 0x16F92}, + {0x16F93, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, + {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BC9C}, + {0x1BC9D, 0x1BC9E}, {0x1BC9F, 0x1BC9F}, {0x1BCA0, 0x1BCA3}, + {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D164}, + {0x1D165, 0x1D166}, {0x1D167, 0x1D169}, {0x1D16A, 0x1D16C}, + {0x1D16D, 0x1D172}, {0x1D173, 0x1D17A}, {0x1D17B, 0x1D182}, + {0x1D183, 0x1D184}, {0x1D185, 0x1D18B}, {0x1D18C, 0x1D1A9}, + {0x1D1AA, 0x1D1AD}, {0x1D1AE, 0x1D1E8}, {0x1D200, 0x1D241}, + {0x1D242, 0x1D244}, {0x1D245, 0x1D245}, {0x1D300, 0x1D356}, + {0x1D360, 0x1D371}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, + {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, + {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, + {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, + {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, + {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, + {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, + {0x1D6C1, 0x1D6C1}, {0x1D6C2, 0x1D6DA}, {0x1D6DB, 0x1D6DB}, + {0x1D6DC, 0x1D6FA}, {0x1D6FB, 0x1D6FB}, {0x1D6FC, 0x1D714}, + {0x1D715, 0x1D715}, {0x1D716, 0x1D734}, {0x1D735, 0x1D735}, + {0x1D736, 0x1D74E}, {0x1D74F, 0x1D74F}, {0x1D750, 0x1D76E}, + {0x1D76F, 0x1D76F}, {0x1D770, 0x1D788}, {0x1D789, 0x1D789}, + {0x1D78A, 0x1D7A8}, {0x1D7A9, 0x1D7A9}, {0x1D7AA, 0x1D7C2}, + {0x1D7C3, 0x1D7C3}, {0x1D7C4, 0x1D7CB}, {0x1D7CE, 0x1D7FF}, + {0x1D800, 0x1D9FF}, {0x1DA00, 0x1DA36}, {0x1DA37, 0x1DA3A}, + {0x1DA3B, 0x1DA6C}, {0x1DA6D, 0x1DA74}, {0x1DA75, 0x1DA75}, + {0x1DA76, 0x1DA83}, {0x1DA84, 0x1DA84}, {0x1DA85, 0x1DA86}, + {0x1DA87, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, + {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, + {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, + {0x1E8C7, 0x1E8CF}, {0x1E8D0, 0x1E8D6}, {0x1E900, 0x1E943}, + {0x1E944, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, + {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, + {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, + {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, + {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, + {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, + {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, + {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, + {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, + {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, + {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, + {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, + {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, + {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, + {0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF}, + {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, + {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, + {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, + {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, + {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, + {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, + {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA}, + {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4}, + {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, + {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{EastAsianWidth} +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + switch { + case r < 0 || r > 0x10FFFF || + inTables(r, nonprint, combining, notassigned): + return 0 + case (c.EastAsianWidth && IsAmbiguousWidth(r)) || + inTables(r, doublewidth, emoji): + return 2 + default: + return 1 + } +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + for _, r := range []rune(s) { + width += c.RuneWidth(r) + } + return width +} + +// Truncate return string truncated with w cells +func (c *Condition) Truncate(s string, w int, tail string) string { + if c.StringWidth(s) <= w { + return s + } + r := []rune(s) + tw := c.StringWidth(tail) + w -= tw + width := 0 + i := 0 + for ; i < len(r); i++ { + cw := c.RuneWidth(r[i]) + if width+cw > w { + break + } + width += cw + } + return string(r[0:i]) + tail +} + +// Wrap return string wrapped with w cells +func (c *Condition) Wrap(s string, w int) string { + width := 0 + out := "" + for _, r := range []rune(s) { + cw := RuneWidth(r) + if r == '\n' { + out += string(r) + width = 0 + continue + } else if width+cw > w { + out += "\n" + width = 0 + out += string(r) + width += cw + continue + } + out += string(r) + width += cw + } + return out +} + +// FillLeft return string filled in left by spaces in w cells +func (c *Condition) FillLeft(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return string(b) + s + } + return s +} + +// FillRight return string filled in left by spaces in w cells +func (c *Condition) FillRight(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return s + string(b) + } + return s +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func RuneWidth(r rune) int { + return DefaultCondition.RuneWidth(r) +} + +// IsAmbiguousWidth returns whether is ambiguous width or not. +func IsAmbiguousWidth(r rune) bool { + return inTables(r, private, ambiguous) +} + +// IsNeutralWidth returns whether is neutral width or not. +func IsNeutralWidth(r rune) bool { + return inTable(r, neutral) +} + +// StringWidth return width as you can see +func StringWidth(s string) (width int) { + return DefaultCondition.StringWidth(s) +} + +// Truncate return string truncated with w cells +func Truncate(s string, w int, tail string) string { + return DefaultCondition.Truncate(s, w, tail) +} + +// Wrap return string wrapped with w cells +func Wrap(s string, w int) string { + return DefaultCondition.Wrap(s, w) +} + +// FillLeft return string filled in left by spaces in w cells +func FillLeft(s string, w int) string { + return DefaultCondition.FillLeft(s, w) +} + +// FillRight return string filled in left by spaces in w cells +func FillRight(s string, w int) string { + return DefaultCondition.FillRight(s, w) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 00000000..0ce32c5e --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,8 @@ +// +build js + +package runewidth + +func IsEastAsian() bool { + // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 00000000..c579e9a3 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,77 @@ +// +build !windows,!js + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_CTYPE") + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_test.go b/vendor/github.com/mattn/go-runewidth/runewidth_test.go new file mode 100644 index 00000000..b0378a19 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_test.go @@ -0,0 +1,275 @@ +package runewidth + +import ( + "sort" + "testing" +) + +var _ sort.Interface = (*table)(nil) + +func (t table) Len() int { + return len(t) +} + +func (t table) Less(i, j int) bool { + return t[i].first < t[j].first +} + +func (t *table) Swap(i, j int) { + (*t)[i], (*t)[j] = (*t)[j], (*t)[i] +} + +var tables = []table{ + private, + nonprint, + combining, + doublewidth, + ambiguous, + emoji, + notassigned, + neutral, +} + +func TestSorted(t *testing.T) { + for _, tbl := range tables { + if !sort.IsSorted(&tbl) { + t.Errorf("not sorted") + } + } +} + +var runewidthtests = []struct { + in rune + out int + eaout int +}{ + {'世', 2, 2}, + {'界', 2, 2}, + {'セ', 1, 1}, + {'カ', 1, 1}, + {'イ', 1, 1}, + {'☆', 1, 2}, // double width in ambiguous + {'\x00', 0, 0}, + {'\x01', 0, 0}, + {'\u0300', 0, 0}, +} + +func TestRuneWidth(t *testing.T) { + c := NewCondition() + for _, tt := range runewidthtests { + if out := c.RuneWidth(tt.in); out != tt.out { + t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.out) + } + } + c.EastAsianWidth = true + for _, tt := range runewidthtests { + if out := c.RuneWidth(tt.in); out != tt.eaout { + t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.eaout) + } + } +} + +var isambiguouswidthtests = []struct { + in rune + out bool +}{ + {'世', false}, + {'■', true}, + {'界', false}, + {'○', true}, + {'㈱', false}, + {'①', true}, + {'②', true}, + {'③', true}, + {'④', true}, + {'⑤', true}, + {'⑥', true}, + {'⑦', true}, + {'⑧', true}, + {'⑨', true}, + {'⑩', true}, + {'⑪', true}, + {'⑫', true}, + {'⑬', true}, + {'⑭', true}, + {'⑮', true}, + {'⑯', true}, + {'⑰', true}, + {'⑱', true}, + {'⑲', true}, + {'⑳', true}, + {'☆', true}, +} + +func TestIsAmbiguousWidth(t *testing.T) { + for _, tt := range isambiguouswidthtests { + if out := IsAmbiguousWidth(tt.in); out != tt.out { + t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out) + } + } +} + +var stringwidthtests = []struct { + in string + out int + eaout int +}{ + {"■㈱の世界①", 10, 12}, + {"スター☆", 7, 8}, + {"つのだ☆HIRO", 11, 12}, +} + +func TestStringWidth(t *testing.T) { + c := NewCondition() + for _, tt := range stringwidthtests { + if out := c.StringWidth(tt.in); out != tt.out { + t.Errorf("StringWidth(%q) = %q, want %q", tt.in, out, tt.out) + } + } + c.EastAsianWidth = true + for _, tt := range stringwidthtests { + if out := c.StringWidth(tt.in); out != tt.eaout { + t.Errorf("StringWidth(%q) = %q, want %q", tt.in, out, tt.eaout) + } + } +} + +func TestStringWidthInvalid(t *testing.T) { + s := "こんにちわ\x00世界" + if out := StringWidth(s); out != 14 { + t.Errorf("StringWidth(%q) = %q, want %q", s, out, 14) + } +} + +func TestTruncateSmaller(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := Truncate(s, 10, "..."); out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } +} + +func TestTruncate(t *testing.T) { + s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 79 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 79, width) + } +} + +func TestTruncateFit(t *testing.T) { + s := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." + + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 80 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) + } +} + +func TestTruncateJustFit(t *testing.T) { + s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" + + out := Truncate(s, 80, "...") + if out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } + width := StringWidth(out) + if width != 80 { + t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) + } +} + +func TestWrap(t *testing.T) { + s := `東京特許許可局局長はよく柿喰う客だ/東京特許許可局局長はよく柿喰う客だ +123456789012345678901234567890 + +END` + expected := `東京特許許可局局長はよく柿喰う +客だ/東京特許許可局局長はよく +柿喰う客だ +123456789012345678901234567890 + +END` + + if out := Wrap(s, 30); out != expected { + t.Errorf("Wrap(%q) = %q, want %q", s, out, expected) + } +} + +func TestTruncateNoNeeded(t *testing.T) { + s := "あいうえおあい" + expected := "あいうえおあい" + + if out := Truncate(s, 80, "..."); out != expected { + t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) + } +} + +var isneutralwidthtests = []struct { + in rune + out bool +}{ + {'→', false}, + {'┊', false}, + {'┈', false}, + {'~', false}, + {'└', false}, + {'⣀', true}, + {'⣀', true}, +} + +func TestIsNeutralWidth(t *testing.T) { + for _, tt := range isneutralwidthtests { + if out := IsNeutralWidth(tt.in); out != tt.out { + t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out) + } + } +} + +func TestFillLeft(t *testing.T) { + s := "あxいうえお" + expected := " あxいうえお" + + if out := FillLeft(s, 15); out != expected { + t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillLeftFit(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := FillLeft(s, 10); out != expected { + t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillRight(t *testing.T) { + s := "あxいうえお" + expected := "あxいうえお " + + if out := FillRight(s, 15); out != expected { + t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) + } +} + +func TestFillRightFit(t *testing.T) { + s := "あいうえお" + expected := "あいうえお" + + if out := FillRight(s, 10); out != expected { + t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) + } +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 00000000..0258876b --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,25 @@ +package runewidth + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") +) + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + r1, _, _ := procGetConsoleOutputCP.Call() + if r1 == 0 { + return false + } + + switch int(r1) { + case 932, 51932, 936, 949, 950: + return true + } + + return false +} diff --git a/vendor/github.com/olekukonko/tablewriter/.travis.yml b/vendor/github.com/olekukonko/tablewriter/.travis.yml new file mode 100644 index 00000000..f156b3b8 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - 1.7 + - 1.8 + - tip diff --git a/vendor/github.com/olekukonko/tablewriter/LICENCE.md b/vendor/github.com/olekukonko/tablewriter/LICENCE.md new file mode 100644 index 00000000..1fd84842 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/LICENCE.md @@ -0,0 +1,19 @@ +Copyright (C) 2014 by Oleku Konko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/README.md b/vendor/github.com/olekukonko/tablewriter/README.md new file mode 100644 index 00000000..d7c36481 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/README.md @@ -0,0 +1,277 @@ +ASCII Table Writer +========= + +[![Build Status](https://travis-ci.org/olekukonko/tablewriter.png?branch=master)](https://travis-ci.org/olekukonko/tablewriter) +[![Total views](https://img.shields.io/sourcegraph/rrc/github.com/olekukonko/tablewriter.svg)](https://sourcegraph.com/github.com/olekukonko/tablewriter) +[![Godoc](https://godoc.org/github.com/olekukonko/tablewriter?status.svg)](https://godoc.org/github.com/olekukonko/tablewriter) + +Generate ASCII table on the fly ... Installation is simple as + + go get github.com/olekukonko/tablewriter + + +#### Features +- Automatic Padding +- Support Multiple Lines +- Supports Alignment +- Support Custom Separators +- Automatic Alignment of numbers & percentage +- Write directly to http , file etc via `io.Writer` +- Read directly from CSV file +- Optional row line via `SetRowLine` +- Normalise table header +- Make CSV Headers optional +- Enable or disable table border +- Set custom footer support +- Optional identical cells merging +- Set custom caption +- Optional reflowing of paragrpahs in multi-line cells. + +#### Example 1 - Basic +```go +data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Name", "Sign", "Rating"}) + +for _, v := range data { + table.Append(v) +} +table.Render() // Send output +``` + +##### Output 1 +``` ++------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| B | The Very very Bad Man | 288 | +| C | The Ugly | 120 | +| D | The Gopher | 800 | ++------+-----------------------+--------+ +``` + +#### Example 2 - Without Border / Footer / Bulk Append +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer +table.SetBorder(false) // Set Border to false +table.AppendBulk(data) // Add Bulk Data +table.Render() +``` + +##### Output 2 +``` + + DATE | DESCRIPTION | CV2 | AMOUNT ++----------+--------------------------+-------+---------+ + 1/1/2014 | Domain name | 2233 | $10.98 + 1/1/2014 | January Hosting | 2233 | $54.95 + 1/4/2014 | February Hosting | 2233 | $51.00 + 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 ++----------+--------------------------+-------+---------+ + TOTAL | $146 93 + +-------+---------+ + +``` + + +#### Example 3 - CSV +```go +table, _ := tablewriter.NewCSV(os.Stdout, "test_info.csv", true) +table.SetAlignment(tablewriter.ALIGN_LEFT) // Set Alignment +table.Render() +``` + +##### Output 3 +``` ++----------+--------------+------+-----+---------+----------------+ +| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA | ++----------+--------------+------+-----+---------+----------------+ +| user_id | smallint(5) | NO | PRI | NULL | auto_increment | +| username | varchar(10) | NO | | NULL | | +| password | varchar(100) | NO | | NULL | | ++----------+--------------+------+-----+---------+----------------+ +``` + +#### Example 4 - Custom Separator +```go +table, _ := tablewriter.NewCSV(os.Stdout, "test.csv", true) +table.SetRowLine(true) // Enable row line + +// Change table lines +table.SetCenterSeparator("*") +table.SetColumnSeparator("‡") +table.SetRowSeparator("-") + +table.SetAlignment(tablewriter.ALIGN_LEFT) +table.Render() +``` + +##### Output 4 +``` +*------------*-----------*---------* +╪ FIRST NAME ╪ LAST NAME ╪ SSN ╪ +*------------*-----------*---------* +╪ John ╪ Barry ╪ 123456 ╪ +*------------*-----------*---------* +╪ Kathy ╪ Smith ╪ 687987 ╪ +*------------*-----------*---------* +╪ Bob ╪ McCornick ╪ 3979870 ╪ +*------------*-----------*---------* +``` + +#### Example 5 - Markdown Format +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) +table.SetCenterSeparator("|") +table.AppendBulk(data) // Add Bulk Data +table.Render() +``` + +##### Output 5 +``` +| DATE | DESCRIPTION | CV2 | AMOUNT | +|----------|--------------------------|------|--------| +| 1/1/2014 | Domain name | 2233 | $10.98 | +| 1/1/2014 | January Hosting | 2233 | $54.95 | +| 1/4/2014 | February Hosting | 2233 | $51.00 | +| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | +``` + +#### Example 6 - Identical cells merging +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "1234", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2345", "$54.95"}, + []string{"1/4/2014", "February Hosting", "3456", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetFooter([]string{"", "", "Total", "$146.93"}) +table.SetAutoMergeCells(true) +table.SetRowLine(true) +table.AppendBulk(data) +table.Render() +``` + +##### Output 6 +``` ++----------+--------------------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+--------------------------+-------+---------+ +| 1/1/2014 | Domain name | 1234 | $10.98 | ++ +--------------------------+-------+---------+ +| | January Hosting | 2345 | $54.95 | ++----------+--------------------------+-------+---------+ +| 1/4/2014 | February Hosting | 3456 | $51.00 | ++ +--------------------------+-------+---------+ +| | February Extra Bandwidth | 4567 | $30.00 | ++----------+--------------------------+-------+---------+ +| TOTAL | $146 93 | ++----------+--------------------------+-------+---------+ +``` + + +#### Table with color +```go +data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) +table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer +table.SetBorder(false) // Set Border to false + +table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor}, + tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor}, + tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor}, + tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor}) + +table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor}, + tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor}, + tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor}, + tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor}) + +table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{}, + tablewriter.Colors{tablewriter.Bold}, + tablewriter.Colors{tablewriter.FgHiRedColor}) + +table.AppendBulk(data) +table.Render() +``` + +#### Table with color Output +![Table with Color](https://cloud.githubusercontent.com/assets/6460392/21101956/bbc7b356-c0a1-11e6-9f36-dba694746efc.png) + +#### Example 6 - Set table caption +```go +data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, +} + +table := tablewriter.NewWriter(os.Stdout) +table.SetHeader([]string{"Name", "Sign", "Rating"}) +table.SetCaption(true, "Movie ratings.") + +for _, v := range data { + table.Append(v) +} +table.Render() // Send output +``` + +Note: Caption text will wrap with total width of rendered table. + +##### Output 6 +``` ++------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| B | The Very very Bad Man | 288 | +| C | The Ugly | 120 | +| D | The Gopher | 800 | ++------+-----------------------+--------+ +Movie ratings. +``` + +#### TODO +- ~~Import Directly from CSV~~ - `done` +- ~~Support for `SetFooter`~~ - `done` +- ~~Support for `SetBorder`~~ - `done` +- ~~Support table with uneven rows~~ - `done` +- ~~Support custom alignment~~ +- General Improvement & Optimisation +- `NewHTML` Parse table from HTML diff --git a/vendor/github.com/olekukonko/tablewriter/csv.go b/vendor/github.com/olekukonko/tablewriter/csv.go new file mode 100644 index 00000000..98878303 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/csv.go @@ -0,0 +1,52 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "encoding/csv" + "io" + "os" +) + +// Start A new table by importing from a CSV file +// Takes io.Writer and csv File name +func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) { + file, err := os.Open(fileName) + if err != nil { + return &Table{}, err + } + defer file.Close() + csvReader := csv.NewReader(file) + t, err := NewCSVReader(writer, csvReader, hasHeader) + return t, err +} + +// Start a New Table Writer with csv.Reader +// This enables customisation such as reader.Comma = ';' +// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94 +func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) { + t := NewWriter(writer) + if hasHeader { + // Read the first row + headers, err := csvReader.Read() + if err != nil { + return &Table{}, err + } + t.SetHeader(headers) + } + for { + record, err := csvReader.Read() + if err == io.EOF { + break + } else if err != nil { + return &Table{}, err + } + t.Append(record) + } + return t, nil +} diff --git a/vendor/github.com/olekukonko/tablewriter/csv2table/README.md b/vendor/github.com/olekukonko/tablewriter/csv2table/README.md new file mode 100644 index 00000000..6cf5628a --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/csv2table/README.md @@ -0,0 +1,43 @@ +ASCII Table Writer Tool +========= + +Generate ASCII table on the fly via command line ... Installation is simple as + +#### Get Tool + + go get github.com/olekukonko/tablewriter/csv2table + +#### Install Tool + + go install github.com/olekukonko/tablewriter/csv2table + + +#### Usage + + csv2table -f test.csv + +#### Support for Piping + + cat test.csv | csv2table -p=true + +#### Output + +``` ++------------+-----------+---------+ +| FIRST NAME | LAST NAME | SSN | ++------------+-----------+---------+ +| John | Barry | 123456 | +| Kathy | Smith | 687987 | +| Bob | McCornick | 3979870 | ++------------+-----------+---------+ +``` + +#### Another Piping with Header set to `false` + + echo dance,with,me | csv2table -p=true -h=false + +#### Output + + +-------+------+-----+ + | dance | with | me | + +-------+------+-----+ diff --git a/vendor/github.com/olekukonko/tablewriter/csv2table/csv2table.go b/vendor/github.com/olekukonko/tablewriter/csv2table/csv2table.go new file mode 100644 index 00000000..5e1d7f24 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/csv2table/csv2table.go @@ -0,0 +1,85 @@ +package main + +import ( + "encoding/csv" + "flag" + "fmt" + "io" + "os" + "unicode/utf8" + + "github.com/olekukonko/tablewriter" +) + +var ( + fileName = flag.String("f", "", "Set file with eg. sample.csv") + delimiter = flag.String("d", ",", "Set CSV File delimiter eg. ,|;|\t ") + header = flag.Bool("h", true, "Set header options eg. true|false ") + align = flag.String("a", "none", "Set aligmement with eg. none|left|right|center") + pipe = flag.Bool("p", false, "Suport for Piping from STDIN") + border = flag.Bool("b", true, "Enable / disable table border") +) + +func main() { + flag.Parse() + fmt.Println() + if *pipe || hasArg("-p") { + process(os.Stdin) + } else { + if *fileName == "" { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + fmt.Println() + os.Exit(1) + } + processFile() + } + fmt.Println() +} + +func hasArg(name string) bool { + for _, v := range os.Args { + if name == v { + return true + } + } + return false +} +func processFile() { + r, err := os.Open(*fileName) + if err != nil { + exit(err) + } + defer r.Close() + process(r) +} +func process(r io.Reader) { + csvReader := csv.NewReader(r) + rune, size := utf8.DecodeRuneInString(*delimiter) + if size == 0 { + rune = ',' + } + csvReader.Comma = rune + + table, err := tablewriter.NewCSVReader(os.Stdout, csvReader, *header) + + if err != nil { + exit(err) + } + + switch *align { + case "left": + table.SetAlignment(tablewriter.ALIGN_LEFT) + case "right": + table.SetAlignment(tablewriter.ALIGN_RIGHT) + case "center": + table.SetAlignment(tablewriter.ALIGN_CENTER) + } + table.SetBorder(*border) + table.Render() +} + +func exit(err error) { + fmt.Fprintf(os.Stderr, "#Error : %s", err) + os.Exit(1) +} diff --git a/vendor/github.com/olekukonko/tablewriter/table.go b/vendor/github.com/olekukonko/tablewriter/table.go new file mode 100644 index 00000000..6bbef96a --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/table.go @@ -0,0 +1,839 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +// Create & Generate text based table +package tablewriter + +import ( + "bytes" + "fmt" + "io" + "regexp" + "strings" +) + +const ( + MAX_ROW_WIDTH = 30 +) + +const ( + CENTER = "+" + ROW = "-" + COLUMN = "|" + SPACE = " " + NEWLINE = "\n" +) + +const ( + ALIGN_DEFAULT = iota + ALIGN_CENTER + ALIGN_RIGHT + ALIGN_LEFT +) + +var ( + decimal = regexp.MustCompile(`^-*\d*\.?\d*$`) + percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`) +) + +type Border struct { + Left bool + Right bool + Top bool + Bottom bool +} + +type Table struct { + out io.Writer + rows [][]string + lines [][][]string + cs map[int]int + rs map[int]int + headers [][]string + footers [][]string + caption bool + captionText string + autoFmt bool + autoWrap bool + reflowText bool + mW int + pCenter string + pRow string + pColumn string + tColumn int + tRow int + hAlign int + fAlign int + align int + newLine string + rowLine bool + autoMergeCells bool + hdrLine bool + borders Border + colSize int + headerParams []string + columnsParams []string + footerParams []string + columnsAlign []int +} + +// Start New Table +// Take io.Writer Directly +func NewWriter(writer io.Writer) *Table { + t := &Table{ + out: writer, + rows: [][]string{}, + lines: [][][]string{}, + cs: make(map[int]int), + rs: make(map[int]int), + headers: [][]string{}, + footers: [][]string{}, + caption: false, + captionText: "Table caption.", + autoFmt: true, + autoWrap: true, + reflowText: true, + mW: MAX_ROW_WIDTH, + pCenter: CENTER, + pRow: ROW, + pColumn: COLUMN, + tColumn: -1, + tRow: -1, + hAlign: ALIGN_DEFAULT, + fAlign: ALIGN_DEFAULT, + align: ALIGN_DEFAULT, + newLine: NEWLINE, + rowLine: false, + hdrLine: true, + borders: Border{Left: true, Right: true, Bottom: true, Top: true}, + colSize: -1, + headerParams: []string{}, + columnsParams: []string{}, + footerParams: []string{}, + columnsAlign: []int{}} + return t +} + +// Render table output +func (t *Table) Render() { + if t.borders.Top { + t.printLine(true) + } + t.printHeading() + if t.autoMergeCells { + t.printRowsMergeCells() + } else { + t.printRows() + } + if !t.rowLine && t.borders.Bottom { + t.printLine(true) + } + t.printFooter() + + if t.caption { + t.printCaption() + } +} + +const ( + headerRowIdx = -1 + footerRowIdx = -2 +) + +// Set table header +func (t *Table) SetHeader(keys []string) { + t.colSize = len(keys) + for i, v := range keys { + lines := t.parseDimension(v, i, headerRowIdx) + t.headers = append(t.headers, lines) + } +} + +// Set table Footer +func (t *Table) SetFooter(keys []string) { + //t.colSize = len(keys) + for i, v := range keys { + lines := t.parseDimension(v, i, footerRowIdx) + t.footers = append(t.footers, lines) + } +} + +// Set table Caption +func (t *Table) SetCaption(caption bool, captionText ...string) { + t.caption = caption + if len(captionText) == 1 { + t.captionText = captionText[0] + } +} + +// Turn header autoformatting on/off. Default is on (true). +func (t *Table) SetAutoFormatHeaders(auto bool) { + t.autoFmt = auto +} + +// Turn automatic multiline text adjustment on/off. Default is on (true). +func (t *Table) SetAutoWrapText(auto bool) { + t.autoWrap = auto +} + +// Turn automatic reflowing of multiline text when rewrapping. Default is on (true). +func (t *Table) SetReflowDuringAutoWrap(auto bool) { + t.reflowText = auto +} + +// Set the Default column width +func (t *Table) SetColWidth(width int) { + t.mW = width +} + +// Set the minimal width for a column +func (t *Table) SetColMinWidth(column int, width int) { + t.cs[column] = width +} + +// Set the Column Separator +func (t *Table) SetColumnSeparator(sep string) { + t.pColumn = sep +} + +// Set the Row Separator +func (t *Table) SetRowSeparator(sep string) { + t.pRow = sep +} + +// Set the center Separator +func (t *Table) SetCenterSeparator(sep string) { + t.pCenter = sep +} + +// Set Header Alignment +func (t *Table) SetHeaderAlignment(hAlign int) { + t.hAlign = hAlign +} + +// Set Footer Alignment +func (t *Table) SetFooterAlignment(fAlign int) { + t.fAlign = fAlign +} + +// Set Table Alignment +func (t *Table) SetAlignment(align int) { + t.align = align +} + +func (t *Table) SetColumnAlignment(keys []int) { + for _, v := range keys { + switch v { + case ALIGN_CENTER: + break + case ALIGN_LEFT: + break + case ALIGN_RIGHT: + break + default: + v = ALIGN_DEFAULT + } + t.columnsAlign = append(t.columnsAlign, v) + } +} + +// Set New Line +func (t *Table) SetNewLine(nl string) { + t.newLine = nl +} + +// Set Header Line +// This would enable / disable a line after the header +func (t *Table) SetHeaderLine(line bool) { + t.hdrLine = line +} + +// Set Row Line +// This would enable / disable a line on each row of the table +func (t *Table) SetRowLine(line bool) { + t.rowLine = line +} + +// Set Auto Merge Cells +// This would enable / disable the merge of cells with identical values +func (t *Table) SetAutoMergeCells(auto bool) { + t.autoMergeCells = auto +} + +// Set Table Border +// This would enable / disable line around the table +func (t *Table) SetBorder(border bool) { + t.SetBorders(Border{border, border, border, border}) +} + +func (t *Table) SetBorders(border Border) { + t.borders = border +} + +// Append row to table +func (t *Table) Append(row []string) { + rowSize := len(t.headers) + if rowSize > t.colSize { + t.colSize = rowSize + } + + n := len(t.lines) + line := [][]string{} + for i, v := range row { + + // Detect string width + // Detect String height + // Break strings into words + out := t.parseDimension(v, i, n) + + // Append broken words + line = append(line, out) + } + t.lines = append(t.lines, line) +} + +// Allow Support for Bulk Append +// Eliminates repeated for loops +func (t *Table) AppendBulk(rows [][]string) { + for _, row := range rows { + t.Append(row) + } +} + +// NumLines to get the number of lines +func (t *Table) NumLines() int { + return len(t.lines) +} + +// Clear rows +func (t *Table) ClearRows() { + t.lines = [][][]string{} +} + +// Clear footer +func (t *Table) ClearFooter() { + t.footers = [][]string{} +} + +// Print line based on row width +func (t *Table) printLine(nl bool) { + fmt.Fprint(t.out, t.pCenter) + for i := 0; i < len(t.cs); i++ { + v := t.cs[i] + fmt.Fprintf(t.out, "%s%s%s%s", + t.pRow, + strings.Repeat(string(t.pRow), v), + t.pRow, + t.pCenter) + } + if nl { + fmt.Fprint(t.out, t.newLine) + } +} + +// Print line based on row width with our without cell separator +func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) { + fmt.Fprint(t.out, t.pCenter) + for i := 0; i < len(t.cs); i++ { + v := t.cs[i] + if i > len(displayCellSeparator) || displayCellSeparator[i] { + // Display the cell separator + fmt.Fprintf(t.out, "%s%s%s%s", + t.pRow, + strings.Repeat(string(t.pRow), v), + t.pRow, + t.pCenter) + } else { + // Don't display the cell separator for this cell + fmt.Fprintf(t.out, "%s%s", + strings.Repeat(" ", v+2), + t.pCenter) + } + } + if nl { + fmt.Fprint(t.out, t.newLine) + } +} + +// Return the PadRight function if align is left, PadLeft if align is right, +// and Pad by default +func pad(align int) func(string, string, int) string { + padFunc := Pad + switch align { + case ALIGN_LEFT: + padFunc = PadRight + case ALIGN_RIGHT: + padFunc = PadLeft + } + return padFunc +} + +// Print heading information +func (t *Table) printHeading() { + // Check if headers is available + if len(t.headers) < 1 { + return + } + + // Identify last column + end := len(t.cs) - 1 + + // Get pad function + padFunc := pad(t.hAlign) + + // Checking for ANSI escape sequences for header + is_esc_seq := false + if len(t.headerParams) > 0 { + is_esc_seq = true + } + + // Maximum height. + max := t.rs[headerRowIdx] + + // Print Heading + for x := 0; x < max; x++ { + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE)) + + for y := 0; y <= end; y++ { + v := t.cs[y] + h := "" + if y < len(t.headers) && x < len(t.headers[y]) { + h = t.headers[y][x] + } + if t.autoFmt { + h = Title(h) + } + pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn) + + if is_esc_seq { + fmt.Fprintf(t.out, " %s %s", + format(padFunc(h, SPACE, v), + t.headerParams[y]), pad) + } else { + fmt.Fprintf(t.out, " %s %s", + padFunc(h, SPACE, v), + pad) + } + } + // Next line + fmt.Fprint(t.out, t.newLine) + } + if t.hdrLine { + t.printLine(true) + } +} + +// Print heading information +func (t *Table) printFooter() { + // Check if headers is available + if len(t.footers) < 1 { + return + } + + // Only print line if border is not set + if !t.borders.Bottom { + t.printLine(true) + } + + // Identify last column + end := len(t.cs) - 1 + + // Get pad function + padFunc := pad(t.fAlign) + + // Checking for ANSI escape sequences for header + is_esc_seq := false + if len(t.footerParams) > 0 { + is_esc_seq = true + } + + // Maximum height. + max := t.rs[footerRowIdx] + + // Print Footer + erasePad := make([]bool, len(t.footers)) + for x := 0; x < max; x++ { + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE)) + + for y := 0; y <= end; y++ { + v := t.cs[y] + f := "" + if y < len(t.footers) && x < len(t.footers[y]) { + f = t.footers[y][x] + } + if t.autoFmt { + f = Title(f) + } + pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn) + + if erasePad[y] || (x == 0 && len(f) == 0) { + pad = SPACE + erasePad[y] = true + } + + if is_esc_seq { + fmt.Fprintf(t.out, " %s %s", + format(padFunc(f, SPACE, v), + t.footerParams[y]), pad) + } else { + fmt.Fprintf(t.out, " %s %s", + padFunc(f, SPACE, v), + pad) + } + + //fmt.Fprintf(t.out, " %s %s", + // padFunc(f, SPACE, v), + // pad) + } + // Next line + fmt.Fprint(t.out, t.newLine) + //t.printLine(true) + } + + hasPrinted := false + + for i := 0; i <= end; i++ { + v := t.cs[i] + pad := t.pRow + center := t.pCenter + length := len(t.footers[i][0]) + + if length > 0 { + hasPrinted = true + } + + // Set center to be space if length is 0 + if length == 0 && !t.borders.Right { + center = SPACE + } + + // Print first junction + if i == 0 { + fmt.Fprint(t.out, center) + } + + // Pad With space of length is 0 + if length == 0 { + pad = SPACE + } + // Ignore left space of it has printed before + if hasPrinted || t.borders.Left { + pad = t.pRow + center = t.pCenter + } + + // Change Center start position + if center == SPACE { + if i < end && len(t.footers[i+1][0]) != 0 { + center = t.pCenter + } + } + + // Print the footer + fmt.Fprintf(t.out, "%s%s%s%s", + pad, + strings.Repeat(string(pad), v), + pad, + center) + + } + + fmt.Fprint(t.out, t.newLine) +} + +// Print caption text +func (t Table) printCaption() { + width := t.getTableWidth() + paragraph, _ := WrapString(t.captionText, width) + for linecount := 0; linecount < len(paragraph); linecount++ { + fmt.Fprintln(t.out, paragraph[linecount]) + } +} + +// Calculate the total number of characters in a row +func (t Table) getTableWidth() int { + var chars int + for _, v := range t.cs { + chars += v + } + + // Add chars, spaces, seperators to calculate the total width of the table. + // ncols := t.colSize + // spaces := ncols * 2 + // seps := ncols + 1 + + return (chars + (3 * t.colSize) + 2) +} + +func (t Table) printRows() { + for i, lines := range t.lines { + t.printRow(lines, i) + } +} + +func (t *Table) fillAlignment(num int) { + if len(t.columnsAlign) < num { + t.columnsAlign = make([]int, num) + for i := range t.columnsAlign { + t.columnsAlign[i] = t.align + } + } +} + +// Print Row Information +// Adjust column alignment based on type + +func (t *Table) printRow(columns [][]string, rowIdx int) { + // Get Maximum Height + max := t.rs[rowIdx] + total := len(columns) + + // TODO Fix uneven col size + // if total < t.colSize { + // for n := t.colSize - total; n < t.colSize ; n++ { + // columns = append(columns, []string{SPACE}) + // t.cs[n] = t.mW + // } + //} + + // Pad Each Height + pads := []int{} + + // Checking for ANSI escape sequences for columns + is_esc_seq := false + if len(t.columnsParams) > 0 { + is_esc_seq = true + } + t.fillAlignment(total) + + for i, line := range columns { + length := len(line) + pad := max - length + pads = append(pads, pad) + for n := 0; n < pad; n++ { + columns[i] = append(columns[i], " ") + } + } + //fmt.Println(max, "\n") + for x := 0; x < max; x++ { + for y := 0; y < total; y++ { + + // Check if border is set + fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn)) + + fmt.Fprintf(t.out, SPACE) + str := columns[y][x] + + // Embedding escape sequence with column value + if is_esc_seq { + str = format(str, t.columnsParams[y]) + } + + // This would print alignment + // Default alignment would use multiple configuration + switch t.columnsAlign[y] { + case ALIGN_CENTER: // + fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y])) + case ALIGN_RIGHT: + fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y])) + case ALIGN_LEFT: + fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + default: + if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) { + fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y])) + } else { + fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + + // TODO Custom alignment per column + //if max == 1 || pads[y] > 0 { + // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y])) + //} else { + // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y])) + //} + + } + } + fmt.Fprintf(t.out, SPACE) + } + // Check if border is set + // Replace with space if not set + fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE)) + fmt.Fprint(t.out, t.newLine) + } + + if t.rowLine { + t.printLine(true) + } +} + +// Print the rows of the table and merge the cells that are identical +func (t *Table) printRowsMergeCells() { + var previousLine []string + var displayCellBorder []bool + var tmpWriter bytes.Buffer + for i, lines := range t.lines { + // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above + previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine) + if i > 0 { //We don't need to print borders above first line + if t.rowLine { + t.printLineOptionalCellSeparators(true, displayCellBorder) + } + } + tmpWriter.WriteTo(t.out) + } + //Print the end of the table + if t.rowLine { + t.printLine(true) + } +} + +// Print Row Information to a writer and merge identical cells. +// Adjust column alignment based on type + +func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) { + // Get Maximum Height + max := t.rs[rowIdx] + total := len(columns) + + // Pad Each Height + pads := []int{} + + for i, line := range columns { + length := len(line) + pad := max - length + pads = append(pads, pad) + for n := 0; n < pad; n++ { + columns[i] = append(columns[i], " ") + } + } + + var displayCellBorder []bool + t.fillAlignment(total) + for x := 0; x < max; x++ { + for y := 0; y < total; y++ { + + // Check if border is set + fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn)) + + fmt.Fprintf(writer, SPACE) + + str := columns[y][x] + + if t.autoMergeCells { + //Store the full line to merge mutli-lines cells + fullLine := strings.Join(columns[y], " ") + if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" { + // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty. + displayCellBorder = append(displayCellBorder, false) + str = "" + } else { + // First line or different content, keep the content and print the cell border + displayCellBorder = append(displayCellBorder, true) + } + } + + // This would print alignment + // Default alignment would use multiple configuration + switch t.columnsAlign[y] { + case ALIGN_CENTER: // + fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y])) + case ALIGN_RIGHT: + fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y])) + case ALIGN_LEFT: + fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y])) + default: + if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) { + fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y])) + } else { + fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y])) + } + } + fmt.Fprintf(writer, SPACE) + } + // Check if border is set + // Replace with space if not set + fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE)) + fmt.Fprint(writer, t.newLine) + } + + //The new previous line is the current one + previousLine = make([]string, total) + for y := 0; y < total; y++ { + previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells + } + //Returns the newly added line and wether or not a border should be displayed above. + return previousLine, displayCellBorder +} + +func (t *Table) parseDimension(str string, colKey, rowKey int) []string { + var ( + raw []string + maxWidth int + ) + + raw = getLines(str) + maxWidth = 0 + for _, line := range raw { + if w := DisplayWidth(line); w > maxWidth { + maxWidth = w + } + } + + // If wrapping, ensure that all paragraphs in the cell fit in the + // specified width. + if t.autoWrap { + // If there's a maximum allowed width for wrapping, use that. + if maxWidth > t.mW { + maxWidth = t.mW + } + + // In the process of doing so, we need to recompute maxWidth. This + // is because perhaps a word in the cell is longer than the + // allowed maximum width in t.mW. + newMaxWidth := maxWidth + newRaw := make([]string, 0, len(raw)) + + if t.reflowText { + // Make a single paragraph of everything. + raw = []string{strings.Join(raw, " ")} + } + for i, para := range raw { + paraLines, _ := WrapString(para, maxWidth) + for _, line := range paraLines { + if w := DisplayWidth(line); w > newMaxWidth { + newMaxWidth = w + } + } + if i > 0 { + newRaw = append(newRaw, " ") + } + newRaw = append(newRaw, paraLines...) + } + raw = newRaw + maxWidth = newMaxWidth + } + + // Store the new known maximum width. + v, ok := t.cs[colKey] + if !ok || v < maxWidth || v == 0 { + t.cs[colKey] = maxWidth + } + + // Remember the number of lines for the row printer. + h := len(raw) + v, ok = t.rs[rowKey] + + if !ok || v < h || v == 0 { + t.rs[rowKey] = h + } + //fmt.Printf("Raw %+v %d\n", raw, len(raw)) + return raw +} diff --git a/vendor/github.com/olekukonko/tablewriter/table_test.go b/vendor/github.com/olekukonko/tablewriter/table_test.go new file mode 100644 index 00000000..fb174d9a --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/table_test.go @@ -0,0 +1,1120 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "bytes" + "fmt" + "io" + "os" + "reflect" + "strings" + "testing" +) + +func ExampleShort() { + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, + } + + table := NewWriter(os.Stdout) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + for _, v := range data { + table.Append(v) + } + table.Render() + + // Output: +------+-----------------------+--------+ + // | NAME | SIGN | RATING | + // +------+-----------------------+--------+ + // | A | The Good | 500 | + // | B | The Very very Bad Man | 288 | + // | C | The Ugly | 120 | + // | D | The Gopher | 800 | + // +------+-----------------------+--------+ +} + +func ExampleLong() { + data := [][]string{ + []string{"Learn East has computers with adapted keyboards with enlarged print etc", " Some Data ", " Another Data"}, + []string{"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"}, + } + + table := NewWriter(os.Stdout) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + table.SetCenterSeparator("*") + table.SetRowSeparator("=") + + for _, v := range data { + table.Append(v) + } + table.Render() +} + +func ExampleCSV() { + table, _ := NewCSV(os.Stdout, "test.csv", true) + table.SetCenterSeparator("*") + table.SetRowSeparator("=") + + table.Render() + + // Output: *============*===========*=========* + // | FIRST NAME | LAST NAME | SSN | + // *============*===========*=========* + // | John | Barry | 123456 | + // | Kathy | Smith | 687987 | + // | Bob | McCornick | 3979870 | + // *============*===========*=========* +} + +// TestNumLines to test the numbers of lines +func TestNumLines(t *testing.T) { + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, + } + + buf := &bytes.Buffer{} + table := NewWriter(buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + for i, v := range data { + table.Append(v) + if i+1 != table.NumLines() { + t.Errorf("Number of lines failed\ngot:\n[%d]\nwant:\n[%d]\n", table.NumLines(), i+1) + } + } + + if len(data) != table.NumLines() { + t.Errorf("Number of lines failed\ngot:\n[%d]\nwant:\n[%d]\n", table.NumLines(), len(data)) + } +} + +func TestCSVInfo(t *testing.T) { + buf := &bytes.Buffer{} + table, err := NewCSV(buf, "test_info.csv", true) + if err != nil { + t.Error(err) + return + } + table.SetAlignment(ALIGN_LEFT) + table.SetBorder(false) + table.Render() + + got := buf.String() + want := ` FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA ++----------+--------------+------+-----+---------+----------------+ + user_id | smallint(5) | NO | PRI | NULL | auto_increment + username | varchar(10) | NO | | NULL | + password | varchar(100) | NO | | NULL | +` + + if got != want { + t.Errorf("CSV info failed\ngot:\n[%s]\nwant:\n[%s]\n", got, want) + } +} + +func TestCSVSeparator(t *testing.T) { + buf := &bytes.Buffer{} + table, err := NewCSV(buf, "test.csv", true) + if err != nil { + t.Error(err) + return + } + table.SetRowLine(true) + table.SetCenterSeparator("+") + table.SetColumnSeparator("|") + table.SetRowSeparator("-") + table.SetAlignment(ALIGN_LEFT) + table.Render() + + want := `+------------+-----------+---------+ +| FIRST NAME | LAST NAME | SSN | ++------------+-----------+---------+ +| John | Barry | 123456 | ++------------+-----------+---------+ +| Kathy | Smith | 687987 | ++------------+-----------+---------+ +| Bob | McCornick | 3979870 | ++------------+-----------+---------+ +` + + got := buf.String() + if got != want { + t.Errorf("CSV info failed\ngot:\n[%s]\nwant:\n[%s]\n", got, want) + } +} + +func TestNoBorder(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"", " (empty)\n (empty)", "", ""}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, + []string{"1/4/2014", " (Discount)", "2233", "-$1.00"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer + table.SetBorder(false) // Set Border to false + table.AppendBulk(data) // Add Bulk Data + table.Render() + + want := ` DATE | DESCRIPTION | CV2 | AMOUNT ++----------+--------------------------+-------+---------+ + 1/1/2014 | Domain name | 2233 | $10.98 + 1/1/2014 | January Hosting | 2233 | $54.95 + | (empty) | | + | (empty) | | + 1/4/2014 | February Hosting | 2233 | $51.00 + 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 + 1/4/2014 | (Discount) | 2233 | -$1.00 ++----------+--------------------------+-------+---------+ + TOTAL | $145 93 + +-------+---------+ +` + got := buf.String() + if got != want { + t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestWithBorder(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"", " (empty)\n (empty)", "", ""}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, + []string{"1/4/2014", " (Discount)", "2233", "-$1.00"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer + table.AppendBulk(data) // Add Bulk Data + table.Render() + + want := `+----------+--------------------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+--------------------------+-------+---------+ +| 1/1/2014 | Domain name | 2233 | $10.98 | +| 1/1/2014 | January Hosting | 2233 | $54.95 | +| | (empty) | | | +| | (empty) | | | +| 1/4/2014 | February Hosting | 2233 | $51.00 | +| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | +| 1/4/2014 | (Discount) | 2233 | -$1.00 | ++----------+--------------------------+-------+---------+ +| TOTAL | $145 93 | ++----------+--------------------------+-------+---------+ +` + got := buf.String() + if got != want { + t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintingInMarkdown(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.AppendBulk(data) // Add Bulk Data + table.SetBorders(Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.Render() + + want := `| DATE | DESCRIPTION | CV2 | AMOUNT | +|----------|--------------------------|------|--------| +| 1/1/2014 | Domain name | 2233 | $10.98 | +| 1/1/2014 | January Hosting | 2233 | $54.95 | +| 1/4/2014 | February Hosting | 2233 | $51.00 | +| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 | +` + got := buf.String() + if got != want { + t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintHeading(t *testing.T) { + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.printHeading() + want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | ++---+---+---+---+---+---+---+---+---+---+---+---+ +` + got := buf.String() + if got != want { + t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintHeadingWithoutAutoFormat(t *testing.T) { + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.SetAutoFormatHeaders(false) + table.printHeading() + want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | ++---+---+---+---+---+---+---+---+---+---+---+---+ +` + got := buf.String() + if got != want { + t.Errorf("header rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintFooter(t *testing.T) { + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.printFooter() + want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | ++---+---+---+---+---+---+---+---+---+---+---+---+ +` + got := buf.String() + if got != want { + t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintFooterWithoutAutoFormat(t *testing.T) { + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetAutoFormatHeaders(false) + table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"}) + table.printFooter() + want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | ++---+---+---+---+---+---+---+---+---+---+---+---+ +` + got := buf.String() + if got != want { + t.Errorf("footer rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintShortCaption(t *testing.T) { + var buf bytes.Buffer + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, + } + + table := NewWriter(&buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + table.SetCaption(true, "Short caption.") + + for _, v := range data { + table.Append(v) + } + table.Render() + + want := `+------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| B | The Very very Bad Man | 288 | +| C | The Ugly | 120 | +| D | The Gopher | 800 | ++------+-----------------------+--------+ +Short caption. +` + got := buf.String() + if got != want { + t.Errorf("long caption for short example rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintLongCaptionWithShortExample(t *testing.T) { + var buf bytes.Buffer + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, + } + + table := NewWriter(&buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.") + + for _, v := range data { + table.Append(v) + } + table.Render() + + want := `+------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| B | The Very very Bad Man | 288 | +| C | The Ugly | 120 | +| D | The Gopher | 800 | ++------+-----------------------+--------+ +This is a very long caption. The text +should wrap. If not, we have a problem +that needs to be solved. +` + got := buf.String() + if got != want { + t.Errorf("long caption for short example rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintCaptionWithFooter(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + []string{"1/1/2014", "January Hosting", "2233", "$54.95"}, + []string{"1/4/2014", "February Hosting", "2233", "$51.00"}, + []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer + table.SetCaption(true, "This is a very long caption. The text should wrap to the width of the table.") // Add caption + table.SetBorder(false) // Set Border to false + table.AppendBulk(data) // Add Bulk Data + table.Render() + + want := ` DATE | DESCRIPTION | CV2 | AMOUNT ++----------+--------------------------+-------+---------+ + 1/1/2014 | Domain name | 2233 | $10.98 + 1/1/2014 | January Hosting | 2233 | $54.95 + 1/4/2014 | February Hosting | 2233 | $51.00 + 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 ++----------+--------------------------+-------+---------+ + TOTAL | $146 93 + +-------+---------+ +This is a very long caption. The text should wrap to the +width of the table. +` + got := buf.String() + if got != want { + t.Errorf("border table rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestPrintLongCaptionWithLongExample(t *testing.T) { + var buf bytes.Buffer + data := [][]string{ + []string{"Learn East has computers with adapted keyboards with enlarged print etc", "Some Data", "Another Data"}, + []string{"Instead of lining up the letters all", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards"}, + } + + table := NewWriter(&buf) + table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.") + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + for _, v := range data { + table.Append(v) + } + table.Render() + + want := `+--------------------------------+--------------------------------+-------------------------------+ +| NAME | SIGN | RATING | ++--------------------------------+--------------------------------+-------------------------------+ +| Learn East has computers | Some Data | Another Data | +| with adapted keyboards with | | | +| enlarged print etc | | | +| Instead of lining up the | the way across, he splits the | Like most ergonomic keyboards | +| letters all | keyboard in two | | ++--------------------------------+--------------------------------+-------------------------------+ +This is a very long caption. The text should wrap. If not, we have a problem that needs to be +solved. +` + got := buf.String() + if got != want { + t.Errorf("long caption for long example rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func Example_autowrap() { + var multiline = `A multiline +string with some lines being really long.` + + const ( + testRow = iota + testHeader + testFooter + testFooter2 + ) + for mode := testRow; mode <= testFooter2; mode++ { + for _, autoFmt := range []bool{false, true} { + if mode == testRow && autoFmt { + // Nothing special to test, skip + continue + } + for _, autoWrap := range []bool{false, true} { + for _, reflow := range []bool{false, true} { + if !autoWrap && reflow { + // Invalid configuration, skip + continue + } + fmt.Println("mode", mode, "autoFmt", autoFmt, "autoWrap", autoWrap, "reflow", reflow) + t := NewWriter(os.Stdout) + t.SetAutoFormatHeaders(autoFmt) + t.SetAutoWrapText(autoWrap) + t.SetReflowDuringAutoWrap(reflow) + if mode == testHeader { + t.SetHeader([]string{"woo", multiline}) + } else { + t.SetHeader([]string{"woo", "waa"}) + } + if mode == testRow { + t.Append([]string{"woo", multiline}) + } else { + t.Append([]string{"woo", "waa"}) + } + if mode == testFooter { + t.SetFooter([]string{"woo", multiline}) + } else if mode == testFooter2 { + t.SetFooter([]string{"", multiline}) + } else { + t.SetFooter([]string{"woo", "waa"}) + } + t.Render() + } + } + } + fmt.Println() + } + + // Output: + // mode 0 autoFmt false autoWrap false reflow false + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | woo | A multiline | + // | | string with some lines being really long. | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // mode 0 autoFmt false autoWrap true reflow false + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | A multiline | + // | | | + // | | string with some lines being | + // | | really long. | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // mode 0 autoFmt false autoWrap true reflow true + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | A multiline string with some | + // | | lines being really long. | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // + // mode 1 autoFmt false autoWrap false reflow false + // +-----+-------------------------------------------+ + // | woo | A multiline | + // | | string with some lines being really long. | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // mode 1 autoFmt false autoWrap true reflow false + // +-----+--------------------------------+ + // | woo | A multiline | + // | | | + // | | string with some lines being | + // | | really long. | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // mode 1 autoFmt false autoWrap true reflow true + // +-----+--------------------------------+ + // | woo | A multiline string with some | + // | | lines being really long. | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // mode 1 autoFmt true autoWrap false reflow false + // +-----+-------------------------------------------+ + // | WOO | A MULTILINE | + // | | STRING WITH SOME LINES BEING REALLY LONG | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | WOO | WAA | + // +-----+-------------------------------------------+ + // mode 1 autoFmt true autoWrap true reflow false + // +-----+--------------------------------+ + // | WOO | A MULTILINE | + // | | | + // | | STRING WITH SOME LINES BEING | + // | | REALLY LONG | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // mode 1 autoFmt true autoWrap true reflow true + // +-----+--------------------------------+ + // | WOO | A MULTILINE STRING WITH SOME | + // | | LINES BEING REALLY LONG | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // + // mode 2 autoFmt false autoWrap false reflow false + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | woo | A multiline | + // | | string with some lines being really long. | + // +-----+-------------------------------------------+ + // mode 2 autoFmt false autoWrap true reflow false + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | A multiline | + // | | | + // | | string with some lines being | + // | | really long. | + // +-----+--------------------------------+ + // mode 2 autoFmt false autoWrap true reflow true + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | A multiline string with some | + // | | lines being really long. | + // +-----+--------------------------------+ + // mode 2 autoFmt true autoWrap false reflow false + // +-----+-------------------------------------------+ + // | WOO | WAA | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | WOO | A MULTILINE | + // | | STRING WITH SOME LINES BEING REALLY LONG | + // +-----+-------------------------------------------+ + // mode 2 autoFmt true autoWrap true reflow false + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | WOO | A MULTILINE | + // | | | + // | | STRING WITH SOME LINES BEING | + // | | REALLY LONG | + // +-----+--------------------------------+ + // mode 2 autoFmt true autoWrap true reflow true + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | WOO | A MULTILINE STRING WITH SOME | + // | | LINES BEING REALLY LONG | + // +-----+--------------------------------+ + // + // mode 3 autoFmt false autoWrap false reflow false + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | A multiline | + // | string with some lines being really long. | + // +-----+-------------------------------------------+ + // mode 3 autoFmt false autoWrap true reflow false + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | A multiline | + // | | + // | string with some lines being | + // | really long. | + // +-----+--------------------------------+ + // mode 3 autoFmt false autoWrap true reflow true + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | A multiline string with some | + // | lines being really long. | + // +-----+--------------------------------+ + // mode 3 autoFmt true autoWrap false reflow false + // +-----+-------------------------------------------+ + // | WOO | WAA | + // +-----+-------------------------------------------+ + // | woo | waa | + // +-----+-------------------------------------------+ + // | A MULTILINE | + // | STRING WITH SOME LINES BEING REALLY LONG | + // +-----+-------------------------------------------+ + // mode 3 autoFmt true autoWrap true reflow false + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | A MULTILINE | + // | | + // | STRING WITH SOME LINES BEING | + // | REALLY LONG | + // +-----+--------------------------------+ + // mode 3 autoFmt true autoWrap true reflow true + // +-----+--------------------------------+ + // | WOO | WAA | + // +-----+--------------------------------+ + // | woo | waa | + // +-----+--------------------------------+ + // | A MULTILINE STRING WITH SOME | + // | LINES BEING REALLY LONG | + // +-----+--------------------------------+ +} + +func TestPrintLine(t *testing.T) { + header := make([]string, 12) + val := " " + want := "" + for i := range header { + header[i] = val + want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1)) + val = val + " " + } + want = want + "+" + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader(header) + table.printLine(false) + got := buf.String() + if got != want { + t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestAnsiStrip(t *testing.T) { + header := make([]string, 12) + val := " " + want := "" + for i := range header { + header[i] = "\033[43;30m" + val + "\033[00m" + want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1)) + val = val + " " + } + want = want + "+" + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader(header) + table.printLine(false) + got := buf.String() + if got != want { + t.Errorf("line rendering failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func NewCustomizedTable(out io.Writer) *Table { + table := NewWriter(out) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetRowSeparator("") + table.SetBorder(false) + table.SetAlignment(ALIGN_LEFT) + table.SetHeader([]string{}) + return table +} + +func TestSubclass(t *testing.T) { + buf := new(bytes.Buffer) + table := NewCustomizedTable(buf) + + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"B", "The Very very Bad Man", "288"}, + []string{"C", "The Ugly", "120"}, + []string{"D", "The Gopher", "800"}, + } + + for _, v := range data { + table.Append(v) + } + table.Render() + + output := string(buf.Bytes()) + want := ` A The Good 500 + B The Very very Bad Man 288 + C The Ugly 120 + D The Gopher 800 +` + if output != want { + t.Error(fmt.Sprintf("Unexpected output '%v' != '%v'", output, want)) + } +} + +func TestAutoMergeRows(t *testing.T) { + data := [][]string{ + []string{"A", "The Good", "500"}, + []string{"A", "The Very very Bad Man", "288"}, + []string{"B", "The Very very Bad Man", "120"}, + []string{"B", "The Very very Bad Man", "200"}, + } + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + for _, v := range data { + table.Append(v) + } + table.SetAutoMergeCells(true) + table.Render() + want := `+------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | +| | The Very very Bad Man | 288 | +| B | | 120 | +| | | 200 | ++------+-----------------------+--------+ +` + got := buf.String() + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } + + buf.Reset() + table = NewWriter(&buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + for _, v := range data { + table.Append(v) + } + table.SetAutoMergeCells(true) + table.SetRowLine(true) + table.Render() + want = `+------+-----------------------+--------+ +| NAME | SIGN | RATING | ++------+-----------------------+--------+ +| A | The Good | 500 | ++ +-----------------------+--------+ +| | The Very very Bad Man | 288 | ++------+ +--------+ +| B | | 120 | ++ + +--------+ +| | | 200 | ++------+-----------------------+--------+ +` + got = buf.String() + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } + + buf.Reset() + table = NewWriter(&buf) + table.SetHeader([]string{"Name", "Sign", "Rating"}) + + dataWithlongText := [][]string{ + []string{"A", "The Good", "500"}, + []string{"A", "The Very very very very very Bad Man", "288"}, + []string{"B", "The Very very very very very Bad Man", "120"}, + []string{"C", "The Very very Bad Man", "200"}, + } + table.AppendBulk(dataWithlongText) + table.SetAutoMergeCells(true) + table.SetRowLine(true) + table.Render() + want = `+------+--------------------------------+--------+ +| NAME | SIGN | RATING | ++------+--------------------------------+--------+ +| A | The Good | 500 | ++------+--------------------------------+--------+ +| A | The Very very very very very | 288 | +| | Bad Man | | ++------+ +--------+ +| B | | 120 | +| | | | ++------+--------------------------------+--------+ +| C | The Very very Bad Man | 200 | ++------+--------------------------------+--------+ +` + got = buf.String() + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestClearRows(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer + table.AppendBulk(data) // Add Bulk Data + table.Render() + + originalWant := `+----------+-------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+-------------+-------+---------+ +| 1/1/2014 | Domain name | 2233 | $10.98 | ++----------+-------------+-------+---------+ +| TOTAL | $145 93 | ++----------+-------------+-------+---------+ +` + want := originalWant + + got := buf.String() + if got != want { + t.Errorf("table clear rows failed\ngot:\n%s\nwant:\n%s\n", got, want) + } + + buf.Reset() + table.ClearRows() + table.Render() + + want = `+----------+-------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+-------------+-------+---------+ ++----------+-------------+-------+---------+ +| TOTAL | $145 93 | ++----------+-------------+-------+---------+ +` + + got = buf.String() + if got != want { + t.Errorf("table clear failed\ngot:\n%s\nwant:\n%s\n", got, want) + } + + buf.Reset() + table.AppendBulk(data) // Add Bulk Data + table.Render() + + want = `+----------+-------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+-------------+-------+---------+ +| 1/1/2014 | Domain name | 2233 | $10.98 | ++----------+-------------+-------+---------+ +| TOTAL | $145 93 | ++----------+-------------+-------+---------+ +` + + got = buf.String() + if got != want { + t.Errorf("table clear rows failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestClearFooters(t *testing.T) { + data := [][]string{ + []string{"1/1/2014", "Domain name", "2233", "$10.98"}, + } + + var buf bytes.Buffer + table := NewWriter(&buf) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Date", "Description", "CV2", "Amount"}) + table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer + table.AppendBulk(data) // Add Bulk Data + table.Render() + + buf.Reset() + table.ClearFooter() + table.Render() + + want := `+----------+-------------+-------+---------+ +| DATE | DESCRIPTION | CV2 | AMOUNT | ++----------+-------------+-------+---------+ +| 1/1/2014 | Domain name | 2233 | $10.98 | ++----------+-------------+-------+---------+ +` + + got := buf.String() + if got != want { + t.Errorf("table clear rows failed\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestMoreDataColumnsThanHeaders(t *testing.T) { + var ( + buf = &bytes.Buffer{} + table = NewWriter(buf) + header = []string{"A", "B", "C"} + data = [][]string{ + []string{"a", "b", "c", "d"}, + []string{"1", "2", "3", "4"}, + } + want = `+---+---+---+---+ +| A | B | C | | ++---+---+---+---+ +| a | b | c | d | +| 1 | 2 | 3 | 4 | ++---+---+---+---+ +` + ) + table.SetHeader(header) + // table.SetFooter(ctx.tableCtx.footer) + table.AppendBulk(data) + table.Render() + + got := buf.String() + + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestMoreFooterColumnsThanHeaders(t *testing.T) { + var ( + buf = &bytes.Buffer{} + table = NewWriter(buf) + header = []string{"A", "B", "C"} + data = [][]string{ + []string{"a", "b", "c", "d"}, + []string{"1", "2", "3", "4"}, + } + footer = []string{"a", "b", "c", "d", "e"} + want = `+---+---+---+---+---+ +| A | B | C | | | ++---+---+---+---+---+ +| a | b | c | d | +| 1 | 2 | 3 | 4 | ++---+---+---+---+---+ +| A | B | C | D | E | ++---+---+---+---+---+ +` + ) + table.SetHeader(header) + table.SetFooter(footer) + table.AppendBulk(data) + table.Render() + + got := buf.String() + + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestSetColMinWidth(t *testing.T) { + var ( + buf = &bytes.Buffer{} + table = NewWriter(buf) + header = []string{"AAA", "BBB", "CCC"} + data = [][]string{ + []string{"a", "b", "c"}, + []string{"1", "2", "3"}, + } + footer = []string{"a", "b", "cccc"} + want = `+-----+-----+-------+ +| AAA | BBB | CCC | ++-----+-----+-------+ +| a | b | c | +| 1 | 2 | 3 | ++-----+-----+-------+ +| A | B | CCCC | ++-----+-----+-------+ +` + ) + table.SetHeader(header) + table.SetFooter(footer) + table.AppendBulk(data) + table.SetColMinWidth(2, 5) + table.Render() + + got := buf.String() + + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } +} + +func TestWrapString(t *testing.T) { + want := []string{"ああああああああああああああああああああああああ", "あああああああ"} + got, _ := WrapString("ああああああああああああああああああああああああ あああああああ", 55) + if !reflect.DeepEqual(got, want) { + t.Errorf("\ngot:\n%v\nwant:\n%v\n", got, want) + } +} + +func TestCustomAlign(t *testing.T) { + var ( + buf = &bytes.Buffer{} + table = NewWriter(buf) + header = []string{"AAA", "BBB", "CCC"} + data = [][]string{ + []string{"a", "b", "c"}, + []string{"1", "2", "3"}, + } + footer = []string{"a", "b", "cccc"} + want = `+-----+-----+-------+ +| AAA | BBB | CCC | ++-----+-----+-------+ +| a | b | c | +| 1 | 2 | 3 | ++-----+-----+-------+ +| A | B | CCCC | ++-----+-----+-------+ +` + ) + table.SetHeader(header) + table.SetFooter(footer) + table.AppendBulk(data) + table.SetColMinWidth(2, 5) + table.SetColumnAlignment([]int{ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT}) + table.Render() + + got := buf.String() + + if got != want { + t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want) + } +} diff --git a/vendor/github.com/olekukonko/tablewriter/table_with_color.go b/vendor/github.com/olekukonko/tablewriter/table_with_color.go new file mode 100644 index 00000000..5a4a53ec --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/table_with_color.go @@ -0,0 +1,134 @@ +package tablewriter + +import ( + "fmt" + "strconv" + "strings" +) + +const ESC = "\033" +const SEP = ";" + +const ( + BgBlackColor int = iota + 40 + BgRedColor + BgGreenColor + BgYellowColor + BgBlueColor + BgMagentaColor + BgCyanColor + BgWhiteColor +) + +const ( + FgBlackColor int = iota + 30 + FgRedColor + FgGreenColor + FgYellowColor + FgBlueColor + FgMagentaColor + FgCyanColor + FgWhiteColor +) + +const ( + BgHiBlackColor int = iota + 100 + BgHiRedColor + BgHiGreenColor + BgHiYellowColor + BgHiBlueColor + BgHiMagentaColor + BgHiCyanColor + BgHiWhiteColor +) + +const ( + FgHiBlackColor int = iota + 90 + FgHiRedColor + FgHiGreenColor + FgHiYellowColor + FgHiBlueColor + FgHiMagentaColor + FgHiCyanColor + FgHiWhiteColor +) + +const ( + Normal = 0 + Bold = 1 + UnderlineSingle = 4 + Italic +) + +type Colors []int + +func startFormat(seq string) string { + return fmt.Sprintf("%s[%sm", ESC, seq) +} + +func stopFormat() string { + return fmt.Sprintf("%s[%dm", ESC, Normal) +} + +// Making the SGR (Select Graphic Rendition) sequence. +func makeSequence(codes []int) string { + codesInString := []string{} + for _, code := range codes { + codesInString = append(codesInString, strconv.Itoa(code)) + } + return strings.Join(codesInString, SEP) +} + +// Adding ANSI escape sequences before and after string +func format(s string, codes interface{}) string { + var seq string + + switch v := codes.(type) { + + case string: + seq = v + case []int: + seq = makeSequence(v) + default: + return s + } + + if len(seq) == 0 { + return s + } + return startFormat(seq) + s + stopFormat() +} + +// Adding header colors (ANSI codes) +func (t *Table) SetHeaderColor(colors ...Colors) { + if t.colSize != len(colors) { + panic("Number of header colors must be equal to number of headers.") + } + for i := 0; i < len(colors); i++ { + t.headerParams = append(t.headerParams, makeSequence(colors[i])) + } +} + +// Adding column colors (ANSI codes) +func (t *Table) SetColumnColor(colors ...Colors) { + if t.colSize != len(colors) { + panic("Number of column colors must be equal to number of headers.") + } + for i := 0; i < len(colors); i++ { + t.columnsParams = append(t.columnsParams, makeSequence(colors[i])) + } +} + +// Adding column colors (ANSI codes) +func (t *Table) SetFooterColor(colors ...Colors) { + if len(t.footers) != len(colors) { + panic("Number of footer colors must be equal to number of footer.") + } + for i := 0; i < len(colors); i++ { + t.footerParams = append(t.footerParams, makeSequence(colors[i])) + } +} + +func Color(colors ...int) []int { + return colors +} diff --git a/vendor/github.com/olekukonko/tablewriter/test.csv b/vendor/github.com/olekukonko/tablewriter/test.csv new file mode 100644 index 00000000..1609327e --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/test.csv @@ -0,0 +1,4 @@ +first_name,last_name,ssn +John,Barry,123456 +Kathy,Smith,687987 +Bob,McCornick,3979870 \ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/test_info.csv b/vendor/github.com/olekukonko/tablewriter/test_info.csv new file mode 100644 index 00000000..e4c40e98 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/test_info.csv @@ -0,0 +1,4 @@ +Field,Type,Null,Key,Default,Extra +user_id,smallint(5),NO,PRI,NULL,auto_increment +username,varchar(10),NO,,NULL, +password,varchar(100),NO,,NULL, \ No newline at end of file diff --git a/vendor/github.com/olekukonko/tablewriter/util.go b/vendor/github.com/olekukonko/tablewriter/util.go new file mode 100644 index 00000000..dea3c7aa --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/util.go @@ -0,0 +1,78 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "math" + "regexp" + "strings" + + "github.com/mattn/go-runewidth" +) + +var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]") + +func DisplayWidth(str string) int { + return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, "")) +} + +// Simple Condition for string +// Returns value based on condition +func ConditionString(cond bool, valid, inValid string) string { + if cond { + return valid + } + return inValid +} + +// Format Table Header +// Replace _ , . and spaces +func Title(name string) string { + origLen := len(name) + name = strings.Replace(name, "_", " ", -1) + name = strings.Replace(name, ".", " ", -1) + name = strings.TrimSpace(name) + if len(name) == 0 && origLen > 0 { + // Keep at least one character. This is important to preserve + // empty lines in multi-line headers/footers. + name = " " + } + return strings.ToUpper(name) +} + +// Pad String +// Attempts to play string in the center +func Pad(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + gapLeft := int(math.Ceil(float64(gap / 2))) + gapRight := gap - gapLeft + return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight) + } + return s +} + +// Pad String Right position +// This would pace string at the left side fo the screen +func PadRight(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + return s + strings.Repeat(string(pad), gap) + } + return s +} + +// Pad String Left position +// This would pace string at the right side fo the screen +func PadLeft(s, pad string, width int) string { + gap := width - DisplayWidth(s) + if gap > 0 { + return strings.Repeat(string(pad), gap) + s + } + return s +} diff --git a/vendor/github.com/olekukonko/tablewriter/wrap.go b/vendor/github.com/olekukonko/tablewriter/wrap.go new file mode 100644 index 00000000..a092ee1f --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/wrap.go @@ -0,0 +1,99 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "math" + "strings" + + "github.com/mattn/go-runewidth" +) + +var ( + nl = "\n" + sp = " " +) + +const defaultPenalty = 1e5 + +// Wrap wraps s into a paragraph of lines of length lim, with minimal +// raggedness. +func WrapString(s string, lim int) ([]string, int) { + words := strings.Split(strings.Replace(s, nl, sp, -1), sp) + var lines []string + max := 0 + for _, v := range words { + max = runewidth.StringWidth(v) + if max > lim { + lim = max + } + } + for _, line := range WrapWords(words, 1, lim, defaultPenalty) { + lines = append(lines, strings.Join(line, sp)) + } + return lines, lim +} + +// WrapWords is the low-level line-breaking algorithm, useful if you need more +// control over the details of the text wrapping process. For most uses, +// WrapString will be sufficient and more convenient. +// +// WrapWords splits a list of words into lines with minimal "raggedness", +// treating each rune as one unit, accounting for spc units between adjacent +// words on each line, and attempting to limit lines to lim units. Raggedness +// is the total error over all lines, where error is the square of the +// difference of the length of the line and lim. Too-long lines (which only +// happen when a single word is longer than lim units) have pen penalty units +// added to the error. +func WrapWords(words []string, spc, lim, pen int) [][]string { + n := len(words) + + length := make([][]int, n) + for i := 0; i < n; i++ { + length[i] = make([]int, n) + length[i][i] = runewidth.StringWidth(words[i]) + for j := i + 1; j < n; j++ { + length[i][j] = length[i][j-1] + spc + runewidth.StringWidth(words[j]) + } + } + nbrk := make([]int, n) + cost := make([]int, n) + for i := range cost { + cost[i] = math.MaxInt32 + } + for i := n - 1; i >= 0; i-- { + if length[i][n-1] <= lim { + cost[i] = 0 + nbrk[i] = n + } else { + for j := i + 1; j < n; j++ { + d := lim - length[i][j-1] + c := d*d + cost[j] + if length[i][j-1] > lim { + c += pen // too-long lines get a worse penalty + } + if c < cost[i] { + cost[i] = c + nbrk[i] = j + } + } + } + } + var lines [][]string + i := 0 + for i < n { + lines = append(lines, words[i:nbrk[i]]) + i = nbrk[i] + } + return lines +} + +// getLines decomposes a multiline string into a slice of strings. +func getLines(s string) []string { + return strings.Split(s, nl) +} diff --git a/vendor/github.com/olekukonko/tablewriter/wrap_test.go b/vendor/github.com/olekukonko/tablewriter/wrap_test.go new file mode 100644 index 00000000..bec56b68 --- /dev/null +++ b/vendor/github.com/olekukonko/tablewriter/wrap_test.go @@ -0,0 +1,66 @@ +// Copyright 2014 Oleku Konko All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// This module is a Table Writer API for the Go Programming Language. +// The protocols were written in pure Go and works on windows and unix systems + +package tablewriter + +import ( + "strings" + "testing" + + "github.com/mattn/go-runewidth" +) + +var text = "The quick brown fox jumps over the lazy dog." + +func TestWrap(t *testing.T) { + exp := []string{ + "The", "quick", "brown", "fox", + "jumps", "over", "the", "lazy", "dog."} + + got, _ := WrapString(text, 6) + if len(exp) != len(got) { + t.Fail() + } +} + +func TestWrapOneLine(t *testing.T) { + exp := "The quick brown fox jumps over the lazy dog." + words, _ := WrapString(text, 500) + got := strings.Join(words, string(sp)) + if exp != got { + t.Errorf("expected: %q, got: %q", exp, got) + } +} + +func TestUnicode(t *testing.T) { + input := "Česká řeřicha" + var wordsUnicode []string + if runewidth.IsEastAsian() { + wordsUnicode, _ = WrapString(input, 14) + } else { + wordsUnicode, _ = WrapString(input, 13) + } + // input contains 13 (or 14 for CJK) runes, so it fits on one line. + if len(wordsUnicode) != 1 { + t.Fail() + } +} + +func TestDisplayWidth(t *testing.T) { + input := "Česká řeřicha" + want := 13 + if runewidth.IsEastAsian() { + want = 14 + } + if n := DisplayWidth(input); n != want { + t.Errorf("Wants: %d Got: %d", want, n) + } + input = "\033[43;30m" + input + "\033[00m" + if n := DisplayWidth(input); n != want { + t.Errorf("Wants: %d Got: %d", want, n) + } +}