Compare commits
No commits in common. "proxy" and "master" have entirely different histories.
1276 changed files with 572 additions and 356874 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
*.swp
|
|
||||||
config.toml
|
|
30
Dockerfile
30
Dockerfile
|
@ -1,30 +0,0 @@
|
||||||
FROM golang:1.8-alpine AS build
|
|
||||||
|
|
||||||
ARG TAG
|
|
||||||
ARG BUILD
|
|
||||||
RUN apk add -U git make curl build-base bash git autoconf automake libtool unzip file
|
|
||||||
RUN git clone https://github.com/google/protobuf /tmp/protobuf && \
|
|
||||||
cd /tmp/protobuf && \
|
|
||||||
./autogen.sh && \
|
|
||||||
./configure && make install
|
|
||||||
RUN go get github.com/LK4D4/vndr
|
|
||||||
RUN go get github.com/golang/protobuf/protoc-gen-go
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gofast
|
|
||||||
RUN go get github.com/gogo/protobuf/proto
|
|
||||||
RUN go get github.com/gogo/protobuf/gogoproto
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gogo
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gogofast
|
|
||||||
RUN go get github.com/stevvooe/protobuild
|
|
||||||
ENV APP element
|
|
||||||
ENV REPO ehazlett/$APP
|
|
||||||
WORKDIR /go/src/github.com/$REPO
|
|
||||||
COPY . /go/src/github.com/$REPO
|
|
||||||
RUN make TAG=$TAG BUILD=$BUILD generate build
|
|
||||||
|
|
||||||
FROM alpine:latest
|
|
||||||
WORKDIR /bin
|
|
||||||
ENV APP element
|
|
||||||
ENV REPO ehazlett/$APP
|
|
||||||
COPY --from=build /go/src/github.com/${REPO}/cmd/${APP}/${APP} /bin/${APP}
|
|
||||||
EXPOSE 8080
|
|
||||||
ENTRYPOINT ["/bin/element"]
|
|
|
@ -1,18 +0,0 @@
|
||||||
FROM golang:1.8-alpine AS build
|
|
||||||
|
|
||||||
RUN apk add -U git make curl build-base bash git autoconf automake libtool unzip file
|
|
||||||
RUN git clone https://github.com/google/protobuf /tmp/protobuf && \
|
|
||||||
cd /tmp/protobuf && \
|
|
||||||
./autogen.sh && \
|
|
||||||
./configure && make install
|
|
||||||
RUN go get github.com/LK4D4/vndr
|
|
||||||
RUN go get github.com/golang/protobuf/protoc-gen-go
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gofast
|
|
||||||
RUN go get github.com/gogo/protobuf/proto
|
|
||||||
RUN go get github.com/gogo/protobuf/gogoproto
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gogo
|
|
||||||
RUN go get github.com/gogo/protobuf/protoc-gen-gogofast
|
|
||||||
RUN go get github.com/stevvooe/protobuild
|
|
||||||
ENV APP element
|
|
||||||
ENV REPO ehazlett/$APP
|
|
||||||
WORKDIR /go/src/github.com/$REPO
|
|
52
Makefile
52
Makefile
|
@ -1,52 +1,4 @@
|
||||||
GOOS=linux
|
|
||||||
GOARCH=amd64
|
|
||||||
COMMIT=`git rev-parse --short HEAD`
|
|
||||||
APP=element
|
|
||||||
REPO?=ehazlett/$(APP)
|
|
||||||
TAG?=latest
|
|
||||||
BUILD?=-dev
|
|
||||||
PACKAGES=$(shell go list ./... | grep -v /vendor/)
|
PACKAGES=$(shell go list ./... | grep -v /vendor/)
|
||||||
|
|
||||||
all: image
|
protos:
|
||||||
|
protobuild --quiet ${PACKAGES}
|
||||||
build: build-static
|
|
||||||
|
|
||||||
generate:
|
|
||||||
@echo ${PACKAGES} | xargs protobuild
|
|
||||||
|
|
||||||
docker-generate:
|
|
||||||
@echo "** This uses a separate Dockerfile (Dockerfile.build) **"
|
|
||||||
@docker build -t element-dev -f Dockerfile.build .
|
|
||||||
@docker run -ti --rm -v $(PWD):/go/src/github.com/ehazlett/element element-dev ash -c "echo ${PACKAGES} | xargs /go/bin/protobuild"
|
|
||||||
|
|
||||||
build-app:
|
|
||||||
@echo " -> Building $(TAG)$(BUILD)"
|
|
||||||
@cd cmd/$(APP) && go build -v -ldflags "-w -X github.com/$(REPO)/version.GitCommit=$(COMMIT) -X github.com/$(REPO)/version.Build=$(BUILD)" .
|
|
||||||
@echo "Built $$(./cmd/$(APP)/$(APP) -v)"
|
|
||||||
|
|
||||||
build-static:
|
|
||||||
@echo " -> Building $(TAG)$(BUILD)"
|
|
||||||
@cd cmd/$(APP) && go build -v -a -tags "netgo static_build" -installsuffix netgo -ldflags "-w -X github.com/$(REPO)/version.GitCommit=$(COMMIT) -X github.com/$(REPO)/version.Build=$(BUILD)" .
|
|
||||||
@echo "Built $$(./cmd/$(APP)/$(APP) -v)"
|
|
||||||
|
|
||||||
image:
|
|
||||||
@docker build --build-arg TAG=$(TAG) --build-arg BUILD=$(BUILD) -t $(REPO):$(TAG) .
|
|
||||||
@echo "Image created: $(REPO):$(TAG)"
|
|
||||||
|
|
||||||
integration: image
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
test-integration:
|
|
||||||
@go test -v $(TEST_ARGS) ./test/integration/...
|
|
||||||
|
|
||||||
check:
|
|
||||||
@go vet -v ${PACKAGES}
|
|
||||||
@golint ${PACKAGES}
|
|
||||||
|
|
||||||
test:
|
|
||||||
@go test -v -cover -race $(TEST_ARGS) $$(glide novendor | grep -v ./test)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@rm cmd/$(APP)/$(APP)
|
|
||||||
|
|
||||||
.PHONY: deps build build-static build-app build-image generate image clean test
|
|
||||||
|
|
|
@ -1,46 +1,30 @@
|
||||||
version = "unstable"
|
version = "unstable"
|
||||||
|
|
||||||
# Generator defines which generator to go. The default is "go". This will be
|
|
||||||
# formatted into the --xxx_out flag provided to protoc. Below, we have an
|
|
||||||
# example that selects the ctrd vanity binary.
|
|
||||||
generator = "gogo"
|
generator = "gogo"
|
||||||
|
plugins = []
|
||||||
# Plugins allows one to specify one or more plugins for use in generation.
|
|
||||||
#
|
|
||||||
# The common example grpc is provided below.
|
|
||||||
plugins = ["grpc"]
|
|
||||||
|
|
||||||
# Control protoc include paths. Below are usually some good defaults, but feel
|
# Control protoc include paths. Below are usually some good defaults, but feel
|
||||||
# free to try it without them if it works for your project.
|
# free to try it without them if it works for your project.
|
||||||
[includes]
|
[includes]
|
||||||
# Include paths that will be added before all others. Typically, you want to
|
# Include paths that will be added before all others. Typically, you want to
|
||||||
# treat the root of the project as an include, but this may not be necessary.
|
# treat the root of the project as an include, but this may not be necessary.
|
||||||
#
|
# before = ["."]
|
||||||
# "." is included by default
|
|
||||||
before = ["."]
|
|
||||||
|
|
||||||
# Paths that should be treated as include roots in relation to the vendor
|
# Paths that should be treated as include roots in relation to the vendor
|
||||||
# directory. These will be calculated with the vendor directory nearest the
|
# directory. These will be calculated with the vendor directory nearest the
|
||||||
# target package.
|
# target package.
|
||||||
#
|
# vendored = ["github.com/gogo/protobuf"]
|
||||||
# With the example below, we import the gogo protobufs from the vendor
|
packages = ["github.com/gogo/protobuf"]
|
||||||
# directory.
|
|
||||||
#
|
|
||||||
# This is empty by default.
|
|
||||||
vendored = ["github.com/gogo/protobuf"]
|
|
||||||
|
|
||||||
# Paths that will be added untouched to the end of the includes. We use
|
# Paths that will be added untouched to the end of the includes. We use
|
||||||
# `/usr/local/include` to pickup the common install location of protobuf.
|
# `/usr/local/include` to pickup the common install location of protobuf.
|
||||||
# This is the default.
|
# This is the default.
|
||||||
# after = ["usr/local/include"]
|
after = ["/usr/local/include"]
|
||||||
|
|
||||||
# This section let's us map protobuf imports to Go packages. These will become
|
# This section maps protobuf imports to Go packages. These will become
|
||||||
# `-M` directives in the call to the go protobuf generator.
|
# `-M` directives in the call to the go protobuf generator.
|
||||||
#
|
|
||||||
# We have a few examples to map packages from the examples.
|
|
||||||
[packages]
|
[packages]
|
||||||
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto"
|
"gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto"
|
||||||
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types"
|
"google/protobuf/any.proto" = "github.com/gogo/protobuf/types"
|
||||||
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
"google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||||
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types"
|
"google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types"
|
||||||
"google/protobuf/empty.proto" = "github.com/gogo/protobuf/types"
|
"google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types"
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
# Element
|
# Element
|
||||||
|
Element handles simplified clustering among nodes. It handles
|
||||||
|
peer management (joins, expiry, etc) as well as exposing GRPC services and publishing
|
||||||
|
the GRPC endpoint address for each node throughout the group. This enables services
|
||||||
|
to be built using element and allow simple publishing of the GRPC endpoints
|
||||||
|
to other nodes for accessing the services.
|
||||||
|
|
113
agent.go
Normal file
113
agent.go
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/types"
|
||||||
|
"github.com/hashicorp/memberlist"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultInterval = time.Second * 10
|
||||||
|
nodeReconcileTimeout = defaultInterval * 3
|
||||||
|
nodeUpdateTimeout = defaultInterval / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnknownConnectionType = errors.New("unknown connection type")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Agent represents the node agent
|
||||||
|
type Agent struct {
|
||||||
|
*subscribers
|
||||||
|
|
||||||
|
config *Config
|
||||||
|
members *memberlist.Memberlist
|
||||||
|
peerUpdateChan chan bool
|
||||||
|
nodeEventChan chan *NodeEvent
|
||||||
|
registeredServices map[string]struct{}
|
||||||
|
memberConfig *memberlist.Config
|
||||||
|
state *State
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAgent returns a new node agent
|
||||||
|
func NewAgent(info *Peer, cfg *Config) (*Agent, error) {
|
||||||
|
var (
|
||||||
|
updateCh = make(chan bool, 64)
|
||||||
|
nodeEventCh = make(chan *NodeEvent, 64)
|
||||||
|
)
|
||||||
|
a := &Agent{
|
||||||
|
subscribers: newSubscribers(),
|
||||||
|
config: cfg,
|
||||||
|
peerUpdateChan: updateCh,
|
||||||
|
nodeEventChan: nodeEventCh,
|
||||||
|
state: &State{
|
||||||
|
Self: info,
|
||||||
|
Peers: make(map[string]*Peer),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mc, err := cfg.memberListConfig(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ml, err := memberlist.Create(mc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a.members = ml
|
||||||
|
a.memberConfig = mc
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncInterval returns the cluster sync interval
|
||||||
|
func (a *Agent) SyncInterval() time.Duration {
|
||||||
|
return a.memberConfig.PushPullInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the agent payload
|
||||||
|
func (a *Agent) Update(payload *types.Any) {
|
||||||
|
a.state.Self.Payload = payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSubscribers() *subscribers {
|
||||||
|
return &subscribers{
|
||||||
|
subs: make(map[chan *NodeEvent]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type subscribers struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
subs map[chan *NodeEvent]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe subscribes to the node event channel
|
||||||
|
func (s *subscribers) Subscribe() chan *NodeEvent {
|
||||||
|
ch := make(chan *NodeEvent, 64)
|
||||||
|
s.mu.Lock()
|
||||||
|
s.subs[ch] = struct{}{}
|
||||||
|
s.mu.Unlock()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe removes the channel from node events
|
||||||
|
func (s *subscribers) Unsubscribe(ch chan *NodeEvent) {
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.subs, ch)
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscribers) send(e *NodeEvent) {
|
||||||
|
s.mu.Lock()
|
||||||
|
for ch := range s.subs {
|
||||||
|
// non-blocking send
|
||||||
|
select {
|
||||||
|
case ch <- e:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
package configuration
|
|
|
@ -1,221 +0,0 @@
|
||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
|
||||||
// source: github.com/ehazlett/element/api/services/configuration/service.proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package configuration is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
github.com/ehazlett/element/api/services/configuration/service.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
CreateServiceRequest
|
|
||||||
CreateServiceResponse
|
|
||||||
ListServicesRequest
|
|
||||||
ListServicesResponse
|
|
||||||
*/
|
|
||||||
package configuration
|
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
import element_v1_types "github.com/ehazlett/element/api/types"
|
|
||||||
|
|
||||||
import (
|
|
||||||
context "golang.org/x/net/context"
|
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
type CreateServiceRequest struct {
|
|
||||||
Service *element_v1_types.Service `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CreateServiceRequest) Reset() { *m = CreateServiceRequest{} }
|
|
||||||
func (m *CreateServiceRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*CreateServiceRequest) ProtoMessage() {}
|
|
||||||
func (*CreateServiceRequest) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{0} }
|
|
||||||
|
|
||||||
func (m *CreateServiceRequest) GetService() *element_v1_types.Service {
|
|
||||||
if m != nil {
|
|
||||||
return m.Service
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateServiceResponse struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CreateServiceResponse) Reset() { *m = CreateServiceResponse{} }
|
|
||||||
func (m *CreateServiceResponse) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*CreateServiceResponse) ProtoMessage() {}
|
|
||||||
func (*CreateServiceResponse) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{1} }
|
|
||||||
|
|
||||||
type ListServicesRequest struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ListServicesRequest) Reset() { *m = ListServicesRequest{} }
|
|
||||||
func (m *ListServicesRequest) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*ListServicesRequest) ProtoMessage() {}
|
|
||||||
func (*ListServicesRequest) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{2} }
|
|
||||||
|
|
||||||
type ListServicesResponse struct {
|
|
||||||
Services []*element_v1_types.Service `protobuf:"bytes,1,rep,name=services" json:"services,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ListServicesResponse) Reset() { *m = ListServicesResponse{} }
|
|
||||||
func (m *ListServicesResponse) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*ListServicesResponse) ProtoMessage() {}
|
|
||||||
func (*ListServicesResponse) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{3} }
|
|
||||||
|
|
||||||
func (m *ListServicesResponse) GetServices() []*element_v1_types.Service {
|
|
||||||
if m != nil {
|
|
||||||
return m.Services
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*CreateServiceRequest)(nil), "element.v1.configuration.CreateServiceRequest")
|
|
||||||
proto.RegisterType((*CreateServiceResponse)(nil), "element.v1.configuration.CreateServiceResponse")
|
|
||||||
proto.RegisterType((*ListServicesRequest)(nil), "element.v1.configuration.ListServicesRequest")
|
|
||||||
proto.RegisterType((*ListServicesResponse)(nil), "element.v1.configuration.ListServicesResponse")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ context.Context
|
|
||||||
var _ grpc.ClientConn
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the grpc package it is being compiled against.
|
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
|
||||||
|
|
||||||
// Client API for Configuration service
|
|
||||||
|
|
||||||
type ConfigurationClient interface {
|
|
||||||
CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error)
|
|
||||||
ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type configurationClient struct {
|
|
||||||
cc *grpc.ClientConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfigurationClient(cc *grpc.ClientConn) ConfigurationClient {
|
|
||||||
return &configurationClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *configurationClient) CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error) {
|
|
||||||
out := new(CreateServiceResponse)
|
|
||||||
err := grpc.Invoke(ctx, "/element.v1.configuration.Configuration/CreateService", in, out, c.cc, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *configurationClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) {
|
|
||||||
out := new(ListServicesResponse)
|
|
||||||
err := grpc.Invoke(ctx, "/element.v1.configuration.Configuration/ListServices", in, out, c.cc, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server API for Configuration service
|
|
||||||
|
|
||||||
type ConfigurationServer interface {
|
|
||||||
CreateService(context.Context, *CreateServiceRequest) (*CreateServiceResponse, error)
|
|
||||||
ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterConfigurationServer(s *grpc.Server, srv ConfigurationServer) {
|
|
||||||
s.RegisterService(&_Configuration_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Configuration_CreateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(CreateServiceRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ConfigurationServer).CreateService(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/element.v1.configuration.Configuration/CreateService",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ConfigurationServer).CreateService(ctx, req.(*CreateServiceRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Configuration_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(ListServicesRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if interceptor == nil {
|
|
||||||
return srv.(ConfigurationServer).ListServices(ctx, in)
|
|
||||||
}
|
|
||||||
info := &grpc.UnaryServerInfo{
|
|
||||||
Server: srv,
|
|
||||||
FullMethod: "/element.v1.configuration.Configuration/ListServices",
|
|
||||||
}
|
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
return srv.(ConfigurationServer).ListServices(ctx, req.(*ListServicesRequest))
|
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _Configuration_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "element.v1.configuration.Configuration",
|
|
||||||
HandlerType: (*ConfigurationServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "CreateService",
|
|
||||||
Handler: _Configuration_CreateService_Handler,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
MethodName: "ListServices",
|
|
||||||
Handler: _Configuration_ListServices_Handler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Streams: []grpc.StreamDesc{},
|
|
||||||
Metadata: "github.com/ehazlett/element/api/services/configuration/service.proto",
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/ehazlett/element/api/services/configuration/service.proto", fileDescriptorService)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptorService = []byte{
|
|
||||||
// 253 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x4e, 0xc3, 0x30,
|
|
||||||
0x10, 0x44, 0x89, 0x90, 0x00, 0x2d, 0xf4, 0x62, 0x5a, 0x11, 0x72, 0xaa, 0x72, 0xe2, 0xc2, 0x5a,
|
|
||||||
0x34, 0xe2, 0x0b, 0xca, 0x0d, 0xb8, 0xc0, 0x17, 0xa4, 0xd1, 0xd2, 0x5a, 0x6a, 0x63, 0xe3, 0xdd,
|
|
||||||
0x54, 0x82, 0xef, 0xe6, 0x03, 0x90, 0x5a, 0x1b, 0xc5, 0xa8, 0x85, 0x5e, 0x47, 0x6f, 0x67, 0x3c,
|
|
||||||
0x63, 0x78, 0x98, 0x1b, 0x59, 0x74, 0x33, 0x6c, 0xec, 0x4a, 0xd3, 0xa2, 0xfe, 0x5c, 0x92, 0x88,
|
|
||||||
0xa6, 0x25, 0xad, 0xa8, 0x15, 0x5d, 0x3b, 0xa3, 0x99, 0xfc, 0xda, 0x34, 0xc4, 0xba, 0xb1, 0xed,
|
|
||||||
0x9b, 0x99, 0x77, 0xbe, 0x16, 0x63, 0xdb, 0x28, 0xa3, 0xf3, 0x56, 0xac, 0xca, 0xc3, 0x05, 0xae,
|
|
||||||
0xef, 0x30, 0xe1, 0x8a, 0xea, 0x3f, 0x7f, 0xf9, 0x70, 0xc4, 0xa9, 0x5d, 0xf9, 0x08, 0xc3, 0xa9,
|
|
||||||
0xa7, 0x5a, 0xe8, 0x75, 0x2b, 0xbf, 0xd0, 0x7b, 0x47, 0x2c, 0xaa, 0x82, 0xd3, 0x00, 0xe6, 0xd9,
|
|
||||||
0x38, 0xbb, 0x39, 0x9f, 0x5c, 0x63, 0x2f, 0x78, 0xe3, 0x84, 0xf1, 0x24, 0x92, 0xe5, 0x15, 0x8c,
|
|
||||||
0x7e, 0x99, 0xb1, 0xb3, 0x2d, 0x53, 0x39, 0x82, 0xcb, 0x27, 0xc3, 0x12, 0x64, 0x0e, 0x21, 0xe5,
|
|
||||||
0x33, 0x0c, 0x53, 0x79, 0x8b, 0xab, 0x7b, 0x38, 0x8b, 0x5b, 0xe4, 0xd9, 0xf8, 0xf8, 0xef, 0xf4,
|
|
||||||
0x1f, 0x74, 0xf2, 0x95, 0xc1, 0x60, 0xda, 0x9f, 0x44, 0x79, 0x18, 0x24, 0x0f, 0x52, 0x88, 0xfb,
|
|
||||||
0xe6, 0xc3, 0x5d, 0x33, 0x14, 0xfa, 0x60, 0x3e, 0x34, 0x3d, 0x52, 0x16, 0x2e, 0xfa, 0xa5, 0xd4,
|
|
||||||
0xed, 0x7e, 0x8b, 0x1d, 0x9b, 0x14, 0x78, 0x28, 0x1e, 0x03, 0x67, 0x27, 0x9b, 0x9f, 0xac, 0xbe,
|
|
||||||
0x03, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x1c, 0x1e, 0x12, 0x60, 0x02, 0x00, 0x00,
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package element.v1.configuration;
|
|
||||||
|
|
||||||
import "github.com/ehazlett/element/api/types/service.proto";
|
|
||||||
|
|
||||||
service Configuration {
|
|
||||||
rpc CreateService(CreateServiceRequest) returns (CreateServiceResponse) {}
|
|
||||||
rpc ListServices(ListServicesRequest) returns (ListServicesResponse) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
message CreateServiceRequest {
|
|
||||||
element.v1.types.Service service = 1;
|
|
||||||
}
|
|
||||||
message CreateServiceResponse {}
|
|
||||||
|
|
||||||
message ListServicesRequest {}
|
|
||||||
message ListServicesResponse{
|
|
||||||
repeated element.v1.types.Service services = 1;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
package types
|
|
|
@ -1,151 +0,0 @@
|
||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
|
||||||
// source: github.com/ehazlett/element/api/types/service.proto
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package types is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
github.com/ehazlett/element/api/types/service.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
Service
|
|
||||||
RuntimeSpec
|
|
||||||
*/
|
|
||||||
package types
|
|
||||||
|
|
||||||
import proto "github.com/gogo/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
import _ "github.com/gogo/protobuf/gogoproto"
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
|
||||||
Hosts []string `protobuf:"bytes,2,rep,name=hosts" json:"hosts,omitempty"`
|
|
||||||
RuntimeSpec *RuntimeSpec `protobuf:"bytes,3,opt,name=runtime_spec,json=runtimeSpec" json:"runtime_spec,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Service) Reset() { *m = Service{} }
|
|
||||||
func (m *Service) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Service) ProtoMessage() {}
|
|
||||||
func (*Service) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{0} }
|
|
||||||
|
|
||||||
func (m *Service) GetID() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.ID
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Service) GetHosts() []string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Hosts
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Service) GetRuntimeSpec() *RuntimeSpec {
|
|
||||||
if m != nil {
|
|
||||||
return m.RuntimeSpec
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuntimeSpec struct {
|
|
||||||
Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
|
||||||
Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"`
|
|
||||||
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
|
|
||||||
Runtime string `protobuf:"bytes,4,opt,name=runtime,proto3" json:"runtime,omitempty"`
|
|
||||||
Port uint64 `protobuf:"varint,5,opt,name=port,proto3" json:"port,omitempty"`
|
|
||||||
Labels map[string]string `protobuf:"bytes,6,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) Reset() { *m = RuntimeSpec{} }
|
|
||||||
func (m *RuntimeSpec) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*RuntimeSpec) ProtoMessage() {}
|
|
||||||
func (*RuntimeSpec) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{1} }
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetNamespace() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Namespace
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetImage() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Image
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetProtocol() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Protocol
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetRuntime() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Runtime
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetPort() uint64 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Port
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *RuntimeSpec) GetLabels() map[string]string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Labels
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*Service)(nil), "element.v1.types.Service")
|
|
||||||
proto.RegisterType((*RuntimeSpec)(nil), "element.v1.types.RuntimeSpec")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterFile("github.com/ehazlett/element/api/types/service.proto", fileDescriptorService)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileDescriptorService = []byte{
|
|
||||||
// 320 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x8f, 0xb1, 0x4e, 0xc3, 0x30,
|
|
||||||
0x10, 0x86, 0x15, 0xa7, 0x4d, 0x89, 0xc3, 0x50, 0x59, 0x15, 0xb2, 0x2a, 0x10, 0x51, 0xa7, 0xb0,
|
|
||||||
0x38, 0xa2, 0x5d, 0x80, 0x09, 0x10, 0x0c, 0x48, 0x4c, 0xee, 0x03, 0x20, 0x37, 0x3d, 0xa5, 0x16,
|
|
||||||
0x49, 0x6c, 0xc5, 0x6e, 0xa5, 0xf0, 0x06, 0xbc, 0x24, 0x03, 0x4f, 0x82, 0xe2, 0x84, 0xb6, 0x62,
|
|
||||||
0x60, 0xbb, 0xef, 0xfe, 0xff, 0x74, 0xff, 0x8f, 0x17, 0xb9, 0xb4, 0x9b, 0xed, 0x8a, 0x65, 0xaa,
|
|
||||||
0x4c, 0x61, 0x23, 0x3e, 0x0a, 0xb0, 0x36, 0x85, 0x02, 0x4a, 0xa8, 0x6c, 0x2a, 0xb4, 0x4c, 0x6d,
|
|
||||||
0xa3, 0xc1, 0xa4, 0x06, 0xea, 0x9d, 0xcc, 0x80, 0xe9, 0x5a, 0x59, 0x45, 0xc6, 0xbd, 0x81, 0xed,
|
|
||||||
0xae, 0x99, 0xd3, 0xa7, 0x93, 0x5c, 0xe5, 0xca, 0x89, 0x69, 0x3b, 0x75, 0xbe, 0x59, 0x83, 0x47,
|
|
||||||
0xcb, 0xee, 0x90, 0x9c, 0x61, 0x24, 0xd7, 0xd4, 0x8b, 0xbd, 0x24, 0x7c, 0x0c, 0xbe, 0xbf, 0x2e,
|
|
||||||
0xd1, 0xcb, 0x13, 0x47, 0x72, 0x4d, 0x26, 0x78, 0xb8, 0x51, 0xc6, 0x1a, 0x8a, 0x62, 0x3f, 0x09,
|
|
||||||
0x79, 0x07, 0xe4, 0x1e, 0x9f, 0xd6, 0xdb, 0xca, 0xca, 0x12, 0xde, 0x8c, 0x86, 0x8c, 0xfa, 0xb1,
|
|
||||||
0x97, 0x44, 0xf3, 0x0b, 0xf6, 0xf7, 0x2f, 0xe3, 0x9d, 0x6b, 0xa9, 0x21, 0xe3, 0x51, 0x7d, 0x80,
|
|
||||||
0xd9, 0x27, 0xc2, 0xd1, 0x91, 0x48, 0xce, 0x71, 0x58, 0x89, 0x12, 0x8c, 0x16, 0x19, 0x74, 0x31,
|
|
||||||
0xf8, 0x61, 0xd1, 0xa6, 0x90, 0xa5, 0xc8, 0x81, 0x22, 0xa7, 0x74, 0x40, 0xa6, 0xf8, 0xc4, 0xf5,
|
|
||||||
0xc8, 0x54, 0xe1, 0x12, 0x84, 0x7c, 0xcf, 0x84, 0xe2, 0x51, 0xff, 0x8e, 0x0e, 0x9c, 0xf4, 0x8b,
|
|
||||||
0x84, 0xe0, 0x81, 0x56, 0xb5, 0xa5, 0xc3, 0xd8, 0x4b, 0x06, 0xdc, 0xcd, 0xe4, 0x01, 0x07, 0x85,
|
|
||||||
0x58, 0x41, 0x61, 0x68, 0x10, 0xfb, 0x49, 0x34, 0xbf, 0xfa, 0xb7, 0x09, 0x7b, 0x75, 0xde, 0xe7,
|
|
||||||
0xca, 0xd6, 0x0d, 0xef, 0x0f, 0xa7, 0xb7, 0x38, 0x3a, 0x5a, 0x93, 0x31, 0xf6, 0xdf, 0xa1, 0xe9,
|
|
||||||
0x9b, 0xb4, 0x63, 0xdb, 0x61, 0x27, 0x8a, 0xed, 0xbe, 0x83, 0x83, 0x3b, 0x74, 0xe3, 0xad, 0x02,
|
|
||||||
0x97, 0x7a, 0xf1, 0x13, 0x00, 0x00, 0xff, 0xff, 0x4d, 0xda, 0x2d, 0xa9, 0xec, 0x01, 0x00, 0x00,
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package element.v1.types;
|
|
||||||
|
|
||||||
import "gogoproto/gogo.proto";
|
|
||||||
|
|
||||||
message Service {
|
|
||||||
string id = 1 [(gogoproto.customname) = "ID"];
|
|
||||||
repeated string hosts = 2;
|
|
||||||
RuntimeSpec runtime_spec = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RuntimeSpec {
|
|
||||||
string namespace = 1;
|
|
||||||
string image = 2;
|
|
||||||
string protocol = 3;
|
|
||||||
string runtime = 4;
|
|
||||||
uint64 port = 5;
|
|
||||||
map<string, string> labels = 6;
|
|
||||||
}
|
|
1
cmd/element/.gitignore
vendored
1
cmd/element/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
element
|
|
|
@ -1,40 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/ehazlett/element/version"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "element"
|
|
||||||
app.Version = version.FullVersion()
|
|
||||||
app.Author = "@ehazlett"
|
|
||||||
app.Email = ""
|
|
||||||
app.Usage = "container execution platform"
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug, D",
|
|
||||||
Usage: "Enable debug logging",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config, c",
|
|
||||||
Usage: "path to configuration file",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
app.Action = runAction
|
|
||||||
app.Before = func(c *cli.Context) error {
|
|
||||||
if c.Bool("debug") {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/ehazlett/element/config"
|
|
||||||
"github.com/ehazlett/element/server"
|
|
||||||
"github.com/ehazlett/element/version"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runAction(c *cli.Context) error {
|
|
||||||
logrus.Infof("element %s", version.FullVersion())
|
|
||||||
|
|
||||||
var data string
|
|
||||||
if configPath := c.GlobalString("config"); configPath != "" && data == "" {
|
|
||||||
logrus.Debugf("loading config: file=%s", configPath)
|
|
||||||
|
|
||||||
d, err := ioutil.ReadFile(configPath)
|
|
||||||
switch {
|
|
||||||
case os.IsNotExist(err):
|
|
||||||
return fmt.Errorf("config not found: file=%s", configPath)
|
|
||||||
case err == nil:
|
|
||||||
data = string(d)
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if data == "" {
|
|
||||||
return fmt.Errorf("You must specify a config from a file or environment variable")
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := config.ParseConfig(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
srv, err := server.NewServer(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := srv.Run(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
84
config.go
Normal file
84
config.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/memberlist"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionType defines the type of connection for the agent to use (wan, lan, local)
|
||||||
|
type ConnectionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LAN is a local area network connection intended for high speed, low latency networks
|
||||||
|
LAN ConnectionType = "lan"
|
||||||
|
// WAN is a wide area connection intended for long distance remote links
|
||||||
|
WAN ConnectionType = "wan"
|
||||||
|
// Local is a local connection intended for high speed local development links
|
||||||
|
Local ConnectionType = "local"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is the agent config
|
||||||
|
type Config struct {
|
||||||
|
// ConnectionType is the connection type the agent will use
|
||||||
|
ConnectionType string
|
||||||
|
// ClusterAddress bind address
|
||||||
|
ClusterAddress string
|
||||||
|
// AdvertiseAddress for nat traversal
|
||||||
|
AdvertiseAddress string
|
||||||
|
// Peers is a local cache of peer members
|
||||||
|
Peers []string
|
||||||
|
// Debug output for memberlist
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Agent) Config() *Config {
|
||||||
|
return a.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) memberListConfig(a *Agent) (*memberlist.Config, error) {
|
||||||
|
var mc *memberlist.Config
|
||||||
|
switch cfg.ConnectionType {
|
||||||
|
case string(Local):
|
||||||
|
mc = memberlist.DefaultLocalConfig()
|
||||||
|
case string(WAN):
|
||||||
|
mc = memberlist.DefaultWANConfig()
|
||||||
|
case string(LAN):
|
||||||
|
mc = memberlist.DefaultLANConfig()
|
||||||
|
default:
|
||||||
|
return nil, ErrUnknownConnectionType
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.Name = a.state.Self.ID
|
||||||
|
mc.Delegate = a
|
||||||
|
mc.Events = a
|
||||||
|
|
||||||
|
if !cfg.Debug {
|
||||||
|
mc.Logger = log.New(ioutil.Discard, "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(cfg.ClusterAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// ml overrides for connection
|
||||||
|
if v := host; v != "" {
|
||||||
|
mc.BindAddr = host
|
||||||
|
}
|
||||||
|
if v := port; v != "" {
|
||||||
|
mc.BindPort, _ = strconv.Atoi(port)
|
||||||
|
}
|
||||||
|
if host, port, err = net.SplitHostPort(cfg.ClusterAddress); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if v := host; v != "" {
|
||||||
|
mc.AdvertiseAddr = host
|
||||||
|
}
|
||||||
|
if v := port; v != "" {
|
||||||
|
mc.AdvertisePort, _ = strconv.Atoi(port)
|
||||||
|
}
|
||||||
|
return mc, nil
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
// Config is the top level application configuration
|
|
||||||
type Config struct {
|
|
||||||
ListenAddr string
|
|
||||||
GRPCAddr string
|
|
||||||
SocketPath string
|
|
||||||
Runtime *Runtime
|
|
||||||
Datastore string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Runtime struct {
|
|
||||||
Name string
|
|
||||||
Config interface{}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultListenAddr = ":8080"
|
|
||||||
defaultSocketPath = "/var/run/element.sock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseConfig returns a Config object from a raw string config TOML
|
|
||||||
func ParseConfig(data string) (*Config, error) {
|
|
||||||
var cfg Config
|
|
||||||
if _, err := toml.Decode(data, &cfg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ListenAddr == "" {
|
|
||||||
logrus.Warnf("using default listen addr: %s", defaultListenAddr)
|
|
||||||
cfg.ListenAddr = defaultListenAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.SocketPath == "" {
|
|
||||||
logrus.Warnf("using default socket path: %s", defaultSocketPath)
|
|
||||||
cfg.SocketPath = defaultSocketPath
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cfg, nil
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
sampleConfig = `
|
|
||||||
ListenAddr = ":8080"
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseConfig(t *testing.T) {
|
|
||||||
cfg, err := ParseConfig(sampleConfig)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing config: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ListenAddr != ":8080" {
|
|
||||||
t.Fatalf("expected listen addr :8080; received %s", cfg.ListenAddr)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package datastore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ehazlett/element/api/types"
|
|
||||||
"github.com/ehazlett/element/proxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Datastore interface {
|
|
||||||
// proxy
|
|
||||||
SaveProxyConfig(id string, cfg *proxy.Config) error
|
|
||||||
DeleteProxyConfig(id string) error
|
|
||||||
GetProxyConfigs() ([]*proxy.Config, error)
|
|
||||||
// services
|
|
||||||
SaveService(service *types.Service) error
|
|
||||||
DeleteService(id string) error
|
|
||||||
GetServices() ([]*types.Service, error)
|
|
||||||
GetServiceByHost(host string) (*types.Service, error)
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package memory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ehazlett/element/api/types"
|
|
||||||
"github.com/ehazlett/element/proxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Memory struct {
|
|
||||||
proxy map[string]*proxy.Config
|
|
||||||
service map[string]*types.Service
|
|
||||||
m sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMemory() (*Memory, error) {
|
|
||||||
return &Memory{
|
|
||||||
proxy: map[string]*proxy.Config{},
|
|
||||||
service: map[string]*types.Service{},
|
|
||||||
m: sync.Mutex{},
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package memory
|
|
||||||
|
|
||||||
import "github.com/ehazlett/element/proxy"
|
|
||||||
|
|
||||||
func (m *Memory) SaveProxyConfig(id string, cfg *proxy.Config) error {
|
|
||||||
m.m.Lock()
|
|
||||||
m.proxy[id] = cfg
|
|
||||||
m.m.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Memory) DeleteProxyConfig(id string) error {
|
|
||||||
m.m.Lock()
|
|
||||||
if _, exists := m.proxy[id]; exists {
|
|
||||||
delete(m.proxy, id)
|
|
||||||
}
|
|
||||||
m.m.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Memory) GetProxyConfigs() ([]*proxy.Config, error) {
|
|
||||||
c := []*proxy.Config{}
|
|
||||||
for _, v := range m.proxy {
|
|
||||||
c = append(c, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package memory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ehazlett/element/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *Memory) SaveService(service *types.Service) error {
|
|
||||||
m.m.Lock()
|
|
||||||
m.service[service.ID] = service
|
|
||||||
m.m.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Memory) DeleteService(id string) error {
|
|
||||||
m.m.Lock()
|
|
||||||
if _, exists := m.service[id]; exists {
|
|
||||||
delete(m.service, id)
|
|
||||||
}
|
|
||||||
m.m.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Memory) GetServices() ([]*types.Service, error) {
|
|
||||||
s := []*types.Service{}
|
|
||||||
for _, v := range m.service {
|
|
||||||
s = append(s, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Memory) GetServiceByHost(host string) (*types.Service, error) {
|
|
||||||
services, err := m.GetServices()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, service := range services {
|
|
||||||
for _, h := range service.Hosts {
|
|
||||||
if strings.Index(h, host) > -1 {
|
|
||||||
return service, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
49
delegate.go
Normal file
49
delegate.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeMeta returns local node meta information
|
||||||
|
func (a *Agent) NodeMeta(limit int) []byte {
|
||||||
|
data, err := proto.Marshal(a.state)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error serializing node meta: %s", err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyMsg is used for handling cluster messages
|
||||||
|
func (a *Agent) NotifyMsg(buf []byte) {
|
||||||
|
// this can be used to receive messages sent (i.e. SendReliable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBroadcasts is called when user messages can be broadcast
|
||||||
|
func (a *Agent) GetBroadcasts(overhead, limit int) [][]byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalState is the local cluster agent state
|
||||||
|
func (a *Agent) LocalState(join bool) []byte {
|
||||||
|
data, err := proto.Marshal(a.state)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("error serializing local state: %s", err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeRemoteState is used to store remote peer information
|
||||||
|
func (a *Agent) MergeRemoteState(buf []byte, join bool) {
|
||||||
|
var state State
|
||||||
|
if err := proto.Unmarshal(buf, &state); err != nil {
|
||||||
|
logrus.Errorf("error parsing remote agent state: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.state.Updated = time.Now()
|
||||||
|
a.state.Peers[state.Self.ID] = state.Self
|
||||||
|
// notify update
|
||||||
|
a.peerUpdateChan <- true
|
||||||
|
}
|
179
element.pb.go
Normal file
179
element.pb.go
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: github.com/ehazlett/element/element.proto
|
||||||
|
|
||||||
|
package element
|
||||||
|
|
||||||
|
import proto "github.com/gogo/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto"
|
||||||
|
import types "github.com/gogo/protobuf/types"
|
||||||
|
|
||||||
|
import time "time"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
var _ = time.Kitchen
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Peer struct {
|
||||||
|
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
|
||||||
|
Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
Payload *types.Any `protobuf:"bytes,4,opt,name=payload" json:"payload,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Peer) Reset() { *m = Peer{} }
|
||||||
|
func (m *Peer) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Peer) ProtoMessage() {}
|
||||||
|
func (*Peer) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_element_429f8a8a8de0c3bf, []int{0}
|
||||||
|
}
|
||||||
|
func (m *Peer) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Peer.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Peer.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Peer) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Peer.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Peer) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Peer.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Peer) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Peer.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Peer proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *Peer) GetID() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Peer) GetAddress() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Address
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Peer) GetLabels() map[string]string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Labels
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Peer) GetPayload() *types.Any {
|
||||||
|
if m != nil {
|
||||||
|
return m.Payload
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Self *Peer `protobuf:"bytes,1,opt,name=self" json:"self,omitempty"`
|
||||||
|
Updated time.Time `protobuf:"bytes,2,opt,name=updated,stdtime" json:"updated"`
|
||||||
|
Peers map[string]*Peer `protobuf:"bytes,3,rep,name=peers" json:"peers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *State) Reset() { *m = State{} }
|
||||||
|
func (m *State) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*State) ProtoMessage() {}
|
||||||
|
func (*State) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_element_429f8a8a8de0c3bf, []int{1}
|
||||||
|
}
|
||||||
|
func (m *State) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_State.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *State) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_State.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *State) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_State.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *State) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_State.Size(m)
|
||||||
|
}
|
||||||
|
func (m *State) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_State.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_State proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *State) GetSelf() *Peer {
|
||||||
|
if m != nil {
|
||||||
|
return m.Self
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *State) GetUpdated() time.Time {
|
||||||
|
if m != nil {
|
||||||
|
return m.Updated
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *State) GetPeers() map[string]*Peer {
|
||||||
|
if m != nil {
|
||||||
|
return m.Peers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Peer)(nil), "io.stellar.element.v1.Peer")
|
||||||
|
proto.RegisterMapType((map[string]string)(nil), "io.stellar.element.v1.Peer.LabelsEntry")
|
||||||
|
proto.RegisterType((*State)(nil), "io.stellar.element.v1.State")
|
||||||
|
proto.RegisterMapType((map[string]*Peer)(nil), "io.stellar.element.v1.State.PeersEntry")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterFile("github.com/ehazlett/element/element.proto", fileDescriptor_element_429f8a8a8de0c3bf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileDescriptor_element_429f8a8a8de0c3bf = []byte{
|
||||||
|
// 368 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xcf, 0x4e, 0x83, 0x40,
|
||||||
|
0x10, 0xc6, 0x0b, 0xfd, 0xa7, 0xcb, 0xc5, 0x6c, 0xaa, 0x41, 0x3c, 0xd0, 0xf4, 0x62, 0xbd, 0x2c,
|
||||||
|
0x29, 0x5e, 0xd4, 0x44, 0x8d, 0x8d, 0x1e, 0x4c, 0x3c, 0x18, 0xd4, 0xc4, 0xeb, 0x22, 0x53, 0x4a,
|
||||||
|
0x5c, 0x58, 0x02, 0x4b, 0x13, 0x7c, 0x08, 0xe3, 0x63, 0xf9, 0x14, 0x35, 0x31, 0x3e, 0x88, 0x61,
|
||||||
|
0x01, 0xdb, 0x68, 0x63, 0x4f, 0xcc, 0x30, 0xdf, 0x37, 0xfb, 0x9b, 0x0f, 0x1d, 0xf8, 0x81, 0x98,
|
||||||
|
0x66, 0x2e, 0x79, 0xe2, 0xa1, 0x05, 0x53, 0xfa, 0xc2, 0x40, 0x08, 0x0b, 0x18, 0x84, 0x10, 0xfd,
|
||||||
|
0x7c, 0x49, 0x9c, 0x70, 0xc1, 0xf1, 0x76, 0xc0, 0x49, 0x2a, 0x80, 0x31, 0x9a, 0x90, 0x7a, 0x32,
|
||||||
|
0x1b, 0x19, 0x3d, 0x9f, 0xfb, 0x5c, 0x2a, 0xac, 0xa2, 0x2a, 0xc5, 0xc6, 0xae, 0xcf, 0xb9, 0xcf,
|
||||||
|
0xc0, 0x92, 0x9d, 0x9b, 0x4d, 0x2c, 0x1a, 0xe5, 0xd5, 0xc8, 0xfc, 0x3d, 0x12, 0x41, 0x08, 0xa9,
|
||||||
|
0xa0, 0x61, 0x5c, 0x0a, 0x06, 0x5f, 0x0a, 0x6a, 0xdd, 0x02, 0x24, 0x78, 0x07, 0xa9, 0x81, 0xa7,
|
||||||
|
0x2b, 0x7d, 0x65, 0xb8, 0x39, 0xee, 0x7c, 0xce, 0x4d, 0xf5, 0xfa, 0xd2, 0x51, 0x03, 0x0f, 0xeb,
|
||||||
|
0xa8, 0x4b, 0x3d, 0x2f, 0x81, 0x34, 0xd5, 0xd5, 0x62, 0xe8, 0xd4, 0x2d, 0x3e, 0x47, 0x1d, 0x46,
|
||||||
|
0x5d, 0x60, 0xa9, 0xde, 0xec, 0x37, 0x87, 0x9a, 0xbd, 0x4f, 0x56, 0x42, 0x93, 0x62, 0x3d, 0xb9,
|
||||||
|
0x91, 0xca, 0xab, 0x48, 0x24, 0xb9, 0x53, 0xd9, 0x30, 0x41, 0xdd, 0x98, 0xe6, 0x8c, 0x53, 0x4f,
|
||||||
|
0x6f, 0xf5, 0x95, 0xa1, 0x66, 0xf7, 0x48, 0x89, 0x4b, 0x6a, 0x5c, 0x72, 0x11, 0xe5, 0x4e, 0x2d,
|
||||||
|
0x32, 0x8e, 0x91, 0xb6, 0xb4, 0x06, 0x6f, 0xa1, 0xe6, 0x33, 0xe4, 0x25, 0xb2, 0x53, 0x94, 0xb8,
|
||||||
|
0x87, 0xda, 0x33, 0xca, 0x32, 0xa8, 0x48, 0xcb, 0xe6, 0x44, 0x3d, 0x52, 0x06, 0xaf, 0x2a, 0x6a,
|
||||||
|
0xdf, 0x09, 0x2a, 0x00, 0x5b, 0xa8, 0x95, 0x02, 0x9b, 0x48, 0x9b, 0x66, 0xef, 0xfd, 0xc3, 0xec,
|
||||||
|
0x48, 0x21, 0x3e, 0x43, 0xdd, 0x2c, 0xf6, 0xa8, 0x00, 0x4f, 0xae, 0xd5, 0x6c, 0xe3, 0x0f, 0xe5,
|
||||||
|
0x7d, 0x1d, 0xea, 0x78, 0xe3, 0x7d, 0x6e, 0x36, 0xde, 0x3e, 0x4c, 0xc5, 0xa9, 0x4d, 0xf8, 0x14,
|
||||||
|
0xb5, 0x63, 0x80, 0x64, 0x5d, 0x4a, 0x92, 0x4e, 0xbe, 0x5b, 0xa5, 0x54, 0xba, 0x8c, 0x07, 0x84,
|
||||||
|
0x16, 0x3f, 0x57, 0xdc, 0x3c, 0x5a, 0xbe, 0x79, 0xcd, 0x41, 0x8b, 0x40, 0x1e, 0x1b, 0x6e, 0x47,
|
||||||
|
0xe2, 0x1f, 0x7e, 0x07, 0x00, 0x00, 0xff, 0xff, 0xec, 0x93, 0xe7, 0x1d, 0x96, 0x02, 0x00, 0x00,
|
||||||
|
}
|
20
element.proto
Normal file
20
element.proto
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package io.stellar.element.v1;
|
||||||
|
|
||||||
|
import weak "gogoproto/gogo.proto";
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
message Peer {
|
||||||
|
string id = 1 [(gogoproto.customname) = "ID"];
|
||||||
|
string address = 2;
|
||||||
|
map<string, string> labels = 3;
|
||||||
|
google.protobuf.Any payload = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message State {
|
||||||
|
Peer self = 1;
|
||||||
|
google.protobuf.Timestamp updated = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||||
|
map<string, Peer> peers = 3;
|
||||||
|
}
|
51
events.go
Normal file
51
events.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/memberlist"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeEventType is the type of node event
|
||||||
|
type NodeEventType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NodeJoin is the event fired upon a node joining the cluster
|
||||||
|
NodeJoin NodeEventType = "join"
|
||||||
|
// NodeLeave is the event fired upon a node leaving the cluster
|
||||||
|
NodeLeave NodeEventType = "leave"
|
||||||
|
// NodeUpdate is the event fired upon a node updating in the cluster
|
||||||
|
NodeUpdate NodeEventType = "update"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeEvent stores the event type and node information
|
||||||
|
type NodeEvent struct {
|
||||||
|
// EventType is the type of event fired
|
||||||
|
Type NodeEventType
|
||||||
|
// Node is the internal cluster node
|
||||||
|
Node *memberlist.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyJoin notifies when a node joins the cluster
|
||||||
|
func (a *Agent) NotifyJoin(n *memberlist.Node) {
|
||||||
|
a.send(&NodeEvent{
|
||||||
|
Type: NodeJoin,
|
||||||
|
Node: n,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyLeave notifies when a node leaves the cluster
|
||||||
|
func (a *Agent) NotifyLeave(n *memberlist.Node) {
|
||||||
|
delete(a.state.Peers, n.Name)
|
||||||
|
a.peerUpdateChan <- true
|
||||||
|
a.send(&NodeEvent{
|
||||||
|
Type: NodeLeave,
|
||||||
|
Node: n,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyUpdate notifies when a node is updated in the cluster
|
||||||
|
func (a *Agent) NotifyUpdate(n *memberlist.Node) {
|
||||||
|
a.send(&NodeEvent{
|
||||||
|
Type: NodeUpdate,
|
||||||
|
Node: n,
|
||||||
|
})
|
||||||
|
}
|
6
health.go
Normal file
6
health.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
// Health returns the relative health score of the node (lower is better)
|
||||||
|
func (a *Agent) Health() int {
|
||||||
|
return a.members.GetHealthScore()
|
||||||
|
}
|
22
peers.go
Normal file
22
peers.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import "github.com/gogo/protobuf/proto"
|
||||||
|
|
||||||
|
// Peers returns all known peers in the cluster
|
||||||
|
func (a *Agent) Peers() ([]*Peer, error) {
|
||||||
|
self := a.members.LocalNode()
|
||||||
|
var state State
|
||||||
|
if err := proto.Unmarshal(self.Meta, &state); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var peers []*Peer
|
||||||
|
for _, p := range state.Peers {
|
||||||
|
peers = append(peers, p)
|
||||||
|
}
|
||||||
|
return peers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self returns the local peer information
|
||||||
|
func (a *Agent) Self() *Peer {
|
||||||
|
return a.state.Self
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
ID string
|
|
||||||
Frontends map[string]*Frontend `json:"frontends,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Frontend struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Hosts []string `json:"hosts,omitempty"`
|
|
||||||
Backend *Backend `json:"backend,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Backend struct {
|
|
||||||
Path string `json:"path"`
|
|
||||||
Upstreams []string `json:"upstreams,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) Body() []byte {
|
|
||||||
t := template.New("proxy")
|
|
||||||
tmpl, err := t.Parse(configTemplate)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorf("error parsing proxy template: %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
if err := tmpl.Execute(&b, c); err != nil {
|
|
||||||
logrus.Errorf("error executing proxy template: %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) Path() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) ServerType() string {
|
|
||||||
return "http"
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
func (p *Proxy) AddFrontend(f *Frontend) error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
if _, ok := p.config.Frontends[f.Name]; ok {
|
|
||||||
return ErrFrontendExists
|
|
||||||
}
|
|
||||||
|
|
||||||
p.config.Frontends[f.Name] = f
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"name": f.Name,
|
|
||||||
"hosts": f.Hosts,
|
|
||||||
}).Debug("frontend added")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) RemoveFrontend(name string) error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
if _, ok := p.config.Frontends[name]; ok {
|
|
||||||
delete(p.config.Frontends, name)
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"name": name,
|
|
||||||
}).Debug("frontend removed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) UpdateFrontend(f *Frontend) error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
if _, ok := p.config.Frontends[f.Name]; !ok {
|
|
||||||
return ErrFrontendDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
p.config.Frontends[f.Name] = f
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"name": f.Name,
|
|
||||||
}).Debug("frontend updated")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ehazlett/element/version"
|
|
||||||
"github.com/mholt/caddy"
|
|
||||||
_ "github.com/mholt/caddy/caddyhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrFrontendExists = errors.New("frontend exists")
|
|
||||||
ErrFrontendDoesNotExist = errors.New("frontend does not exist")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Proxy struct {
|
|
||||||
config *Config
|
|
||||||
instance *caddy.Instance
|
|
||||||
m sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProxy(config *Config) (*Proxy, error) {
|
|
||||||
if config.Frontends == nil {
|
|
||||||
config.Frontends = map[string]*Frontend{}
|
|
||||||
}
|
|
||||||
caddy.AppName = "element"
|
|
||||||
caddy.AppVersion = version.Version + version.Build
|
|
||||||
|
|
||||||
return &Proxy{
|
|
||||||
config: config,
|
|
||||||
m: sync.Mutex{},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Proxy) Config() (*Config, error) {
|
|
||||||
return p.config, nil
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
func (p *Proxy) Reload() error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
i, err := p.instance.Restart(p.config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.instance = i
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import "github.com/mholt/caddy"
|
|
||||||
|
|
||||||
func (p *Proxy) Start() error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
i, err := caddy.Start(p.config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.instance = i
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
func (p *Proxy) Stop() error {
|
|
||||||
if p.instance != nil {
|
|
||||||
return p.instance.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
const configTemplate = ` # element router configuration
|
|
||||||
{{ range $frontend := .Frontends }}
|
|
||||||
# {{ $frontend.Name }}
|
|
||||||
{{ range $host := $frontend.Hosts }}{{ $host }} {
|
|
||||||
{{ if $frontend.Backend }}proxy {{ $frontend.Backend.Path }}{{ range $upstream := $frontend.Backend.Upstreams }} {{ $upstream }} {{ end }} {
|
|
||||||
transparent
|
|
||||||
}
|
|
||||||
{{ end }}
|
|
||||||
} {{ end }} {{ end }}
|
|
||||||
`
|
|
|
@ -1,10 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
func (p *Proxy) Update(config *Config) error {
|
|
||||||
p.m.Lock()
|
|
||||||
defer p.m.Unlock()
|
|
||||||
|
|
||||||
p.config = config
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
func (p *Proxy) Wait() {
|
|
||||||
if p.instance != nil {
|
|
||||||
p.instance.Wait()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
type Container struct {
|
|
||||||
id string
|
|
||||||
endpoint string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Container) ID() string {
|
|
||||||
return c.id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Container) Endpoint() string {
|
|
||||||
// TODO
|
|
||||||
return c.endpoint
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
dockertypes "github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/ehazlett/element/api/types"
|
|
||||||
"github.com/ehazlett/element/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *Docker) Create(spec *types.RuntimeSpec) (runtime.Container, error) {
|
|
||||||
if spec.Labels == nil {
|
|
||||||
spec.Labels = map[string]string{}
|
|
||||||
}
|
|
||||||
labels := spec.Labels
|
|
||||||
// insert element labels
|
|
||||||
labels[elementRuntimeLabel] = "docker"
|
|
||||||
|
|
||||||
p := strconv.Itoa(int(spec.Port))
|
|
||||||
port, err := nat.NewPort(spec.Protocol, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
containerConfig := &container.Config{
|
|
||||||
Image: spec.Image,
|
|
||||||
Labels: spec.Labels,
|
|
||||||
ExposedPorts: nat.PortSet{
|
|
||||||
port: struct{}{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
hostConfig := &container.HostConfig{
|
|
||||||
PortBindings: nat.PortMap{
|
|
||||||
port: []nat.PortBinding{
|
|
||||||
{
|
|
||||||
HostIP: "0.0.0.0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("%+v", containerConfig)
|
|
||||||
|
|
||||||
// create
|
|
||||||
resp, err := d.client.ContainerCreate(context.Background(), containerConfig, hostConfig, nil, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// start
|
|
||||||
if err := d.client.ContainerStart(context.Background(), resp.ID, dockertypes.ContainerStartOptions{}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
optFilters := filters.NewArgs()
|
|
||||||
optFilters.Add("id", resp.ID)
|
|
||||||
optFilters.Add("label", elementRuntimeLabel)
|
|
||||||
|
|
||||||
containers, err := d.client.ContainerList(context.Background(), dockertypes.ContainerListOptions{
|
|
||||||
Filters: optFilters,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(containers) == 0 {
|
|
||||||
return nil, fmt.Errorf("error creating container: no container found after start")
|
|
||||||
}
|
|
||||||
|
|
||||||
container := containers[0]
|
|
||||||
endpoint := getContainerEndpoint(container)
|
|
||||||
|
|
||||||
return Container{
|
|
||||||
id: container.ID,
|
|
||||||
endpoint: endpoint,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
func (d *Docker) Delete(namespace, id string) error {
|
|
||||||
// TODO
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
elementRuntimeLabel = "element.runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrContainerNotFound = errors.New("container not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Docker struct {
|
|
||||||
client *client.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() (*Docker, error) {
|
|
||||||
c, err := client.NewEnvClient()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Docker{
|
|
||||||
client: c,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import "github.com/ehazlett/element/runtime"
|
|
||||||
|
|
||||||
func (d *Docker) Get(namespace, id string) (runtime.Container, error) {
|
|
||||||
containers, err := d.List(namespace)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range containers {
|
|
||||||
if c.ID() == id {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ErrContainerNotFound
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
|
||||||
"github.com/ehazlett/element/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *Docker) List(namespace string) ([]runtime.Container, error) {
|
|
||||||
optFilters := filters.NewArgs()
|
|
||||||
optFilters.Add("label", elementRuntimeLabel)
|
|
||||||
|
|
||||||
dockerContainers, err := d.client.ContainerList(context.Background(), types.ContainerListOptions{
|
|
||||||
Filters: optFilters,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var containers []runtime.Container
|
|
||||||
for _, c := range dockerContainers {
|
|
||||||
endpoint := getContainerEndpoint(c)
|
|
||||||
containers = append(containers, Container{
|
|
||||||
id: c.ID,
|
|
||||||
endpoint: endpoint,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return containers, nil
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getContainerEndpoint(c types.Container) string {
|
|
||||||
logrus.Debug("getting container endpoint")
|
|
||||||
endpoint := ""
|
|
||||||
if len(c.Ports) > 0 {
|
|
||||||
for _, p := range c.Ports {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"port": fmt.Sprintf("%+v", p),
|
|
||||||
}).Debug("checking container port")
|
|
||||||
|
|
||||||
if p.IP != "" && p.PublicPort != 0 {
|
|
||||||
endpoint = fmt.Sprintf("%s:%d", p.IP, p.PublicPort)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return endpoint
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package runtime
|
|
||||||
|
|
||||||
import "github.com/ehazlett/element/api/types"
|
|
||||||
|
|
||||||
type Container interface {
|
|
||||||
ID() string
|
|
||||||
Endpoint() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Runtime interface {
|
|
||||||
// Create creates a new container
|
|
||||||
Create(spec *types.RuntimeSpec) (Container, error)
|
|
||||||
// Delete deletes a container
|
|
||||||
Delete(namespace, id string) error
|
|
||||||
// List returns all containers in the runtime
|
|
||||||
List(namespace string) ([]Container, error)
|
|
||||||
// Get returns the specified container
|
|
||||||
Get(namespace, id string) (Container, error)
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package runtime
|
|
||||||
|
|
||||||
// Spec is the runtime specification
|
|
||||||
type Spec struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
Port int `json:"port"`
|
|
||||||
Runtime string `json:"runtime"`
|
|
||||||
Labels map[string]string `json:"labels,omitempty"`
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) apiGetConfig(w http.ResponseWriter, r *http.Request) {
|
|
||||||
cfg, err := s.proxy.Config()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error getting config: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(cfg); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error serializing config: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) apiGetConfigRaw(w http.ResponseWriter, r *http.Request) {
|
|
||||||
cfg, err := s.proxy.Config()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error getting config: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(cfg.Body())
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ehazlett/element/proxy"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) connect(host string) error {
|
|
||||||
service, err := s.store.GetServiceByHost(host)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if service == nil {
|
|
||||||
return ErrServiceNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// create container
|
|
||||||
container, err := s.runtime.Create(service.RuntimeSpec)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"id": container.ID(),
|
|
||||||
"endpoint": container.Endpoint(),
|
|
||||||
}).Debug("container created")
|
|
||||||
|
|
||||||
// configure proxy
|
|
||||||
frontend := &proxy.Frontend{
|
|
||||||
Name: service.ID,
|
|
||||||
Hosts: service.Hosts,
|
|
||||||
Backend: &proxy.Backend{
|
|
||||||
Path: "/",
|
|
||||||
Upstreams: []string{container.Endpoint()},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"frontend": frontend,
|
|
||||||
}).Debug("configuring proxy")
|
|
||||||
|
|
||||||
if err := s.proxy.AddFrontend(frontend); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// reload
|
|
||||||
if err := s.proxy.Reload(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/ehazlett/element/proxy"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) apiAddFrontend(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var frontend *proxy.Frontend
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&frontend); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("invalid frontend: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.proxy.AddFrontend(frontend); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) apiRemoveFrontend(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
name := vars["name"]
|
|
||||||
if err := s.proxy.RemoveFrontend(name); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error removing frontend: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) apiUpdateFrontend(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var frontend *proxy.Frontend
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&frontend); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("invalid fronend: %s", err), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.proxy.UpdateFrontend(frontend); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error adding frontend: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) genericHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"host": r.Host,
|
|
||||||
"uri": r.RequestURI,
|
|
||||||
}).Debug("request")
|
|
||||||
|
|
||||||
if err := s.connect(r.Host); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error connecting to backend: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: issue redirect to host to have client re-send and connect to backend
|
|
||||||
w.Header().Set("Location", r.RequestURI)
|
|
||||||
w.WriteHeader(http.StatusFound)
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) apiReload(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := s.proxy.Reload(); err != nil {
|
|
||||||
http.Error(w, fmt.Sprintf("error reloading: %s", err), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import "github.com/gorilla/mux"
|
|
||||||
|
|
||||||
func (s *Server) router() *mux.Router {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
// generic
|
|
||||||
r.HandleFunc("/", s.genericHandler)
|
|
||||||
// proxy
|
|
||||||
r.HandleFunc("/config", s.apiGetConfig).Methods("GET")
|
|
||||||
r.HandleFunc("/config/raw", s.apiGetConfigRaw).Methods("GET")
|
|
||||||
r.HandleFunc("/frontends", s.apiAddFrontend).Methods("POST")
|
|
||||||
r.HandleFunc("/frontends", s.apiUpdateFrontend).Methods("PUT")
|
|
||||||
r.HandleFunc("/frontends/{name}", s.apiRemoveFrontend).Methods("DELETE")
|
|
||||||
r.HandleFunc("/reload", s.apiReload).Methods("POST")
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
107
server/server.go
107
server/server.go
|
@ -1,107 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
configurationapi "github.com/ehazlett/element/api/services/configuration"
|
|
||||||
"github.com/ehazlett/element/config"
|
|
||||||
"github.com/ehazlett/element/datastore"
|
|
||||||
"github.com/ehazlett/element/proxy"
|
|
||||||
"github.com/ehazlett/element/runtime"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrServiceNotFound = errors.New("service not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
cfg *config.Config
|
|
||||||
proxy *proxy.Proxy
|
|
||||||
runtime runtime.Runtime
|
|
||||||
store datastore.Datastore
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(cfg *config.Config) (*Server, error) {
|
|
||||||
p, err := proxy.NewProxy(&proxy.Config{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r, err := loadRuntime(cfg.Runtime)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err := loadDatastore(cfg.Datastore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := r.List("")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.WithFields(logrus.Fields{
|
|
||||||
"containers": c,
|
|
||||||
}).Debug("runtime containers")
|
|
||||||
|
|
||||||
return &Server{
|
|
||||||
cfg: cfg,
|
|
||||||
proxy: p,
|
|
||||||
runtime: r,
|
|
||||||
store: store,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Run() error {
|
|
||||||
grpcServer := grpc.NewServer()
|
|
||||||
configurationapi.RegisterConfigurationServer(grpcServer, s)
|
|
||||||
|
|
||||||
l, err := net.Listen("tcp", s.cfg.GRPCAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//cfg := &proxy.Config{
|
|
||||||
// Frontends: map[string]*proxy.Frontend{
|
|
||||||
// "element": &proxy.Frontend{
|
|
||||||
// Name: "element",
|
|
||||||
// Hosts: []string{s.cfg.ListenAddr},
|
|
||||||
// Backend: &proxy.Backend{
|
|
||||||
// Path: "/",
|
|
||||||
// Upstreams: []string{"unix:" + s.cfg.SocketPath},
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
//}
|
|
||||||
|
|
||||||
if err := s.proxy.Start(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle SIGHUP
|
|
||||||
c := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(c, syscall.SIGHUP)
|
|
||||||
go func() {
|
|
||||||
for range c {
|
|
||||||
logrus.Debugf("received SIGHUP; reloading")
|
|
||||||
if err := s.proxy.Reload(); err != nil {
|
|
||||||
logrus.Errorf("error reloading proxy: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
grpcServer.Serve(l)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
|
||||||
return s.proxy.Stop()
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
configurationapi "github.com/ehazlett/element/api/services/configuration"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Server) CreateService(ctx context.Context, req *configurationapi.CreateServiceRequest) (*configurationapi.CreateServiceResponse, error) {
|
|
||||||
// TODO
|
|
||||||
// save to datastore
|
|
||||||
if err := s.store.SaveService(req.Service); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ListServices(ctx context.Context, req *configurationapi.ListServicesRequest) (*configurationapi.ListServicesResponse, error) {
|
|
||||||
// TODO
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/ehazlett/element/config"
|
|
||||||
"github.com/ehazlett/element/datastore"
|
|
||||||
"github.com/ehazlett/element/datastore/memory"
|
|
||||||
"github.com/ehazlett/element/runtime"
|
|
||||||
"github.com/ehazlett/element/runtime/docker"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidRuntime = errors.New("invalid runtime specified")
|
|
||||||
ErrInvalidDatastore = errors.New("invalid datastore specified")
|
|
||||||
)
|
|
||||||
|
|
||||||
// loadRuntime loads a runtime from the specified configuration
|
|
||||||
func loadRuntime(cfg *config.Runtime) (runtime.Runtime, error) {
|
|
||||||
var rt runtime.Runtime
|
|
||||||
|
|
||||||
switch cfg.Name {
|
|
||||||
case "docker":
|
|
||||||
r, err := docker.New()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rt = r
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidRuntime
|
|
||||||
}
|
|
||||||
|
|
||||||
return rt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadDatastore loads a datastore from the specified configuration
|
|
||||||
func loadDatastore(ds string) (datastore.Datastore, error) {
|
|
||||||
u, err := url.Parse(ds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var d datastore.Datastore
|
|
||||||
switch u.Scheme {
|
|
||||||
case "memory":
|
|
||||||
m, err := memory.NewMemory()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
d = m
|
|
||||||
default:
|
|
||||||
return nil, ErrInvalidDatastore
|
|
||||||
}
|
|
||||||
|
|
||||||
return d, nil
|
|
||||||
}
|
|
9
shutdown.go
Normal file
9
shutdown.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
// Shutdown causes the local node to leave the cluster and perform a clean shutdown
|
||||||
|
func (a *Agent) Shutdown() error {
|
||||||
|
if err := a.members.Leave(nodeUpdateTimeout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return a.members.Shutdown()
|
||||||
|
}
|
22
start.go
Normal file
22
start.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package element
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start handles cluster events
|
||||||
|
func (a *Agent) Start() error {
|
||||||
|
go func() {
|
||||||
|
for range a.peerUpdateChan {
|
||||||
|
if err := a.members.UpdateNode(nodeUpdateTimeout); err != nil {
|
||||||
|
logrus.Errorf("error updating node metadata: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if len(a.config.Peers) > 0 {
|
||||||
|
if _, err := a.members.Join(a.config.Peers); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
18
vendor.conf
18
vendor.conf
|
@ -1,18 +0,0 @@
|
||||||
github.com/sirupsen/logrus 181d419aa9e2223811b824e8f0b4af96f9ba9302
|
|
||||||
golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
|
|
||||||
golang.org/x/sys 0f826bdd13b500be0f1d4004938ad978fcc6031e
|
|
||||||
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
|
|
||||||
github.com/pkg/errors c605e284fe17294bda444b34710735b29d1a9d90
|
|
||||||
golang.org/x/net f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f
|
|
||||||
github.com/gogo/protobuf 7b6c6391c4ff245962047fc1e2c6e08b1cdfa0e8
|
|
||||||
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
|
|
||||||
github.com/mholt/caddy e7f08bff38988c3049b7fda301c52a681af63cd8
|
|
||||||
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
|
|
||||||
github.com/codegangsta/cli 4b90d79a682b4bf685762c7452db20f2a676ecb2
|
|
||||||
google.golang.org/grpc 7657092a1303cc5a6fa3fee988d57c665683a4da
|
|
||||||
google.golang.org/genproto 09f6ed296fc66555a25fe4ce95173148778dfa85
|
|
||||||
github.com/docker/docker d9b785cf2e004bb0024f398014146e534b2cd8c5
|
|
||||||
github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20
|
|
||||||
github.com/docker/distribution edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
|
|
||||||
github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf
|
|
||||||
github.com/opencontainers/image-spec ef2b9a1d696677abd664a0879758d2b115b1ded3
|
|
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
21
vendor/github.com/BurntSushi/toml/COPYING
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 TOML authors
|
|
||||||
|
|
||||||
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.
|
|
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
218
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
|
@ -1,218 +0,0 @@
|
||||||
## TOML parser and encoder for Go with reflection
|
|
||||||
|
|
||||||
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
|
|
||||||
reflection interface similar to Go's standard library `json` and `xml`
|
|
||||||
packages. This package also supports the `encoding.TextUnmarshaler` and
|
|
||||||
`encoding.TextMarshaler` interfaces so that you can define custom data
|
|
||||||
representations. (There is an example of this below.)
|
|
||||||
|
|
||||||
Spec: https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
Compatible with TOML version
|
|
||||||
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
|
||||||
|
|
||||||
Documentation: https://godoc.org/github.com/BurntSushi/toml
|
|
||||||
|
|
||||||
Installation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/BurntSushi/toml
|
|
||||||
```
|
|
||||||
|
|
||||||
Try the toml validator:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/BurntSushi/toml/cmd/tomlv
|
|
||||||
tomlv some-toml-file.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml)
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
This package passes all tests in
|
|
||||||
[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
|
|
||||||
and the encoder.
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
This package works similarly to how the Go standard library handles `XML`
|
|
||||||
and `JSON`. Namely, data is loaded into Go values via reflection.
|
|
||||||
|
|
||||||
For the simplest example, consider some TOML file as just a list of keys
|
|
||||||
and values:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
Age = 25
|
|
||||||
Cats = [ "Cauchy", "Plato" ]
|
|
||||||
Pi = 3.14
|
|
||||||
Perfection = [ 6, 28, 496, 8128 ]
|
|
||||||
DOB = 1987-07-05T05:45:00Z
|
|
||||||
```
|
|
||||||
|
|
||||||
Which could be defined in Go as:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Config struct {
|
|
||||||
Age int
|
|
||||||
Cats []string
|
|
||||||
Pi float64
|
|
||||||
Perfection []int
|
|
||||||
DOB time.Time // requires `import time`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And then decoded with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
var conf Config
|
|
||||||
if _, err := toml.Decode(tomlData, &conf); err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use struct tags if your struct field name doesn't map to a TOML
|
|
||||||
key value directly:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
some_key_NAME = "wat"
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
type TOML struct {
|
|
||||||
ObscureKey string `toml:"some_key_NAME"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the `encoding.TextUnmarshaler` interface
|
|
||||||
|
|
||||||
Here's an example that automatically parses duration strings into
|
|
||||||
`time.Duration` values:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[song]]
|
|
||||||
name = "Thunder Road"
|
|
||||||
duration = "4m49s"
|
|
||||||
|
|
||||||
[[song]]
|
|
||||||
name = "Stairway to Heaven"
|
|
||||||
duration = "8m03s"
|
|
||||||
```
|
|
||||||
|
|
||||||
Which can be decoded with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type song struct {
|
|
||||||
Name string
|
|
||||||
Duration duration
|
|
||||||
}
|
|
||||||
type songs struct {
|
|
||||||
Song []song
|
|
||||||
}
|
|
||||||
var favorites songs
|
|
||||||
if _, err := toml.Decode(blob, &favorites); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range favorites.Song {
|
|
||||||
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And you'll also need a `duration` type that satisfies the
|
|
||||||
`encoding.TextUnmarshaler` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type duration struct {
|
|
||||||
time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *duration) UnmarshalText(text []byte) error {
|
|
||||||
var err error
|
|
||||||
d.Duration, err = time.ParseDuration(string(text))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### More complex usage
|
|
||||||
|
|
||||||
Here's an example of how to load the example from the official spec page:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# This is a TOML document. Boom.
|
|
||||||
|
|
||||||
title = "TOML Example"
|
|
||||||
|
|
||||||
[owner]
|
|
||||||
name = "Tom Preston-Werner"
|
|
||||||
organization = "GitHub"
|
|
||||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
|
||||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
|
||||||
|
|
||||||
[database]
|
|
||||||
server = "192.168.1.1"
|
|
||||||
ports = [ 8001, 8001, 8002 ]
|
|
||||||
connection_max = 5000
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[servers]
|
|
||||||
|
|
||||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
|
||||||
[servers.alpha]
|
|
||||||
ip = "10.0.0.1"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[servers.beta]
|
|
||||||
ip = "10.0.0.2"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[clients]
|
|
||||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
||||||
|
|
||||||
# Line breaks are OK when inside arrays
|
|
||||||
hosts = [
|
|
||||||
"alpha",
|
|
||||||
"omega"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
And the corresponding Go types are:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type tomlConfig struct {
|
|
||||||
Title string
|
|
||||||
Owner ownerInfo
|
|
||||||
DB database `toml:"database"`
|
|
||||||
Servers map[string]server
|
|
||||||
Clients clients
|
|
||||||
}
|
|
||||||
|
|
||||||
type ownerInfo struct {
|
|
||||||
Name string
|
|
||||||
Org string `toml:"organization"`
|
|
||||||
Bio string
|
|
||||||
DOB time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type database struct {
|
|
||||||
Server string
|
|
||||||
Ports []int
|
|
||||||
ConnMax int `toml:"connection_max"`
|
|
||||||
Enabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type server struct {
|
|
||||||
IP string
|
|
||||||
DC string
|
|
||||||
}
|
|
||||||
|
|
||||||
type clients struct {
|
|
||||||
Data [][]interface{}
|
|
||||||
Hosts []string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that a case insensitive match will be tried if an exact match can't be
|
|
||||||
found.
|
|
||||||
|
|
||||||
A working example of the above can be found in `_examples/example.{go,toml}`.
|
|
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
509
vendor/github.com/BurntSushi/toml/decode.go
generated
vendored
|
@ -1,509 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func e(format string, args ...interface{}) error {
|
|
||||||
return fmt.Errorf("toml: "+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is the interface implemented by objects that can unmarshal a
|
|
||||||
// TOML description of themselves.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
UnmarshalTOML(interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
|
|
||||||
func Unmarshal(p []byte, v interface{}) error {
|
|
||||||
_, err := Decode(string(p), v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitive is a TOML value that hasn't been decoded into a Go value.
|
|
||||||
// When using the various `Decode*` functions, the type `Primitive` may
|
|
||||||
// be given to any value, and its decoding will be delayed.
|
|
||||||
//
|
|
||||||
// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
|
|
||||||
//
|
|
||||||
// The underlying representation of a `Primitive` value is subject to change.
|
|
||||||
// Do not rely on it.
|
|
||||||
//
|
|
||||||
// N.B. Primitive values are still parsed, so using them will only avoid
|
|
||||||
// the overhead of reflection. They can be useful when you don't know the
|
|
||||||
// exact type of TOML data until run time.
|
|
||||||
type Primitive struct {
|
|
||||||
undecoded interface{}
|
|
||||||
context Key
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED!
|
|
||||||
//
|
|
||||||
// Use MetaData.PrimitiveDecode instead.
|
|
||||||
func PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md := MetaData{decoded: make(map[string]bool)}
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrimitiveDecode is just like the other `Decode*` functions, except it
|
|
||||||
// decodes a TOML value that has already been parsed. Valid primitive values
|
|
||||||
// can *only* be obtained from values filled by the decoder functions,
|
|
||||||
// including this method. (i.e., `v` may contain more `Primitive`
|
|
||||||
// values.)
|
|
||||||
//
|
|
||||||
// Meta data for primitive values is included in the meta data returned by
|
|
||||||
// the `Decode*` functions with one exception: keys returned by the Undecoded
|
|
||||||
// method will only reflect keys that were decoded. Namely, any keys hidden
|
|
||||||
// behind a Primitive will be considered undecoded. Executing this method will
|
|
||||||
// update the undecoded keys in the meta data. (See the example.)
|
|
||||||
func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
|
|
||||||
md.context = primValue.context
|
|
||||||
defer func() { md.context = nil }()
|
|
||||||
return md.unify(primValue.undecoded, rvalue(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode will decode the contents of `data` in TOML format into a pointer
|
|
||||||
// `v`.
|
|
||||||
//
|
|
||||||
// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
|
|
||||||
// used interchangeably.)
|
|
||||||
//
|
|
||||||
// TOML arrays of tables correspond to either a slice of structs or a slice
|
|
||||||
// of maps.
|
|
||||||
//
|
|
||||||
// TOML datetimes correspond to Go `time.Time` values.
|
|
||||||
//
|
|
||||||
// All other TOML types (float, string, int, bool and array) correspond
|
|
||||||
// to the obvious Go types.
|
|
||||||
//
|
|
||||||
// An exception to the above rules is if a type implements the
|
|
||||||
// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
|
|
||||||
// (floats, strings, integers, booleans and datetimes) will be converted to
|
|
||||||
// a byte string and given to the value's UnmarshalText method. See the
|
|
||||||
// Unmarshaler example for a demonstration with time duration strings.
|
|
||||||
//
|
|
||||||
// Key mapping
|
|
||||||
//
|
|
||||||
// TOML keys can map to either keys in a Go map or field names in a Go
|
|
||||||
// struct. The special `toml` struct tag may be used to map TOML keys to
|
|
||||||
// struct fields that don't match the key name exactly. (See the example.)
|
|
||||||
// A case insensitive match to struct names will be tried if an exact match
|
|
||||||
// can't be found.
|
|
||||||
//
|
|
||||||
// The mapping between TOML values and Go values is loose. That is, there
|
|
||||||
// may exist TOML values that cannot be placed into your representation, and
|
|
||||||
// there may be parts of your representation that do not correspond to
|
|
||||||
// TOML values. This loose mapping can be made stricter by using the IsDefined
|
|
||||||
// and/or Undecoded methods on the MetaData returned.
|
|
||||||
//
|
|
||||||
// This decoder will not handle cyclic types. If a cyclic type is passed,
|
|
||||||
// `Decode` will not terminate.
|
|
||||||
func Decode(data string, v interface{}) (MetaData, error) {
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.Kind() != reflect.Ptr {
|
|
||||||
return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
|
|
||||||
}
|
|
||||||
p, err := parse(data)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
md := MetaData{
|
|
||||||
p.mapping, p.types, p.ordered,
|
|
||||||
make(map[string]bool, len(p.ordered)), nil,
|
|
||||||
}
|
|
||||||
return md, md.unify(p.mapping, indirect(rv))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFile is just like Decode, except it will automatically read the
|
|
||||||
// contents of the file at `fpath` and decode it for you.
|
|
||||||
func DecodeFile(fpath string, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadFile(fpath)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeReader is just like Decode, except it will consume all bytes
|
|
||||||
// from the reader and decode it for you.
|
|
||||||
func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
|
|
||||||
bs, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return MetaData{}, err
|
|
||||||
}
|
|
||||||
return Decode(string(bs), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unify performs a sort of type unification based on the structure of `rv`,
|
|
||||||
// which is the client representation.
|
|
||||||
//
|
|
||||||
// Any type mismatch produces an error. Finding a type that we don't know
|
|
||||||
// how to handle produces an unsupported type error.
|
|
||||||
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
|
|
||||||
|
|
||||||
// Special case. Look for a `Primitive` value.
|
|
||||||
if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
|
|
||||||
// Save the undecoded data and the key context into the primitive
|
|
||||||
// value.
|
|
||||||
context := make(Key, len(md.context))
|
|
||||||
copy(context, md.context)
|
|
||||||
rv.Set(reflect.ValueOf(Primitive{
|
|
||||||
undecoded: data,
|
|
||||||
context: context,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Unmarshaler Interface support.
|
|
||||||
if rv.CanAddr() {
|
|
||||||
if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
|
|
||||||
return v.UnmarshalTOML(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Handle time.Time values specifically.
|
|
||||||
// TODO: Remove this code when we decide to drop support for Go 1.1.
|
|
||||||
// This isn't necessary in Go 1.2 because time.Time satisfies the encoding
|
|
||||||
// interfaces.
|
|
||||||
if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
|
|
||||||
return md.unifyDatetime(data, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case. Look for a value satisfying the TextUnmarshaler interface.
|
|
||||||
if v, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return md.unifyText(data, v)
|
|
||||||
}
|
|
||||||
// BUG(burntsushi)
|
|
||||||
// The behavior here is incorrect whenever a Go type satisfies the
|
|
||||||
// encoding.TextUnmarshaler interface but also corresponds to a TOML
|
|
||||||
// hash or array. In particular, the unmarshaler should only be applied
|
|
||||||
// to primitive TOML values. But at this point, it will be applied to
|
|
||||||
// all kinds of values and produce an incorrect error whenever those values
|
|
||||||
// are hashes or arrays (including arrays of tables).
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
|
|
||||||
// laziness
|
|
||||||
if k >= reflect.Int && k <= reflect.Uint64 {
|
|
||||||
return md.unifyInt(data, rv)
|
|
||||||
}
|
|
||||||
switch k {
|
|
||||||
case reflect.Ptr:
|
|
||||||
elem := reflect.New(rv.Type().Elem())
|
|
||||||
err := md.unify(data, reflect.Indirect(elem))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rv.Set(elem)
|
|
||||||
return nil
|
|
||||||
case reflect.Struct:
|
|
||||||
return md.unifyStruct(data, rv)
|
|
||||||
case reflect.Map:
|
|
||||||
return md.unifyMap(data, rv)
|
|
||||||
case reflect.Array:
|
|
||||||
return md.unifyArray(data, rv)
|
|
||||||
case reflect.Slice:
|
|
||||||
return md.unifySlice(data, rv)
|
|
||||||
case reflect.String:
|
|
||||||
return md.unifyString(data, rv)
|
|
||||||
case reflect.Bool:
|
|
||||||
return md.unifyBool(data, rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
// we only support empty interfaces.
|
|
||||||
if rv.NumMethod() > 0 {
|
|
||||||
return e("unsupported type %s", rv.Type())
|
|
||||||
}
|
|
||||||
return md.unifyAnything(data, rv)
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
return md.unifyFloat64(data, rv)
|
|
||||||
}
|
|
||||||
return e("unsupported type %s", rv.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if mapping == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return e("type mismatch for %s: expected table but found %T",
|
|
||||||
rv.Type().String(), mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, datum := range tmap {
|
|
||||||
var f *field
|
|
||||||
fields := cachedTypeFields(rv.Type())
|
|
||||||
for i := range fields {
|
|
||||||
ff := &fields[i]
|
|
||||||
if ff.name == key {
|
|
||||||
f = ff
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f == nil && strings.EqualFold(ff.name, key) {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f != nil {
|
|
||||||
subv := rv
|
|
||||||
for _, i := range f.index {
|
|
||||||
subv = indirect(subv.Field(i))
|
|
||||||
}
|
|
||||||
if isUnifiable(subv) {
|
|
||||||
md.decoded[md.context.add(key).String()] = true
|
|
||||||
md.context = append(md.context, key)
|
|
||||||
if err := md.unify(datum, subv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
} else if f.name != "" {
|
|
||||||
// Bad user! No soup for you!
|
|
||||||
return e("cannot write unexported field %s.%s",
|
|
||||||
rv.Type().String(), f.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
|
|
||||||
tmap, ok := mapping.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
if tmap == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("map", mapping)
|
|
||||||
}
|
|
||||||
if rv.IsNil() {
|
|
||||||
rv.Set(reflect.MakeMap(rv.Type()))
|
|
||||||
}
|
|
||||||
for k, v := range tmap {
|
|
||||||
md.decoded[md.context.add(k).String()] = true
|
|
||||||
md.context = append(md.context, k)
|
|
||||||
|
|
||||||
rvkey := indirect(reflect.New(rv.Type().Key()))
|
|
||||||
rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
|
|
||||||
if err := md.unify(v, rvval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
md.context = md.context[0 : len(md.context)-1]
|
|
||||||
|
|
||||||
rvkey.SetString(k)
|
|
||||||
rv.SetMapIndex(rvkey, rvval)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
sliceLen := datav.Len()
|
|
||||||
if sliceLen != rv.Len() {
|
|
||||||
return e("expected array length %d; got TOML array of length %d",
|
|
||||||
rv.Len(), sliceLen)
|
|
||||||
}
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
|
|
||||||
datav := reflect.ValueOf(data)
|
|
||||||
if datav.Kind() != reflect.Slice {
|
|
||||||
if !datav.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("slice", data)
|
|
||||||
}
|
|
||||||
n := datav.Len()
|
|
||||||
if rv.IsNil() || rv.Cap() < n {
|
|
||||||
rv.Set(reflect.MakeSlice(rv.Type(), n, n))
|
|
||||||
}
|
|
||||||
rv.SetLen(n)
|
|
||||||
return md.unifySliceArray(datav, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
|
|
||||||
sliceLen := data.Len()
|
|
||||||
for i := 0; i < sliceLen; i++ {
|
|
||||||
v := data.Index(i).Interface()
|
|
||||||
sliceval := indirect(rv.Index(i))
|
|
||||||
if err := md.unify(v, sliceval); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
|
|
||||||
if _, ok := data.(time.Time); ok {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("time.Time", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
|
|
||||||
if s, ok := data.(string); ok {
|
|
||||||
rv.SetString(s)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("string", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(float64); ok {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Float32:
|
|
||||||
fallthrough
|
|
||||||
case reflect.Float64:
|
|
||||||
rv.SetFloat(num)
|
|
||||||
default:
|
|
||||||
panic("bug")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("float", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
|
|
||||||
if num, ok := data.(int64); ok {
|
|
||||||
if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Int8:
|
|
||||||
if num < math.MinInt8 || num > math.MaxInt8 {
|
|
||||||
return e("value %d is out of range for int8", num)
|
|
||||||
}
|
|
||||||
case reflect.Int16:
|
|
||||||
if num < math.MinInt16 || num > math.MaxInt16 {
|
|
||||||
return e("value %d is out of range for int16", num)
|
|
||||||
}
|
|
||||||
case reflect.Int32:
|
|
||||||
if num < math.MinInt32 || num > math.MaxInt32 {
|
|
||||||
return e("value %d is out of range for int32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetInt(num)
|
|
||||||
} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
|
|
||||||
unum := uint64(num)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Uint, reflect.Uint64:
|
|
||||||
// No bounds checking necessary.
|
|
||||||
case reflect.Uint8:
|
|
||||||
if num < 0 || unum > math.MaxUint8 {
|
|
||||||
return e("value %d is out of range for uint8", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint16:
|
|
||||||
if num < 0 || unum > math.MaxUint16 {
|
|
||||||
return e("value %d is out of range for uint16", num)
|
|
||||||
}
|
|
||||||
case reflect.Uint32:
|
|
||||||
if num < 0 || unum > math.MaxUint32 {
|
|
||||||
return e("value %d is out of range for uint32", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rv.SetUint(unum)
|
|
||||||
} else {
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("integer", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
|
|
||||||
if b, ok := data.(bool); ok {
|
|
||||||
rv.SetBool(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return badtype("boolean", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
|
|
||||||
rv.Set(reflect.ValueOf(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
|
|
||||||
var s string
|
|
||||||
switch sdata := data.(type) {
|
|
||||||
case TextMarshaler:
|
|
||||||
text, err := sdata.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s = string(text)
|
|
||||||
case fmt.Stringer:
|
|
||||||
s = sdata.String()
|
|
||||||
case string:
|
|
||||||
s = sdata
|
|
||||||
case bool:
|
|
||||||
s = fmt.Sprintf("%v", sdata)
|
|
||||||
case int64:
|
|
||||||
s = fmt.Sprintf("%d", sdata)
|
|
||||||
case float64:
|
|
||||||
s = fmt.Sprintf("%f", sdata)
|
|
||||||
default:
|
|
||||||
return badtype("primitive (string-like)", data)
|
|
||||||
}
|
|
||||||
if err := v.UnmarshalText([]byte(s)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
|
|
||||||
func rvalue(v interface{}) reflect.Value {
|
|
||||||
return indirect(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// indirect returns the value pointed to by a pointer.
|
|
||||||
// Pointers are followed until the value is not a pointer.
|
|
||||||
// New values are allocated for each nil pointer.
|
|
||||||
//
|
|
||||||
// An exception to this rule is if the value satisfies an interface of
|
|
||||||
// interest to us (like encoding.TextUnmarshaler).
|
|
||||||
func indirect(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() != reflect.Ptr {
|
|
||||||
if v.CanSet() {
|
|
||||||
pv := v.Addr()
|
|
||||||
if _, ok := pv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
if v.IsNil() {
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
}
|
|
||||||
return indirect(reflect.Indirect(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUnifiable(rv reflect.Value) bool {
|
|
||||||
if rv.CanSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := rv.Interface().(TextUnmarshaler); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func badtype(expected string, data interface{}) error {
|
|
||||||
return e("cannot load TOML value of type %T into a Go %s", data, expected)
|
|
||||||
}
|
|
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
121
vendor/github.com/BurntSushi/toml/decode_meta.go
generated
vendored
|
@ -1,121 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// MetaData allows access to meta information about TOML data that may not
|
|
||||||
// be inferrable via reflection. In particular, whether a key has been defined
|
|
||||||
// and the TOML type of a key.
|
|
||||||
type MetaData struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
keys []Key
|
|
||||||
decoded map[string]bool
|
|
||||||
context Key // Used only during decoding.
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDefined returns true if the key given exists in the TOML data. The key
|
|
||||||
// should be specified hierarchially. e.g.,
|
|
||||||
//
|
|
||||||
// // access the TOML key 'a.b.c'
|
|
||||||
// IsDefined("a", "b", "c")
|
|
||||||
//
|
|
||||||
// IsDefined will return false if an empty key given. Keys are case sensitive.
|
|
||||||
func (md *MetaData) IsDefined(key ...string) bool {
|
|
||||||
if len(key) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash map[string]interface{}
|
|
||||||
var ok bool
|
|
||||||
var hashOrVal interface{} = md.mapping
|
|
||||||
for _, k := range key {
|
|
||||||
if hash, ok = hashOrVal.(map[string]interface{}); !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if hashOrVal, ok = hash[k]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns a string representation of the type of the key specified.
|
|
||||||
//
|
|
||||||
// Type will return the empty string if given an empty key or a key that
|
|
||||||
// does not exist. Keys are case sensitive.
|
|
||||||
func (md *MetaData) Type(key ...string) string {
|
|
||||||
fullkey := strings.Join(key, ".")
|
|
||||||
if typ, ok := md.types[fullkey]; ok {
|
|
||||||
return typ.typeString()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
|
|
||||||
// to get values of this type.
|
|
||||||
type Key []string
|
|
||||||
|
|
||||||
func (k Key) String() string {
|
|
||||||
return strings.Join(k, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuotedAll() string {
|
|
||||||
var ss []string
|
|
||||||
for i := range k {
|
|
||||||
ss = append(ss, k.maybeQuoted(i))
|
|
||||||
}
|
|
||||||
return strings.Join(ss, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) maybeQuoted(i int) string {
|
|
||||||
quote := false
|
|
||||||
for _, c := range k[i] {
|
|
||||||
if !isBareKeyChar(c) {
|
|
||||||
quote = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if quote {
|
|
||||||
return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
|
|
||||||
}
|
|
||||||
return k[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k Key) add(piece string) Key {
|
|
||||||
newKey := make(Key, len(k)+1)
|
|
||||||
copy(newKey, k)
|
|
||||||
newKey[len(k)] = piece
|
|
||||||
return newKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns a slice of every key in the TOML data, including key groups.
|
|
||||||
// Each key is itself a slice, where the first element is the top of the
|
|
||||||
// hierarchy and the last is the most specific.
|
|
||||||
//
|
|
||||||
// The list will have the same order as the keys appeared in the TOML data.
|
|
||||||
//
|
|
||||||
// All keys returned are non-empty.
|
|
||||||
func (md *MetaData) Keys() []Key {
|
|
||||||
return md.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Undecoded returns all keys that have not been decoded in the order in which
|
|
||||||
// they appear in the original TOML document.
|
|
||||||
//
|
|
||||||
// This includes keys that haven't been decoded because of a Primitive value.
|
|
||||||
// Once the Primitive value is decoded, the keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// Also note that decoding into an empty interface will result in no decoding,
|
|
||||||
// and so no keys will be considered decoded.
|
|
||||||
//
|
|
||||||
// In this sense, the Undecoded keys correspond to keys in the TOML document
|
|
||||||
// that do not have a concrete type in your representation.
|
|
||||||
func (md *MetaData) Undecoded() []Key {
|
|
||||||
undecoded := make([]Key, 0, len(md.keys))
|
|
||||||
for _, key := range md.keys {
|
|
||||||
if !md.decoded[key.String()] {
|
|
||||||
undecoded = append(undecoded, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undecoded
|
|
||||||
}
|
|
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
27
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
Package toml provides facilities for decoding and encoding TOML configuration
|
|
||||||
files via reflection. There is also support for delaying decoding with
|
|
||||||
the Primitive type, and querying the set of keys in a TOML document with the
|
|
||||||
MetaData type.
|
|
||||||
|
|
||||||
The specification implemented: https://github.com/toml-lang/toml
|
|
||||||
|
|
||||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
|
||||||
whether a file is a valid TOML document. It can also be used to print the
|
|
||||||
type of each key in a TOML document.
|
|
||||||
|
|
||||||
Testing
|
|
||||||
|
|
||||||
There are two important types of tests used for this package. The first is
|
|
||||||
contained inside '*_test.go' files and uses the standard Go unit testing
|
|
||||||
framework. These tests are primarily devoted to holistically testing the
|
|
||||||
decoder and encoder.
|
|
||||||
|
|
||||||
The second type of testing is used to verify the implementation's adherence
|
|
||||||
to the TOML specification. These tests have been factored into their own
|
|
||||||
project: https://github.com/BurntSushi/toml-test
|
|
||||||
|
|
||||||
The reason the tests are in a separate project is so that they can be used by
|
|
||||||
any implementation of TOML. Namely, it is language agnostic.
|
|
||||||
*/
|
|
||||||
package toml
|
|
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
568
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
|
@ -1,568 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlEncodeError struct{ error }
|
|
||||||
|
|
||||||
var (
|
|
||||||
errArrayMixedElementTypes = errors.New(
|
|
||||||
"toml: cannot encode array with mixed element types")
|
|
||||||
errArrayNilElement = errors.New(
|
|
||||||
"toml: cannot encode array with nil element")
|
|
||||||
errNonString = errors.New(
|
|
||||||
"toml: cannot encode a map with non-string key type")
|
|
||||||
errAnonNonStruct = errors.New(
|
|
||||||
"toml: cannot encode an anonymous field that is not a struct")
|
|
||||||
errArrayNoTable = errors.New(
|
|
||||||
"toml: TOML array element cannot contain a table")
|
|
||||||
errNoKey = errors.New(
|
|
||||||
"toml: top-level values must be Go maps or structs")
|
|
||||||
errAnything = errors.New("") // used in testing
|
|
||||||
)
|
|
||||||
|
|
||||||
var quotedReplacer = strings.NewReplacer(
|
|
||||||
"\t", "\\t",
|
|
||||||
"\n", "\\n",
|
|
||||||
"\r", "\\r",
|
|
||||||
"\"", "\\\"",
|
|
||||||
"\\", "\\\\",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encoder controls the encoding of Go values to a TOML document to some
|
|
||||||
// io.Writer.
|
|
||||||
//
|
|
||||||
// The indentation level can be controlled with the Indent field.
|
|
||||||
type Encoder struct {
|
|
||||||
// A single indentation level. By default it is two spaces.
|
|
||||||
Indent string
|
|
||||||
|
|
||||||
// hasWritten is whether we have written any output to w yet.
|
|
||||||
hasWritten bool
|
|
||||||
w *bufio.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
|
|
||||||
// given. By default, a single indentation level is 2 spaces.
|
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
|
||||||
return &Encoder{
|
|
||||||
w: bufio.NewWriter(w),
|
|
||||||
Indent: " ",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode writes a TOML representation of the Go value to the underlying
|
|
||||||
// io.Writer. If the value given cannot be encoded to a valid TOML document,
|
|
||||||
// then an error is returned.
|
|
||||||
//
|
|
||||||
// The mapping between Go values and TOML values should be precisely the same
|
|
||||||
// as for the Decode* functions. Similarly, the TextMarshaler interface is
|
|
||||||
// supported by encoding the resulting bytes as strings. (If you want to write
|
|
||||||
// arbitrary binary data then you will need to use something like base64 since
|
|
||||||
// TOML does not have any binary types.)
|
|
||||||
//
|
|
||||||
// When encoding TOML hashes (i.e., Go maps or structs), keys without any
|
|
||||||
// sub-hashes are encoded first.
|
|
||||||
//
|
|
||||||
// If a Go map is encoded, then its keys are sorted alphabetically for
|
|
||||||
// deterministic output. More control over this behavior may be provided if
|
|
||||||
// there is demand for it.
|
|
||||||
//
|
|
||||||
// Encoding Go values without a corresponding TOML representation---like map
|
|
||||||
// types with non-string keys---will cause an error to be returned. Similarly
|
|
||||||
// for mixed arrays/slices, arrays/slices with nil elements, embedded
|
|
||||||
// non-struct types and nested slices containing maps or structs.
|
|
||||||
// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
|
|
||||||
// and so is []map[string][]string.)
|
|
||||||
func (enc *Encoder) Encode(v interface{}) error {
|
|
||||||
rv := eindirect(reflect.ValueOf(v))
|
|
||||||
if err := enc.safeEncode(Key([]string{}), rv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return enc.w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if terr, ok := r.(tomlEncodeError); ok {
|
|
||||||
err = terr.error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
enc.encode(key, rv)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) encode(key Key, rv reflect.Value) {
|
|
||||||
// Special case. Time needs to be in ISO8601 format.
|
|
||||||
// Special case. If we can marshal the type to text, then we used that.
|
|
||||||
// Basically, this prevents the encoder for handling these types as
|
|
||||||
// generic structs (or whatever the underlying type of a TextMarshaler is).
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time, TextMarshaler:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
k := rv.Kind()
|
|
||||||
switch k {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64,
|
|
||||||
reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
|
|
||||||
enc.eArrayOfTables(key, rv)
|
|
||||||
} else {
|
|
||||||
enc.keyEqElement(key, rv)
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Map:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
case reflect.Ptr:
|
|
||||||
if rv.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
enc.encode(key, rv.Elem())
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eTable(key, rv)
|
|
||||||
default:
|
|
||||||
panic(e("unsupported type for key '%s': %s", key, k))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// eElement encodes any value that can be an array element (primitives and
|
|
||||||
// arrays).
|
|
||||||
func (enc *Encoder) eElement(rv reflect.Value) {
|
|
||||||
switch v := rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
// Special case time.Time as a primitive. Has to come before
|
|
||||||
// TextMarshaler below because time.Time implements
|
|
||||||
// encoding.TextMarshaler, but we need to always use UTC.
|
|
||||||
enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
|
|
||||||
return
|
|
||||||
case TextMarshaler:
|
|
||||||
// Special case. Use text marshaler if it's available for this value.
|
|
||||||
if s, err := v.MarshalText(); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
} else {
|
|
||||||
enc.writeQuoted(string(s))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
enc.wf(strconv.FormatBool(rv.Bool()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64:
|
|
||||||
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
|
||||||
reflect.Uint32, reflect.Uint64:
|
|
||||||
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
|
||||||
case reflect.Float32:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
|
|
||||||
case reflect.Float64:
|
|
||||||
enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
enc.eArrayOrSliceElement(rv)
|
|
||||||
case reflect.Interface:
|
|
||||||
enc.eElement(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
enc.writeQuoted(rv.String())
|
|
||||||
default:
|
|
||||||
panic(e("unexpected primitive type: %s", rv.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// By the TOML spec, all floats must have a decimal with at least one
|
|
||||||
// number on either side.
|
|
||||||
func floatAddDecimal(fstr string) string {
|
|
||||||
if !strings.Contains(fstr, ".") {
|
|
||||||
return fstr + ".0"
|
|
||||||
}
|
|
||||||
return fstr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) writeQuoted(s string) {
|
|
||||||
enc.wf("\"%s\"", quotedReplacer.Replace(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
|
||||||
length := rv.Len()
|
|
||||||
enc.wf("[")
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
enc.eElement(elem)
|
|
||||||
if i != length-1 {
|
|
||||||
enc.wf(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enc.wf("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
trv := rv.Index(i)
|
|
||||||
if isNil(trv) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.newline()
|
|
||||||
enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
enc.eMapOrStruct(key, trv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
if len(key) == 1 {
|
|
||||||
// Output an extra newline between top-level tables.
|
|
||||||
// (The newline isn't written if nothing else has been written though.)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
if len(key) > 0 {
|
|
||||||
enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
enc.eMapOrStruct(key, rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
|
|
||||||
switch rv := eindirect(rv); rv.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
enc.eMap(key, rv)
|
|
||||||
case reflect.Struct:
|
|
||||||
enc.eStruct(key, rv)
|
|
||||||
default:
|
|
||||||
panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eMap(key Key, rv reflect.Value) {
|
|
||||||
rt := rv.Type()
|
|
||||||
if rt.Key().Kind() != reflect.String {
|
|
||||||
encPanic(errNonString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort keys so that we have deterministic output. And write keys directly
|
|
||||||
// underneath this key first, before writing sub-structs or sub-maps.
|
|
||||||
var mapKeysDirect, mapKeysSub []string
|
|
||||||
for _, mapKey := range rv.MapKeys() {
|
|
||||||
k := mapKey.String()
|
|
||||||
if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
|
|
||||||
mapKeysSub = append(mapKeysSub, k)
|
|
||||||
} else {
|
|
||||||
mapKeysDirect = append(mapKeysDirect, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var writeMapKeys = func(mapKeys []string) {
|
|
||||||
sort.Strings(mapKeys)
|
|
||||||
for _, mapKey := range mapKeys {
|
|
||||||
mrv := rv.MapIndex(reflect.ValueOf(mapKey))
|
|
||||||
if isNil(mrv) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.encode(key.add(mapKey), mrv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeMapKeys(mapKeysDirect)
|
|
||||||
writeMapKeys(mapKeysSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
|
|
||||||
// Write keys for fields directly under this key first, because if we write
|
|
||||||
// a field that creates a new table, then all keys under it will be in that
|
|
||||||
// table (not the one we're writing here).
|
|
||||||
rt := rv.Type()
|
|
||||||
var fieldsDirect, fieldsSub [][]int
|
|
||||||
var addFields func(rt reflect.Type, rv reflect.Value, start []int)
|
|
||||||
addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
|
|
||||||
for i := 0; i < rt.NumField(); i++ {
|
|
||||||
f := rt.Field(i)
|
|
||||||
// skip unexported fields
|
|
||||||
if f.PkgPath != "" && !f.Anonymous {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
frv := rv.Field(i)
|
|
||||||
if f.Anonymous {
|
|
||||||
t := f.Type
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
// Treat anonymous struct fields with
|
|
||||||
// tag names as though they are not
|
|
||||||
// anonymous, like encoding/json does.
|
|
||||||
if getOptions(f.Tag).name == "" {
|
|
||||||
addFields(t, frv, f.Index)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if t.Elem().Kind() == reflect.Struct &&
|
|
||||||
getOptions(f.Tag).name == "" {
|
|
||||||
if !frv.IsNil() {
|
|
||||||
addFields(t.Elem(), frv.Elem(), f.Index)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Fall through to the normal field encoding logic below
|
|
||||||
// for non-struct anonymous fields.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if typeIsHash(tomlTypeOfGo(frv)) {
|
|
||||||
fieldsSub = append(fieldsSub, append(start, f.Index...))
|
|
||||||
} else {
|
|
||||||
fieldsDirect = append(fieldsDirect, append(start, f.Index...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addFields(rt, rv, nil)
|
|
||||||
|
|
||||||
var writeFields = func(fields [][]int) {
|
|
||||||
for _, fieldIndex := range fields {
|
|
||||||
sft := rt.FieldByIndex(fieldIndex)
|
|
||||||
sf := rv.FieldByIndex(fieldIndex)
|
|
||||||
if isNil(sf) {
|
|
||||||
// Don't write anything for nil fields.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := getOptions(sft.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
keyName := sft.Name
|
|
||||||
if opts.name != "" {
|
|
||||||
keyName = opts.name
|
|
||||||
}
|
|
||||||
if opts.omitempty && isEmpty(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if opts.omitzero && isZero(sf) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
enc.encode(key.add(keyName), sf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeFields(fieldsDirect)
|
|
||||||
writeFields(fieldsSub)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlTypeName returns the TOML type name of the Go value's type. It is
|
|
||||||
// used to determine whether the types of array elements are mixed (which is
|
|
||||||
// forbidden). If the Go value is nil, then it is illegal for it to be an array
|
|
||||||
// element, and valueIsNil is returned as true.
|
|
||||||
|
|
||||||
// Returns the TOML type of a Go value. The type may be `nil`, which means
|
|
||||||
// no concrete TOML type could be found.
|
|
||||||
func tomlTypeOfGo(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return tomlBool
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
|
||||||
reflect.Int64,
|
|
||||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
|
||||||
reflect.Uint64:
|
|
||||||
return tomlInteger
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return tomlFloat
|
|
||||||
case reflect.Array, reflect.Slice:
|
|
||||||
if typeEqual(tomlHash, tomlArrayType(rv)) {
|
|
||||||
return tomlArrayHash
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return tomlTypeOfGo(rv.Elem())
|
|
||||||
case reflect.String:
|
|
||||||
return tomlString
|
|
||||||
case reflect.Map:
|
|
||||||
return tomlHash
|
|
||||||
case reflect.Struct:
|
|
||||||
switch rv.Interface().(type) {
|
|
||||||
case time.Time:
|
|
||||||
return tomlDatetime
|
|
||||||
case TextMarshaler:
|
|
||||||
return tomlString
|
|
||||||
default:
|
|
||||||
return tomlHash
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("unexpected reflect.Kind: " + rv.Kind().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// tomlArrayType returns the element type of a TOML array. The type returned
|
|
||||||
// may be nil if it cannot be determined (e.g., a nil slice or a zero length
|
|
||||||
// slize). This function may also panic if it finds a type that cannot be
|
|
||||||
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
|
|
||||||
// nested arrays of tables).
|
|
||||||
func tomlArrayType(rv reflect.Value) tomlType {
|
|
||||||
if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
firstType := tomlTypeOfGo(rv.Index(0))
|
|
||||||
if firstType == nil {
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
rvlen := rv.Len()
|
|
||||||
for i := 1; i < rvlen; i++ {
|
|
||||||
elem := rv.Index(i)
|
|
||||||
switch elemType := tomlTypeOfGo(elem); {
|
|
||||||
case elemType == nil:
|
|
||||||
encPanic(errArrayNilElement)
|
|
||||||
case !typeEqual(firstType, elemType):
|
|
||||||
encPanic(errArrayMixedElementTypes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we have a nested array, then we must make sure that the nested
|
|
||||||
// array contains ONLY primitives.
|
|
||||||
// This checks arbitrarily nested arrays.
|
|
||||||
if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
|
|
||||||
nest := tomlArrayType(eindirect(rv.Index(0)))
|
|
||||||
if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
|
|
||||||
encPanic(errArrayNoTable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstType
|
|
||||||
}
|
|
||||||
|
|
||||||
type tagOptions struct {
|
|
||||||
skip bool // "-"
|
|
||||||
name string
|
|
||||||
omitempty bool
|
|
||||||
omitzero bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOptions(tag reflect.StructTag) tagOptions {
|
|
||||||
t := tag.Get("toml")
|
|
||||||
if t == "-" {
|
|
||||||
return tagOptions{skip: true}
|
|
||||||
}
|
|
||||||
var opts tagOptions
|
|
||||||
parts := strings.Split(t, ",")
|
|
||||||
opts.name = parts[0]
|
|
||||||
for _, s := range parts[1:] {
|
|
||||||
switch s {
|
|
||||||
case "omitempty":
|
|
||||||
opts.omitempty = true
|
|
||||||
case "omitzero":
|
|
||||||
opts.omitzero = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func isZero(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return rv.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return rv.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return rv.Float() == 0.0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isEmpty(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
|
||||||
return rv.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
return !rv.Bool()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) newline() {
|
|
||||||
if enc.hasWritten {
|
|
||||||
enc.wf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
|
|
||||||
if len(key) == 0 {
|
|
||||||
encPanic(errNoKey)
|
|
||||||
}
|
|
||||||
panicIfInvalidKey(key)
|
|
||||||
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
|
||||||
enc.eElement(val)
|
|
||||||
enc.newline()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) wf(format string, v ...interface{}) {
|
|
||||||
if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
|
|
||||||
encPanic(err)
|
|
||||||
}
|
|
||||||
enc.hasWritten = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (enc *Encoder) indentStr(key Key) string {
|
|
||||||
return strings.Repeat(enc.Indent, len(key)-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encPanic(err error) {
|
|
||||||
panic(tomlEncodeError{err})
|
|
||||||
}
|
|
||||||
|
|
||||||
func eindirect(v reflect.Value) reflect.Value {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Ptr, reflect.Interface:
|
|
||||||
return eindirect(v.Elem())
|
|
||||||
default:
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNil(rv reflect.Value) bool {
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
|
||||||
return rv.IsNil()
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func panicIfInvalidKey(key Key) {
|
|
||||||
for _, k := range key {
|
|
||||||
if len(k) == 0 {
|
|
||||||
encPanic(e("Key '%s' is not a valid table name. Key names "+
|
|
||||||
"cannot be empty.", key.maybeQuotedAll()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidKeyName(s string) bool {
|
|
||||||
return len(s) != 0
|
|
||||||
}
|
|
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
19
vendor/github.com/BurntSushi/toml/encoding_types.go
generated
vendored
|
@ -1,19 +0,0 @@
|
||||||
// +build go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// In order to support Go 1.1, we define our own TextMarshaler and
|
|
||||||
// TextUnmarshaler types. For Go 1.2+, we just alias them with the
|
|
||||||
// standard library interfaces.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler encoding.TextMarshaler
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler encoding.TextUnmarshaler
|
|
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
18
vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
// +build !go1.2
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
// These interfaces were introduced in Go 1.2, so we add them manually when
|
|
||||||
// compiling for Go 1.1.
|
|
||||||
|
|
||||||
// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
|
|
||||||
// so that Go 1.1 can be supported.
|
|
||||||
type TextMarshaler interface {
|
|
||||||
MarshalText() (text []byte, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
|
|
||||||
// here so that Go 1.1 can be supported.
|
|
||||||
type TextUnmarshaler interface {
|
|
||||||
UnmarshalText(text []byte) error
|
|
||||||
}
|
|
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
953
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
|
@ -1,953 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type itemType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
itemError itemType = iota
|
|
||||||
itemNIL // used in the parser to indicate no type
|
|
||||||
itemEOF
|
|
||||||
itemText
|
|
||||||
itemString
|
|
||||||
itemRawString
|
|
||||||
itemMultilineString
|
|
||||||
itemRawMultilineString
|
|
||||||
itemBool
|
|
||||||
itemInteger
|
|
||||||
itemFloat
|
|
||||||
itemDatetime
|
|
||||||
itemArray // the start of an array
|
|
||||||
itemArrayEnd
|
|
||||||
itemTableStart
|
|
||||||
itemTableEnd
|
|
||||||
itemArrayTableStart
|
|
||||||
itemArrayTableEnd
|
|
||||||
itemKeyStart
|
|
||||||
itemCommentStart
|
|
||||||
itemInlineTableStart
|
|
||||||
itemInlineTableEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
eof = 0
|
|
||||||
comma = ','
|
|
||||||
tableStart = '['
|
|
||||||
tableEnd = ']'
|
|
||||||
arrayTableStart = '['
|
|
||||||
arrayTableEnd = ']'
|
|
||||||
tableSep = '.'
|
|
||||||
keySep = '='
|
|
||||||
arrayStart = '['
|
|
||||||
arrayEnd = ']'
|
|
||||||
commentStart = '#'
|
|
||||||
stringStart = '"'
|
|
||||||
stringEnd = '"'
|
|
||||||
rawStringStart = '\''
|
|
||||||
rawStringEnd = '\''
|
|
||||||
inlineTableStart = '{'
|
|
||||||
inlineTableEnd = '}'
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateFn func(lx *lexer) stateFn
|
|
||||||
|
|
||||||
type lexer struct {
|
|
||||||
input string
|
|
||||||
start int
|
|
||||||
pos int
|
|
||||||
line int
|
|
||||||
state stateFn
|
|
||||||
items chan item
|
|
||||||
|
|
||||||
// Allow for backing up up to three runes.
|
|
||||||
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
|
||||||
prevWidths [3]int
|
|
||||||
nprev int // how many of prevWidths are in use
|
|
||||||
// If we emit an eof, we can still back up, but it is not OK to call
|
|
||||||
// next again.
|
|
||||||
atEOF bool
|
|
||||||
|
|
||||||
// A stack of state functions used to maintain context.
|
|
||||||
// The idea is to reuse parts of the state machine in various places.
|
|
||||||
// For example, values can appear at the top level or within arbitrarily
|
|
||||||
// nested arrays. The last state on the stack is used after a value has
|
|
||||||
// been lexed. Similarly for comments.
|
|
||||||
stack []stateFn
|
|
||||||
}
|
|
||||||
|
|
||||||
type item struct {
|
|
||||||
typ itemType
|
|
||||||
val string
|
|
||||||
line int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) nextItem() item {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case item := <-lx.items:
|
|
||||||
return item
|
|
||||||
default:
|
|
||||||
lx.state = lx.state(lx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lex(input string) *lexer {
|
|
||||||
lx := &lexer{
|
|
||||||
input: input,
|
|
||||||
state: lexTop,
|
|
||||||
line: 1,
|
|
||||||
items: make(chan item, 10),
|
|
||||||
stack: make([]stateFn, 0, 10),
|
|
||||||
}
|
|
||||||
return lx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) push(state stateFn) {
|
|
||||||
lx.stack = append(lx.stack, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) pop() stateFn {
|
|
||||||
if len(lx.stack) == 0 {
|
|
||||||
return lx.errorf("BUG in lexer: no states to pop")
|
|
||||||
}
|
|
||||||
last := lx.stack[len(lx.stack)-1]
|
|
||||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
|
||||||
return last
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) current() string {
|
|
||||||
return lx.input[lx.start:lx.pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emit(typ itemType) {
|
|
||||||
lx.items <- item{typ, lx.current(), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) emitTrim(typ itemType) {
|
|
||||||
lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lx *lexer) next() (r rune) {
|
|
||||||
if lx.atEOF {
|
|
||||||
panic("next called after EOF")
|
|
||||||
}
|
|
||||||
if lx.pos >= len(lx.input) {
|
|
||||||
lx.atEOF = true
|
|
||||||
return eof
|
|
||||||
}
|
|
||||||
|
|
||||||
if lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line++
|
|
||||||
}
|
|
||||||
lx.prevWidths[2] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[0]
|
|
||||||
if lx.nprev < 3 {
|
|
||||||
lx.nprev++
|
|
||||||
}
|
|
||||||
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
|
||||||
lx.prevWidths[0] = w
|
|
||||||
lx.pos += w
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore skips over the pending input before this point.
|
|
||||||
func (lx *lexer) ignore() {
|
|
||||||
lx.start = lx.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup steps back one rune. Can be called only twice between calls to next.
|
|
||||||
func (lx *lexer) backup() {
|
|
||||||
if lx.atEOF {
|
|
||||||
lx.atEOF = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if lx.nprev < 1 {
|
|
||||||
panic("backed up too far")
|
|
||||||
}
|
|
||||||
w := lx.prevWidths[0]
|
|
||||||
lx.prevWidths[0] = lx.prevWidths[1]
|
|
||||||
lx.prevWidths[1] = lx.prevWidths[2]
|
|
||||||
lx.nprev--
|
|
||||||
lx.pos -= w
|
|
||||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
|
||||||
lx.line--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// accept consumes the next rune if it's equal to `valid`.
|
|
||||||
func (lx *lexer) accept(valid rune) bool {
|
|
||||||
if lx.next() == valid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek returns but does not consume the next rune in the input.
|
|
||||||
func (lx *lexer) peek() rune {
|
|
||||||
r := lx.next()
|
|
||||||
lx.backup()
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip ignores all input that matches the given predicate.
|
|
||||||
func (lx *lexer) skip(pred func(rune) bool) {
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if pred(r) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.ignore()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
|
||||||
// Note that any value that is a character is escaped if it's a special
|
|
||||||
// character (newlines, tabs, etc.).
|
|
||||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
|
||||||
lx.items <- item{
|
|
||||||
itemError,
|
|
||||||
fmt.Sprintf(format, values...),
|
|
||||||
lx.line,
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTop consumes elements at the top level of TOML data.
|
|
||||||
func lexTop(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isWhitespace(r) || isNL(r) {
|
|
||||||
return lexSkip(lx, lexTop)
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case commentStart:
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case tableStart:
|
|
||||||
return lexTableStart
|
|
||||||
case eof:
|
|
||||||
if lx.pos > lx.start {
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point, the only valid item can be a key, so we back up
|
|
||||||
// and let the key lexer do the rest.
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexTopEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
|
||||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
|
||||||
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
|
||||||
func lexTopEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == commentStart:
|
|
||||||
// a comment will read to a newline for us.
|
|
||||||
lx.push(lexTop)
|
|
||||||
return lexCommentStart
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTopEnd
|
|
||||||
case isNL(r):
|
|
||||||
lx.ignore()
|
|
||||||
return lexTop
|
|
||||||
case r == eof:
|
|
||||||
lx.emit(itemEOF)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a top-level item to end with a newline, "+
|
|
||||||
"comment, or EOF, but got %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
|
||||||
// it starts with a character other than '.' and ']'.
|
|
||||||
// It assumes that '[' has already been consumed.
|
|
||||||
// It also handles the case that this is an item in an array of tables.
|
|
||||||
// e.g., '[[name]]'.
|
|
||||||
func lexTableStart(lx *lexer) stateFn {
|
|
||||||
if lx.peek() == arrayTableStart {
|
|
||||||
lx.next()
|
|
||||||
lx.emit(itemArrayTableStart)
|
|
||||||
lx.push(lexArrayTableEnd)
|
|
||||||
} else {
|
|
||||||
lx.emit(itemTableStart)
|
|
||||||
lx.push(lexTableEnd)
|
|
||||||
}
|
|
||||||
return lexTableNameStart
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.emit(itemTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
|
||||||
if r := lx.next(); r != arrayTableEnd {
|
|
||||||
return lx.errorf("expected end of table array name delimiter %q, "+
|
|
||||||
"but got %q instead", arrayTableEnd, r)
|
|
||||||
}
|
|
||||||
lx.emit(itemArrayTableEnd)
|
|
||||||
return lexTopEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexTableNameStart(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.peek(); {
|
|
||||||
case r == tableEnd || r == eof:
|
|
||||||
return lx.errorf("unexpected end of table name " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == tableSep:
|
|
||||||
return lx.errorf("unexpected table separator " +
|
|
||||||
"(table names cannot be empty)")
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.push(lexTableNameEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareTableName lexes the name of a table. It assumes that at least one
|
|
||||||
// valid character for the table has already been read.
|
|
||||||
func lexBareTableName(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isBareKeyChar(r) {
|
|
||||||
return lexBareTableName
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexTableNameEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexTableNameEnd reads the end of a piece of a table name, optionally
|
|
||||||
// consuming whitespace.
|
|
||||||
func lexTableNameEnd(lx *lexer) stateFn {
|
|
||||||
lx.skip(isWhitespace)
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexTableNameEnd
|
|
||||||
case r == tableSep:
|
|
||||||
lx.ignore()
|
|
||||||
return lexTableNameStart
|
|
||||||
case r == tableEnd:
|
|
||||||
return lx.pop()
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected '.' or ']' to end table name, "+
|
|
||||||
"but got %q instead", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyStart consumes a key name up until the first non-whitespace character.
|
|
||||||
// lexKeyStart will ignore whitespace.
|
|
||||||
func lexKeyStart(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
switch {
|
|
||||||
case r == keySep:
|
|
||||||
return lx.errorf("unexpected key separator %q", keySep)
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
lx.next()
|
|
||||||
return lexSkip(lx, lexKeyStart)
|
|
||||||
case r == stringStart || r == rawStringStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
lx.push(lexKeyEnd)
|
|
||||||
return lexValue // reuse string lexing
|
|
||||||
default:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemKeyStart)
|
|
||||||
return lexBareKey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBareKey consumes the text of a bare key. Assumes that the first character
|
|
||||||
// (which is not whitespace) has not yet been consumed.
|
|
||||||
func lexBareKey(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case isBareKeyChar(r):
|
|
||||||
return lexBareKey
|
|
||||||
case isWhitespace(r):
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
case r == keySep:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lexKeyEnd
|
|
||||||
default:
|
|
||||||
return lx.errorf("bare keys cannot contain %q", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
|
|
||||||
// separator).
|
|
||||||
func lexKeyEnd(lx *lexer) stateFn {
|
|
||||||
switch r := lx.next(); {
|
|
||||||
case r == keySep:
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexKeyEnd)
|
|
||||||
default:
|
|
||||||
return lx.errorf("expected key separator %q, but got %q instead",
|
|
||||||
keySep, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexValue starts the consumption of a value anywhere a value is expected.
|
|
||||||
// lexValue will ignore whitespace.
|
|
||||||
// After a value is lexed, the last state on the next is popped and returned.
|
|
||||||
func lexValue(lx *lexer) stateFn {
|
|
||||||
// We allow whitespace to precede a value, but NOT newlines.
|
|
||||||
// In array syntax, the array states are responsible for ignoring newlines.
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexValue)
|
|
||||||
case isDigit(r):
|
|
||||||
lx.backup() // avoid an extra state and use the same as above
|
|
||||||
return lexNumberOrDateStart
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case arrayStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArray)
|
|
||||||
return lexArrayValue
|
|
||||||
case inlineTableStart:
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableStart)
|
|
||||||
return lexInlineTableValue
|
|
||||||
case stringStart:
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
if lx.accept(stringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the '"'
|
|
||||||
return lexString
|
|
||||||
case rawStringStart:
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
if lx.accept(rawStringStart) {
|
|
||||||
lx.ignore() // Ignore """
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
lx.ignore() // ignore the "'"
|
|
||||||
return lexRawString
|
|
||||||
case '+', '-':
|
|
||||||
return lexNumberStart
|
|
||||||
case '.': // special error case, be kind to users
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
if unicode.IsLetter(r) {
|
|
||||||
// Be permissive here; lexBool will give a nice error if the
|
|
||||||
// user wrote something like
|
|
||||||
// x = foo
|
|
||||||
// (i.e. not 'true' or 'false' but is something else word-like.)
|
|
||||||
lx.backup()
|
|
||||||
return lexBool
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
|
||||||
// have already been consumed. All whitespace and newlines are ignored.
|
|
||||||
func lexArrayValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValue)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == arrayEnd:
|
|
||||||
// NOTE(caleb): The spec isn't clear about whether you can have
|
|
||||||
// a trailing comma or not, so we'll allow it.
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayValueEnd consumes everything between the end of an array value and
|
|
||||||
// the next value (or the end of the array): it ignores whitespace and newlines
|
|
||||||
// and expects either a ',' or a ']'.
|
|
||||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r) || isNL(r):
|
|
||||||
return lexSkip(lx, lexArrayValueEnd)
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexArrayValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexArrayValue // move on to the next value
|
|
||||||
case r == arrayEnd:
|
|
||||||
return lexArrayEnd
|
|
||||||
}
|
|
||||||
return lx.errorf(
|
|
||||||
"expected a comma or array terminator %q, but got %q instead",
|
|
||||||
arrayEnd, r,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexArrayEnd finishes the lexing of an array.
|
|
||||||
// It assumes that a ']' has just been consumed.
|
|
||||||
func lexArrayEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemArrayEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValue consumes one key/value pair in an inline table.
|
|
||||||
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
|
||||||
func lexInlineTableValue(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValue)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValue)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
return lx.errorf("unexpected comma")
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexKeyStart
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
|
||||||
// key/value pair and the next pair (or the end of the table):
|
|
||||||
// it ignores whitespace and expects either a ',' or a '}'.
|
|
||||||
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case isWhitespace(r):
|
|
||||||
return lexSkip(lx, lexInlineTableValueEnd)
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("newlines not allowed within inline tables")
|
|
||||||
case r == commentStart:
|
|
||||||
lx.push(lexInlineTableValueEnd)
|
|
||||||
return lexCommentStart
|
|
||||||
case r == comma:
|
|
||||||
lx.ignore()
|
|
||||||
return lexInlineTableValue
|
|
||||||
case r == inlineTableEnd:
|
|
||||||
return lexInlineTableEnd
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
|
||||||
"but got %q instead", inlineTableEnd, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexInlineTableEnd finishes the lexing of an inline table.
|
|
||||||
// It assumes that a '}' has just been consumed.
|
|
||||||
func lexInlineTableEnd(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemInlineTableEnd)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexString consumes the inner contents of a string. It assumes that the
|
|
||||||
// beginning '"' has already been consumed and ignored.
|
|
||||||
func lexString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == '\\':
|
|
||||||
lx.push(lexString)
|
|
||||||
return lexStringEscape
|
|
||||||
case r == stringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
|
||||||
// the beginning '"""' has already been consumed and ignored.
|
|
||||||
func lexMultilineString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case '\\':
|
|
||||||
return lexMultilineStringEscape
|
|
||||||
case stringEnd:
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
if lx.accept(stringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexRawString consumes a raw string. Nothing can be escaped in such a string.
|
|
||||||
// It assumes that the beginning "'" has already been consumed and ignored.
|
|
||||||
func lexRawString(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch {
|
|
||||||
case r == eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case isNL(r):
|
|
||||||
return lx.errorf("strings cannot contain newlines")
|
|
||||||
case r == rawStringEnd:
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawString)
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lexRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
|
||||||
// a string. It assumes that the beginning "'''" has already been consumed and
|
|
||||||
// ignored.
|
|
||||||
func lexMultilineRawString(lx *lexer) stateFn {
|
|
||||||
switch lx.next() {
|
|
||||||
case eof:
|
|
||||||
return lx.errorf("unexpected EOF")
|
|
||||||
case rawStringEnd:
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
if lx.accept(rawStringEnd) {
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemRawMultilineString)
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.next()
|
|
||||||
lx.ignore()
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexMultilineRawString
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexMultilineStringEscape consumes an escaped character. It assumes that the
|
|
||||||
// preceding '\\' has already been consumed.
|
|
||||||
func lexMultilineStringEscape(lx *lexer) stateFn {
|
|
||||||
// Handle the special case first:
|
|
||||||
if isNL(lx.next()) {
|
|
||||||
return lexMultilineString
|
|
||||||
}
|
|
||||||
lx.backup()
|
|
||||||
lx.push(lexMultilineString)
|
|
||||||
return lexStringEscape(lx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexStringEscape(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
switch r {
|
|
||||||
case 'b':
|
|
||||||
fallthrough
|
|
||||||
case 't':
|
|
||||||
fallthrough
|
|
||||||
case 'n':
|
|
||||||
fallthrough
|
|
||||||
case 'f':
|
|
||||||
fallthrough
|
|
||||||
case 'r':
|
|
||||||
fallthrough
|
|
||||||
case '"':
|
|
||||||
fallthrough
|
|
||||||
case '\\':
|
|
||||||
return lx.pop()
|
|
||||||
case 'u':
|
|
||||||
return lexShortUnicodeEscape
|
|
||||||
case 'U':
|
|
||||||
return lexLongUnicodeEscape
|
|
||||||
}
|
|
||||||
return lx.errorf("invalid escape character %q; only the following "+
|
|
||||||
"escape characters are allowed: "+
|
|
||||||
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func lexLongUnicodeEscape(lx *lexer) stateFn {
|
|
||||||
var r rune
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
r = lx.next()
|
|
||||||
if !isHexadecimal(r) {
|
|
||||||
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
|
||||||
"but got %q instead", lx.current())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDateStart consumes either an integer, a float, or datetime.
|
|
||||||
func lexNumberOrDateStart(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
case '.':
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
|
||||||
func lexNumberOrDate(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumberOrDate
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-':
|
|
||||||
return lexDatetime
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexDatetime consumes a Datetime, to a first approximation.
|
|
||||||
// The parser validates that it matches one of the accepted formats.
|
|
||||||
func lexDatetime(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '-', 'T', ':', '.', 'Z', '+':
|
|
||||||
return lexDatetime
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemDatetime)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumberStart consumes either an integer or a float. It assumes that a sign
|
|
||||||
// has already been read, but that *no* digits have been consumed.
|
|
||||||
// lexNumberStart will move to the appropriate integer or float states.
|
|
||||||
func lexNumberStart(lx *lexer) stateFn {
|
|
||||||
// We MUST see a digit. Even floats have to start with a digit.
|
|
||||||
r := lx.next()
|
|
||||||
if !isDigit(r) {
|
|
||||||
if r == '.' {
|
|
||||||
return lx.errorf("floats must start with a digit, not '.'")
|
|
||||||
}
|
|
||||||
return lx.errorf("expected a digit but got %q", r)
|
|
||||||
}
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexNumber consumes an integer or a float after seeing the first digit.
|
|
||||||
func lexNumber(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexNumber
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_':
|
|
||||||
return lexNumber
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemInteger)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexFloat consumes the elements of a float. It allows any sequence of
|
|
||||||
// float-like characters, so floats emitted by the lexer are only a first
|
|
||||||
// approximation and must be validated by the parser.
|
|
||||||
func lexFloat(lx *lexer) stateFn {
|
|
||||||
r := lx.next()
|
|
||||||
if isDigit(r) {
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
switch r {
|
|
||||||
case '_', '.', '-', '+', 'e', 'E':
|
|
||||||
return lexFloat
|
|
||||||
}
|
|
||||||
|
|
||||||
lx.backup()
|
|
||||||
lx.emit(itemFloat)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexBool consumes a bool string: 'true' or 'false.
|
|
||||||
func lexBool(lx *lexer) stateFn {
|
|
||||||
var rs []rune
|
|
||||||
for {
|
|
||||||
r := lx.next()
|
|
||||||
if !unicode.IsLetter(r) {
|
|
||||||
lx.backup()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rs = append(rs, r)
|
|
||||||
}
|
|
||||||
s := string(rs)
|
|
||||||
switch s {
|
|
||||||
case "true", "false":
|
|
||||||
lx.emit(itemBool)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
return lx.errorf("expected value but found %q instead", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexCommentStart begins the lexing of a comment. It will emit
|
|
||||||
// itemCommentStart and consume no characters, passing control to lexComment.
|
|
||||||
func lexCommentStart(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
lx.emit(itemCommentStart)
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
|
||||||
// It will consume *up to* the first newline character, and pass control
|
|
||||||
// back to the last state on the stack.
|
|
||||||
func lexComment(lx *lexer) stateFn {
|
|
||||||
r := lx.peek()
|
|
||||||
if isNL(r) || r == eof {
|
|
||||||
lx.emit(itemText)
|
|
||||||
return lx.pop()
|
|
||||||
}
|
|
||||||
lx.next()
|
|
||||||
return lexComment
|
|
||||||
}
|
|
||||||
|
|
||||||
// lexSkip ignores all slurped input and moves on to the next state.
|
|
||||||
func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
|
||||||
return func(lx *lexer) stateFn {
|
|
||||||
lx.ignore()
|
|
||||||
return nextState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isWhitespace returns true if `r` is a whitespace character according
|
|
||||||
// to the spec.
|
|
||||||
func isWhitespace(r rune) bool {
|
|
||||||
return r == '\t' || r == ' '
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNL(r rune) bool {
|
|
||||||
return r == '\n' || r == '\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(r rune) bool {
|
|
||||||
return r >= '0' && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexadecimal(r rune) bool {
|
|
||||||
return (r >= '0' && r <= '9') ||
|
|
||||||
(r >= 'a' && r <= 'f') ||
|
|
||||||
(r >= 'A' && r <= 'F')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBareKeyChar(r rune) bool {
|
|
||||||
return (r >= 'A' && r <= 'Z') ||
|
|
||||||
(r >= 'a' && r <= 'z') ||
|
|
||||||
(r >= '0' && r <= '9') ||
|
|
||||||
r == '_' ||
|
|
||||||
r == '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
func (itype itemType) String() string {
|
|
||||||
switch itype {
|
|
||||||
case itemError:
|
|
||||||
return "Error"
|
|
||||||
case itemNIL:
|
|
||||||
return "NIL"
|
|
||||||
case itemEOF:
|
|
||||||
return "EOF"
|
|
||||||
case itemText:
|
|
||||||
return "Text"
|
|
||||||
case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
|
|
||||||
return "String"
|
|
||||||
case itemBool:
|
|
||||||
return "Bool"
|
|
||||||
case itemInteger:
|
|
||||||
return "Integer"
|
|
||||||
case itemFloat:
|
|
||||||
return "Float"
|
|
||||||
case itemDatetime:
|
|
||||||
return "DateTime"
|
|
||||||
case itemTableStart:
|
|
||||||
return "TableStart"
|
|
||||||
case itemTableEnd:
|
|
||||||
return "TableEnd"
|
|
||||||
case itemKeyStart:
|
|
||||||
return "KeyStart"
|
|
||||||
case itemArray:
|
|
||||||
return "Array"
|
|
||||||
case itemArrayEnd:
|
|
||||||
return "ArrayEnd"
|
|
||||||
case itemCommentStart:
|
|
||||||
return "CommentStart"
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item item) String() string {
|
|
||||||
return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
|
|
||||||
}
|
|
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
592
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
|
@ -1,592 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
type parser struct {
|
|
||||||
mapping map[string]interface{}
|
|
||||||
types map[string]tomlType
|
|
||||||
lx *lexer
|
|
||||||
|
|
||||||
// A list of keys in the order that they appear in the TOML data.
|
|
||||||
ordered []Key
|
|
||||||
|
|
||||||
// the full key for the current hash in scope
|
|
||||||
context Key
|
|
||||||
|
|
||||||
// the base key name for everything except hashes
|
|
||||||
currentKey string
|
|
||||||
|
|
||||||
// rough approximation of line number
|
|
||||||
approxLine int
|
|
||||||
|
|
||||||
// A map of 'key.group.names' to whether they were created implicitly.
|
|
||||||
implicits map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type parseError string
|
|
||||||
|
|
||||||
func (pe parseError) Error() string {
|
|
||||||
return string(pe)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(data string) (p *parser, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
var ok bool
|
|
||||||
if err, ok = r.(parseError); ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
p = &parser{
|
|
||||||
mapping: make(map[string]interface{}),
|
|
||||||
types: make(map[string]tomlType),
|
|
||||||
lx: lex(data),
|
|
||||||
ordered: make([]Key, 0),
|
|
||||||
implicits: make(map[string]bool),
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
item := p.next()
|
|
||||||
if item.typ == itemEOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.topLevel(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) panicf(format string, v ...interface{}) {
|
|
||||||
msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
|
|
||||||
p.approxLine, p.current(), fmt.Sprintf(format, v...))
|
|
||||||
panic(parseError(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) next() item {
|
|
||||||
it := p.lx.nextItem()
|
|
||||||
if it.typ == itemError {
|
|
||||||
p.panicf("%s", it.val)
|
|
||||||
}
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) bug(format string, v ...interface{}) {
|
|
||||||
panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) expect(typ itemType) item {
|
|
||||||
it := p.next()
|
|
||||||
p.assertEqual(typ, it.typ)
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) assertEqual(expected, got itemType) {
|
|
||||||
if expected != got {
|
|
||||||
p.bug("Expected '%s' but got '%s'.", expected, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) topLevel(item item) {
|
|
||||||
switch item.typ {
|
|
||||||
case itemCommentStart:
|
|
||||||
p.approxLine = item.line
|
|
||||||
p.expect(itemText)
|
|
||||||
case itemTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, false)
|
|
||||||
p.setType("", tomlHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemArrayTableStart:
|
|
||||||
kg := p.next()
|
|
||||||
p.approxLine = kg.line
|
|
||||||
|
|
||||||
var key Key
|
|
||||||
for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
|
|
||||||
key = append(key, p.keyString(kg))
|
|
||||||
}
|
|
||||||
p.assertEqual(itemArrayTableEnd, kg.typ)
|
|
||||||
|
|
||||||
p.establishContext(key, true)
|
|
||||||
p.setType("", tomlArrayHash)
|
|
||||||
p.ordered = append(p.ordered, key)
|
|
||||||
case itemKeyStart:
|
|
||||||
kname := p.next()
|
|
||||||
p.approxLine = kname.line
|
|
||||||
p.currentKey = p.keyString(kname)
|
|
||||||
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
p.setValue(p.currentKey, val)
|
|
||||||
p.setType(p.currentKey, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
p.currentKey = ""
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected type at top level: %s", item.typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets a string for a key (or part of a key in a table name).
|
|
||||||
func (p *parser) keyString(it item) string {
|
|
||||||
switch it.typ {
|
|
||||||
case itemText:
|
|
||||||
return it.val
|
|
||||||
case itemString, itemMultilineString,
|
|
||||||
itemRawString, itemRawMultilineString:
|
|
||||||
s, _ := p.value(it)
|
|
||||||
return s.(string)
|
|
||||||
default:
|
|
||||||
p.bug("Unexpected key type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// value translates an expected value from the lexer into a Go value wrapped
|
|
||||||
// as an empty interface.
|
|
||||||
func (p *parser) value(it item) (interface{}, tomlType) {
|
|
||||||
switch it.typ {
|
|
||||||
case itemString:
|
|
||||||
return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemMultilineString:
|
|
||||||
trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
|
|
||||||
return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
|
|
||||||
case itemRawString:
|
|
||||||
return it.val, p.typeOfPrimitive(it)
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return stripFirstNewline(it.val), p.typeOfPrimitive(it)
|
|
||||||
case itemBool:
|
|
||||||
switch it.val {
|
|
||||||
case "true":
|
|
||||||
return true, p.typeOfPrimitive(it)
|
|
||||||
case "false":
|
|
||||||
return false, p.typeOfPrimitive(it)
|
|
||||||
}
|
|
||||||
p.bug("Expected boolean value, but got '%s'.", it.val)
|
|
||||||
case itemInteger:
|
|
||||||
if !numUnderscoresOK(it.val) {
|
|
||||||
p.panicf("Invalid integer %q: underscores must be surrounded by digits",
|
|
||||||
it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseInt(val, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
// Distinguish integer values. Normally, it'd be a bug if the lexer
|
|
||||||
// provides an invalid integer, but it's possible that the number is
|
|
||||||
// out of range of valid values (which the lexer cannot determine).
|
|
||||||
// So mark the former as a bug but the latter as a legitimate user
|
|
||||||
// error.
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Integer '%s' is out of the range of 64-bit "+
|
|
||||||
"signed integers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.bug("Expected integer value, but got '%s'.", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemFloat:
|
|
||||||
parts := strings.FieldsFunc(it.val, func(r rune) bool {
|
|
||||||
switch r {
|
|
||||||
case '.', 'e', 'E':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
for _, part := range parts {
|
|
||||||
if !numUnderscoresOK(part) {
|
|
||||||
p.panicf("Invalid float %q: underscores must be "+
|
|
||||||
"surrounded by digits", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !numPeriodsOK(it.val) {
|
|
||||||
// As a special case, numbers like '123.' or '1.e2',
|
|
||||||
// which are valid as far as Go/strconv are concerned,
|
|
||||||
// must be rejected because TOML says that a fractional
|
|
||||||
// part consists of '.' followed by 1+ digits.
|
|
||||||
p.panicf("Invalid float %q: '.' must be followed "+
|
|
||||||
"by one or more digits", it.val)
|
|
||||||
}
|
|
||||||
val := strings.Replace(it.val, "_", "", -1)
|
|
||||||
num, err := strconv.ParseFloat(val, 64)
|
|
||||||
if err != nil {
|
|
||||||
if e, ok := err.(*strconv.NumError); ok &&
|
|
||||||
e.Err == strconv.ErrRange {
|
|
||||||
|
|
||||||
p.panicf("Float '%s' is out of the range of 64-bit "+
|
|
||||||
"IEEE-754 floating-point numbers.", it.val)
|
|
||||||
} else {
|
|
||||||
p.panicf("Invalid float value: %q", it.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num, p.typeOfPrimitive(it)
|
|
||||||
case itemDatetime:
|
|
||||||
var t time.Time
|
|
||||||
var ok bool
|
|
||||||
var err error
|
|
||||||
for _, format := range []string{
|
|
||||||
"2006-01-02T15:04:05Z07:00",
|
|
||||||
"2006-01-02T15:04:05",
|
|
||||||
"2006-01-02",
|
|
||||||
} {
|
|
||||||
t, err = time.ParseInLocation(format, it.val, time.Local)
|
|
||||||
if err == nil {
|
|
||||||
ok = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
p.panicf("Invalid TOML Datetime: %q.", it.val)
|
|
||||||
}
|
|
||||||
return t, p.typeOfPrimitive(it)
|
|
||||||
case itemArray:
|
|
||||||
array := make([]interface{}, 0)
|
|
||||||
types := make([]tomlType, 0)
|
|
||||||
|
|
||||||
for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, typ := p.value(it)
|
|
||||||
array = append(array, val)
|
|
||||||
types = append(types, typ)
|
|
||||||
}
|
|
||||||
return array, p.typeOfArray(types)
|
|
||||||
case itemInlineTableStart:
|
|
||||||
var (
|
|
||||||
hash = make(map[string]interface{})
|
|
||||||
outerContext = p.context
|
|
||||||
outerKey = p.currentKey
|
|
||||||
)
|
|
||||||
|
|
||||||
p.context = append(p.context, p.currentKey)
|
|
||||||
p.currentKey = ""
|
|
||||||
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
|
||||||
if it.typ != itemKeyStart {
|
|
||||||
p.bug("Expected key start but instead found %q, around line %d",
|
|
||||||
it.val, p.approxLine)
|
|
||||||
}
|
|
||||||
if it.typ == itemCommentStart {
|
|
||||||
p.expect(itemText)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve key
|
|
||||||
k := p.next()
|
|
||||||
p.approxLine = k.line
|
|
||||||
kname := p.keyString(k)
|
|
||||||
|
|
||||||
// retrieve value
|
|
||||||
p.currentKey = kname
|
|
||||||
val, typ := p.value(p.next())
|
|
||||||
// make sure we keep metadata up to date
|
|
||||||
p.setType(kname, typ)
|
|
||||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
|
||||||
hash[kname] = val
|
|
||||||
}
|
|
||||||
p.context = outerContext
|
|
||||||
p.currentKey = outerKey
|
|
||||||
return hash, tomlHash
|
|
||||||
}
|
|
||||||
p.bug("Unexpected value type: %s", it.typ)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// numUnderscoresOK checks whether each underscore in s is surrounded by
|
|
||||||
// characters that are not underscores.
|
|
||||||
func numUnderscoresOK(s string) bool {
|
|
||||||
accept := false
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '_' {
|
|
||||||
if !accept {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
accept = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
accept = true
|
|
||||||
}
|
|
||||||
return accept
|
|
||||||
}
|
|
||||||
|
|
||||||
// numPeriodsOK checks whether every period in s is followed by a digit.
|
|
||||||
func numPeriodsOK(s string) bool {
|
|
||||||
period := false
|
|
||||||
for _, r := range s {
|
|
||||||
if period && !isDigit(r) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
period = r == '.'
|
|
||||||
}
|
|
||||||
return !period
|
|
||||||
}
|
|
||||||
|
|
||||||
// establishContext sets the current context of the parser,
|
|
||||||
// where the context is either a hash or an array of hashes. Which one is
|
|
||||||
// set depends on the value of the `array` parameter.
|
|
||||||
//
|
|
||||||
// Establishing the context also makes sure that the key isn't a duplicate, and
|
|
||||||
// will create implicit hashes automatically.
|
|
||||||
func (p *parser) establishContext(key Key, array bool) {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
// Always start at the top level and drill down for our context.
|
|
||||||
hashContext := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
|
|
||||||
// We only need implicit hashes for key[0:-1]
|
|
||||||
for _, k := range key[0 : len(key)-1] {
|
|
||||||
_, ok = hashContext[k]
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
|
|
||||||
// No key? Make an implicit hash and move on.
|
|
||||||
if !ok {
|
|
||||||
p.addImplicit(keyContext)
|
|
||||||
hashContext[k] = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the hash context is actually an array of tables, then set
|
|
||||||
// the hash context to the last element in that array.
|
|
||||||
//
|
|
||||||
// Otherwise, it better be a table, since this MUST be a key group (by
|
|
||||||
// virtue of it not being the last element in a key).
|
|
||||||
switch t := hashContext[k].(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
hashContext = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hashContext = t
|
|
||||||
default:
|
|
||||||
p.panicf("Key '%s' was already created as a hash.", keyContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.context = keyContext
|
|
||||||
if array {
|
|
||||||
// If this is the first element for this array, then allocate a new
|
|
||||||
// list of tables for it.
|
|
||||||
k := key[len(key)-1]
|
|
||||||
if _, ok := hashContext[k]; !ok {
|
|
||||||
hashContext[k] = make([]map[string]interface{}, 0, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new table. But make sure the key hasn't already been used
|
|
||||||
// for something else.
|
|
||||||
if hash, ok := hashContext[k].([]map[string]interface{}); ok {
|
|
||||||
hashContext[k] = append(hash, make(map[string]interface{}))
|
|
||||||
} else {
|
|
||||||
p.panicf("Key '%s' was already created and cannot be used as "+
|
|
||||||
"an array.", keyContext)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.setValue(key[len(key)-1], make(map[string]interface{}))
|
|
||||||
}
|
|
||||||
p.context = append(p.context, key[len(key)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// setValue sets the given key to the given value in the current context.
|
|
||||||
// It will make sure that the key hasn't already been defined, account for
|
|
||||||
// implicit key groups.
|
|
||||||
func (p *parser) setValue(key string, value interface{}) {
|
|
||||||
var tmpHash interface{}
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
hash := p.mapping
|
|
||||||
keyContext := make(Key, 0)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
if tmpHash, ok = hash[k]; !ok {
|
|
||||||
p.bug("Context for key '%s' has not been established.", keyContext)
|
|
||||||
}
|
|
||||||
switch t := tmpHash.(type) {
|
|
||||||
case []map[string]interface{}:
|
|
||||||
// The context is a table of hashes. Pick the most recent table
|
|
||||||
// defined as the current hash.
|
|
||||||
hash = t[len(t)-1]
|
|
||||||
case map[string]interface{}:
|
|
||||||
hash = t
|
|
||||||
default:
|
|
||||||
p.bug("Expected hash to have type 'map[string]interface{}', but "+
|
|
||||||
"it has '%T' instead.", tmpHash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
|
|
||||||
if _, ok := hash[key]; ok {
|
|
||||||
// Typically, if the given key has already been set, then we have
|
|
||||||
// to raise an error since duplicate keys are disallowed. However,
|
|
||||||
// it's possible that a key was previously defined implicitly. In this
|
|
||||||
// case, it is allowed to be redefined concretely. (See the
|
|
||||||
// `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
|
|
||||||
//
|
|
||||||
// But we have to make sure to stop marking it as an implicit. (So that
|
|
||||||
// another redefinition provokes an error.)
|
|
||||||
//
|
|
||||||
// Note that since it has already been defined (as a hash), we don't
|
|
||||||
// want to overwrite it. So our business is done.
|
|
||||||
if p.isImplicit(keyContext) {
|
|
||||||
p.removeImplicit(keyContext)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we have a concrete key trying to override a previous
|
|
||||||
// key, which is *always* wrong.
|
|
||||||
p.panicf("Key '%s' has already been defined.", keyContext)
|
|
||||||
}
|
|
||||||
hash[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// setType sets the type of a particular value at a given key.
|
|
||||||
// It should be called immediately AFTER setValue.
|
|
||||||
//
|
|
||||||
// Note that if `key` is empty, then the type given will be applied to the
|
|
||||||
// current context (which is either a table or an array of tables).
|
|
||||||
func (p *parser) setType(key string, typ tomlType) {
|
|
||||||
keyContext := make(Key, 0, len(p.context)+1)
|
|
||||||
for _, k := range p.context {
|
|
||||||
keyContext = append(keyContext, k)
|
|
||||||
}
|
|
||||||
if len(key) > 0 { // allow type setting for hashes
|
|
||||||
keyContext = append(keyContext, key)
|
|
||||||
}
|
|
||||||
p.types[keyContext.String()] = typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// addImplicit sets the given Key as having been created implicitly.
|
|
||||||
func (p *parser) addImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeImplicit stops tagging the given key as having been implicitly
|
|
||||||
// created.
|
|
||||||
func (p *parser) removeImplicit(key Key) {
|
|
||||||
p.implicits[key.String()] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// isImplicit returns true if the key group pointed to by the key was created
|
|
||||||
// implicitly.
|
|
||||||
func (p *parser) isImplicit(key Key) bool {
|
|
||||||
return p.implicits[key.String()]
|
|
||||||
}
|
|
||||||
|
|
||||||
// current returns the full key name of the current context.
|
|
||||||
func (p *parser) current() string {
|
|
||||||
if len(p.currentKey) == 0 {
|
|
||||||
return p.context.String()
|
|
||||||
}
|
|
||||||
if len(p.context) == 0 {
|
|
||||||
return p.currentKey
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s.%s", p.context, p.currentKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripFirstNewline(s string) string {
|
|
||||||
if len(s) == 0 || s[0] != '\n' {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripEscapedWhitespace(s string) string {
|
|
||||||
esc := strings.Split(s, "\\\n")
|
|
||||||
if len(esc) > 1 {
|
|
||||||
for i := 1; i < len(esc); i++ {
|
|
||||||
esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(esc, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) replaceEscapes(str string) string {
|
|
||||||
var replaced []rune
|
|
||||||
s := []byte(str)
|
|
||||||
r := 0
|
|
||||||
for r < len(s) {
|
|
||||||
if s[r] != '\\' {
|
|
||||||
c, size := utf8.DecodeRune(s[r:])
|
|
||||||
r += size
|
|
||||||
replaced = append(replaced, c)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
r += 1
|
|
||||||
if r >= len(s) {
|
|
||||||
p.bug("Escape sequence at end of string.")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
switch s[r] {
|
|
||||||
default:
|
|
||||||
p.bug("Expected valid escape code after \\, but got %q.", s[r])
|
|
||||||
return ""
|
|
||||||
case 'b':
|
|
||||||
replaced = append(replaced, rune(0x0008))
|
|
||||||
r += 1
|
|
||||||
case 't':
|
|
||||||
replaced = append(replaced, rune(0x0009))
|
|
||||||
r += 1
|
|
||||||
case 'n':
|
|
||||||
replaced = append(replaced, rune(0x000A))
|
|
||||||
r += 1
|
|
||||||
case 'f':
|
|
||||||
replaced = append(replaced, rune(0x000C))
|
|
||||||
r += 1
|
|
||||||
case 'r':
|
|
||||||
replaced = append(replaced, rune(0x000D))
|
|
||||||
r += 1
|
|
||||||
case '"':
|
|
||||||
replaced = append(replaced, rune(0x0022))
|
|
||||||
r += 1
|
|
||||||
case '\\':
|
|
||||||
replaced = append(replaced, rune(0x005C))
|
|
||||||
r += 1
|
|
||||||
case 'u':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+5). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 5
|
|
||||||
case 'U':
|
|
||||||
// At this point, we know we have a Unicode escape of the form
|
|
||||||
// `uXXXX` at [r, r+9). (Because the lexer guarantees this
|
|
||||||
// for us.)
|
|
||||||
escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
|
|
||||||
replaced = append(replaced, escaped)
|
|
||||||
r += 9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(replaced)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
|
|
||||||
s := string(bs)
|
|
||||||
hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
p.bug("Could not parse '%s' as a hexadecimal number, but the "+
|
|
||||||
"lexer claims it's OK: %s", s, err)
|
|
||||||
}
|
|
||||||
if !utf8.ValidRune(rune(hex)) {
|
|
||||||
p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
|
|
||||||
}
|
|
||||||
return rune(hex)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isStringType(ty itemType) bool {
|
|
||||||
return ty == itemString || ty == itemMultilineString ||
|
|
||||||
ty == itemRawString || ty == itemRawMultilineString
|
|
||||||
}
|
|
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
91
vendor/github.com/BurntSushi/toml/type_check.go
generated
vendored
|
@ -1,91 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// tomlType represents any Go type that corresponds to a TOML type.
|
|
||||||
// While the first draft of the TOML spec has a simplistic type system that
|
|
||||||
// probably doesn't need this level of sophistication, we seem to be militating
|
|
||||||
// toward adding real composite types.
|
|
||||||
type tomlType interface {
|
|
||||||
typeString() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeEqual accepts any two types and returns true if they are equal.
|
|
||||||
func typeEqual(t1, t2 tomlType) bool {
|
|
||||||
if t1 == nil || t2 == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t1.typeString() == t2.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
func typeIsHash(t tomlType) bool {
|
|
||||||
return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tomlBaseType string
|
|
||||||
|
|
||||||
func (btype tomlBaseType) typeString() string {
|
|
||||||
return string(btype)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (btype tomlBaseType) String() string {
|
|
||||||
return btype.typeString()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
tomlInteger tomlBaseType = "Integer"
|
|
||||||
tomlFloat tomlBaseType = "Float"
|
|
||||||
tomlDatetime tomlBaseType = "Datetime"
|
|
||||||
tomlString tomlBaseType = "String"
|
|
||||||
tomlBool tomlBaseType = "Bool"
|
|
||||||
tomlArray tomlBaseType = "Array"
|
|
||||||
tomlHash tomlBaseType = "Hash"
|
|
||||||
tomlArrayHash tomlBaseType = "ArrayHash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// typeOfPrimitive returns a tomlType of any primitive value in TOML.
|
|
||||||
// Primitive values are: Integer, Float, Datetime, String and Bool.
|
|
||||||
//
|
|
||||||
// Passing a lexer item other than the following will cause a BUG message
|
|
||||||
// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
|
|
||||||
func (p *parser) typeOfPrimitive(lexItem item) tomlType {
|
|
||||||
switch lexItem.typ {
|
|
||||||
case itemInteger:
|
|
||||||
return tomlInteger
|
|
||||||
case itemFloat:
|
|
||||||
return tomlFloat
|
|
||||||
case itemDatetime:
|
|
||||||
return tomlDatetime
|
|
||||||
case itemString:
|
|
||||||
return tomlString
|
|
||||||
case itemMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawString:
|
|
||||||
return tomlString
|
|
||||||
case itemRawMultilineString:
|
|
||||||
return tomlString
|
|
||||||
case itemBool:
|
|
||||||
return tomlBool
|
|
||||||
}
|
|
||||||
p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeOfArray returns a tomlType for an array given a list of types of its
|
|
||||||
// values.
|
|
||||||
//
|
|
||||||
// In the current spec, if an array is homogeneous, then its type is always
|
|
||||||
// "Array". If the array is not homogeneous, an error is generated.
|
|
||||||
func (p *parser) typeOfArray(types []tomlType) tomlType {
|
|
||||||
// Empty arrays are cool.
|
|
||||||
if len(types) == 0 {
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
||||||
|
|
||||||
theType := types[0]
|
|
||||||
for _, t := range types[1:] {
|
|
||||||
if !typeEqual(theType, t) {
|
|
||||||
p.panicf("Array contains values of type '%s' and '%s', but "+
|
|
||||||
"arrays must be homogeneous.", theType, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
242
vendor/github.com/BurntSushi/toml/type_fields.go
generated
vendored
|
@ -1,242 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// Struct field handling is adapted from code in encoding/json:
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the Go distribution.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A field represents a single field found in a struct.
|
|
||||||
type field struct {
|
|
||||||
name string // the name of the field (`toml` tag included)
|
|
||||||
tag bool // whether field has a `toml` tag
|
|
||||||
index []int // represents the depth of an anonymous field
|
|
||||||
typ reflect.Type // the type of the field
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName sorts field by name, breaking ties with depth,
|
|
||||||
// then breaking ties with "name came from toml tag", then
|
|
||||||
// breaking ties with index sequence.
|
|
||||||
type byName []field
|
|
||||||
|
|
||||||
func (x byName) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byName) Less(i, j int) bool {
|
|
||||||
if x[i].name != x[j].name {
|
|
||||||
return x[i].name < x[j].name
|
|
||||||
}
|
|
||||||
if len(x[i].index) != len(x[j].index) {
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
if x[i].tag != x[j].tag {
|
|
||||||
return x[i].tag
|
|
||||||
}
|
|
||||||
return byIndex(x).Less(i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// byIndex sorts field by index sequence.
|
|
||||||
type byIndex []field
|
|
||||||
|
|
||||||
func (x byIndex) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byIndex) Less(i, j int) bool {
|
|
||||||
for k, xik := range x[i].index {
|
|
||||||
if k >= len(x[j].index) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if xik != x[j].index[k] {
|
|
||||||
return xik < x[j].index[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeFields returns a list of fields that TOML should recognize for the given
|
|
||||||
// type. The algorithm is breadth-first search over the set of structs to
|
|
||||||
// include - the top struct and then any reachable anonymous structs.
|
|
||||||
func typeFields(t reflect.Type) []field {
|
|
||||||
// Anonymous fields to explore at the current level and the next.
|
|
||||||
current := []field{}
|
|
||||||
next := []field{{typ: t}}
|
|
||||||
|
|
||||||
// Count of queued names for current level and the next.
|
|
||||||
count := map[reflect.Type]int{}
|
|
||||||
nextCount := map[reflect.Type]int{}
|
|
||||||
|
|
||||||
// Types already visited at an earlier level.
|
|
||||||
visited := map[reflect.Type]bool{}
|
|
||||||
|
|
||||||
// Fields found.
|
|
||||||
var fields []field
|
|
||||||
|
|
||||||
for len(next) > 0 {
|
|
||||||
current, next = next, current[:0]
|
|
||||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
|
||||||
|
|
||||||
for _, f := range current {
|
|
||||||
if visited[f.typ] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[f.typ] = true
|
|
||||||
|
|
||||||
// Scan f.typ for fields to include.
|
|
||||||
for i := 0; i < f.typ.NumField(); i++ {
|
|
||||||
sf := f.typ.Field(i)
|
|
||||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
opts := getOptions(sf.Tag)
|
|
||||||
if opts.skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
index := make([]int, len(f.index)+1)
|
|
||||||
copy(index, f.index)
|
|
||||||
index[len(f.index)] = i
|
|
||||||
|
|
||||||
ft := sf.Type
|
|
||||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
|
||||||
// Follow pointer.
|
|
||||||
ft = ft.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record found field and index sequence.
|
|
||||||
if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
|
||||||
tagged := opts.name != ""
|
|
||||||
name := opts.name
|
|
||||||
if name == "" {
|
|
||||||
name = sf.Name
|
|
||||||
}
|
|
||||||
fields = append(fields, field{name, tagged, index, ft})
|
|
||||||
if count[f.typ] > 1 {
|
|
||||||
// If there were multiple instances, add a second,
|
|
||||||
// so that the annihilation code will see a duplicate.
|
|
||||||
// It only cares about the distinction between 1 or 2,
|
|
||||||
// so don't bother generating any more copies.
|
|
||||||
fields = append(fields, fields[len(fields)-1])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record new anonymous struct to explore in next round.
|
|
||||||
nextCount[ft]++
|
|
||||||
if nextCount[ft] == 1 {
|
|
||||||
f := field{name: ft.Name(), index: index, typ: ft}
|
|
||||||
next = append(next, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byName(fields))
|
|
||||||
|
|
||||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
|
||||||
// except that fields with TOML tags are promoted.
|
|
||||||
|
|
||||||
// The fields are sorted in primary order of name, secondary order
|
|
||||||
// of field index length. Loop over names; for each name, delete
|
|
||||||
// hidden fields by choosing the one dominant field that survives.
|
|
||||||
out := fields[:0]
|
|
||||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
|
||||||
// One iteration per name.
|
|
||||||
// Find the sequence of fields with the name of this first field.
|
|
||||||
fi := fields[i]
|
|
||||||
name := fi.name
|
|
||||||
for advance = 1; i+advance < len(fields); advance++ {
|
|
||||||
fj := fields[i+advance]
|
|
||||||
if fj.name != name {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if advance == 1 { // Only one field with this name
|
|
||||||
out = append(out, fi)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dominant, ok := dominantField(fields[i : i+advance])
|
|
||||||
if ok {
|
|
||||||
out = append(out, dominant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = out
|
|
||||||
sort.Sort(byIndex(fields))
|
|
||||||
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// dominantField looks through the fields, all of which are known to
|
|
||||||
// have the same name, to find the single field that dominates the
|
|
||||||
// others using Go's embedding rules, modified by the presence of
|
|
||||||
// TOML tags. If there are multiple top-level fields, the boolean
|
|
||||||
// will be false: This condition is an error in Go and we skip all
|
|
||||||
// the fields.
|
|
||||||
func dominantField(fields []field) (field, bool) {
|
|
||||||
// The fields are sorted in increasing index-length order. The winner
|
|
||||||
// must therefore be one with the shortest index length. Drop all
|
|
||||||
// longer entries, which is easy: just truncate the slice.
|
|
||||||
length := len(fields[0].index)
|
|
||||||
tagged := -1 // Index of first tagged field.
|
|
||||||
for i, f := range fields {
|
|
||||||
if len(f.index) > length {
|
|
||||||
fields = fields[:i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f.tag {
|
|
||||||
if tagged >= 0 {
|
|
||||||
// Multiple tagged fields at the same level: conflict.
|
|
||||||
// Return no field.
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
tagged = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tagged >= 0 {
|
|
||||||
return fields[tagged], true
|
|
||||||
}
|
|
||||||
// All remaining fields have the same length. If there's more than one,
|
|
||||||
// we have a conflict (two fields named "X" at the same level) and we
|
|
||||||
// return no field.
|
|
||||||
if len(fields) > 1 {
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
return fields[0], true
|
|
||||||
}
|
|
||||||
|
|
||||||
var fieldCache struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[reflect.Type][]field
|
|
||||||
}
|
|
||||||
|
|
||||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
||||||
func cachedTypeFields(t reflect.Type) []field {
|
|
||||||
fieldCache.RLock()
|
|
||||||
f := fieldCache.m[t]
|
|
||||||
fieldCache.RUnlock()
|
|
||||||
if f != nil {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fields without lock.
|
|
||||||
// Might duplicate effort but won't hold other computations back.
|
|
||||||
f = typeFields(t)
|
|
||||||
if f == nil {
|
|
||||||
f = []field{}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldCache.Lock()
|
|
||||||
if fieldCache.m == nil {
|
|
||||||
fieldCache.m = map[reflect.Type][]field{}
|
|
||||||
}
|
|
||||||
fieldCache.m[t] = f
|
|
||||||
fieldCache.Unlock()
|
|
||||||
return f
|
|
||||||
}
|
|
21
vendor/github.com/codahale/aesnicheck/LICENSE
generated
vendored
21
vendor/github.com/codahale/aesnicheck/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Coda Hale
|
|
||||||
|
|
||||||
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.
|
|
6
vendor/github.com/codahale/aesnicheck/README.md
generated
vendored
6
vendor/github.com/codahale/aesnicheck/README.md
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
aesnicheck
|
|
||||||
==========
|
|
||||||
|
|
||||||
Provides a function for checking to see if the host machine has AES-NI support.
|
|
||||||
|
|
||||||
For documentation, check [godoc](http://godoc.org/github.com/codahale/aesnicheck).
|
|
9
vendor/github.com/codahale/aesnicheck/asm_amd64.s
generated
vendored
9
vendor/github.com/codahale/aesnicheck/asm_amd64.s
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
// func HasAESNI() bool
|
|
||||||
TEXT ·HasAESNI(SB),$0
|
|
||||||
XORQ AX, AX
|
|
||||||
INCL AX
|
|
||||||
CPUID
|
|
||||||
SHRQ $25, CX
|
|
||||||
ANDQ $1, CX
|
|
||||||
MOVB CX, ret+0(FP)
|
|
||||||
RET
|
|
6
vendor/github.com/codahale/aesnicheck/check_asm.go
generated
vendored
6
vendor/github.com/codahale/aesnicheck/check_asm.go
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
// +build amd64
|
|
||||||
|
|
||||||
package aesnicheck
|
|
||||||
|
|
||||||
// HasAESNI returns whether AES-NI is supported by the CPU.
|
|
||||||
func HasAESNI() bool
|
|
8
vendor/github.com/codahale/aesnicheck/check_generic.go
generated
vendored
8
vendor/github.com/codahale/aesnicheck/check_generic.go
generated
vendored
|
@ -1,8 +0,0 @@
|
||||||
// +build !amd64
|
|
||||||
|
|
||||||
package aesnicheck
|
|
||||||
|
|
||||||
// HasAESNI returns whether AES-NI is supported by the CPU.
|
|
||||||
func HasAESNI() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
9
vendor/github.com/codahale/aesnicheck/docs.go
generated
vendored
9
vendor/github.com/codahale/aesnicheck/docs.go
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
// Package aesnicheck provides a simple check to see if crypto/aes is using
|
|
||||||
// AES-NI instructions or if the AES transform is being done in software. AES-NI
|
|
||||||
// is constant-time, which makes it impervious to cache-level timing attacks. For
|
|
||||||
// security-conscious deployments on public cloud infrastructure (Amazon EC2,
|
|
||||||
// Google Compute Engine, Microsoft Azure, etc.) this may be critical.
|
|
||||||
//
|
|
||||||
// See http://eprint.iacr.org/2014/248 for details on cross-VM timing attacks on
|
|
||||||
// AES keys.
|
|
||||||
package aesnicheck
|
|
21
vendor/github.com/codegangsta/cli/LICENSE
generated
vendored
21
vendor/github.com/codegangsta/cli/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2016 Jeremy Saenz & Contributors
|
|
||||||
|
|
||||||
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.
|
|
1381
vendor/github.com/codegangsta/cli/README.md
generated
vendored
1381
vendor/github.com/codegangsta/cli/README.md
generated
vendored
File diff suppressed because it is too large
Load diff
497
vendor/github.com/codegangsta/cli/app.go
generated
vendored
497
vendor/github.com/codegangsta/cli/app.go
generated
vendored
|
@ -1,497 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
|
|
||||||
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
|
|
||||||
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
|
|
||||||
|
|
||||||
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
|
|
||||||
|
|
||||||
errInvalidActionType = NewExitError("ERROR invalid Action type. "+
|
|
||||||
fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
|
|
||||||
fmt.Sprintf("See %s", appActionDeprecationURL), 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
// App is the main structure of a cli application. It is recommended that
|
|
||||||
// an app be created with the cli.NewApp() function
|
|
||||||
type App struct {
|
|
||||||
// The name of the program. Defaults to path.Base(os.Args[0])
|
|
||||||
Name string
|
|
||||||
// Full name of command for help, defaults to Name
|
|
||||||
HelpName string
|
|
||||||
// Description of the program.
|
|
||||||
Usage string
|
|
||||||
// Text to override the USAGE section of help
|
|
||||||
UsageText string
|
|
||||||
// Description of the program argument format.
|
|
||||||
ArgsUsage string
|
|
||||||
// Version of the program
|
|
||||||
Version string
|
|
||||||
// Description of the program
|
|
||||||
Description string
|
|
||||||
// List of commands to execute
|
|
||||||
Commands []Command
|
|
||||||
// List of flags to parse
|
|
||||||
Flags []Flag
|
|
||||||
// Boolean to enable bash completion commands
|
|
||||||
EnableBashCompletion bool
|
|
||||||
// Boolean to hide built-in help command
|
|
||||||
HideHelp bool
|
|
||||||
// Boolean to hide built-in version flag and the VERSION section of help
|
|
||||||
HideVersion bool
|
|
||||||
// Populate on app startup, only gettable through method Categories()
|
|
||||||
categories CommandCategories
|
|
||||||
// An action to execute when the bash-completion flag is set
|
|
||||||
BashComplete BashCompleteFunc
|
|
||||||
// An action to execute before any subcommands are run, but after the context is ready
|
|
||||||
// If a non-nil error is returned, no subcommands are run
|
|
||||||
Before BeforeFunc
|
|
||||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
|
||||||
// It is run even if Action() panics
|
|
||||||
After AfterFunc
|
|
||||||
|
|
||||||
// The action to execute when no subcommands are specified
|
|
||||||
// Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
|
|
||||||
// *Note*: support for the deprecated `Action` signature will be removed in a future version
|
|
||||||
Action interface{}
|
|
||||||
|
|
||||||
// Execute this function if the proper command cannot be found
|
|
||||||
CommandNotFound CommandNotFoundFunc
|
|
||||||
// Execute this function if an usage error occurs
|
|
||||||
OnUsageError OnUsageErrorFunc
|
|
||||||
// Compilation date
|
|
||||||
Compiled time.Time
|
|
||||||
// List of all authors who contributed
|
|
||||||
Authors []Author
|
|
||||||
// Copyright of the binary if any
|
|
||||||
Copyright string
|
|
||||||
// Name of Author (Note: Use App.Authors, this is deprecated)
|
|
||||||
Author string
|
|
||||||
// Email of Author (Note: Use App.Authors, this is deprecated)
|
|
||||||
Email string
|
|
||||||
// Writer writer to write output to
|
|
||||||
Writer io.Writer
|
|
||||||
// ErrWriter writes error output
|
|
||||||
ErrWriter io.Writer
|
|
||||||
// Other custom info
|
|
||||||
Metadata map[string]interface{}
|
|
||||||
// Carries a function which returns app specific info.
|
|
||||||
ExtraInfo func() map[string]string
|
|
||||||
// CustomAppHelpTemplate the text template for app help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
CustomAppHelpTemplate string
|
|
||||||
|
|
||||||
didSetup bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tries to find out when this binary was compiled.
|
|
||||||
// Returns the current time if it fails to find it.
|
|
||||||
func compileTime() time.Time {
|
|
||||||
info, err := os.Stat(os.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
return time.Now()
|
|
||||||
}
|
|
||||||
return info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApp creates a new cli Application with some reasonable defaults for Name,
|
|
||||||
// Usage, Version and Action.
|
|
||||||
func NewApp() *App {
|
|
||||||
return &App{
|
|
||||||
Name: filepath.Base(os.Args[0]),
|
|
||||||
HelpName: filepath.Base(os.Args[0]),
|
|
||||||
Usage: "A new cli application",
|
|
||||||
UsageText: "",
|
|
||||||
Version: "0.0.0",
|
|
||||||
BashComplete: DefaultAppComplete,
|
|
||||||
Action: helpCommand.Action,
|
|
||||||
Compiled: compileTime(),
|
|
||||||
Writer: os.Stdout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup runs initialization code to ensure all data structures are ready for
|
|
||||||
// `Run` or inspection prior to `Run`. It is internally called by `Run`, but
|
|
||||||
// will return early if setup has already happened.
|
|
||||||
func (a *App) Setup() {
|
|
||||||
if a.didSetup {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a.didSetup = true
|
|
||||||
|
|
||||||
if a.Author != "" || a.Email != "" {
|
|
||||||
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
|
|
||||||
}
|
|
||||||
|
|
||||||
newCmds := []Command{}
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HelpName == "" {
|
|
||||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
||||||
}
|
|
||||||
newCmds = append(newCmds, c)
|
|
||||||
}
|
|
||||||
a.Commands = newCmds
|
|
||||||
|
|
||||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
||||||
a.Commands = append(a.Commands, helpCommand)
|
|
||||||
if (HelpFlag != BoolFlag{}) {
|
|
||||||
a.appendFlag(HelpFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion {
|
|
||||||
a.appendFlag(VersionFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.categories = CommandCategories{}
|
|
||||||
for _, command := range a.Commands {
|
|
||||||
a.categories = a.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
sort.Sort(a.categories)
|
|
||||||
|
|
||||||
if a.Metadata == nil {
|
|
||||||
a.Metadata = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Writer == nil {
|
|
||||||
a.Writer = os.Stdout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run is the entry point to the cli app. Parses the arguments slice and routes
|
|
||||||
// to the proper flag/args combination
|
|
||||||
func (a *App) Run(arguments []string) (err error) {
|
|
||||||
a.Setup()
|
|
||||||
|
|
||||||
// handle the completion flag separately from the flagset since
|
|
||||||
// completion could be attempted after a flag, but before its value was put
|
|
||||||
// on the command line. this causes the flagset to interpret the completion
|
|
||||||
// flag name as the value of the flag before it which is undesirable
|
|
||||||
// note that we can only do this because the shell autocomplete function
|
|
||||||
// always appends the completion flag at the end of the command
|
|
||||||
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
|
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set, err := flagSet(a.Name, a.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
err = set.Parse(arguments[1:])
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
context := NewContext(a, set, nil)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(a.Writer, nerr)
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
context.shellComplete = shellComplete
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if a.OnUsageError != nil {
|
|
||||||
err := a.OnUsageError(context, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideHelp && checkHelp(context) {
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.HideVersion && checkVersion(context) {
|
|
||||||
ShowVersion(context)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil {
|
|
||||||
defer func() {
|
|
||||||
if afterErr := a.After(context); afterErr != nil {
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil {
|
|
||||||
beforeErr := a.Before(context)
|
|
||||||
if beforeErr != nil {
|
|
||||||
ShowAppHelp(context)
|
|
||||||
HandleExitCoder(beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args := context.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
c := a.Command(name)
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Action == nil {
|
|
||||||
a.Action = helpCommand.Action
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = HandleAction(a.Action, context)
|
|
||||||
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunAndExitOnError calls .Run() and exits non-zero if an error was returned
|
|
||||||
//
|
|
||||||
// Deprecated: instead you should return an error that fulfills cli.ExitCoder
|
|
||||||
// to cli.App.Run. This will cause the application to exit with the given eror
|
|
||||||
// code in the cli.ExitCoder
|
|
||||||
func (a *App) RunAndExitOnError() {
|
|
||||||
if err := a.Run(os.Args); err != nil {
|
|
||||||
fmt.Fprintln(a.errWriter(), err)
|
|
||||||
OsExiter(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
|
|
||||||
// generate command-specific flags
|
|
||||||
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|
||||||
// append help to commands
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
|
|
||||||
a.Commands = append(a.Commands, helpCommand)
|
|
||||||
if (HelpFlag != BoolFlag{}) {
|
|
||||||
a.appendFlag(HelpFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newCmds := []Command{}
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HelpName == "" {
|
|
||||||
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
|
|
||||||
}
|
|
||||||
newCmds = append(newCmds, c)
|
|
||||||
}
|
|
||||||
a.Commands = newCmds
|
|
||||||
|
|
||||||
// parse flags
|
|
||||||
set, err := flagSet(a.Name, a.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
|
||||||
context := NewContext(a, set, ctx)
|
|
||||||
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(a.Writer, nerr)
|
|
||||||
fmt.Fprintln(a.Writer)
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
ShowSubcommandHelp(context)
|
|
||||||
} else {
|
|
||||||
ShowCommandHelp(ctx, context.Args().First())
|
|
||||||
}
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if a.OnUsageError != nil {
|
|
||||||
err = a.OnUsageError(context, err, true)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
|
||||||
ShowSubcommandHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a.Commands) > 0 {
|
|
||||||
if checkSubcommandHelp(context) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if checkCommandHelp(ctx, context.Args().First()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.After != nil {
|
|
||||||
defer func() {
|
|
||||||
afterErr := a.After(context)
|
|
||||||
if afterErr != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Before != nil {
|
|
||||||
beforeErr := a.Before(context)
|
|
||||||
if beforeErr != nil {
|
|
||||||
HandleExitCoder(beforeErr)
|
|
||||||
err = beforeErr
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args := context.Args()
|
|
||||||
if args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
c := a.Command(name)
|
|
||||||
if c != nil {
|
|
||||||
return c.Run(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run default Action
|
|
||||||
err = HandleAction(a.Action, context)
|
|
||||||
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command returns the named command on App. Returns nil if the command does not exist
|
|
||||||
func (a *App) Command(name string) *Command {
|
|
||||||
for _, c := range a.Commands {
|
|
||||||
if c.HasName(name) {
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Categories returns a slice containing all the categories with the commands they contain
|
|
||||||
func (a *App) Categories() CommandCategories {
|
|
||||||
return a.categories
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCategories returns a slice of categories and commands that are
|
|
||||||
// Hidden=false
|
|
||||||
func (a *App) VisibleCategories() []*CommandCategory {
|
|
||||||
ret := []*CommandCategory{}
|
|
||||||
for _, category := range a.categories {
|
|
||||||
if visible := func() *CommandCategory {
|
|
||||||
for _, command := range category.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
return category
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}(); visible != nil {
|
|
||||||
ret = append(ret, visible)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
||||||
func (a *App) VisibleCommands() []Command {
|
|
||||||
ret := []Command{}
|
|
||||||
for _, command := range a.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
ret = append(ret, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
||||||
func (a *App) VisibleFlags() []Flag {
|
|
||||||
return visibleFlags(a.Flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) hasFlag(flag Flag) bool {
|
|
||||||
for _, f := range a.Flags {
|
|
||||||
if flag == f {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) errWriter() io.Writer {
|
|
||||||
|
|
||||||
// When the app ErrWriter is nil use the package level one.
|
|
||||||
if a.ErrWriter == nil {
|
|
||||||
return ErrWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.ErrWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) appendFlag(flag Flag) {
|
|
||||||
if !a.hasFlag(flag) {
|
|
||||||
a.Flags = append(a.Flags, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author represents someone who has contributed to a cli project.
|
|
||||||
type Author struct {
|
|
||||||
Name string // The Authors name
|
|
||||||
Email string // The Authors email
|
|
||||||
}
|
|
||||||
|
|
||||||
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
|
|
||||||
func (a Author) String() string {
|
|
||||||
e := ""
|
|
||||||
if a.Email != "" {
|
|
||||||
e = " <" + a.Email + ">"
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v%v", a.Name, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleAction attempts to figure out which Action signature was used. If
|
|
||||||
// it's an ActionFunc or a func with the legacy signature for Action, the func
|
|
||||||
// is run!
|
|
||||||
func HandleAction(action interface{}, context *Context) (err error) {
|
|
||||||
if a, ok := action.(ActionFunc); ok {
|
|
||||||
return a(context)
|
|
||||||
} else if a, ok := action.(func(*Context) error); ok {
|
|
||||||
return a(context)
|
|
||||||
} else if a, ok := action.(func(*Context)); ok { // deprecated function signature
|
|
||||||
a(context)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return errInvalidActionType
|
|
||||||
}
|
|
||||||
}
|
|
44
vendor/github.com/codegangsta/cli/category.go
generated
vendored
44
vendor/github.com/codegangsta/cli/category.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
// CommandCategories is a slice of *CommandCategory.
|
|
||||||
type CommandCategories []*CommandCategory
|
|
||||||
|
|
||||||
// CommandCategory is a category containing commands.
|
|
||||||
type CommandCategory struct {
|
|
||||||
Name string
|
|
||||||
Commands Commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Less(i, j int) bool {
|
|
||||||
return c[i].Name < c[j].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Len() int {
|
|
||||||
return len(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandCategories) Swap(i, j int) {
|
|
||||||
c[i], c[j] = c[j], c[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCommand adds a command to a category.
|
|
||||||
func (c CommandCategories) AddCommand(category string, command Command) CommandCategories {
|
|
||||||
for _, commandCategory := range c {
|
|
||||||
if commandCategory.Name == category {
|
|
||||||
commandCategory.Commands = append(commandCategory.Commands, command)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return append(c, &CommandCategory{Name: category, Commands: []Command{command}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleCommands returns a slice of the Commands with Hidden=false
|
|
||||||
func (c *CommandCategory) VisibleCommands() []Command {
|
|
||||||
ret := []Command{}
|
|
||||||
for _, command := range c.Commands {
|
|
||||||
if !command.Hidden {
|
|
||||||
ret = append(ret, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
22
vendor/github.com/codegangsta/cli/cli.go
generated
vendored
22
vendor/github.com/codegangsta/cli/cli.go
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
// Package cli provides a minimal framework for creating and organizing command line
|
|
||||||
// Go applications. cli is designed to be easy to understand and write, the most simple
|
|
||||||
// cli application can be written as follows:
|
|
||||||
// func main() {
|
|
||||||
// cli.NewApp().Run(os.Args)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Of course this application does not do much, so let's make this an actual application:
|
|
||||||
// func main() {
|
|
||||||
// app := cli.NewApp()
|
|
||||||
// app.Name = "greet"
|
|
||||||
// app.Usage = "say a greeting"
|
|
||||||
// app.Action = func(c *cli.Context) error {
|
|
||||||
// println("Greetings")
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// app.Run(os.Args)
|
|
||||||
// }
|
|
||||||
package cli
|
|
||||||
|
|
||||||
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
|
|
304
vendor/github.com/codegangsta/cli/command.go
generated
vendored
304
vendor/github.com/codegangsta/cli/command.go
generated
vendored
|
@ -1,304 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Command is a subcommand for a cli.App.
|
|
||||||
type Command struct {
|
|
||||||
// The name of the command
|
|
||||||
Name string
|
|
||||||
// short name of the command. Typically one character (deprecated, use `Aliases`)
|
|
||||||
ShortName string
|
|
||||||
// A list of aliases for the command
|
|
||||||
Aliases []string
|
|
||||||
// A short description of the usage of this command
|
|
||||||
Usage string
|
|
||||||
// Custom text to show on USAGE section of help
|
|
||||||
UsageText string
|
|
||||||
// A longer explanation of how the command works
|
|
||||||
Description string
|
|
||||||
// A short description of the arguments of this command
|
|
||||||
ArgsUsage string
|
|
||||||
// The category the command is part of
|
|
||||||
Category string
|
|
||||||
// The function to call when checking for bash command completions
|
|
||||||
BashComplete BashCompleteFunc
|
|
||||||
// An action to execute before any sub-subcommands are run, but after the context is ready
|
|
||||||
// If a non-nil error is returned, no sub-subcommands are run
|
|
||||||
Before BeforeFunc
|
|
||||||
// An action to execute after any subcommands are run, but after the subcommand has finished
|
|
||||||
// It is run even if Action() panics
|
|
||||||
After AfterFunc
|
|
||||||
// The function to call when this command is invoked
|
|
||||||
Action interface{}
|
|
||||||
// TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind
|
|
||||||
// of deprecation period has passed, maybe?
|
|
||||||
|
|
||||||
// Execute this function if a usage error occurs.
|
|
||||||
OnUsageError OnUsageErrorFunc
|
|
||||||
// List of child commands
|
|
||||||
Subcommands Commands
|
|
||||||
// List of flags to parse
|
|
||||||
Flags []Flag
|
|
||||||
// Treat all flags as normal arguments if true
|
|
||||||
SkipFlagParsing bool
|
|
||||||
// Skip argument reordering which attempts to move flags before arguments,
|
|
||||||
// but only works if all flags appear after all arguments. This behavior was
|
|
||||||
// removed n version 2 since it only works under specific conditions so we
|
|
||||||
// backport here by exposing it as an option for compatibility.
|
|
||||||
SkipArgReorder bool
|
|
||||||
// Boolean to hide built-in help command
|
|
||||||
HideHelp bool
|
|
||||||
// Boolean to hide this command from help or completion
|
|
||||||
Hidden bool
|
|
||||||
|
|
||||||
// Full name of command for help, defaults to full command name, including parent commands.
|
|
||||||
HelpName string
|
|
||||||
commandNamePath []string
|
|
||||||
|
|
||||||
// CustomHelpTemplate the text template for the command help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
CustomHelpTemplate string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandsByName []Command
|
|
||||||
|
|
||||||
func (c CommandsByName) Len() int {
|
|
||||||
return len(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandsByName) Less(i, j int) bool {
|
|
||||||
return c[i].Name < c[j].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c CommandsByName) Swap(i, j int) {
|
|
||||||
c[i], c[j] = c[j], c[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullName returns the full name of the command.
|
|
||||||
// For subcommands this ensures that parent commands are part of the command path
|
|
||||||
func (c Command) FullName() string {
|
|
||||||
if c.commandNamePath == nil {
|
|
||||||
return c.Name
|
|
||||||
}
|
|
||||||
return strings.Join(c.commandNamePath, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commands is a slice of Command
|
|
||||||
type Commands []Command
|
|
||||||
|
|
||||||
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
|
||||||
func (c Command) Run(ctx *Context) (err error) {
|
|
||||||
if len(c.Subcommands) > 0 {
|
|
||||||
return c.startApp(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
|
|
||||||
// append help to flags
|
|
||||||
c.Flags = append(
|
|
||||||
c.Flags,
|
|
||||||
HelpFlag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
set, err := flagSet(c.Name, c.Flags)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
set.SetOutput(ioutil.Discard)
|
|
||||||
|
|
||||||
if c.SkipFlagParsing {
|
|
||||||
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
|
||||||
} else if !c.SkipArgReorder {
|
|
||||||
firstFlagIndex := -1
|
|
||||||
terminatorIndex := -1
|
|
||||||
for index, arg := range ctx.Args() {
|
|
||||||
if arg == "--" {
|
|
||||||
terminatorIndex = index
|
|
||||||
break
|
|
||||||
} else if arg == "-" {
|
|
||||||
// Do nothing. A dash alone is not really a flag.
|
|
||||||
continue
|
|
||||||
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
|
||||||
firstFlagIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if firstFlagIndex > -1 {
|
|
||||||
args := ctx.Args()
|
|
||||||
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
|
||||||
copy(regularArgs, args[1:firstFlagIndex])
|
|
||||||
|
|
||||||
var flagArgs []string
|
|
||||||
if terminatorIndex > -1 {
|
|
||||||
flagArgs = args[firstFlagIndex:terminatorIndex]
|
|
||||||
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
|
||||||
} else {
|
|
||||||
flagArgs = args[firstFlagIndex:]
|
|
||||||
}
|
|
||||||
|
|
||||||
err = set.Parse(append(flagArgs, regularArgs...))
|
|
||||||
} else {
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = set.Parse(ctx.Args().Tail())
|
|
||||||
}
|
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
|
||||||
if nerr != nil {
|
|
||||||
fmt.Fprintln(ctx.App.Writer, nerr)
|
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
|
||||||
ShowCommandHelp(ctx, c.Name)
|
|
||||||
return nerr
|
|
||||||
}
|
|
||||||
|
|
||||||
context := NewContext(ctx.App, set, ctx)
|
|
||||||
context.Command = c
|
|
||||||
if checkCommandCompletions(context, c.Name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if c.OnUsageError != nil {
|
|
||||||
err := c.OnUsageError(context, err, false)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
|
|
||||||
fmt.Fprintln(context.App.Writer)
|
|
||||||
ShowCommandHelp(context, c.Name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCommandHelp(context, c.Name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.After != nil {
|
|
||||||
defer func() {
|
|
||||||
afterErr := c.After(context)
|
|
||||||
if afterErr != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
if err != nil {
|
|
||||||
err = NewMultiError(err, afterErr)
|
|
||||||
} else {
|
|
||||||
err = afterErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Before != nil {
|
|
||||||
err = c.Before(context)
|
|
||||||
if err != nil {
|
|
||||||
ShowCommandHelp(context, c.Name)
|
|
||||||
HandleExitCoder(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Action == nil {
|
|
||||||
c.Action = helpSubcommand.Action
|
|
||||||
}
|
|
||||||
|
|
||||||
err = HandleAction(c.Action, context)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
HandleExitCoder(err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names returns the names including short names and aliases.
|
|
||||||
func (c Command) Names() []string {
|
|
||||||
names := []string{c.Name}
|
|
||||||
|
|
||||||
if c.ShortName != "" {
|
|
||||||
names = append(names, c.ShortName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(names, c.Aliases...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasName returns true if Command.Name or Command.ShortName matches given name
|
|
||||||
func (c Command) HasName(name string) bool {
|
|
||||||
for _, n := range c.Names() {
|
|
||||||
if n == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Command) startApp(ctx *Context) error {
|
|
||||||
app := NewApp()
|
|
||||||
app.Metadata = ctx.App.Metadata
|
|
||||||
// set the name and usage
|
|
||||||
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
|
||||||
if c.HelpName == "" {
|
|
||||||
app.HelpName = c.HelpName
|
|
||||||
} else {
|
|
||||||
app.HelpName = app.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Usage = c.Usage
|
|
||||||
app.Description = c.Description
|
|
||||||
app.ArgsUsage = c.ArgsUsage
|
|
||||||
|
|
||||||
// set CommandNotFound
|
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
|
||||||
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
|
||||||
|
|
||||||
// set the flags and commands
|
|
||||||
app.Commands = c.Subcommands
|
|
||||||
app.Flags = c.Flags
|
|
||||||
app.HideHelp = c.HideHelp
|
|
||||||
|
|
||||||
app.Version = ctx.App.Version
|
|
||||||
app.HideVersion = ctx.App.HideVersion
|
|
||||||
app.Compiled = ctx.App.Compiled
|
|
||||||
app.Author = ctx.App.Author
|
|
||||||
app.Email = ctx.App.Email
|
|
||||||
app.Writer = ctx.App.Writer
|
|
||||||
app.ErrWriter = ctx.App.ErrWriter
|
|
||||||
|
|
||||||
app.categories = CommandCategories{}
|
|
||||||
for _, command := range c.Subcommands {
|
|
||||||
app.categories = app.categories.AddCommand(command.Category, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(app.categories)
|
|
||||||
|
|
||||||
// bash completion
|
|
||||||
app.EnableBashCompletion = ctx.App.EnableBashCompletion
|
|
||||||
if c.BashComplete != nil {
|
|
||||||
app.BashComplete = c.BashComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the actions
|
|
||||||
app.Before = c.Before
|
|
||||||
app.After = c.After
|
|
||||||
if c.Action != nil {
|
|
||||||
app.Action = c.Action
|
|
||||||
} else {
|
|
||||||
app.Action = helpSubcommand.Action
|
|
||||||
}
|
|
||||||
app.OnUsageError = c.OnUsageError
|
|
||||||
|
|
||||||
for index, cc := range app.Commands {
|
|
||||||
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.RunAsSubcommand(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibleFlags returns a slice of the Flags with Hidden=false
|
|
||||||
func (c Command) VisibleFlags() []Flag {
|
|
||||||
return visibleFlags(c.Flags)
|
|
||||||
}
|
|
278
vendor/github.com/codegangsta/cli/context.go
generated
vendored
278
vendor/github.com/codegangsta/cli/context.go
generated
vendored
|
@ -1,278 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Context is a type that is passed through to
|
|
||||||
// each Handler action in a cli application. Context
|
|
||||||
// can be used to retrieve context-specific Args and
|
|
||||||
// parsed command-line options.
|
|
||||||
type Context struct {
|
|
||||||
App *App
|
|
||||||
Command Command
|
|
||||||
shellComplete bool
|
|
||||||
flagSet *flag.FlagSet
|
|
||||||
setFlags map[string]bool
|
|
||||||
parentContext *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext creates a new context. For use in when invoking an App or Command action.
|
|
||||||
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
|
|
||||||
c := &Context{App: app, flagSet: set, parentContext: parentCtx}
|
|
||||||
|
|
||||||
if parentCtx != nil {
|
|
||||||
c.shellComplete = parentCtx.shellComplete
|
|
||||||
}
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumFlags returns the number of flags set
|
|
||||||
func (c *Context) NumFlags() int {
|
|
||||||
return c.flagSet.NFlag()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets a context flag to a value.
|
|
||||||
func (c *Context) Set(name, value string) error {
|
|
||||||
c.setFlags = nil
|
|
||||||
return c.flagSet.Set(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalSet sets a context flag to a value on the global flagset
|
|
||||||
func (c *Context) GlobalSet(name, value string) error {
|
|
||||||
globalContext(c).setFlags = nil
|
|
||||||
return globalContext(c).flagSet.Set(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSet determines if the flag was actually set
|
|
||||||
func (c *Context) IsSet(name string) bool {
|
|
||||||
if c.setFlags == nil {
|
|
||||||
c.setFlags = make(map[string]bool)
|
|
||||||
|
|
||||||
c.flagSet.Visit(func(f *flag.Flag) {
|
|
||||||
c.setFlags[f.Name] = true
|
|
||||||
})
|
|
||||||
|
|
||||||
c.flagSet.VisitAll(func(f *flag.Flag) {
|
|
||||||
if _, ok := c.setFlags[f.Name]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.setFlags[f.Name] = false
|
|
||||||
})
|
|
||||||
|
|
||||||
// XXX hack to support IsSet for flags with EnvVar
|
|
||||||
//
|
|
||||||
// There isn't an easy way to do this with the current implementation since
|
|
||||||
// whether a flag was set via an environment variable is very difficult to
|
|
||||||
// determine here. Instead, we intend to introduce a backwards incompatible
|
|
||||||
// change in version 2 to add `IsSet` to the Flag interface to push the
|
|
||||||
// responsibility closer to where the information required to determine
|
|
||||||
// whether a flag is set by non-standard means such as environment
|
|
||||||
// variables is avaliable.
|
|
||||||
//
|
|
||||||
// See https://github.com/urfave/cli/issues/294 for additional discussion
|
|
||||||
flags := c.Command.Flags
|
|
||||||
if c.Command.Name == "" { // cannot == Command{} since it contains slice types
|
|
||||||
if c.App != nil {
|
|
||||||
flags = c.App.Flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, f := range flags {
|
|
||||||
eachName(f.GetName(), func(name string) {
|
|
||||||
if isSet, ok := c.setFlags[name]; isSet || !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val := reflect.ValueOf(f)
|
|
||||||
if val.Kind() == reflect.Ptr {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
envVarValue := val.FieldByName("EnvVar")
|
|
||||||
if !envVarValue.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(envVarValue.String(), func(envVar string) {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if _, ok := syscall.Getenv(envVar); ok {
|
|
||||||
c.setFlags[name] = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.setFlags[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalIsSet determines if the global flag was actually set
|
|
||||||
func (c *Context) GlobalIsSet(name string) bool {
|
|
||||||
ctx := c
|
|
||||||
if ctx.parentContext != nil {
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; ctx != nil; ctx = ctx.parentContext {
|
|
||||||
if ctx.IsSet(name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagNames returns a slice of flag names used in this context.
|
|
||||||
func (c *Context) FlagNames() (names []string) {
|
|
||||||
for _, flag := range c.Command.Flags {
|
|
||||||
name := strings.Split(flag.GetName(), ",")[0]
|
|
||||||
if name == "help" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalFlagNames returns a slice of global flag names used by the app.
|
|
||||||
func (c *Context) GlobalFlagNames() (names []string) {
|
|
||||||
for _, flag := range c.App.Flags {
|
|
||||||
name := strings.Split(flag.GetName(), ",")[0]
|
|
||||||
if name == "help" || name == "version" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent returns the parent context, if any
|
|
||||||
func (c *Context) Parent() *Context {
|
|
||||||
return c.parentContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// value returns the value of the flag coressponding to `name`
|
|
||||||
func (c *Context) value(name string) interface{} {
|
|
||||||
return c.flagSet.Lookup(name).Value.(flag.Getter).Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args contains apps console arguments
|
|
||||||
type Args []string
|
|
||||||
|
|
||||||
// Args returns the command line arguments associated with the context.
|
|
||||||
func (c *Context) Args() Args {
|
|
||||||
args := Args(c.flagSet.Args())
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// NArg returns the number of the command line arguments.
|
|
||||||
func (c *Context) NArg() int {
|
|
||||||
return len(c.Args())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the nth argument, or else a blank string
|
|
||||||
func (a Args) Get(n int) string {
|
|
||||||
if len(a) > n {
|
|
||||||
return a[n]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// First returns the first argument, or else a blank string
|
|
||||||
func (a Args) First() string {
|
|
||||||
return a.Get(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tail returns the rest of the arguments (not the first one)
|
|
||||||
// or else an empty string slice
|
|
||||||
func (a Args) Tail() []string {
|
|
||||||
if len(a) >= 2 {
|
|
||||||
return []string(a)[1:]
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present checks if there are any arguments present
|
|
||||||
func (a Args) Present() bool {
|
|
||||||
return len(a) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap swaps arguments at the given indexes
|
|
||||||
func (a Args) Swap(from, to int) error {
|
|
||||||
if from >= len(a) || to >= len(a) {
|
|
||||||
return errors.New("index out of range")
|
|
||||||
}
|
|
||||||
a[from], a[to] = a[to], a[from]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func globalContext(ctx *Context) *Context {
|
|
||||||
if ctx == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if ctx.parentContext == nil {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
|
|
||||||
if ctx.parentContext != nil {
|
|
||||||
ctx = ctx.parentContext
|
|
||||||
}
|
|
||||||
for ; ctx != nil; ctx = ctx.parentContext {
|
|
||||||
if f := ctx.flagSet.Lookup(name); f != nil {
|
|
||||||
return ctx.flagSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
|
|
||||||
switch ff.Value.(type) {
|
|
||||||
case *StringSlice:
|
|
||||||
default:
|
|
||||||
set.Set(name, ff.Value.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
|
||||||
visited := make(map[string]bool)
|
|
||||||
set.Visit(func(f *flag.Flag) {
|
|
||||||
visited[f.Name] = true
|
|
||||||
})
|
|
||||||
for _, f := range flags {
|
|
||||||
parts := strings.Split(f.GetName(), ",")
|
|
||||||
if len(parts) == 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var ff *flag.Flag
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
if visited[name] {
|
|
||||||
if ff != nil {
|
|
||||||
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
|
|
||||||
}
|
|
||||||
ff = set.Lookup(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ff == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
if !visited[name] {
|
|
||||||
copyFlag(name, ff, set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
115
vendor/github.com/codegangsta/cli/errors.go
generated
vendored
115
vendor/github.com/codegangsta/cli/errors.go
generated
vendored
|
@ -1,115 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OsExiter is the function used when the app exits. If not set defaults to os.Exit.
|
|
||||||
var OsExiter = os.Exit
|
|
||||||
|
|
||||||
// ErrWriter is used to write errors to the user. This can be anything
|
|
||||||
// implementing the io.Writer interface and defaults to os.Stderr.
|
|
||||||
var ErrWriter io.Writer = os.Stderr
|
|
||||||
|
|
||||||
// MultiError is an error that wraps multiple errors.
|
|
||||||
type MultiError struct {
|
|
||||||
Errors []error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMultiError creates a new MultiError. Pass in one or more errors.
|
|
||||||
func NewMultiError(err ...error) MultiError {
|
|
||||||
return MultiError{Errors: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the error interface.
|
|
||||||
func (m MultiError) Error() string {
|
|
||||||
errs := make([]string, len(m.Errors))
|
|
||||||
for i, err := range m.Errors {
|
|
||||||
errs[i] = err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(errs, "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorFormatter interface {
|
|
||||||
Format(s fmt.State, verb rune)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitCoder is the interface checked by `App` and `Command` for a custom exit
|
|
||||||
// code
|
|
||||||
type ExitCoder interface {
|
|
||||||
error
|
|
||||||
ExitCode() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitError fulfills both the builtin `error` interface and `ExitCoder`
|
|
||||||
type ExitError struct {
|
|
||||||
exitCode int
|
|
||||||
message interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewExitError makes a new *ExitError
|
|
||||||
func NewExitError(message interface{}, exitCode int) *ExitError {
|
|
||||||
return &ExitError{
|
|
||||||
exitCode: exitCode,
|
|
||||||
message: message,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the string message, fulfilling the interface required by
|
|
||||||
// `error`
|
|
||||||
func (ee *ExitError) Error() string {
|
|
||||||
return fmt.Sprintf("%v", ee.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExitCode returns the exit code, fulfilling the interface required by
|
|
||||||
// `ExitCoder`
|
|
||||||
func (ee *ExitError) ExitCode() int {
|
|
||||||
return ee.exitCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleExitCoder checks if the error fulfills the ExitCoder interface, and if
|
|
||||||
// so prints the error to stderr (if it is non-empty) and calls OsExiter with the
|
|
||||||
// given exit code. If the given error is a MultiError, then this func is
|
|
||||||
// called on all members of the Errors slice and calls OsExiter with the last exit code.
|
|
||||||
func HandleExitCoder(err error) {
|
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if exitErr, ok := err.(ExitCoder); ok {
|
|
||||||
if err.Error() != "" {
|
|
||||||
if _, ok := exitErr.(ErrorFormatter); ok {
|
|
||||||
fmt.Fprintf(ErrWriter, "%+v\n", err)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(ErrWriter, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OsExiter(exitErr.ExitCode())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if multiErr, ok := err.(MultiError); ok {
|
|
||||||
code := handleMultiError(multiErr)
|
|
||||||
OsExiter(code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMultiError(multiErr MultiError) int {
|
|
||||||
code := 1
|
|
||||||
for _, merr := range multiErr.Errors {
|
|
||||||
if multiErr2, ok := merr.(MultiError); ok {
|
|
||||||
code = handleMultiError(multiErr2)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintln(ErrWriter, merr)
|
|
||||||
if exitErr, ok := merr.(ExitCoder); ok {
|
|
||||||
code = exitErr.ExitCode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return code
|
|
||||||
}
|
|
799
vendor/github.com/codegangsta/cli/flag.go
generated
vendored
799
vendor/github.com/codegangsta/cli/flag.go
generated
vendored
|
@ -1,799 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultPlaceholder = "value"
|
|
||||||
|
|
||||||
// BashCompletionFlag enables bash-completion for all commands and subcommands
|
|
||||||
var BashCompletionFlag Flag = BoolFlag{
|
|
||||||
Name: "generate-bash-completion",
|
|
||||||
Hidden: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// VersionFlag prints the version for the application
|
|
||||||
var VersionFlag Flag = BoolFlag{
|
|
||||||
Name: "version, v",
|
|
||||||
Usage: "print the version",
|
|
||||||
}
|
|
||||||
|
|
||||||
// HelpFlag prints the help for all commands and subcommands
|
|
||||||
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
|
|
||||||
// unless HideHelp is set to true)
|
|
||||||
var HelpFlag Flag = BoolFlag{
|
|
||||||
Name: "help, h",
|
|
||||||
Usage: "show help",
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagStringer converts a flag definition to a string. This is used by help
|
|
||||||
// to display a flag.
|
|
||||||
var FlagStringer FlagStringFunc = stringifyFlag
|
|
||||||
|
|
||||||
// FlagsByName is a slice of Flag.
|
|
||||||
type FlagsByName []Flag
|
|
||||||
|
|
||||||
func (f FlagsByName) Len() int {
|
|
||||||
return len(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FlagsByName) Less(i, j int) bool {
|
|
||||||
return f[i].GetName() < f[j].GetName()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FlagsByName) Swap(i, j int) {
|
|
||||||
f[i], f[j] = f[j], f[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag is a common interface related to parsing flags in cli.
|
|
||||||
// For more advanced flag parsing techniques, it is recommended that
|
|
||||||
// this interface be implemented.
|
|
||||||
type Flag interface {
|
|
||||||
fmt.Stringer
|
|
||||||
// Apply Flag settings to the given flag set
|
|
||||||
Apply(*flag.FlagSet)
|
|
||||||
GetName() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorableFlag is an interface that allows us to return errors during apply
|
|
||||||
// it allows flags defined in this library to return errors in a fashion backwards compatible
|
|
||||||
// TODO remove in v2 and modify the existing Flag interface to return errors
|
|
||||||
type errorableFlag interface {
|
|
||||||
Flag
|
|
||||||
|
|
||||||
ApplyWithError(*flag.FlagSet) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
|
|
||||||
set := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
||||||
|
|
||||||
for _, f := range flags {
|
|
||||||
//TODO remove in v2 when errorableFlag is removed
|
|
||||||
if ef, ok := f.(errorableFlag); ok {
|
|
||||||
if err := ef.ApplyWithError(set); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f.Apply(set)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return set, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func eachName(longName string, fn func(string)) {
|
|
||||||
parts := strings.Split(longName, ",")
|
|
||||||
for _, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
fn(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic is a generic parseable type identified by a specific flag
|
|
||||||
type Generic interface {
|
|
||||||
Set(value string) error
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
|
||||||
// provided by the user for parsing by the flag
|
|
||||||
// Ignores parsing errors
|
|
||||||
func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
|
|
||||||
// provided by the user for parsing by the flag
|
|
||||||
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
val := f.Value
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
if err := val.Set(envVal); err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
set.Var(f.Value, name, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
|
|
||||||
type StringSlice []string
|
|
||||||
|
|
||||||
// Set appends the string value to the list of values
|
|
||||||
func (f *StringSlice) Set(value string) error {
|
|
||||||
*f = append(*f, value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
|
||||||
func (f *StringSlice) String() string {
|
|
||||||
return fmt.Sprintf("%s", *f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the slice of strings set by this flag
|
|
||||||
func (f *StringSlice) Value() []string {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the slice of strings set by this flag
|
|
||||||
func (f *StringSlice) Get() interface{} {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
newVal := &StringSlice{}
|
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
if err := newVal.Set(s); err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Value = newVal
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Value == nil {
|
|
||||||
f.Value = &StringSlice{}
|
|
||||||
}
|
|
||||||
set.Var(f.Value, name, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
|
||||||
type IntSlice []int
|
|
||||||
|
|
||||||
// Set parses the value into an integer and appends it to the list of values
|
|
||||||
func (f *IntSlice) Set(value string) error {
|
|
||||||
tmp, err := strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*f = append(*f, tmp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
|
||||||
func (f *IntSlice) String() string {
|
|
||||||
return fmt.Sprintf("%#v", *f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the slice of ints set by this flag
|
|
||||||
func (f *IntSlice) Value() []int {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the slice of ints set by this flag
|
|
||||||
func (f *IntSlice) Get() interface{} {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
newVal := &IntSlice{}
|
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
if err := newVal.Set(s); err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Value = newVal
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Value == nil {
|
|
||||||
f.Value = &IntSlice{}
|
|
||||||
}
|
|
||||||
set.Var(f.Value, name, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
|
|
||||||
type Int64Slice []int64
|
|
||||||
|
|
||||||
// Set parses the value into an integer and appends it to the list of values
|
|
||||||
func (f *Int64Slice) Set(value string) error {
|
|
||||||
tmp, err := strconv.ParseInt(value, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*f = append(*f, tmp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value (for usage defaults)
|
|
||||||
func (f *Int64Slice) String() string {
|
|
||||||
return fmt.Sprintf("%#v", *f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the slice of ints set by this flag
|
|
||||||
func (f *Int64Slice) Value() []int64 {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the slice of ints set by this flag
|
|
||||||
func (f *Int64Slice) Get() interface{} {
|
|
||||||
return *f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
newVal := &Int64Slice{}
|
|
||||||
for _, s := range strings.Split(envVal, ",") {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
if err := newVal.Set(s); err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.Value = newVal
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Value == nil {
|
|
||||||
f.Value = &Int64Slice{}
|
|
||||||
}
|
|
||||||
set.Var(f.Value, name, f.Usage)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
val := false
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
if envVal == "" {
|
|
||||||
val = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
val = envValBool
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.BoolVar(f.Destination, name, val, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Bool(name, val, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
val := true
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
if envVal == "" {
|
|
||||||
val = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
envValBool, err := strconv.ParseBool(envVal)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
val = envValBool
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.BoolVar(f.Destination, name, val, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Bool(name, val, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f StringFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
f.Value = envVal
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.StringVar(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.String(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f IntFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
f.Value = int(envValInt)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.IntVar(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Int(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f Int64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValInt, err := strconv.ParseInt(envVal, 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Value = envValInt
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.Int64Var(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Int64(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f UintFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Value = uint(envValInt)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.UintVar(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Uint(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValInt, err := strconv.ParseUint(envVal, 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Value = uint64(envValInt)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Uint64(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValDuration, err := time.ParseDuration(envVal)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Value = envValDuration
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.DurationVar(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Duration(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
|
||||||
// Ignores errors
|
|
||||||
func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|
||||||
f.ApplyWithError(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyWithError populates the flag given the flag set and environment
|
|
||||||
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
|
||||||
if f.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
|
||||||
envValFloat, err := strconv.ParseFloat(envVal, 10)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Value = float64(envValFloat)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(f.Name, func(name string) {
|
|
||||||
if f.Destination != nil {
|
|
||||||
set.Float64Var(f.Destination, name, f.Value, f.Usage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
set.Float64(name, f.Value, f.Usage)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func visibleFlags(fl []Flag) []Flag {
|
|
||||||
visible := []Flag{}
|
|
||||||
for _, flag := range fl {
|
|
||||||
field := flagValue(flag).FieldByName("Hidden")
|
|
||||||
if !field.IsValid() || !field.Bool() {
|
|
||||||
visible = append(visible, flag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return visible
|
|
||||||
}
|
|
||||||
|
|
||||||
func prefixFor(name string) (prefix string) {
|
|
||||||
if len(name) == 1 {
|
|
||||||
prefix = "-"
|
|
||||||
} else {
|
|
||||||
prefix = "--"
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the placeholder, if any, and the unquoted usage string.
|
|
||||||
func unquoteUsage(usage string) (string, string) {
|
|
||||||
for i := 0; i < len(usage); i++ {
|
|
||||||
if usage[i] == '`' {
|
|
||||||
for j := i + 1; j < len(usage); j++ {
|
|
||||||
if usage[j] == '`' {
|
|
||||||
name := usage[i+1 : j]
|
|
||||||
usage = usage[:i] + name + usage[j+1:]
|
|
||||||
return name, usage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", usage
|
|
||||||
}
|
|
||||||
|
|
||||||
func prefixedNames(fullName, placeholder string) string {
|
|
||||||
var prefixed string
|
|
||||||
parts := strings.Split(fullName, ",")
|
|
||||||
for i, name := range parts {
|
|
||||||
name = strings.Trim(name, " ")
|
|
||||||
prefixed += prefixFor(name) + name
|
|
||||||
if placeholder != "" {
|
|
||||||
prefixed += " " + placeholder
|
|
||||||
}
|
|
||||||
if i < len(parts)-1 {
|
|
||||||
prefixed += ", "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefixed
|
|
||||||
}
|
|
||||||
|
|
||||||
func withEnvHint(envVar, str string) string {
|
|
||||||
envText := ""
|
|
||||||
if envVar != "" {
|
|
||||||
prefix := "$"
|
|
||||||
suffix := ""
|
|
||||||
sep := ", $"
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
prefix = "%"
|
|
||||||
suffix = "%"
|
|
||||||
sep = "%, %"
|
|
||||||
}
|
|
||||||
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
|
|
||||||
}
|
|
||||||
return str + envText
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagValue(f Flag) reflect.Value {
|
|
||||||
fv := reflect.ValueOf(f)
|
|
||||||
for fv.Kind() == reflect.Ptr {
|
|
||||||
fv = reflect.Indirect(fv)
|
|
||||||
}
|
|
||||||
return fv
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringifyFlag(f Flag) string {
|
|
||||||
fv := flagValue(f)
|
|
||||||
|
|
||||||
switch f.(type) {
|
|
||||||
case IntSliceFlag:
|
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
||||||
stringifyIntSliceFlag(f.(IntSliceFlag)))
|
|
||||||
case Int64SliceFlag:
|
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
||||||
stringifyInt64SliceFlag(f.(Int64SliceFlag)))
|
|
||||||
case StringSliceFlag:
|
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
||||||
stringifyStringSliceFlag(f.(StringSliceFlag)))
|
|
||||||
}
|
|
||||||
|
|
||||||
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
|
|
||||||
|
|
||||||
needsPlaceholder := false
|
|
||||||
defaultValueString := ""
|
|
||||||
|
|
||||||
if val := fv.FieldByName("Value"); val.IsValid() {
|
|
||||||
needsPlaceholder = true
|
|
||||||
defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface())
|
|
||||||
|
|
||||||
if val.Kind() == reflect.String && val.String() != "" {
|
|
||||||
defaultValueString = fmt.Sprintf(" (default: %q)", val.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if defaultValueString == " (default: )" {
|
|
||||||
defaultValueString = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if needsPlaceholder && placeholder == "" {
|
|
||||||
placeholder = defaultPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
|
|
||||||
|
|
||||||
return withEnvHint(fv.FieldByName("EnvVar").String(),
|
|
||||||
fmt.Sprintf("%s\t%s", prefixedNames(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringifyIntSliceFlag(f IntSliceFlag) string {
|
|
||||||
defaultVals := []string{}
|
|
||||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
|
||||||
for _, i := range f.Value.Value() {
|
|
||||||
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringifyInt64SliceFlag(f Int64SliceFlag) string {
|
|
||||||
defaultVals := []string{}
|
|
||||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
|
||||||
for _, i := range f.Value.Value() {
|
|
||||||
defaultVals = append(defaultVals, fmt.Sprintf("%d", i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringifyStringSliceFlag(f StringSliceFlag) string {
|
|
||||||
defaultVals := []string{}
|
|
||||||
if f.Value != nil && len(f.Value.Value()) > 0 {
|
|
||||||
for _, s := range f.Value.Value() {
|
|
||||||
if len(s) > 0 {
|
|
||||||
defaultVals = append(defaultVals, fmt.Sprintf("%q", s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringifySliceFlag(f.Usage, f.Name, defaultVals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringifySliceFlag(usage, name string, defaultVals []string) string {
|
|
||||||
placeholder, usage := unquoteUsage(usage)
|
|
||||||
if placeholder == "" {
|
|
||||||
placeholder = defaultPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultVal := ""
|
|
||||||
if len(defaultVals) > 0 {
|
|
||||||
defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
|
|
||||||
return fmt.Sprintf("%s\t%s", prefixedNames(name, placeholder), usageWithDefault)
|
|
||||||
}
|
|
627
vendor/github.com/codegangsta/cli/flag_generated.go
generated
vendored
627
vendor/github.com/codegangsta/cli/flag_generated.go
generated
vendored
|
@ -1,627 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WARNING: This file is generated!
|
|
||||||
|
|
||||||
// BoolFlag is a flag with type bool
|
|
||||||
type BoolFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Destination *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f BoolFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f BoolFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool looks up the value of a local BoolFlag, returns
|
|
||||||
// false if not found
|
|
||||||
func (c *Context) Bool(name string) bool {
|
|
||||||
return lookupBool(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalBool looks up the value of a global BoolFlag, returns
|
|
||||||
// false if not found
|
|
||||||
func (c *Context) GlobalBool(name string) bool {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupBool(name, fs)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupBool(name string, set *flag.FlagSet) bool {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseBool(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolTFlag is a flag with type bool that is true by default
|
|
||||||
type BoolTFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Destination *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f BoolTFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f BoolTFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolT looks up the value of a local BoolTFlag, returns
|
|
||||||
// false if not found
|
|
||||||
func (c *Context) BoolT(name string) bool {
|
|
||||||
return lookupBoolT(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalBoolT looks up the value of a global BoolTFlag, returns
|
|
||||||
// false if not found
|
|
||||||
func (c *Context) GlobalBoolT(name string) bool {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupBoolT(name, fs)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupBoolT(name string, set *flag.FlagSet) bool {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseBool(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
|
|
||||||
type DurationFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value time.Duration
|
|
||||||
Destination *time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f DurationFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f DurationFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration looks up the value of a local DurationFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Duration(name string) time.Duration {
|
|
||||||
return lookupDuration(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalDuration looks up the value of a global DurationFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalDuration(name string) time.Duration {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupDuration(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := time.ParseDuration(f.Value.String())
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64Flag is a flag with type float64
|
|
||||||
type Float64Flag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value float64
|
|
||||||
Destination *float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f Float64Flag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f Float64Flag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 looks up the value of a local Float64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Float64(name string) float64 {
|
|
||||||
return lookupFloat64(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalFloat64 looks up the value of a global Float64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalFloat64(name string) float64 {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupFloat64(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseFloat(f.Value.String(), 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenericFlag is a flag with type Generic
|
|
||||||
type GenericFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value Generic
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f GenericFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f GenericFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic looks up the value of a local GenericFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) Generic(name string) interface{} {
|
|
||||||
return lookupGeneric(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalGeneric looks up the value of a global GenericFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) GlobalGeneric(name string) interface{} {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupGeneric(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := f.Value, error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Flag is a flag with type int64
|
|
||||||
type Int64Flag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value int64
|
|
||||||
Destination *int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f Int64Flag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f Int64Flag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 looks up the value of a local Int64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Int64(name string) int64 {
|
|
||||||
return lookupInt64(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalInt64 looks up the value of a global Int64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalInt64(name string) int64 {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupInt64(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupInt64(name string, set *flag.FlagSet) int64 {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntFlag is a flag with type int
|
|
||||||
type IntFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value int
|
|
||||||
Destination *int
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f IntFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f IntFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int looks up the value of a local IntFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Int(name string) int {
|
|
||||||
return lookupInt(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalInt looks up the value of a global IntFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalInt(name string) int {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupInt(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupInt(name string, set *flag.FlagSet) int {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(parsed)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSliceFlag is a flag with type *IntSlice
|
|
||||||
type IntSliceFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value *IntSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f IntSliceFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f IntSliceFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntSlice looks up the value of a local IntSliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) IntSlice(name string) []int {
|
|
||||||
return lookupIntSlice(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) GlobalIntSlice(name string) []int {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupIntSlice(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupIntSlice(name string, set *flag.FlagSet) []int {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := (f.Value.(*IntSlice)).Value(), error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64SliceFlag is a flag with type *Int64Slice
|
|
||||||
type Int64SliceFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value *Int64Slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f Int64SliceFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f Int64SliceFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Slice looks up the value of a local Int64SliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) Int64Slice(name string) []int64 {
|
|
||||||
return lookupInt64Slice(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) GlobalInt64Slice(name string) []int64 {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupInt64Slice(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringFlag is a flag with type string
|
|
||||||
type StringFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value string
|
|
||||||
Destination *string
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f StringFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f StringFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// String looks up the value of a local StringFlag, returns
|
|
||||||
// "" if not found
|
|
||||||
func (c *Context) String(name string) string {
|
|
||||||
return lookupString(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalString looks up the value of a global StringFlag, returns
|
|
||||||
// "" if not found
|
|
||||||
func (c *Context) GlobalString(name string) string {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupString(name, fs)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupString(name string, set *flag.FlagSet) string {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := f.Value.String(), error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSliceFlag is a flag with type *StringSlice
|
|
||||||
type StringSliceFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value *StringSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f StringSliceFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f StringSliceFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringSlice looks up the value of a local StringSliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) StringSlice(name string) []string {
|
|
||||||
return lookupStringSlice(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
|
|
||||||
// nil if not found
|
|
||||||
func (c *Context) GlobalStringSlice(name string) []string {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupStringSlice(name, fs)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupStringSlice(name string, set *flag.FlagSet) []string {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := (f.Value.(*StringSlice)).Value(), error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64Flag is a flag with type uint64
|
|
||||||
type Uint64Flag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value uint64
|
|
||||||
Destination *uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f Uint64Flag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f Uint64Flag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 looks up the value of a local Uint64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Uint64(name string) uint64 {
|
|
||||||
return lookupUint64(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalUint64 looks up the value of a global Uint64Flag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalUint64(name string) uint64 {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupUint64(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupUint64(name string, set *flag.FlagSet) uint64 {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return parsed
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// UintFlag is a flag with type uint
|
|
||||||
type UintFlag struct {
|
|
||||||
Name string
|
|
||||||
Usage string
|
|
||||||
EnvVar string
|
|
||||||
Hidden bool
|
|
||||||
Value uint
|
|
||||||
Destination *uint
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a readable representation of this value
|
|
||||||
// (for usage defaults)
|
|
||||||
func (f UintFlag) String() string {
|
|
||||||
return FlagStringer(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the name of the flag
|
|
||||||
func (f UintFlag) GetName() string {
|
|
||||||
return f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint looks up the value of a local UintFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) Uint(name string) uint {
|
|
||||||
return lookupUint(name, c.flagSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalUint looks up the value of a global UintFlag, returns
|
|
||||||
// 0 if not found
|
|
||||||
func (c *Context) GlobalUint(name string) uint {
|
|
||||||
if fs := lookupGlobalFlagSet(name, c); fs != nil {
|
|
||||||
return lookupUint(name, fs)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupUint(name string, set *flag.FlagSet) uint {
|
|
||||||
f := set.Lookup(name)
|
|
||||||
if f != nil {
|
|
||||||
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return uint(parsed)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
28
vendor/github.com/codegangsta/cli/funcs.go
generated
vendored
28
vendor/github.com/codegangsta/cli/funcs.go
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
// BashCompleteFunc is an action to execute when the bash-completion flag is set
|
|
||||||
type BashCompleteFunc func(*Context)
|
|
||||||
|
|
||||||
// BeforeFunc is an action to execute before any subcommands are run, but after
|
|
||||||
// the context is ready if a non-nil error is returned, no subcommands are run
|
|
||||||
type BeforeFunc func(*Context) error
|
|
||||||
|
|
||||||
// AfterFunc is an action to execute after any subcommands are run, but after the
|
|
||||||
// subcommand has finished it is run even if Action() panics
|
|
||||||
type AfterFunc func(*Context) error
|
|
||||||
|
|
||||||
// ActionFunc is the action to execute when no subcommands are specified
|
|
||||||
type ActionFunc func(*Context) error
|
|
||||||
|
|
||||||
// CommandNotFoundFunc is executed if the proper command cannot be found
|
|
||||||
type CommandNotFoundFunc func(*Context, string)
|
|
||||||
|
|
||||||
// OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying
|
|
||||||
// customized usage error messages. This function is able to replace the
|
|
||||||
// original error messages. If this function is not set, the "Incorrect usage"
|
|
||||||
// is displayed and the execution is interrupted.
|
|
||||||
type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error
|
|
||||||
|
|
||||||
// FlagStringFunc is used by the help generation to display a flag, which is
|
|
||||||
// expected to be a single line.
|
|
||||||
type FlagStringFunc func(Flag) string
|
|
338
vendor/github.com/codegangsta/cli/help.go
generated
vendored
338
vendor/github.com/codegangsta/cli/help.go
generated
vendored
|
@ -1,338 +0,0 @@
|
||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AppHelpTemplate is the text template for the Default help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
var AppHelpTemplate = `NAME:
|
|
||||||
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
|
||||||
|
|
||||||
VERSION:
|
|
||||||
{{.Version}}{{end}}{{end}}{{if .Description}}
|
|
||||||
|
|
||||||
DESCRIPTION:
|
|
||||||
{{.Description}}{{end}}{{if len .Authors}}
|
|
||||||
|
|
||||||
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
|
|
||||||
{{range $index, $author := .Authors}}{{if $index}}
|
|
||||||
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
|
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
|
||||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
|
||||||
|
|
||||||
GLOBAL OPTIONS:
|
|
||||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
|
||||||
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
|
|
||||||
|
|
||||||
COPYRIGHT:
|
|
||||||
{{.Copyright}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
// CommandHelpTemplate is the text template for the command help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
var CommandHelpTemplate = `NAME:
|
|
||||||
{{.HelpName}} - {{.Usage}}
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
|
|
||||||
|
|
||||||
CATEGORY:
|
|
||||||
{{.Category}}{{end}}{{if .Description}}
|
|
||||||
|
|
||||||
DESCRIPTION:
|
|
||||||
{{.Description}}{{end}}{{if .VisibleFlags}}
|
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
{{range .VisibleFlags}}{{.}}
|
|
||||||
{{end}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
// SubcommandHelpTemplate is the text template for the subcommand help topic.
|
|
||||||
// cli.go uses text/template to render templates. You can
|
|
||||||
// render custom help text by setting this variable.
|
|
||||||
var SubcommandHelpTemplate = `NAME:
|
|
||||||
{{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}}
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
|
||||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
|
|
||||||
{{end}}{{if .VisibleFlags}}
|
|
||||||
OPTIONS:
|
|
||||||
{{range .VisibleFlags}}{{.}}
|
|
||||||
{{end}}{{end}}
|
|
||||||
`
|
|
||||||
|
|
||||||
var helpCommand = Command{
|
|
||||||
Name: "help",
|
|
||||||
Aliases: []string{"h"},
|
|
||||||
Usage: "Shows a list of commands or help for one command",
|
|
||||||
ArgsUsage: "[command]",
|
|
||||||
Action: func(c *Context) error {
|
|
||||||
args := c.Args()
|
|
||||||
if args.Present() {
|
|
||||||
return ShowCommandHelp(c, args.First())
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowAppHelp(c)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var helpSubcommand = Command{
|
|
||||||
Name: "help",
|
|
||||||
Aliases: []string{"h"},
|
|
||||||
Usage: "Shows a list of commands or help for one command",
|
|
||||||
ArgsUsage: "[command]",
|
|
||||||
Action: func(c *Context) error {
|
|
||||||
args := c.Args()
|
|
||||||
if args.Present() {
|
|
||||||
return ShowCommandHelp(c, args.First())
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShowSubcommandHelp(c)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints help for the App or Command
|
|
||||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
|
||||||
|
|
||||||
// Prints help for the App or Command with custom template function.
|
|
||||||
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
|
|
||||||
|
|
||||||
// HelpPrinter is a function that writes the help output. If not set a default
|
|
||||||
// is used. The function signature is:
|
|
||||||
// func(w io.Writer, templ string, data interface{})
|
|
||||||
var HelpPrinter helpPrinter = printHelp
|
|
||||||
|
|
||||||
// HelpPrinterCustom is same as HelpPrinter but
|
|
||||||
// takes a custom function for template function map.
|
|
||||||
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
|
|
||||||
|
|
||||||
// VersionPrinter prints the version for the App
|
|
||||||
var VersionPrinter = printVersion
|
|
||||||
|
|
||||||
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
|
||||||
func ShowAppHelpAndExit(c *Context, exitCode int) {
|
|
||||||
ShowAppHelp(c)
|
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowAppHelp is an action that displays the help.
|
|
||||||
func ShowAppHelp(c *Context) (err error) {
|
|
||||||
if c.App.CustomAppHelpTemplate == "" {
|
|
||||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
customAppData := func() map[string]interface{} {
|
|
||||||
if c.App.ExtraInfo == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return map[string]interface{}{
|
|
||||||
"ExtraInfo": c.App.ExtraInfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultAppComplete prints the list of subcommands as the default app completion method
|
|
||||||
func DefaultAppComplete(c *Context) {
|
|
||||||
for _, command := range c.App.Commands {
|
|
||||||
if command.Hidden {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, name := range command.Names() {
|
|
||||||
fmt.Fprintln(c.App.Writer, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowCommandHelpAndExit - exits with code after showing help
|
|
||||||
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
|
||||||
ShowCommandHelp(c, command)
|
|
||||||
os.Exit(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowCommandHelp prints help for the given command
|
|
||||||
func ShowCommandHelp(ctx *Context, command string) error {
|
|
||||||
// show the subcommand help for a command with subcommands
|
|
||||||
if command == "" {
|
|
||||||
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range ctx.App.Commands {
|
|
||||||
if c.HasName(command) {
|
|
||||||
if c.CustomHelpTemplate != "" {
|
|
||||||
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
|
|
||||||
} else {
|
|
||||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.App.CommandNotFound == nil {
|
|
||||||
return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.App.CommandNotFound(ctx, command)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowSubcommandHelp prints help for the given subcommand
|
|
||||||
func ShowSubcommandHelp(c *Context) error {
|
|
||||||
return ShowCommandHelp(c, c.Command.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowVersion prints the version number of the App
|
|
||||||
func ShowVersion(c *Context) {
|
|
||||||
VersionPrinter(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func printVersion(c *Context) {
|
|
||||||
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowCompletions prints the lists of commands within a given context
|
|
||||||
func ShowCompletions(c *Context) {
|
|
||||||
a := c.App
|
|
||||||
if a != nil && a.BashComplete != nil {
|
|
||||||
a.BashComplete(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowCommandCompletions prints the custom completions for a given command
|
|
||||||
func ShowCommandCompletions(ctx *Context, command string) {
|
|
||||||
c := ctx.App.Command(command)
|
|
||||||
if c != nil && c.BashComplete != nil {
|
|
||||||
c.BashComplete(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
|
|
||||||
funcMap := template.FuncMap{
|
|
||||||
"join": strings.Join,
|
|
||||||
}
|
|
||||||
if customFunc != nil {
|
|
||||||
for key, value := range customFunc {
|
|
||||||
funcMap[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
|
||||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
|
||||||
err := t.Execute(w, data)
|
|
||||||
if err != nil {
|
|
||||||
// If the writer is closed, t.Execute will fail, and there's nothing
|
|
||||||
// we can do to recover.
|
|
||||||
if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" {
|
|
||||||
fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
|
||||||
printHelpCustom(out, templ, data, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkVersion(c *Context) bool {
|
|
||||||
found := false
|
|
||||||
if VersionFlag.GetName() != "" {
|
|
||||||
eachName(VersionFlag.GetName(), func(name string) {
|
|
||||||
if c.GlobalBool(name) || c.Bool(name) {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkHelp(c *Context) bool {
|
|
||||||
found := false
|
|
||||||
if HelpFlag.GetName() != "" {
|
|
||||||
eachName(HelpFlag.GetName(), func(name string) {
|
|
||||||
if c.GlobalBool(name) || c.Bool(name) {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkCommandHelp(c *Context, name string) bool {
|
|
||||||
if c.Bool("h") || c.Bool("help") {
|
|
||||||
ShowCommandHelp(c, name)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSubcommandHelp(c *Context) bool {
|
|
||||||
if c.Bool("h") || c.Bool("help") {
|
|
||||||
ShowSubcommandHelp(c)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) {
|
|
||||||
if !a.EnableBashCompletion {
|
|
||||||
return false, arguments
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := len(arguments) - 1
|
|
||||||
lastArg := arguments[pos]
|
|
||||||
|
|
||||||
if lastArg != "--"+BashCompletionFlag.GetName() {
|
|
||||||
return false, arguments
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, arguments[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkCompletions(c *Context) bool {
|
|
||||||
if !c.shellComplete {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if args := c.Args(); args.Present() {
|
|
||||||
name := args.First()
|
|
||||||
if cmd := c.App.Command(name); cmd != nil {
|
|
||||||
// let the command handle the completion
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowCompletions(c)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkCommandCompletions(c *Context, name string) bool {
|
|
||||||
if !c.shellComplete {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowCommandCompletions(c, name)
|
|
||||||
return true
|
|
||||||
}
|
|
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
202
vendor/github.com/docker/distribution/LICENSE
generated
vendored
|
@ -1,202 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
130
vendor/github.com/docker/distribution/README.md
generated
vendored
130
vendor/github.com/docker/distribution/README.md
generated
vendored
|
@ -1,130 +0,0 @@
|
||||||
# Distribution
|
|
||||||
|
|
||||||
The Docker toolset to pack, ship, store, and deliver content.
|
|
||||||
|
|
||||||
This repository's main product is the Docker Registry 2.0 implementation
|
|
||||||
for storing and distributing Docker images. It supersedes the
|
|
||||||
[docker/docker-registry](https://github.com/docker/docker-registry)
|
|
||||||
project with a new API design, focused around security and performance.
|
|
||||||
|
|
||||||
<img src="https://www.docker.com/sites/default/files/oyster-registry-3.png" width=200px/>
|
|
||||||
|
|
||||||
[![Circle CI](https://circleci.com/gh/docker/distribution/tree/master.svg?style=svg)](https://circleci.com/gh/docker/distribution/tree/master)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/docker/distribution?status.svg)](https://godoc.org/github.com/docker/distribution)
|
|
||||||
|
|
||||||
This repository contains the following components:
|
|
||||||
|
|
||||||
|**Component** |Description |
|
|
||||||
|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| **registry** | An implementation of the [Docker Registry HTTP API V2](docs/spec/api.md) for use with docker 1.6+. |
|
|
||||||
| **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. |
|
|
||||||
| **specifications** | _Distribution_ related specifications are available in [docs/spec](docs/spec) |
|
|
||||||
| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. |
|
|
||||||
|
|
||||||
### How does this integrate with Docker engine?
|
|
||||||
|
|
||||||
This project should provide an implementation to a V2 API for use in the [Docker
|
|
||||||
core project](https://github.com/docker/docker). The API should be embeddable
|
|
||||||
and simplify the process of securely pulling and pushing content from `docker`
|
|
||||||
daemons.
|
|
||||||
|
|
||||||
### What are the long term goals of the Distribution project?
|
|
||||||
|
|
||||||
The _Distribution_ project has the further long term goal of providing a
|
|
||||||
secure tool chain for distributing content. The specifications, APIs and tools
|
|
||||||
should be as useful with Docker as they are without.
|
|
||||||
|
|
||||||
Our goal is to design a professional grade and extensible content distribution
|
|
||||||
system that allow users to:
|
|
||||||
|
|
||||||
* Enjoy an efficient, secured and reliable way to store, manage, package and
|
|
||||||
exchange content
|
|
||||||
* Hack/roll their own on top of healthy open-source components
|
|
||||||
* Implement their own home made solution through good specs, and solid
|
|
||||||
extensions mechanism.
|
|
||||||
|
|
||||||
## More about Registry 2.0
|
|
||||||
|
|
||||||
The new registry implementation provides the following benefits:
|
|
||||||
|
|
||||||
- faster push and pull
|
|
||||||
- new, more efficient implementation
|
|
||||||
- simplified deployment
|
|
||||||
- pluggable storage backend
|
|
||||||
- webhook notifications
|
|
||||||
|
|
||||||
For information on upcoming functionality, please see [ROADMAP.md](ROADMAP.md).
|
|
||||||
|
|
||||||
### Who needs to deploy a registry?
|
|
||||||
|
|
||||||
By default, Docker users pull images from Docker's public registry instance.
|
|
||||||
[Installing Docker](https://docs.docker.com/engine/installation/) gives users this
|
|
||||||
ability. Users can also push images to a repository on Docker's public registry,
|
|
||||||
if they have a [Docker Hub](https://hub.docker.com/) account.
|
|
||||||
|
|
||||||
For some users and even companies, this default behavior is sufficient. For
|
|
||||||
others, it is not.
|
|
||||||
|
|
||||||
For example, users with their own software products may want to maintain a
|
|
||||||
registry for private, company images. Also, you may wish to deploy your own
|
|
||||||
image repository for images used to test or in continuous integration. For these
|
|
||||||
use cases and others, [deploying your own registry instance](https://github.com/docker/docker.github.io/blob/master/registry/deploying.md)
|
|
||||||
may be the better choice.
|
|
||||||
|
|
||||||
### Migration to Registry 2.0
|
|
||||||
|
|
||||||
For those who have previously deployed their own registry based on the Registry
|
|
||||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
|
||||||
data migration is required. A tool to assist with migration efforts has been
|
|
||||||
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
|
||||||
|
|
||||||
## Contribute
|
|
||||||
|
|
||||||
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute
|
|
||||||
issues, fixes, and patches to this project. If you are contributing code, see
|
|
||||||
the instructions for [building a development environment](BUILDING.md).
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
If any issues are encountered while using the _Distribution_ project, several
|
|
||||||
avenues are available for support:
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
IRC
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
#docker-distribution on FreeNode
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Issue Tracker
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
github.com/docker/distribution/issues
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Google Groups
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
https://groups.google.com/a/dockerproject.org/forum/#!forum/distribution
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th align="left">
|
|
||||||
Mailing List
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
docker@dockerproject.org
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is distributed under [Apache License, Version 2.0](LICENSE).
|
|
247
vendor/github.com/docker/distribution/digestset/set.go
generated
vendored
247
vendor/github.com/docker/distribution/digestset/set.go
generated
vendored
|
@ -1,247 +0,0 @@
|
||||||
package digestset
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
digest "github.com/opencontainers/go-digest"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrDigestNotFound is used when a matching digest
|
|
||||||
// could not be found in a set.
|
|
||||||
ErrDigestNotFound = errors.New("digest not found")
|
|
||||||
|
|
||||||
// ErrDigestAmbiguous is used when multiple digests
|
|
||||||
// are found in a set. None of the matching digests
|
|
||||||
// should be considered valid matches.
|
|
||||||
ErrDigestAmbiguous = errors.New("ambiguous digest string")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set is used to hold a unique set of digests which
|
|
||||||
// may be easily referenced by easily referenced by a string
|
|
||||||
// representation of the digest as well as short representation.
|
|
||||||
// The uniqueness of the short representation is based on other
|
|
||||||
// digests in the set. If digests are omitted from this set,
|
|
||||||
// collisions in a larger set may not be detected, therefore it
|
|
||||||
// is important to always do short representation lookups on
|
|
||||||
// the complete set of digests. To mitigate collisions, an
|
|
||||||
// appropriately long short code should be used.
|
|
||||||
type Set struct {
|
|
||||||
mutex sync.RWMutex
|
|
||||||
entries digestEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSet creates an empty set of digests
|
|
||||||
// which may have digests added.
|
|
||||||
func NewSet() *Set {
|
|
||||||
return &Set{
|
|
||||||
entries: digestEntries{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkShortMatch checks whether two digests match as either whole
|
|
||||||
// values or short values. This function does not test equality,
|
|
||||||
// rather whether the second value could match against the first
|
|
||||||
// value.
|
|
||||||
func checkShortMatch(alg digest.Algorithm, hex, shortAlg, shortHex string) bool {
|
|
||||||
if len(hex) == len(shortHex) {
|
|
||||||
if hex != shortHex {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(shortAlg) > 0 && string(alg) != shortAlg {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else if !strings.HasPrefix(hex, shortHex) {
|
|
||||||
return false
|
|
||||||
} else if len(shortAlg) > 0 && string(alg) != shortAlg {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup looks for a digest matching the given string representation.
|
|
||||||
// If no digests could be found ErrDigestNotFound will be returned
|
|
||||||
// with an empty digest value. If multiple matches are found
|
|
||||||
// ErrDigestAmbiguous will be returned with an empty digest value.
|
|
||||||
func (dst *Set) Lookup(d string) (digest.Digest, error) {
|
|
||||||
dst.mutex.RLock()
|
|
||||||
defer dst.mutex.RUnlock()
|
|
||||||
if len(dst.entries) == 0 {
|
|
||||||
return "", ErrDigestNotFound
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
searchFunc func(int) bool
|
|
||||||
alg digest.Algorithm
|
|
||||||
hex string
|
|
||||||
)
|
|
||||||
dgst, err := digest.Parse(d)
|
|
||||||
if err == digest.ErrDigestInvalidFormat {
|
|
||||||
hex = d
|
|
||||||
searchFunc = func(i int) bool {
|
|
||||||
return dst.entries[i].val >= d
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hex = dgst.Hex()
|
|
||||||
alg = dgst.Algorithm()
|
|
||||||
searchFunc = func(i int) bool {
|
|
||||||
if dst.entries[i].val == hex {
|
|
||||||
return dst.entries[i].alg >= alg
|
|
||||||
}
|
|
||||||
return dst.entries[i].val >= hex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx := sort.Search(len(dst.entries), searchFunc)
|
|
||||||
if idx == len(dst.entries) || !checkShortMatch(dst.entries[idx].alg, dst.entries[idx].val, string(alg), hex) {
|
|
||||||
return "", ErrDigestNotFound
|
|
||||||
}
|
|
||||||
if dst.entries[idx].alg == alg && dst.entries[idx].val == hex {
|
|
||||||
return dst.entries[idx].digest, nil
|
|
||||||
}
|
|
||||||
if idx+1 < len(dst.entries) && checkShortMatch(dst.entries[idx+1].alg, dst.entries[idx+1].val, string(alg), hex) {
|
|
||||||
return "", ErrDigestAmbiguous
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst.entries[idx].digest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds the given digest to the set. An error will be returned
|
|
||||||
// if the given digest is invalid. If the digest already exists in the
|
|
||||||
// set, this operation will be a no-op.
|
|
||||||
func (dst *Set) Add(d digest.Digest) error {
|
|
||||||
if err := d.Validate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dst.mutex.Lock()
|
|
||||||
defer dst.mutex.Unlock()
|
|
||||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
|
||||||
searchFunc := func(i int) bool {
|
|
||||||
if dst.entries[i].val == entry.val {
|
|
||||||
return dst.entries[i].alg >= entry.alg
|
|
||||||
}
|
|
||||||
return dst.entries[i].val >= entry.val
|
|
||||||
}
|
|
||||||
idx := sort.Search(len(dst.entries), searchFunc)
|
|
||||||
if idx == len(dst.entries) {
|
|
||||||
dst.entries = append(dst.entries, entry)
|
|
||||||
return nil
|
|
||||||
} else if dst.entries[idx].digest == d {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
entries := append(dst.entries, nil)
|
|
||||||
copy(entries[idx+1:], entries[idx:len(entries)-1])
|
|
||||||
entries[idx] = entry
|
|
||||||
dst.entries = entries
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the given digest from the set. An err will be
|
|
||||||
// returned if the given digest is invalid. If the digest does
|
|
||||||
// not exist in the set, this operation will be a no-op.
|
|
||||||
func (dst *Set) Remove(d digest.Digest) error {
|
|
||||||
if err := d.Validate(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dst.mutex.Lock()
|
|
||||||
defer dst.mutex.Unlock()
|
|
||||||
entry := &digestEntry{alg: d.Algorithm(), val: d.Hex(), digest: d}
|
|
||||||
searchFunc := func(i int) bool {
|
|
||||||
if dst.entries[i].val == entry.val {
|
|
||||||
return dst.entries[i].alg >= entry.alg
|
|
||||||
}
|
|
||||||
return dst.entries[i].val >= entry.val
|
|
||||||
}
|
|
||||||
idx := sort.Search(len(dst.entries), searchFunc)
|
|
||||||
// Not found if idx is after or value at idx is not digest
|
|
||||||
if idx == len(dst.entries) || dst.entries[idx].digest != d {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
entries := dst.entries
|
|
||||||
copy(entries[idx:], entries[idx+1:])
|
|
||||||
entries = entries[:len(entries)-1]
|
|
||||||
dst.entries = entries
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// All returns all the digests in the set
|
|
||||||
func (dst *Set) All() []digest.Digest {
|
|
||||||
dst.mutex.RLock()
|
|
||||||
defer dst.mutex.RUnlock()
|
|
||||||
retValues := make([]digest.Digest, len(dst.entries))
|
|
||||||
for i := range dst.entries {
|
|
||||||
retValues[i] = dst.entries[i].digest
|
|
||||||
}
|
|
||||||
|
|
||||||
return retValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShortCodeTable returns a map of Digest to unique short codes. The
|
|
||||||
// length represents the minimum value, the maximum length may be the
|
|
||||||
// entire value of digest if uniqueness cannot be achieved without the
|
|
||||||
// full value. This function will attempt to make short codes as short
|
|
||||||
// as possible to be unique.
|
|
||||||
func ShortCodeTable(dst *Set, length int) map[digest.Digest]string {
|
|
||||||
dst.mutex.RLock()
|
|
||||||
defer dst.mutex.RUnlock()
|
|
||||||
m := make(map[digest.Digest]string, len(dst.entries))
|
|
||||||
l := length
|
|
||||||
resetIdx := 0
|
|
||||||
for i := 0; i < len(dst.entries); i++ {
|
|
||||||
var short string
|
|
||||||
extended := true
|
|
||||||
for extended {
|
|
||||||
extended = false
|
|
||||||
if len(dst.entries[i].val) <= l {
|
|
||||||
short = dst.entries[i].digest.String()
|
|
||||||
} else {
|
|
||||||
short = dst.entries[i].val[:l]
|
|
||||||
for j := i + 1; j < len(dst.entries); j++ {
|
|
||||||
if checkShortMatch(dst.entries[j].alg, dst.entries[j].val, "", short) {
|
|
||||||
if j > resetIdx {
|
|
||||||
resetIdx = j
|
|
||||||
}
|
|
||||||
extended = true
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if extended {
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m[dst.entries[i].digest] = short
|
|
||||||
if i >= resetIdx {
|
|
||||||
l = length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
type digestEntry struct {
|
|
||||||
alg digest.Algorithm
|
|
||||||
val string
|
|
||||||
digest digest.Digest
|
|
||||||
}
|
|
||||||
|
|
||||||
type digestEntries []*digestEntry
|
|
||||||
|
|
||||||
func (d digestEntries) Len() int {
|
|
||||||
return len(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d digestEntries) Less(i, j int) bool {
|
|
||||||
if d[i].val != d[j].val {
|
|
||||||
return d[i].val < d[j].val
|
|
||||||
}
|
|
||||||
return d[i].alg < d[j].alg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d digestEntries) Swap(i, j int) {
|
|
||||||
d[i], d[j] = d[j], d[i]
|
|
||||||
}
|
|
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
42
vendor/github.com/docker/distribution/reference/helpers.go
generated
vendored
|
@ -1,42 +0,0 @@
|
||||||
package reference
|
|
||||||
|
|
||||||
import "path"
|
|
||||||
|
|
||||||
// IsNameOnly returns true if reference only contains a repo name.
|
|
||||||
func IsNameOnly(ref Named) bool {
|
|
||||||
if _, ok := ref.(NamedTagged); ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if _, ok := ref.(Canonical); ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// FamiliarName returns the familiar name string
|
|
||||||
// for the given named, familiarizing if needed.
|
|
||||||
func FamiliarName(ref Named) string {
|
|
||||||
if nn, ok := ref.(normalizedNamed); ok {
|
|
||||||
return nn.Familiar().Name()
|
|
||||||
}
|
|
||||||
return ref.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FamiliarString returns the familiar string representation
|
|
||||||
// for the given reference, familiarizing if needed.
|
|
||||||
func FamiliarString(ref Reference) string {
|
|
||||||
if nn, ok := ref.(normalizedNamed); ok {
|
|
||||||
return nn.Familiar().String()
|
|
||||||
}
|
|
||||||
return ref.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FamiliarMatch reports whether ref matches the specified pattern.
|
|
||||||
// See https://godoc.org/path#Match for supported patterns.
|
|
||||||
func FamiliarMatch(pattern string, ref Reference) (bool, error) {
|
|
||||||
matched, err := path.Match(pattern, FamiliarString(ref))
|
|
||||||
if namedRef, isNamed := ref.(Named); isNamed && !matched {
|
|
||||||
matched, _ = path.Match(pattern, FamiliarName(namedRef))
|
|
||||||
}
|
|
||||||
return matched, err
|
|
||||||
}
|
|
170
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
170
vendor/github.com/docker/distribution/reference/normalize.go
generated
vendored
|
@ -1,170 +0,0 @@
|
||||||
package reference
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/distribution/digestset"
|
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
legacyDefaultDomain = "index.docker.io"
|
|
||||||
defaultDomain = "docker.io"
|
|
||||||
officialRepoName = "library"
|
|
||||||
defaultTag = "latest"
|
|
||||||
)
|
|
||||||
|
|
||||||
// normalizedNamed represents a name which has been
|
|
||||||
// normalized and has a familiar form. A familiar name
|
|
||||||
// is what is used in Docker UI. An example normalized
|
|
||||||
// name is "docker.io/library/ubuntu" and corresponding
|
|
||||||
// familiar name of "ubuntu".
|
|
||||||
type normalizedNamed interface {
|
|
||||||
Named
|
|
||||||
Familiar() Named
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseNormalizedNamed parses a string into a named reference
|
|
||||||
// transforming a familiar name from Docker UI to a fully
|
|
||||||
// qualified reference. If the value may be an identifier
|
|
||||||
// use ParseAnyReference.
|
|
||||||
func ParseNormalizedNamed(s string) (Named, error) {
|
|
||||||
if ok := anchoredIdentifierRegexp.MatchString(s); ok {
|
|
||||||
return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s)
|
|
||||||
}
|
|
||||||
domain, remainder := splitDockerDomain(s)
|
|
||||||
var remoteName string
|
|
||||||
if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 {
|
|
||||||
remoteName = remainder[:tagSep]
|
|
||||||
} else {
|
|
||||||
remoteName = remainder
|
|
||||||
}
|
|
||||||
if strings.ToLower(remoteName) != remoteName {
|
|
||||||
return nil, errors.New("invalid reference format: repository name must be lowercase")
|
|
||||||
}
|
|
||||||
|
|
||||||
ref, err := Parse(domain + "/" + remainder)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
named, isNamed := ref.(Named)
|
|
||||||
if !isNamed {
|
|
||||||
return nil, fmt.Errorf("reference %s has no name", ref.String())
|
|
||||||
}
|
|
||||||
return named, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitDockerDomain splits a repository name to domain and remotename string.
|
|
||||||
// If no valid domain is found, the default domain is used. Repository name
|
|
||||||
// needs to be already validated before.
|
|
||||||
func splitDockerDomain(name string) (domain, remainder string) {
|
|
||||||
i := strings.IndexRune(name, '/')
|
|
||||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
|
||||||
domain, remainder = defaultDomain, name
|
|
||||||
} else {
|
|
||||||
domain, remainder = name[:i], name[i+1:]
|
|
||||||
}
|
|
||||||
if domain == legacyDefaultDomain {
|
|
||||||
domain = defaultDomain
|
|
||||||
}
|
|
||||||
if domain == defaultDomain && !strings.ContainsRune(remainder, '/') {
|
|
||||||
remainder = officialRepoName + "/" + remainder
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// familiarizeName returns a shortened version of the name familiar
|
|
||||||
// to to the Docker UI. Familiar names have the default domain
|
|
||||||
// "docker.io" and "library/" repository prefix removed.
|
|
||||||
// For example, "docker.io/library/redis" will have the familiar
|
|
||||||
// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp".
|
|
||||||
// Returns a familiarized named only reference.
|
|
||||||
func familiarizeName(named namedRepository) repository {
|
|
||||||
repo := repository{
|
|
||||||
domain: named.Domain(),
|
|
||||||
path: named.Path(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.domain == defaultDomain {
|
|
||||||
repo.domain = ""
|
|
||||||
// Handle official repositories which have the pattern "library/<official repo name>"
|
|
||||||
if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName {
|
|
||||||
repo.path = split[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r reference) Familiar() Named {
|
|
||||||
return reference{
|
|
||||||
namedRepository: familiarizeName(r.namedRepository),
|
|
||||||
tag: r.tag,
|
|
||||||
digest: r.digest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r repository) Familiar() Named {
|
|
||||||
return familiarizeName(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t taggedReference) Familiar() Named {
|
|
||||||
return taggedReference{
|
|
||||||
namedRepository: familiarizeName(t.namedRepository),
|
|
||||||
tag: t.tag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c canonicalReference) Familiar() Named {
|
|
||||||
return canonicalReference{
|
|
||||||
namedRepository: familiarizeName(c.namedRepository),
|
|
||||||
digest: c.digest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TagNameOnly adds the default tag "latest" to a reference if it only has
|
|
||||||
// a repo name.
|
|
||||||
func TagNameOnly(ref Named) Named {
|
|
||||||
if IsNameOnly(ref) {
|
|
||||||
namedTagged, err := WithTag(ref, defaultTag)
|
|
||||||
if err != nil {
|
|
||||||
// Default tag must be valid, to create a NamedTagged
|
|
||||||
// type with non-validated input the WithTag function
|
|
||||||
// should be used instead
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return namedTagged
|
|
||||||
}
|
|
||||||
return ref
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAnyReference parses a reference string as a possible identifier,
|
|
||||||
// full digest, or familiar name.
|
|
||||||
func ParseAnyReference(ref string) (Reference, error) {
|
|
||||||
if ok := anchoredIdentifierRegexp.MatchString(ref); ok {
|
|
||||||
return digestReference("sha256:" + ref), nil
|
|
||||||
}
|
|
||||||
if dgst, err := digest.Parse(ref); err == nil {
|
|
||||||
return digestReference(dgst), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseNormalizedNamed(ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAnyReferenceWithSet parses a reference string as a possible short
|
|
||||||
// identifier to be matched in a digest set, a full digest, or familiar name.
|
|
||||||
func ParseAnyReferenceWithSet(ref string, ds *digestset.Set) (Reference, error) {
|
|
||||||
if ok := anchoredShortIdentifierRegexp.MatchString(ref); ok {
|
|
||||||
dgst, err := ds.Lookup(ref)
|
|
||||||
if err == nil {
|
|
||||||
return digestReference(dgst), nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if dgst, err := digest.Parse(ref); err == nil {
|
|
||||||
return digestReference(dgst), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseNormalizedNamed(ref)
|
|
||||||
}
|
|
433
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
433
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
|
@ -1,433 +0,0 @@
|
||||||
// Package reference provides a general type to represent any way of referencing images within the registry.
|
|
||||||
// Its main purpose is to abstract tags and digests (content-addressable hash).
|
|
||||||
//
|
|
||||||
// Grammar
|
|
||||||
//
|
|
||||||
// reference := name [ ":" tag ] [ "@" digest ]
|
|
||||||
// name := [domain '/'] path-component ['/' path-component]*
|
|
||||||
// domain := domain-component ['.' domain-component]* [':' port-number]
|
|
||||||
// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/
|
|
||||||
// port-number := /[0-9]+/
|
|
||||||
// path-component := alpha-numeric [separator alpha-numeric]*
|
|
||||||
// alpha-numeric := /[a-z0-9]+/
|
|
||||||
// separator := /[_.]|__|[-]*/
|
|
||||||
//
|
|
||||||
// tag := /[\w][\w.-]{0,127}/
|
|
||||||
//
|
|
||||||
// digest := digest-algorithm ":" digest-hex
|
|
||||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
|
||||||
// digest-algorithm-separator := /[+.-_]/
|
|
||||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
|
||||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
|
||||||
//
|
|
||||||
// identifier := /[a-f0-9]{64}/
|
|
||||||
// short-identifier := /[a-f0-9]{6,64}/
|
|
||||||
package reference
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// NameTotalLengthMax is the maximum total number of characters in a repository name.
|
|
||||||
NameTotalLengthMax = 255
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference.
|
|
||||||
ErrReferenceInvalidFormat = errors.New("invalid reference format")
|
|
||||||
|
|
||||||
// ErrTagInvalidFormat represents an error while trying to parse a string as a tag.
|
|
||||||
ErrTagInvalidFormat = errors.New("invalid tag format")
|
|
||||||
|
|
||||||
// ErrDigestInvalidFormat represents an error while trying to parse a string as a tag.
|
|
||||||
ErrDigestInvalidFormat = errors.New("invalid digest format")
|
|
||||||
|
|
||||||
// ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters.
|
|
||||||
ErrNameContainsUppercase = errors.New("repository name must be lowercase")
|
|
||||||
|
|
||||||
// ErrNameEmpty is returned for empty, invalid repository names.
|
|
||||||
ErrNameEmpty = errors.New("repository name must have at least one component")
|
|
||||||
|
|
||||||
// ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax.
|
|
||||||
ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax)
|
|
||||||
|
|
||||||
// ErrNameNotCanonical is returned when a name is not canonical.
|
|
||||||
ErrNameNotCanonical = errors.New("repository name must be canonical")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference is an opaque object reference identifier that may include
|
|
||||||
// modifiers such as a hostname, name, tag, and digest.
|
|
||||||
type Reference interface {
|
|
||||||
// String returns the full reference
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Field provides a wrapper type for resolving correct reference types when
|
|
||||||
// working with encoding.
|
|
||||||
type Field struct {
|
|
||||||
reference Reference
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsField wraps a reference in a Field for encoding.
|
|
||||||
func AsField(reference Reference) Field {
|
|
||||||
return Field{reference}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference unwraps the reference type from the field to
|
|
||||||
// return the Reference object. This object should be
|
|
||||||
// of the appropriate type to further check for different
|
|
||||||
// reference types.
|
|
||||||
func (f Field) Reference() Reference {
|
|
||||||
return f.reference
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText serializes the field to byte text which
|
|
||||||
// is the string of the reference.
|
|
||||||
func (f Field) MarshalText() (p []byte, err error) {
|
|
||||||
return []byte(f.reference.String()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText parses text bytes by invoking the
|
|
||||||
// reference parser to ensure the appropriately
|
|
||||||
// typed reference object is wrapped by field.
|
|
||||||
func (f *Field) UnmarshalText(p []byte) error {
|
|
||||||
r, err := Parse(string(p))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.reference = r
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Named is an object with a full name
|
|
||||||
type Named interface {
|
|
||||||
Reference
|
|
||||||
Name() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tagged is an object which has a tag
|
|
||||||
type Tagged interface {
|
|
||||||
Reference
|
|
||||||
Tag() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedTagged is an object including a name and tag.
|
|
||||||
type NamedTagged interface {
|
|
||||||
Named
|
|
||||||
Tag() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Digested is an object which has a digest
|
|
||||||
// in which it can be referenced by
|
|
||||||
type Digested interface {
|
|
||||||
Reference
|
|
||||||
Digest() digest.Digest
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canonical reference is an object with a fully unique
|
|
||||||
// name including a name with domain and digest
|
|
||||||
type Canonical interface {
|
|
||||||
Named
|
|
||||||
Digest() digest.Digest
|
|
||||||
}
|
|
||||||
|
|
||||||
// namedRepository is a reference to a repository with a name.
|
|
||||||
// A namedRepository has both domain and path components.
|
|
||||||
type namedRepository interface {
|
|
||||||
Named
|
|
||||||
Domain() string
|
|
||||||
Path() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Domain returns the domain part of the Named reference
|
|
||||||
func Domain(named Named) string {
|
|
||||||
if r, ok := named.(namedRepository); ok {
|
|
||||||
return r.Domain()
|
|
||||||
}
|
|
||||||
domain, _ := splitDomain(named.Name())
|
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path returns the name without the domain part of the Named reference
|
|
||||||
func Path(named Named) (name string) {
|
|
||||||
if r, ok := named.(namedRepository); ok {
|
|
||||||
return r.Path()
|
|
||||||
}
|
|
||||||
_, path := splitDomain(named.Name())
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitDomain(name string) (string, string) {
|
|
||||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
|
||||||
if len(match) != 3 {
|
|
||||||
return "", name
|
|
||||||
}
|
|
||||||
return match[1], match[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitHostname splits a named reference into a
|
|
||||||
// hostname and name string. If no valid hostname is
|
|
||||||
// found, the hostname is empty and the full value
|
|
||||||
// is returned as name
|
|
||||||
// DEPRECATED: Use Domain or Path
|
|
||||||
func SplitHostname(named Named) (string, string) {
|
|
||||||
if r, ok := named.(namedRepository); ok {
|
|
||||||
return r.Domain(), r.Path()
|
|
||||||
}
|
|
||||||
return splitDomain(named.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses s and returns a syntactically valid Reference.
|
|
||||||
// If an error was encountered it is returned, along with a nil Reference.
|
|
||||||
// NOTE: Parse will not handle short digests.
|
|
||||||
func Parse(s string) (Reference, error) {
|
|
||||||
matches := ReferenceRegexp.FindStringSubmatch(s)
|
|
||||||
if matches == nil {
|
|
||||||
if s == "" {
|
|
||||||
return nil, ErrNameEmpty
|
|
||||||
}
|
|
||||||
if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil {
|
|
||||||
return nil, ErrNameContainsUppercase
|
|
||||||
}
|
|
||||||
return nil, ErrReferenceInvalidFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(matches[1]) > NameTotalLengthMax {
|
|
||||||
return nil, ErrNameTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
var repo repository
|
|
||||||
|
|
||||||
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
|
|
||||||
if nameMatch != nil && len(nameMatch) == 3 {
|
|
||||||
repo.domain = nameMatch[1]
|
|
||||||
repo.path = nameMatch[2]
|
|
||||||
} else {
|
|
||||||
repo.domain = ""
|
|
||||||
repo.path = matches[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
ref := reference{
|
|
||||||
namedRepository: repo,
|
|
||||||
tag: matches[2],
|
|
||||||
}
|
|
||||||
if matches[3] != "" {
|
|
||||||
var err error
|
|
||||||
ref.digest, err = digest.Parse(matches[3])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r := getBestReferenceType(ref)
|
|
||||||
if r == nil {
|
|
||||||
return nil, ErrNameEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseNamed parses s and returns a syntactically valid reference implementing
|
|
||||||
// the Named interface. The reference must have a name and be in the canonical
|
|
||||||
// form, otherwise an error is returned.
|
|
||||||
// If an error was encountered it is returned, along with a nil Reference.
|
|
||||||
// NOTE: ParseNamed will not handle short digests.
|
|
||||||
func ParseNamed(s string) (Named, error) {
|
|
||||||
named, err := ParseNormalizedNamed(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if named.String() != s {
|
|
||||||
return nil, ErrNameNotCanonical
|
|
||||||
}
|
|
||||||
return named, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithName returns a named object representing the given string. If the input
|
|
||||||
// is invalid ErrReferenceInvalidFormat will be returned.
|
|
||||||
func WithName(name string) (Named, error) {
|
|
||||||
if len(name) > NameTotalLengthMax {
|
|
||||||
return nil, ErrNameTooLong
|
|
||||||
}
|
|
||||||
|
|
||||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
|
||||||
if match == nil || len(match) != 3 {
|
|
||||||
return nil, ErrReferenceInvalidFormat
|
|
||||||
}
|
|
||||||
return repository{
|
|
||||||
domain: match[1],
|
|
||||||
path: match[2],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTag combines the name from "name" and the tag from "tag" to form a
|
|
||||||
// reference incorporating both the name and the tag.
|
|
||||||
func WithTag(name Named, tag string) (NamedTagged, error) {
|
|
||||||
if !anchoredTagRegexp.MatchString(tag) {
|
|
||||||
return nil, ErrTagInvalidFormat
|
|
||||||
}
|
|
||||||
var repo repository
|
|
||||||
if r, ok := name.(namedRepository); ok {
|
|
||||||
repo.domain = r.Domain()
|
|
||||||
repo.path = r.Path()
|
|
||||||
} else {
|
|
||||||
repo.path = name.Name()
|
|
||||||
}
|
|
||||||
if canonical, ok := name.(Canonical); ok {
|
|
||||||
return reference{
|
|
||||||
namedRepository: repo,
|
|
||||||
tag: tag,
|
|
||||||
digest: canonical.Digest(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return taggedReference{
|
|
||||||
namedRepository: repo,
|
|
||||||
tag: tag,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithDigest combines the name from "name" and the digest from "digest" to form
|
|
||||||
// a reference incorporating both the name and the digest.
|
|
||||||
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
|
|
||||||
if !anchoredDigestRegexp.MatchString(digest.String()) {
|
|
||||||
return nil, ErrDigestInvalidFormat
|
|
||||||
}
|
|
||||||
var repo repository
|
|
||||||
if r, ok := name.(namedRepository); ok {
|
|
||||||
repo.domain = r.Domain()
|
|
||||||
repo.path = r.Path()
|
|
||||||
} else {
|
|
||||||
repo.path = name.Name()
|
|
||||||
}
|
|
||||||
if tagged, ok := name.(Tagged); ok {
|
|
||||||
return reference{
|
|
||||||
namedRepository: repo,
|
|
||||||
tag: tagged.Tag(),
|
|
||||||
digest: digest,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return canonicalReference{
|
|
||||||
namedRepository: repo,
|
|
||||||
digest: digest,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrimNamed removes any tag or digest from the named reference.
|
|
||||||
func TrimNamed(ref Named) Named {
|
|
||||||
domain, path := SplitHostname(ref)
|
|
||||||
return repository{
|
|
||||||
domain: domain,
|
|
||||||
path: path,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBestReferenceType(ref reference) Reference {
|
|
||||||
if ref.Name() == "" {
|
|
||||||
// Allow digest only references
|
|
||||||
if ref.digest != "" {
|
|
||||||
return digestReference(ref.digest)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ref.tag == "" {
|
|
||||||
if ref.digest != "" {
|
|
||||||
return canonicalReference{
|
|
||||||
namedRepository: ref.namedRepository,
|
|
||||||
digest: ref.digest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ref.namedRepository
|
|
||||||
}
|
|
||||||
if ref.digest == "" {
|
|
||||||
return taggedReference{
|
|
||||||
namedRepository: ref.namedRepository,
|
|
||||||
tag: ref.tag,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref
|
|
||||||
}
|
|
||||||
|
|
||||||
type reference struct {
|
|
||||||
namedRepository
|
|
||||||
tag string
|
|
||||||
digest digest.Digest
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r reference) String() string {
|
|
||||||
return r.Name() + ":" + r.tag + "@" + r.digest.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r reference) Tag() string {
|
|
||||||
return r.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r reference) Digest() digest.Digest {
|
|
||||||
return r.digest
|
|
||||||
}
|
|
||||||
|
|
||||||
type repository struct {
|
|
||||||
domain string
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r repository) String() string {
|
|
||||||
return r.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r repository) Name() string {
|
|
||||||
if r.domain == "" {
|
|
||||||
return r.path
|
|
||||||
}
|
|
||||||
return r.domain + "/" + r.path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r repository) Domain() string {
|
|
||||||
return r.domain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r repository) Path() string {
|
|
||||||
return r.path
|
|
||||||
}
|
|
||||||
|
|
||||||
type digestReference digest.Digest
|
|
||||||
|
|
||||||
func (d digestReference) String() string {
|
|
||||||
return digest.Digest(d).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d digestReference) Digest() digest.Digest {
|
|
||||||
return digest.Digest(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
type taggedReference struct {
|
|
||||||
namedRepository
|
|
||||||
tag string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t taggedReference) String() string {
|
|
||||||
return t.Name() + ":" + t.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t taggedReference) Tag() string {
|
|
||||||
return t.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
type canonicalReference struct {
|
|
||||||
namedRepository
|
|
||||||
digest digest.Digest
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c canonicalReference) String() string {
|
|
||||||
return c.Name() + "@" + c.digest.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c canonicalReference) Digest() digest.Digest {
|
|
||||||
return c.digest
|
|
||||||
}
|
|
143
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
143
vendor/github.com/docker/distribution/reference/regexp.go
generated
vendored
|
@ -1,143 +0,0 @@
|
||||||
package reference
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
|
||||||
// component of names. This only allows lower case characters and digits.
|
|
||||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
|
||||||
|
|
||||||
// separatorRegexp defines the separators allowed to be embedded in name
|
|
||||||
// components. This allow one period, one or two underscore and multiple
|
|
||||||
// dashes.
|
|
||||||
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
|
||||||
|
|
||||||
// nameComponentRegexp restricts registry path component names to start
|
|
||||||
// with at least one letter or number, with following parts able to be
|
|
||||||
// separated by one period, one or two underscore and multiple dashes.
|
|
||||||
nameComponentRegexp = expression(
|
|
||||||
alphaNumericRegexp,
|
|
||||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
|
||||||
|
|
||||||
// domainComponentRegexp restricts the registry domain component of a
|
|
||||||
// repository name to start with a component as defined by DomainRegexp
|
|
||||||
// and followed by an optional port.
|
|
||||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
|
||||||
|
|
||||||
// DomainRegexp defines the structure of potential domain components
|
|
||||||
// that may be part of image names. This is purposely a subset of what is
|
|
||||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
|
||||||
// names.
|
|
||||||
DomainRegexp = expression(
|
|
||||||
domainComponentRegexp,
|
|
||||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
|
||||||
optional(literal(`:`), match(`[0-9]+`)))
|
|
||||||
|
|
||||||
// TagRegexp matches valid tag names. From docker/docker:graph/tags.go.
|
|
||||||
TagRegexp = match(`[\w][\w.-]{0,127}`)
|
|
||||||
|
|
||||||
// anchoredTagRegexp matches valid tag names, anchored at the start and
|
|
||||||
// end of the matched string.
|
|
||||||
anchoredTagRegexp = anchored(TagRegexp)
|
|
||||||
|
|
||||||
// DigestRegexp matches valid digests.
|
|
||||||
DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`)
|
|
||||||
|
|
||||||
// anchoredDigestRegexp matches valid digests, anchored at the start and
|
|
||||||
// end of the matched string.
|
|
||||||
anchoredDigestRegexp = anchored(DigestRegexp)
|
|
||||||
|
|
||||||
// NameRegexp is the format for the name component of references. The
|
|
||||||
// regexp has capturing groups for the domain and name part omitting
|
|
||||||
// the separating forward slash from either.
|
|
||||||
NameRegexp = expression(
|
|
||||||
optional(DomainRegexp, literal(`/`)),
|
|
||||||
nameComponentRegexp,
|
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
|
||||||
|
|
||||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
|
||||||
// domain and trailing components.
|
|
||||||
anchoredNameRegexp = anchored(
|
|
||||||
optional(capture(DomainRegexp), literal(`/`)),
|
|
||||||
capture(nameComponentRegexp,
|
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
|
||||||
|
|
||||||
// ReferenceRegexp is the full supported format of a reference. The regexp
|
|
||||||
// is anchored and has capturing groups for name, tag, and digest
|
|
||||||
// components.
|
|
||||||
ReferenceRegexp = anchored(capture(NameRegexp),
|
|
||||||
optional(literal(":"), capture(TagRegexp)),
|
|
||||||
optional(literal("@"), capture(DigestRegexp)))
|
|
||||||
|
|
||||||
// IdentifierRegexp is the format for string identifier used as a
|
|
||||||
// content addressable identifier using sha256. These identifiers
|
|
||||||
// are like digests without the algorithm, since sha256 is used.
|
|
||||||
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
|
||||||
|
|
||||||
// ShortIdentifierRegexp is the format used to represent a prefix
|
|
||||||
// of an identifier. A prefix may be used to match a sha256 identifier
|
|
||||||
// within a list of trusted identifiers.
|
|
||||||
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
|
||||||
|
|
||||||
// anchoredIdentifierRegexp is used to check or match an
|
|
||||||
// identifier value, anchored at start and end of string.
|
|
||||||
anchoredIdentifierRegexp = anchored(IdentifierRegexp)
|
|
||||||
|
|
||||||
// anchoredShortIdentifierRegexp is used to check if a value
|
|
||||||
// is a possible identifier prefix, anchored at start and end
|
|
||||||
// of string.
|
|
||||||
anchoredShortIdentifierRegexp = anchored(ShortIdentifierRegexp)
|
|
||||||
)
|
|
||||||
|
|
||||||
// match compiles the string to a regular expression.
|
|
||||||
var match = regexp.MustCompile
|
|
||||||
|
|
||||||
// literal compiles s into a literal regular expression, escaping any regexp
|
|
||||||
// reserved characters.
|
|
||||||
func literal(s string) *regexp.Regexp {
|
|
||||||
re := match(regexp.QuoteMeta(s))
|
|
||||||
|
|
||||||
if _, complete := re.LiteralPrefix(); !complete {
|
|
||||||
panic("must be a literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
return re
|
|
||||||
}
|
|
||||||
|
|
||||||
// expression defines a full expression, where each regular expression must
|
|
||||||
// follow the previous.
|
|
||||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
var s string
|
|
||||||
for _, re := range res {
|
|
||||||
s += re.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return match(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// optional wraps the expression in a non-capturing group and makes the
|
|
||||||
// production optional.
|
|
||||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(group(expression(res...)).String() + `?`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// repeated wraps the regexp in a non-capturing group to get one or more
|
|
||||||
// matches.
|
|
||||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(group(expression(res...)).String() + `+`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// group wraps the regexp in a non-capturing group.
|
|
||||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`(?:` + expression(res...).String() + `)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// capture wraps the expression in a capturing group.
|
|
||||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`(` + expression(res...).String() + `)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// anchored anchors the regular expression by adding start and end delimiters.
|
|
||||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`^` + expression(res...).String() + `$`)
|
|
||||||
}
|
|
43
vendor/github.com/docker/distribution/vendor.conf
generated
vendored
43
vendor/github.com/docker/distribution/vendor.conf
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
github.com/Azure/azure-sdk-for-go 088007b3b08cc02b27f2eadfdcd870958460ce7e
|
|
||||||
github.com/Azure/go-autorest ec5f4903f77ed9927ac95b19ab8e44ada64c1356
|
|
||||||
github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4
|
|
||||||
github.com/aws/aws-sdk-go c6fc52983ea2375810aa38ddb5370e9cdf611716
|
|
||||||
github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a
|
|
||||||
github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274
|
|
||||||
github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702
|
|
||||||
github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782
|
|
||||||
github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2
|
|
||||||
github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04
|
|
||||||
github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85
|
|
||||||
github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21
|
|
||||||
github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257
|
|
||||||
github.com/go-ini/ini 2ba15ac2dc9cdf88c110ec2dc0ced7fa45f5678c
|
|
||||||
github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3
|
|
||||||
github.com/gorilla/context 14f550f51af52180c2eefed15e5fd18d63c0a64a
|
|
||||||
github.com/gorilla/handlers 60c7bfde3e33c201519a200a4507a158cc03a17b
|
|
||||||
github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604
|
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
|
||||||
github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
|
||||||
github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39
|
|
||||||
github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef
|
|
||||||
github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6
|
|
||||||
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
|
|
||||||
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
|
|
||||||
github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870
|
|
||||||
github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985
|
|
||||||
github.com/yvasiyarov/go-metrics 57bccd1ccd43f94bb17fdd8bf3007059b802f85e
|
|
||||||
github.com/yvasiyarov/gorelic a9bba5b9ab508a086f9a12b8c51fab68478e2128
|
|
||||||
github.com/yvasiyarov/newrelic_platform_go b21fdbd4370f3717f3bbd2bf41c223bc273068e6
|
|
||||||
golang.org/x/crypto c10c31b5e94b6f7a0283272dc2bb27163dcea24b
|
|
||||||
golang.org/x/net 4876518f9e71663000c348837735820161a42df7
|
|
||||||
golang.org/x/oauth2 045497edb6234273d67dbc25da3f2ddbc4c4cacf
|
|
||||||
golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb
|
|
||||||
google.golang.org/api 9bf6e6e569ff057f75d9604a46c52928f17d2b54
|
|
||||||
google.golang.org/appengine 12d5545dc1cfa6047a286d5e853841b6471f4c19
|
|
||||||
google.golang.org/cloud 975617b05ea8a58727e6c1a06b6161ff4185a9f2
|
|
||||||
google.golang.org/grpc d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994
|
|
||||||
gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
|
|
||||||
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
|
|
||||||
gopkg.in/yaml.v2 bef53efd0c76e49e6de55ead051f886bea7e9420
|
|
||||||
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
|
|
||||||
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
|
|
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
191
vendor/github.com/docker/docker/LICENSE
generated
vendored
|
@ -1,191 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
https://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
Copyright 2013-2017 Docker, Inc.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue