Bump github.com/weaveworks/common

This commit is contained in:
Alfonso Acosta
2017-02-24 13:42:39 +00:00
parent bc1c964828
commit e8cb4fb4c6
29 changed files with 1927 additions and 5793 deletions

View File

@@ -14,6 +14,7 @@ type Cmd interface {
Kill() error
Output() ([]byte, error)
Run() error
SetEnv([]string)
}
// Command is a hook for mocking
@@ -28,3 +29,7 @@ type realCmd struct {
func (c *realCmd) Kill() error {
return c.Cmd.Process.Kill()
}
func (c *realCmd) SetEnv(env []string) {
c.Cmd.Env = env
}

View File

@@ -0,0 +1,170 @@
package httpgrpc
import (
"bytes"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"strings"
"sync"
"time"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/mwitkow/go-grpc-middleware"
"github.com/opentracing/opentracing-go"
"github.com/sercand/kuberesolver"
"golang.org/x/net/context"
"google.golang.org/grpc"
"github.com/weaveworks/common/middleware"
)
const dialTimeout = 5 * time.Second
// Server implements HTTPServer. HTTPServer is a generated interface that gRPC
// servers must implement.
type Server struct {
handler http.Handler
}
// NewServer makes a new Server.
func NewServer(handler http.Handler) *Server {
return &Server{
handler: handler,
}
}
// Handle implements HTTPServer.
func (s Server) Handle(ctx context.Context, r *HTTPRequest) (*HTTPResponse, error) {
req, err := http.NewRequest(r.Method, r.Url, ioutil.NopCloser(bytes.NewReader(r.Body)))
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
toHeader(r.Headers, req.Header)
req.RequestURI = r.Url
recorder := httptest.NewRecorder()
s.handler.ServeHTTP(recorder, req)
resp := &HTTPResponse{
Code: int32(recorder.Code),
Headers: fromHeader(recorder.Header()),
Body: recorder.Body.Bytes(),
}
return resp, nil
}
// Client is a http.Handler that forwards the request over gRPC.
type Client struct {
mtx sync.RWMutex
service string
namespace string
port string
client HTTPClient
conn *grpc.ClientConn
}
// NewClient makes a new Client, given a kubernetes service address. Expects
// an address of the form <service>.<namespace>:<port>
func NewClient(address string) (*Client, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
parts := strings.SplitN(host, ".", 2)
service, namespace := parts[0], "default"
if len(parts) == 2 {
namespace = parts[1]
}
return &Client{
service: service,
namespace: namespace,
port: port,
}, nil
}
func (c *Client) connect(ctx context.Context) error {
c.mtx.RLock()
connected := c.conn != nil
c.mtx.RUnlock()
if connected {
return nil
}
c.mtx.Lock()
defer c.mtx.Unlock()
if c.conn != nil {
return nil
}
balancer := kuberesolver.NewWithNamespace(c.namespace)
ctxDeadline, cancel := context.WithTimeout(ctx, dialTimeout)
defer cancel()
conn, err := grpc.DialContext(
ctxDeadline,
fmt.Sprintf("kubernetes://%s:%s", c.service, c.port),
balancer.DialOption(),
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer()),
middleware.ClientUserHeaderInterceptor,
)),
)
if err != nil {
return err
}
c.client = NewHTTPClient(conn)
c.conn = conn
return nil
}
// ServeHTTP implements http.Handler
func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := c.connect(r.Context()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
req := &HTTPRequest{
Method: r.Method,
Url: r.RequestURI,
Body: body,
Headers: fromHeader(r.Header),
}
resp, err := c.client.Handle(r.Context(), req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
toHeader(resp.Headers, w.Header())
w.WriteHeader(int(resp.Code))
if _, err := w.Write(resp.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func toHeader(hs []*Header, header http.Header) {
for _, h := range hs {
header[h.Key] = h.Values
}
}
func fromHeader(hs http.Header) []*Header {
result := make([]*Header, 0, len(hs))
for k, vs := range hs {
result = append(result, &Header{
Key: k,
Values: vs,
})
}
return result
}

View File

@@ -0,0 +1,231 @@
// Code generated by protoc-gen-go.
// source: httpgrpc.proto
// DO NOT EDIT!
/*
Package httpgrpc is a generated protocol buffer package.
It is generated from these files:
httpgrpc.proto
It has these top-level messages:
HTTPRequest
HTTPResponse
Header
*/
package httpgrpc
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
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.ProtoPackageIsVersion2 // please upgrade the proto package
type HTTPRequest struct {
Method string `protobuf:"bytes,1,opt,name=method" json:"method,omitempty"`
Url string `protobuf:"bytes,2,opt,name=url" json:"url,omitempty"`
Headers []*Header `protobuf:"bytes,3,rep,name=headers" json:"headers,omitempty"`
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
}
func (m *HTTPRequest) Reset() { *m = HTTPRequest{} }
func (m *HTTPRequest) String() string { return proto.CompactTextString(m) }
func (*HTTPRequest) ProtoMessage() {}
func (*HTTPRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *HTTPRequest) GetMethod() string {
if m != nil {
return m.Method
}
return ""
}
func (m *HTTPRequest) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
func (m *HTTPRequest) GetHeaders() []*Header {
if m != nil {
return m.Headers
}
return nil
}
func (m *HTTPRequest) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
type HTTPResponse struct {
Code int32 `protobuf:"varint,1,opt,name=Code" json:"Code,omitempty"`
Headers []*Header `protobuf:"bytes,2,rep,name=headers" json:"headers,omitempty"`
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
}
func (m *HTTPResponse) Reset() { *m = HTTPResponse{} }
func (m *HTTPResponse) String() string { return proto.CompactTextString(m) }
func (*HTTPResponse) ProtoMessage() {}
func (*HTTPResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *HTTPResponse) GetCode() int32 {
if m != nil {
return m.Code
}
return 0
}
func (m *HTTPResponse) GetHeaders() []*Header {
if m != nil {
return m.Headers
}
return nil
}
func (m *HTTPResponse) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
type Header struct {
Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
Values []string `protobuf:"bytes,2,rep,name=values" json:"values,omitempty"`
}
func (m *Header) Reset() { *m = Header{} }
func (m *Header) String() string { return proto.CompactTextString(m) }
func (*Header) ProtoMessage() {}
func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *Header) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *Header) GetValues() []string {
if m != nil {
return m.Values
}
return nil
}
func init() {
proto.RegisterType((*HTTPRequest)(nil), "httpgrpc.HTTPRequest")
proto.RegisterType((*HTTPResponse)(nil), "httpgrpc.HTTPResponse")
proto.RegisterType((*Header)(nil), "httpgrpc.Header")
}
// 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 HTTP service
type HTTPClient interface {
Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error)
}
type hTTPClient struct {
cc *grpc.ClientConn
}
func NewHTTPClient(cc *grpc.ClientConn) HTTPClient {
return &hTTPClient{cc}
}
func (c *hTTPClient) Handle(ctx context.Context, in *HTTPRequest, opts ...grpc.CallOption) (*HTTPResponse, error) {
out := new(HTTPResponse)
err := grpc.Invoke(ctx, "/httpgrpc.HTTP/Handle", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for HTTP service
type HTTPServer interface {
Handle(context.Context, *HTTPRequest) (*HTTPResponse, error)
}
func RegisterHTTPServer(s *grpc.Server, srv HTTPServer) {
s.RegisterService(&_HTTP_serviceDesc, srv)
}
func _HTTP_Handle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HTTPRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HTTPServer).Handle(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/httpgrpc.HTTP/Handle",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HTTPServer).Handle(ctx, req.(*HTTPRequest))
}
return interceptor(ctx, in, info, handler)
}
var _HTTP_serviceDesc = grpc.ServiceDesc{
ServiceName: "httpgrpc.HTTP",
HandlerType: (*HTTPServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Handle",
Handler: _HTTP_Handle_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "httpgrpc.proto",
}
func init() { proto.RegisterFile("httpgrpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 231 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x31, 0x4f, 0xc3, 0x30,
0x10, 0x85, 0x49, 0x1d, 0x0c, 0xbd, 0x56, 0xa8, 0x3a, 0x89, 0xca, 0x62, 0x8a, 0x32, 0x45, 0x0c,
0x1d, 0xc2, 0xc4, 0x88, 0x58, 0x32, 0x22, 0xab, 0x7f, 0x20, 0xc1, 0x27, 0x22, 0x11, 0x6a, 0x63,
0x3b, 0xa0, 0xfe, 0x7b, 0x64, 0x3b, 0x85, 0x88, 0xa9, 0xdb, 0x7b, 0xe7, 0x27, 0x7f, 0xf7, 0x0e,
0x6e, 0x7a, 0xef, 0xcd, 0x9b, 0x35, 0xaf, 0x3b, 0x63, 0xb5, 0xd7, 0x78, 0x7d, 0xf2, 0xe5, 0x37,
0xac, 0x9a, 0xfd, 0xfe, 0x45, 0xd2, 0xe7, 0x48, 0xce, 0xe3, 0x16, 0xf8, 0x07, 0xf9, 0x5e, 0x2b,
0x91, 0x15, 0x59, 0xb5, 0x94, 0x93, 0xc3, 0x0d, 0xb0, 0xd1, 0x0e, 0x62, 0x11, 0x87, 0x41, 0xe2,
0x3d, 0x5c, 0xf5, 0xd4, 0x2a, 0xb2, 0x4e, 0xb0, 0x82, 0x55, 0xab, 0x7a, 0xb3, 0xfb, 0x85, 0x34,
0xf1, 0x41, 0x9e, 0x02, 0x88, 0x90, 0x77, 0x5a, 0x1d, 0x45, 0x5e, 0x64, 0xd5, 0x5a, 0x46, 0x5d,
0x76, 0xb0, 0x4e, 0x60, 0x67, 0xf4, 0xc1, 0x51, 0xc8, 0x3c, 0x6b, 0x45, 0x91, 0x7b, 0x29, 0xa3,
0x9e, 0x33, 0x16, 0xe7, 0x32, 0xd8, 0x8c, 0x51, 0x03, 0x4f, 0xb1, 0xb0, 0xff, 0x3b, 0x1d, 0xa7,
0x52, 0x41, 0x86, 0xa6, 0x5f, 0xed, 0x30, 0x52, 0xfa, 0x7a, 0x29, 0x27, 0x57, 0x3f, 0x41, 0x1e,
0xf6, 0xc2, 0x47, 0xe0, 0x4d, 0x7b, 0x50, 0x03, 0xe1, 0xed, 0x0c, 0xfa, 0x77, 0xaa, 0xbb, 0xed,
0xff, 0x71, 0x2a, 0x52, 0x5e, 0x74, 0x3c, 0x1e, 0xf9, 0xe1, 0x27, 0x00, 0x00, 0xff, 0xff, 0x47,
0x4e, 0x55, 0x95, 0x76, 0x01, 0x00, 0x00,
}

View File

@@ -48,6 +48,8 @@ func TimeRequestHistogramStatus(ctx context.Context, method string, metric *prom
ext.Error.Set(sp, true)
}
sp.Finish()
metric.WithLabelValues(method, toStatusCode(err)).Observe(time.Now().Sub(startTime).Seconds())
if metric != nil {
metric.WithLabelValues(method, toStatusCode(err)).Observe(time.Now().Sub(startTime).Seconds())
}
return err
}

1267
vendor/github.com/weaveworks/common/mflag/flag.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
package mflagext
import (
"fmt"
"github.com/weaveworks/common/mflag"
)
type listOpts struct {
value *[]string
hasBeenSet bool
}
// ListVar creates an mflag.Var for repeated flags.
func ListVar(p *[]string, names []string, value []string, usage string) {
*p = value
mflag.Var(&listOpts{p, false}, names, usage)
}
// Set implements mflag.Var
func (opts *listOpts) Set(value string) error {
if opts.hasBeenSet {
(*opts.value) = append((*opts.value), value)
} else {
(*opts.value) = []string{value}
opts.hasBeenSet = true
}
return nil
}
// String implements mflag.Var
func (opts *listOpts) String() string {
return fmt.Sprintf("%v", []string(*opts.value))
}

View File

@@ -0,0 +1,58 @@
package middleware
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/weaveworks/common/user"
)
// ClientUserHeaderInterceptor propagates the user ID from the context to gRPC metadata, which eventually ends up as a HTTP2 header.
func ClientUserHeaderInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
userID, err := user.GetID(ctx)
if err != nil {
return err
}
md, ok := metadata.FromContext(ctx)
if !ok {
md = metadata.New(map[string]string{})
}
newCtx := ctx
if userIDs, ok := md[user.LowerOrgIDHeaderName]; ok {
switch len(userIDs) {
case 1:
if userIDs[0] != userID {
return fmt.Errorf("wrong user ID found")
}
default:
return fmt.Errorf("multiple user IDs found")
}
} else {
md = md.Copy()
md[user.LowerOrgIDHeaderName] = []string{userID}
newCtx = metadata.NewContext(ctx, md)
}
return invoker(newCtx, method, req, reply, cc, opts...)
}
// ServerUserHeaderInterceptor propagates the user ID from the gRPC metadata back to our context.
func ServerUserHeaderInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromContext(ctx)
if !ok {
return nil, fmt.Errorf("no metadata")
}
userIDs, ok := md[user.LowerOrgIDHeaderName]
if !ok || len(userIDs) != 1 {
return nil, fmt.Errorf("no user id")
}
newCtx := user.WithID(ctx, userIDs[0])
return handler(newCtx, req)
}

View File

@@ -0,0 +1,23 @@
package middleware
import (
"time"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// ServerInstrumentInterceptor instruments gRPC requests for errors and latency.
func ServerInstrumentInterceptor(duration *prometheus.HistogramVec) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
begin := time.Now()
resp, err := handler(ctx, req)
status := "success"
if err != nil {
status = "error"
}
duration.WithLabelValues(gRPC, info.FullMethod, status, "false").Observe(time.Since(begin).Seconds())
return resp, err
}
}

View File

@@ -0,0 +1,25 @@
package middleware
import (
"time"
log "github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const gRPC = "gRPC"
// ServerLoggingInterceptor logs gRPC requests, errors and latency.
func ServerLoggingInterceptor(logSuccess bool) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
begin := time.Now()
resp, err := handler(ctx, req)
if err != nil {
log.Errorf("%s %s (%v) %s", gRPC, info.FullMethod, err, time.Since(begin))
} else if logSuccess {
log.Infof("%s %s (success) %s", gRPC, info.FullMethod, time.Since(begin))
}
return resp, err
}
}

View File

@@ -0,0 +1,25 @@
package middleware
import (
"net/http"
)
// HeaderAdder adds headers to responses
type HeaderAdder struct {
http.Header
}
// Wrap implements Middleware
func (h HeaderAdder) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do it in pre-order since headers need to be added before
// writing the response body
dst := w.Header()
for k, vv := range h.Header {
for _, v := range vv {
dst.Add(k, v)
}
}
next.ServeHTTP(w, r)
})
}

View File

@@ -11,12 +11,15 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
// RouteMatcher matches routes
type RouteMatcher interface {
Match(*http.Request, *mux.RouteMatch) bool
}
// Instrument is a Middleware which records timings for every HTTP request
type Instrument struct {
RouteMatcher interface {
Match(*http.Request, *mux.RouteMatch) bool
}
Duration *prometheus.HistogramVec
RouteMatcher RouteMatcher
Duration *prometheus.HistogramVec
}
// IsWSHandshakeRequest returns true if the given request is a websocket handshake request.

View File

@@ -1,48 +0,0 @@
package middleware
import (
"net/http"
"net/url"
)
// Redirect middleware, will redirect requests to hosts which match any of the
// Matches to RedirectScheme://RedirectHost
type Redirect struct {
Matches []Match
RedirectHost string
RedirectScheme string
}
// Match specifies a match for a redirect. Host and/or Scheme can be empty
// signify match-all.
type Match struct {
Host, Scheme string
}
func (m Match) match(u *url.URL) bool {
if m.Host != "" && m.Host != u.Host {
return false
}
if m.Scheme != "" && m.Scheme != u.Scheme {
return false
}
return true
}
// Wrap implements Middleware
func (m Redirect) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for _, match := range m.Matches {
if match.match(r.URL) {
r.URL.Host = m.RedirectHost
r.URL.Scheme = m.RedirectScheme
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
return
}
}
next.ServeHTTP(w, r)
})
}

41
vendor/github.com/weaveworks/common/signals/signals.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package signals
import (
"os"
"os/signal"
"runtime"
"syscall"
)
// SignalReceiver represents a subsystem/server/... that can be stopped or
// queried about the status with a signal
type SignalReceiver interface {
Stop() error
}
// Logger is something to log too.
type Logger interface {
Infof(format string, args ...interface{})
}
// SignalHandlerLoop blocks until it receives a SIGINT, SIGTERM or SIGQUIT.
// For SIGINT and SIGTERM, it exits; for SIGQUIT is print a goroutine stack
// dump.
func SignalHandlerLoop(log Logger, ss ...SignalReceiver) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
buf := make([]byte, 1<<20)
for {
switch <-sigs {
case syscall.SIGINT, syscall.SIGTERM:
log.Infof("=== received SIGINT/SIGTERM ===\n*** exiting")
for _, subsystem := range ss {
subsystem.Stop()
}
return
case syscall.SIGQUIT:
stacklen := runtime.Stack(buf, true)
log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen])
}
}
}

View File

@@ -68,6 +68,8 @@ func (c *mockCmd) Run() error {
return nil
}
func (c *mockCmd) SetEnv([]string) {}
func (b *blockingReader) Read(p []byte) (n int, err error) {
<-b.quit
return 0, nil

View File

@@ -1,238 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"strings"
"time"
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v2"
)
// ArrayFlags allows you to collect repeated flags
type ArrayFlags []string
func (a *ArrayFlags) String() string {
return strings.Join(*a, ",")
}
// Set implements flags.Value
func (a *ArrayFlags) Set(value string) error {
*a = append(*a, value)
return nil
}
func env(key, def string) string {
if val, ok := os.LookupEnv(key); ok {
return val
}
return def
}
var (
token = env("SERVICE_TOKEN", "")
baseURL = env("BASE_URL", "https://cloud.weave.works")
)
func usage() {
fmt.Println(`Usage:
deploy <image>:<version> Deploy image to your configured env
list List recent deployments
config (<filename>) Get (or set) the configured env
logs <deploy> Show lots for the given deployment`)
}
func main() {
if len(os.Args) <= 1 {
usage()
os.Exit(1)
}
c := NewClient(token, baseURL)
switch os.Args[1] {
case "deploy":
deploy(c, os.Args[2:])
case "list":
list(c, os.Args[2:])
case "config":
config(c, os.Args[2:])
case "logs":
logs(c, os.Args[2:])
case "events":
events(c, os.Args[2:])
case "help":
usage()
default:
usage()
}
}
func deploy(c Client, args []string) {
var (
flags = flag.NewFlagSet("", flag.ContinueOnError)
username = flags.String("u", "", "Username to report to deploy service (default with be current user)")
services ArrayFlags
)
flags.Var(&services, "service", "Service to update (can be repeated)")
if err := flags.Parse(args); err != nil {
usage()
return
}
args = flags.Args()
if len(args) != 1 {
usage()
return
}
parts := strings.SplitN(args[0], ":", 2)
if len(parts) < 2 {
usage()
return
}
if *username == "" {
user, err := user.Current()
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
*username = user.Username
}
deployment := Deployment{
ImageName: parts[0],
Version: parts[1],
TriggeringUser: *username,
IntendedServices: services,
}
if err := c.Deploy(deployment); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
}
func list(c Client, args []string) {
var (
flags = flag.NewFlagSet("", flag.ContinueOnError)
since = flags.Duration("since", 7*24*time.Hour, "How far back to fetch results")
)
if err := flags.Parse(args); err != nil {
usage()
return
}
through := time.Now()
from := through.Add(-*since)
deployments, err := c.GetDeployments(from.Unix(), through.Unix())
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Created", "ID", "Image", "Version", "State"})
table.SetBorder(false)
table.SetColumnSeparator(" ")
for _, deployment := range deployments {
table.Append([]string{
deployment.CreatedAt.Format(time.RFC822),
deployment.ID,
deployment.ImageName,
deployment.Version,
deployment.State,
})
}
table.Render()
}
func events(c Client, args []string) {
var (
flags = flag.NewFlagSet("", flag.ContinueOnError)
since = flags.Duration("since", 7*24*time.Hour, "How far back to fetch results")
)
if err := flags.Parse(args); err != nil {
usage()
return
}
through := time.Now()
from := through.Add(-*since)
events, err := c.GetEvents(from.Unix(), through.Unix())
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println("events: ", string(events))
}
func loadConfig(filename string) (*Config, error) {
extension := filepath.Ext(filename)
var config Config
buf, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
if extension == ".yaml" || extension == ".yml" {
if err := yaml.Unmarshal(buf, &config); err != nil {
return nil, err
}
} else {
if err := json.NewDecoder(bytes.NewReader(buf)).Decode(&config); err != nil {
return nil, err
}
}
return &config, nil
}
func config(c Client, args []string) {
if len(args) > 1 {
usage()
return
}
if len(args) == 1 {
config, err := loadConfig(args[0])
if err != nil {
fmt.Println("Error reading config:", err)
os.Exit(1)
}
if err := c.SetConfig(config); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
} else {
config, err := c.GetConfig()
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
buf, err := yaml.Marshal(config)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println(string(buf))
}
}
func logs(c Client, args []string) {
if len(args) != 1 {
usage()
return
}
output, err := c.GetLogs(args[0])
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println(string(output))
}

View File

@@ -1,150 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
)
// Client for the deployment service
type Client struct {
token string
baseURL string
}
// NewClient makes a new Client
func NewClient(token, baseURL string) Client {
return Client{
token: token,
baseURL: baseURL,
}
}
func (c Client) newRequest(method, path string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, c.baseURL+path, body)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", fmt.Sprintf("Scope-Probe token=%s", c.token))
return req, nil
}
// Deploy notifies the deployment service about a new deployment
func (c Client) Deploy(deployment Deployment) error {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(deployment); err != nil {
return err
}
req, err := c.newRequest("POST", "/api/deploy/deploy", &buf)
if err != nil {
return err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != 204 {
return fmt.Errorf("error making request: %s", res.Status)
}
return nil
}
// GetDeployments returns a list of deployments
func (c Client) GetDeployments(from, through int64) ([]Deployment, error) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/deploy/deploy?from=%d&through=%d", from, through), nil)
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("error making request: %s", res.Status)
}
var response struct {
Deployments []Deployment `json:"deployments"`
}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
return response.Deployments, nil
}
// GetEvents returns the raw events.
func (c Client) GetEvents(from, through int64) ([]byte, error) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/deploy/event?from=%d&through=%d", from, through), nil)
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("error making request: %s", res.Status)
}
return ioutil.ReadAll(res.Body)
}
// GetConfig returns the current Config
func (c Client) GetConfig() (*Config, error) {
req, err := c.newRequest("GET", "/api/config/deploy", nil)
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode == 404 {
return nil, fmt.Errorf("no configuration uploaded yet")
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("error making request: %s", res.Status)
}
var config Config
if err := json.NewDecoder(res.Body).Decode(&config); err != nil {
return nil, err
}
return &config, nil
}
// SetConfig sets the current Config
func (c Client) SetConfig(config *Config) error {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(config); err != nil {
return err
}
req, err := c.newRequest("POST", "/api/config/deploy", &buf)
if err != nil {
return err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != 204 {
return fmt.Errorf("error making request: %s", res.Status)
}
return nil
}
// GetLogs returns the logs for a given deployment.
func (c Client) GetLogs(deployID string) ([]byte, error) {
req, err := c.newRequest("GET", fmt.Sprintf("/api/deploy/deploy/%s/log", deployID), nil)
if err != nil {
return nil, err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("error making request: %s", res.Status)
}
return ioutil.ReadAll(res.Body)
}

View File

@@ -1,43 +0,0 @@
package main
import (
"time"
)
// Deployment describes a deployment
type Deployment struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
ImageName string `json:"image_name"`
Version string `json:"version"`
Priority int `json:"priority"`
State string `json:"status"`
TriggeringUser string `json:"triggering_user"`
IntendedServices []string `json:"intended_services"`
}
// Config for the deployment system for a user.
type Config struct {
RepoURL string `json:"repo_url" yaml:"repo_url"`
RepoBranch string `json:"repo_branch" yaml:"repo_branch"`
RepoPath string `json:"repo_path" yaml:"repo_path"`
RepoKey string `json:"repo_key" yaml:"repo_key"`
KubeconfigPath string `json:"kubeconfig_path" yaml:"kubeconfig_path"`
AutoApply bool `json:"auto_apply" yaml:"auto_apply"`
Notifications []NotificationConfig `json:"notifications" yaml:"notifications"`
// Globs of files not to change, relative to the route of the repo
ConfigFileBlackList []string `json:"config_file_black_list" yaml:"config_file_black_list"`
CommitMessageTemplate string `json:"commit_message_template" yaml:"commit_message_template"` // See https://golang.org/pkg/text/template/
}
// NotificationConfig describes how to send notifications
type NotificationConfig struct {
SlackWebhookURL string `json:"slack_webhook_url" yaml:"slack_webhook_url"`
SlackUsername string `json:"slack_username" yaml:"slack_username"`
MessageTemplate string `json:"message_template" yaml:"message_template"`
ApplyMessageTemplate string `json:"apply_message_template" yaml:"apply_message_template"`
}

View File

@@ -15,7 +15,7 @@ import (
"time"
"github.com/mgutz/ansi"
"github.com/weaveworks/docker/pkg/mflag"
"github.com/weaveworks/common/mflag"
)
const (

View File

@@ -9,8 +9,8 @@ import (
"text/template"
socks5 "github.com/armon/go-socks5"
"github.com/weaveworks/docker/pkg/mflag"
"github.com/weaveworks/weave/common/mflagext"
"github.com/weaveworks/common/mflag"
"github.com/weaveworks/common/mflagext"
"golang.org/x/net/context"
)

View File

@@ -1,202 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package main
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"regexp"
"runtime/pprof"
"strings"
"github.com/mvdan/sh/syntax"
)
var (
write = flag.Bool("w", false, "write result to file instead of stdout")
list = flag.Bool("l", false, "list files whose formatting differs from shfmt's")
indent = flag.Int("i", 0, "indent: 0 for tabs (default), >0 for number of spaces")
posix = flag.Bool("p", false, "parse POSIX shell code instead of bash")
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
parseMode syntax.ParseMode
printConfig syntax.PrintConfig
readBuf, writeBuf bytes.Buffer
out io.Writer
)
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
out = os.Stdout
printConfig.Spaces = *indent
parseMode |= syntax.ParseComments
if *posix {
parseMode |= syntax.PosixConformant
}
if flag.NArg() == 0 {
if err := formatStdin(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return
}
anyErr := false
onError := func(err error) {
anyErr = true
fmt.Fprintln(os.Stderr, err)
}
for _, path := range flag.Args() {
walk(path, onError)
}
if anyErr {
os.Exit(1)
}
}
func formatStdin() error {
if *write || *list {
return fmt.Errorf("-w and -l can only be used on files")
}
readBuf.Reset()
if _, err := io.Copy(&readBuf, os.Stdin); err != nil {
return err
}
src := readBuf.Bytes()
prog, err := syntax.Parse(src, "", parseMode)
if err != nil {
return err
}
return printConfig.Fprint(out, prog)
}
var (
shellFile = regexp.MustCompile(`\.(sh|bash)$`)
validShebang = regexp.MustCompile(`^#!\s?/(usr/)?bin/(env *)?(sh|bash)`)
vcsDir = regexp.MustCompile(`^\.(git|svn|hg)$`)
)
type shellConfidence int
const (
notShellFile shellConfidence = iota
ifValidShebang
isShellFile
)
func getConfidence(info os.FileInfo) shellConfidence {
name := info.Name()
switch {
case info.IsDir(), name[0] == '.', !info.Mode().IsRegular():
return notShellFile
case shellFile.MatchString(name):
return isShellFile
case strings.Contains(name, "."):
return notShellFile // different extension
case info.Size() < 8:
return notShellFile // cannot possibly hold valid shebang
default:
return ifValidShebang
}
}
func walk(path string, onError func(error)) {
info, err := os.Stat(path)
if err != nil {
onError(err)
return
}
if !info.IsDir() {
if err := formatPath(path, false); err != nil {
onError(err)
}
return
}
filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if info.IsDir() && vcsDir.MatchString(info.Name()) {
return filepath.SkipDir
}
if err != nil {
onError(err)
return nil
}
conf := getConfidence(info)
if conf == notShellFile {
return nil
}
err = formatPath(path, conf == ifValidShebang)
if err != nil && !os.IsNotExist(err) {
onError(err)
}
return nil
})
}
func empty(f *os.File) error {
if err := f.Truncate(0); err != nil {
return err
}
_, err := f.Seek(0, 0)
return err
}
func formatPath(path string, checkShebang bool) error {
openMode := os.O_RDONLY
if *write {
openMode = os.O_RDWR
}
f, err := os.OpenFile(path, openMode, 0)
if err != nil {
return err
}
defer f.Close()
readBuf.Reset()
if _, err := io.Copy(&readBuf, f); err != nil {
return err
}
src := readBuf.Bytes()
if checkShebang && !validShebang.Match(src[:32]) {
return nil
}
prog, err := syntax.Parse(src, path, parseMode)
if err != nil {
return err
}
writeBuf.Reset()
printConfig.Fprint(&writeBuf, prog)
res := writeBuf.Bytes()
if !bytes.Equal(src, res) {
if *list {
fmt.Fprintln(out, path)
}
if *write {
if err := empty(f); err != nil {
return err
}
if _, err := f.Write(res); err != nil {
return err
}
}
}
if !*list && !*write {
if _, err := out.Write(res); err != nil {
return err
}
}
return nil
}

View File

@@ -1,6 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
// Package syntax implements parsing and formatting of shell programs.
// It supports both POSIX Shell and Bash.
package syntax

View File

@@ -1,962 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package syntax
import (
"bytes"
)
// bytes that form or start a token
func regOps(b byte) bool {
return b == ';' || b == '"' || b == '\'' || b == '(' ||
b == ')' || b == '$' || b == '|' || b == '&' ||
b == '>' || b == '<' || b == '`'
}
// tokenize these inside parameter expansions
func paramOps(b byte) bool {
return b == '}' || b == '#' || b == ':' || b == '-' || b == '+' ||
b == '=' || b == '?' || b == '%' || b == '[' || b == '/' ||
b == '^' || b == ','
}
// tokenize these inside arithmetic expansions
func arithmOps(b byte) bool {
return b == '+' || b == '-' || b == '!' || b == '*' ||
b == '/' || b == '%' || b == '(' || b == ')' ||
b == '^' || b == '<' || b == '>' || b == ':' ||
b == '=' || b == ',' || b == '?' || b == '|' ||
b == '&'
}
func wordBreak(b byte) bool {
return b == ' ' || b == '\t' || b == '\r' || b == '\n' ||
b == '&' || b == '>' || b == '<' || b == '|' ||
b == ';' || b == '(' || b == ')'
}
func (p *parser) next() {
if p.tok == _EOF || p.npos >= len(p.src) {
p.tok = _EOF
return
}
p.spaced, p.newLine = false, false
b, q := p.src[p.npos], p.quote
p.pos = Pos(p.npos + 1)
switch q {
case hdocWord:
if wordBreak(b) {
p.tok = illegalTok
p.spaced = true
return
}
case paramExpRepl:
switch b {
case '}':
p.npos++
p.tok = rightBrace
case '/':
p.npos++
p.tok = Quo
case '`', '"', '$':
p.tok = p.dqToken(b)
default:
p.advanceLitOther(q)
}
return
case dblQuotes:
if b == '`' || b == '"' || b == '$' {
p.tok = p.dqToken(b)
} else {
p.advanceLitDquote()
}
return
case hdocBody, hdocBodyTabs:
if b == '`' || b == '$' {
p.tok = p.dqToken(b)
} else if p.hdocStop == nil {
p.tok = illegalTok
} else {
p.advanceLitHdoc()
}
return
case paramExpExp:
switch b {
case '}':
p.npos++
p.tok = rightBrace
case '`', '"', '$':
p.tok = p.dqToken(b)
default:
p.advanceLitOther(q)
}
return
case sglQuotes:
if b == '\'' {
p.npos++
p.tok = sglQuote
} else {
p.advanceLitOther(q)
}
return
}
skipSpace:
for {
switch b {
case ' ', '\t', '\r':
p.spaced = true
p.npos++
case '\n':
if p.quote == arithmExprLet {
p.tok = illegalTok
p.newLine, p.spaced = true, true
return
}
p.spaced = true
if p.npos < len(p.src) {
p.npos++
}
p.f.Lines = append(p.f.Lines, p.npos)
p.newLine = true
if len(p.heredocs) > p.buriedHdocs {
p.doHeredocs()
if p.tok == _EOF {
return
}
}
case '\\':
if p.npos < len(p.src)-1 && p.src[p.npos+1] == '\n' {
p.npos += 2
p.f.Lines = append(p.f.Lines, p.npos)
} else {
break skipSpace
}
default:
break skipSpace
}
if p.npos >= len(p.src) {
p.tok = _EOF
return
}
b = p.src[p.npos]
}
p.pos = Pos(p.npos + 1)
switch {
case q&allRegTokens != 0:
switch b {
case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`':
p.tok = p.regToken(b)
case '#':
p.npos++
bs, _ := p.readUntil('\n')
p.npos += len(bs)
if p.mode&ParseComments > 0 {
p.f.Comments = append(p.f.Comments, &Comment{
Hash: p.pos,
Text: string(bs),
})
}
p.next()
case '?', '*', '+', '@', '!':
if p.bash() && p.npos+1 < len(p.src) && p.src[p.npos+1] == '(' {
switch b {
case '?':
p.tok = globQuest
case '*':
p.tok = globMul
case '+':
p.tok = globAdd
case '@':
p.tok = globAt
default: // '!'
p.tok = globNot
}
p.npos += 2
} else {
p.advanceLitNone()
}
default:
p.advanceLitNone()
}
case q == paramExpName && paramOps(b):
p.tok = p.paramToken(b)
case q&allArithmExpr != 0 && arithmOps(b):
p.tok = p.arithmToken(b)
case q&allRbrack != 0 && b == ']':
p.npos++
p.tok = rightBrack
case q == testRegexp:
p.advanceLitRe()
case regOps(b):
p.tok = p.regToken(b)
default:
p.advanceLitOther(q)
}
}
func byteAt(src []byte, i int) byte {
if i >= len(src) {
return 0
}
return src[i]
}
func (p *parser) regToken(b byte) Token {
switch b {
case '\'':
p.npos++
return sglQuote
case '"':
p.npos++
return dblQuote
case '`':
p.npos++
return bckQuote
case '&':
switch byteAt(p.src, p.npos+1) {
case '&':
p.npos += 2
return AndExpr
case '>':
if !p.bash() {
break
}
if byteAt(p.src, p.npos+2) == '>' {
p.npos += 3
return appAll
}
p.npos += 2
return rdrAll
}
p.npos++
return And
case '|':
switch byteAt(p.src, p.npos+1) {
case '|':
p.npos += 2
return OrExpr
case '&':
if !p.bash() {
break
}
p.npos += 2
return pipeAll
}
p.npos++
return Or
case '$':
switch byteAt(p.src, p.npos+1) {
case '\'':
if !p.bash() {
break
}
p.npos += 2
return dollSglQuote
case '"':
if !p.bash() {
break
}
p.npos += 2
return dollDblQuote
case '{':
p.npos += 2
return dollBrace
case '[':
if !p.bash() {
break
}
p.npos += 2
return dollBrack
case '(':
if byteAt(p.src, p.npos+2) == '(' {
p.npos += 3
return dollDblParen
}
p.npos += 2
return dollParen
}
p.npos++
return dollar
case '(':
if p.bash() && byteAt(p.src, p.npos+1) == '(' {
p.npos += 2
return dblLeftParen
}
p.npos++
return leftParen
case ')':
p.npos++
return rightParen
case ';':
switch byteAt(p.src, p.npos+1) {
case ';':
if p.bash() && byteAt(p.src, p.npos+2) == '&' {
p.npos += 3
return dblSemiFall
}
p.npos += 2
return dblSemicolon
case '&':
if !p.bash() {
break
}
p.npos += 2
return semiFall
}
p.npos++
return semicolon
case '<':
switch byteAt(p.src, p.npos+1) {
case '<':
if b := byteAt(p.src, p.npos+2); b == '-' {
p.npos += 3
return dashHdoc
} else if p.bash() && b == '<' {
p.npos += 3
return wordHdoc
}
p.npos += 2
return Shl
case '>':
p.npos += 2
return rdrInOut
case '&':
p.npos += 2
return dplIn
case '(':
if !p.bash() {
break
}
p.npos += 2
return cmdIn
}
p.npos++
return Lss
default: // '>'
switch byteAt(p.src, p.npos+1) {
case '>':
p.npos += 2
return Shr
case '&':
p.npos += 2
return dplOut
case '|':
p.npos += 2
return clbOut
case '(':
if !p.bash() {
break
}
p.npos += 2
return cmdOut
}
p.npos++
return Gtr
}
}
func (p *parser) dqToken(b byte) Token {
switch b {
case '"':
p.npos++
return dblQuote
case '`':
p.npos++
return bckQuote
default: // '$'
switch byteAt(p.src, p.npos+1) {
case '{':
p.npos += 2
return dollBrace
case '[':
if !p.bash() {
break
}
p.npos += 2
return dollBrack
case '(':
if byteAt(p.src, p.npos+2) == '(' {
p.npos += 3
return dollDblParen
}
p.npos += 2
return dollParen
}
p.npos++
return dollar
}
}
func (p *parser) paramToken(b byte) Token {
switch b {
case '}':
p.npos++
return rightBrace
case ':':
switch byteAt(p.src, p.npos+1) {
case '+':
p.npos += 2
return ColAdd
case '-':
p.npos += 2
return ColSub
case '?':
p.npos += 2
return ColQuest
case '=':
p.npos += 2
return ColAssgn
}
p.npos++
return Colon
case '+':
p.npos++
return Add
case '-':
p.npos++
return Sub
case '?':
p.npos++
return Quest
case '=':
p.npos++
return Assgn
case '%':
if byteAt(p.src, p.npos+1) == '%' {
p.npos += 2
return dblRem
}
p.npos++
return Rem
case '#':
if byteAt(p.src, p.npos+1) == '#' {
p.npos += 2
return dblHash
}
p.npos++
return Hash
case '[':
p.npos++
return leftBrack
case '^':
if byteAt(p.src, p.npos+1) == '^' {
p.npos += 2
return dblXor
}
p.npos++
return Xor
case ',':
if byteAt(p.src, p.npos+1) == ',' {
p.npos += 2
return dblComma
}
p.npos++
return Comma
default: // '/'
if byteAt(p.src, p.npos+1) == '/' {
p.npos += 2
return dblQuo
}
p.npos++
return Quo
}
}
func (p *parser) arithmToken(b byte) Token {
switch b {
case '!':
if byteAt(p.src, p.npos+1) == '=' {
p.npos += 2
return Neq
}
p.npos++
return Not
case '=':
if byteAt(p.src, p.npos+1) == '=' {
p.npos += 2
return Eql
}
p.npos++
return Assgn
case '(':
p.npos++
return leftParen
case ')':
p.npos++
return rightParen
case '&':
switch byteAt(p.src, p.npos+1) {
case '&':
p.npos += 2
return AndExpr
case '=':
p.npos += 2
return AndAssgn
}
p.npos++
return And
case '|':
switch byteAt(p.src, p.npos+1) {
case '|':
p.npos += 2
return OrExpr
case '=':
p.npos += 2
return OrAssgn
}
p.npos++
return Or
case '<':
switch byteAt(p.src, p.npos+1) {
case '<':
if byteAt(p.src, p.npos+2) == '=' {
p.npos += 3
return ShlAssgn
}
p.npos += 2
return Shl
case '=':
p.npos += 2
return Leq
}
p.npos++
return Lss
case '>':
switch byteAt(p.src, p.npos+1) {
case '>':
if byteAt(p.src, p.npos+2) == '=' {
p.npos += 3
return ShrAssgn
}
p.npos += 2
return Shr
case '=':
p.npos += 2
return Geq
}
p.npos++
return Gtr
case '+':
switch byteAt(p.src, p.npos+1) {
case '+':
p.npos += 2
return Inc
case '=':
p.npos += 2
return AddAssgn
}
p.npos++
return Add
case '-':
switch byteAt(p.src, p.npos+1) {
case '-':
p.npos += 2
return Dec
case '=':
p.npos += 2
return SubAssgn
}
p.npos++
return Sub
case '%':
if byteAt(p.src, p.npos+1) == '=' {
p.npos += 2
return RemAssgn
}
p.npos++
return Rem
case '*':
switch byteAt(p.src, p.npos+1) {
case '*':
p.npos += 2
return Pow
case '=':
p.npos += 2
return MulAssgn
}
p.npos++
return Mul
case '/':
if byteAt(p.src, p.npos+1) == '=' {
p.npos += 2
return QuoAssgn
}
p.npos++
return Quo
case '^':
if byteAt(p.src, p.npos+1) == '=' {
p.npos += 2
return XorAssgn
}
p.npos++
return Xor
case ',':
p.npos++
return Comma
case '?':
p.npos++
return Quest
default: // ':'
p.npos++
return Colon
}
}
func (p *parser) advanceLitOther(q quoteState) {
bs := p.litBuf[:0]
for {
if p.npos >= len(p.src) {
p.tok, p.val = _LitWord, string(bs)
return
}
b := p.src[p.npos]
switch {
case b == '\\': // escaped byte follows
if p.npos == len(p.src)-1 {
p.npos++
bs = append(bs, '\\')
p.tok, p.val = _LitWord, string(bs)
return
}
b = p.src[p.npos+1]
p.npos += 2
if b == '\n' {
p.f.Lines = append(p.f.Lines, p.npos)
} else {
bs = append(bs, '\\', b)
}
continue
case q == sglQuotes:
switch b {
case '\n':
p.f.Lines = append(p.f.Lines, p.npos+1)
case '\'':
p.tok, p.val = _LitWord, string(bs)
return
}
case b == '`', b == '$':
p.tok, p.val = _Lit, string(bs)
return
case q == paramExpExp:
if b == '}' {
p.tok, p.val = _LitWord, string(bs)
return
} else if b == '"' {
p.tok, p.val = _Lit, string(bs)
return
}
case q == paramExpRepl:
if b == '}' || b == '/' {
p.tok, p.val = _LitWord, string(bs)
return
}
case wordBreak(b), regOps(b), q&allArithmExpr != 0 && arithmOps(b),
q == paramExpName && paramOps(b),
q&allRbrack != 0 && b == ']':
p.tok, p.val = _LitWord, string(bs)
return
}
bs = append(bs, p.src[p.npos])
p.npos++
}
}
func (p *parser) advanceLitNone() {
var i int
tok := _Lit
p.asPos = 0
loop:
for i = p.npos; i < len(p.src); i++ {
switch p.src[i] {
case '\\': // escaped byte follows
if i == len(p.src)-1 {
break
}
if i++; p.src[i] == '\n' {
p.f.Lines = append(p.f.Lines, i+1)
bs := p.src[p.npos : i-1]
p.npos = i + 1
p.advanceLitNoneCont(bs)
return
}
case ' ', '\t', '\n', '\r', '&', '>', '<', '|', ';', '(', ')':
tok = _LitWord
break loop
case '?', '*', '+', '@', '!':
if p.bash() && i+1 < len(p.src) && p.src[i+1] == '(' {
break loop
}
case '`':
if p.quote == subCmdBckquo {
tok = _LitWord
}
break loop
case '"', '\'', '$':
break loop
case '=':
p.asPos = i - p.npos
if p.bash() && p.asPos > 0 && p.src[p.npos+p.asPos-1] == '+' {
p.asPos-- // a+=b
}
}
}
if i == len(p.src) {
tok = _LitWord
}
p.tok, p.val = tok, string(p.src[p.npos:i])
p.npos = i
}
func (p *parser) advanceLitNoneCont(bs []byte) {
for {
if p.npos >= len(p.src) {
p.tok, p.val = _LitWord, string(bs)
return
}
switch p.src[p.npos] {
case '\\': // escaped byte follows
if p.npos == len(p.src)-1 {
p.npos++
bs = append(bs, '\\')
p.tok, p.val = _LitWord, string(bs)
return
}
b := p.src[p.npos+1]
p.npos += 2
if b == '\n' {
p.f.Lines = append(p.f.Lines, p.npos)
} else {
bs = append(bs, '\\', b)
}
case ' ', '\t', '\n', '\r', '&', '>', '<', '|', ';', '(', ')':
p.tok, p.val = _LitWord, string(bs)
return
case '`':
if p.quote == subCmdBckquo {
p.tok, p.val = _LitWord, string(bs)
return
}
fallthrough
case '"', '\'', '$':
p.tok, p.val = _Lit, string(bs)
return
default:
bs = append(bs, p.src[p.npos])
p.npos++
}
}
}
func (p *parser) advanceLitDquote() {
var i int
tok := _Lit
loop:
for i = p.npos; i < len(p.src); i++ {
switch p.src[i] {
case '\\': // escaped byte follows
if i == len(p.src)-1 {
break
}
if i++; p.src[i] == '\n' {
p.f.Lines = append(p.f.Lines, i+1)
}
case '"':
tok = _LitWord
break loop
case '`', '$':
break loop
case '\n':
p.f.Lines = append(p.f.Lines, i+1)
}
}
p.tok, p.val = tok, string(p.src[p.npos:i])
p.npos = i
}
func (p *parser) isHdocEnd(i int) bool {
end := p.hdocStop
if end == nil || len(p.src) < i+len(end) {
return false
}
if !bytes.Equal(end, p.src[i:i+len(end)]) {
return false
}
return len(p.src) == i+len(end) || p.src[i+len(end)] == '\n'
}
func (p *parser) advanceLitHdoc() {
n := p.npos
if p.quote == hdocBodyTabs {
for n < len(p.src) && p.src[n] == '\t' {
n++
}
}
if p.isHdocEnd(n) {
if n > p.npos {
p.tok, p.val = _LitWord, string(p.src[p.npos:n])
}
p.npos = n + len(p.hdocStop)
p.hdocStop = nil
return
}
var i int
loop:
for i = p.npos; i < len(p.src); i++ {
switch p.src[i] {
case '\\': // escaped byte follows
if i++; i == len(p.src) {
break loop
}
if p.src[i] == '\n' {
p.f.Lines = append(p.f.Lines, i+1)
}
case '`', '$':
break loop
case '\n':
n := i + 1
p.f.Lines = append(p.f.Lines, n)
if p.quote == hdocBodyTabs {
for n < len(p.src) && p.src[n] == '\t' {
n++
}
}
if p.isHdocEnd(n) {
p.tok, p.val = _LitWord, string(p.src[p.npos:n])
p.npos = n + len(p.hdocStop)
p.hdocStop = nil
return
}
}
}
p.tok, p.val = _Lit, string(p.src[p.npos:i])
p.npos = i
}
func (p *parser) hdocLitWord() Word {
pos := p.npos
end := pos
for p.npos < len(p.src) {
end = p.npos
bs, found := p.readUntil('\n')
p.npos += len(bs) + 1
if found {
p.f.Lines = append(p.f.Lines, p.npos)
}
if p.quote == hdocBodyTabs {
for end < len(p.src) && p.src[end] == '\t' {
end++
}
}
if p.isHdocEnd(end) {
break
}
}
if p.npos == len(p.src) {
end = p.npos
}
l := p.lit(Pos(pos+1), string(p.src[pos:end]))
return Word{Parts: p.singleWps(l)}
}
func (p *parser) readUntil(b byte) ([]byte, bool) {
rem := p.src[p.npos:]
if i := bytes.IndexByte(rem, b); i >= 0 {
return rem[:i], true
}
return rem, false
}
func (p *parser) advanceLitRe() {
end := bytes.Index(p.src[p.npos:], []byte(" ]]"))
p.tok = _LitWord
if end == -1 {
p.val = string(p.src[p.npos:])
p.npos = len(p.src)
return
}
p.val = string(p.src[p.npos : p.npos+end])
p.npos += end
}
func testUnaryOp(val string) Token {
switch val {
case "!":
return Not
case "-e", "-a":
return tsExists
case "-f":
return tsRegFile
case "-d":
return tsDirect
case "-c":
return tsCharSp
case "-b":
return tsBlckSp
case "-p":
return tsNmPipe
case "-S":
return tsSocket
case "-L", "-h":
return tsSmbLink
case "-g":
return tsGIDSet
case "-u":
return tsUIDSet
case "-r":
return tsRead
case "-w":
return tsWrite
case "-x":
return tsExec
case "-s":
return tsNoEmpty
case "-t":
return tsFdTerm
case "-z":
return tsEmpStr
case "-n":
return tsNempStr
case "-o":
return tsOptSet
case "-v":
return tsVarSet
case "-R":
return tsRefVar
default:
return illegalTok
}
}
func testBinaryOp(val string) Token {
switch val {
case "=":
return Assgn
case "==":
return Eql
case "!=":
return Neq
case "=~":
return tsReMatch
case "-nt":
return tsNewer
case "-ot":
return tsOlder
case "-ef":
return tsDevIno
case "-eq":
return tsEql
case "-ne":
return tsNeq
case "-le":
return tsLeq
case "-ge":
return tsGeq
case "-lt":
return tsLss
case "-gt":
return tsGtr
default:
return illegalTok
}
}

View File

@@ -1,717 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package syntax
// Node represents an AST node.
type Node interface {
// Pos returns the first character of the node
Pos() Pos
// End returns the character immediately after the node
End() Pos
}
// File is a shell program.
type File struct {
Name string
Stmts []*Stmt
Comments []*Comment
// Lines contains the offset of the first character for each
// line (the first entry is always 0)
Lines []int
}
func (f *File) Pos() Pos { return stmtFirstPos(f.Stmts) }
func (f *File) End() Pos { return stmtLastEnd(f.Stmts) }
func (f *File) Position(p Pos) (pos Position) {
intp := int(p)
pos.Offset = intp - 1
if i := searchInts(f.Lines, intp); i >= 0 {
pos.Line, pos.Column = i+1, intp-f.Lines[i]
}
return
}
// Inlined version of:
// sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
func searchInts(a []int, x int) int {
i, j := 0, len(a)
for i < j {
h := i + (j-i)/2
if a[h] <= x {
i = h + 1
} else {
j = h
}
}
return i - 1
}
func posMax(p1, p2 Pos) Pos {
if p2 > p1 {
return p2
}
return p1
}
// Comment represents a single comment on a single line.
type Comment struct {
Hash Pos
Text string
}
func (c *Comment) Pos() Pos { return c.Hash }
func (c *Comment) End() Pos { return posAddStr(c.Hash, c.Text) }
// Stmt represents a statement, otherwise known as a compound command.
// It is compromised of a command and other components that may come
// before or after it.
type Stmt struct {
Cmd Command
Position Pos
SemiPos Pos
Negated bool
Background bool
Assigns []*Assign
Redirs []*Redirect
}
func (s *Stmt) Pos() Pos { return s.Position }
func (s *Stmt) End() Pos {
if s.SemiPos > 0 {
return s.SemiPos + 1
}
end := s.Position
if s.Negated {
end = posAdd(end, 1)
}
if s.Cmd != nil {
end = s.Cmd.End()
}
if len(s.Assigns) > 0 {
assEnd := s.Assigns[len(s.Assigns)-1].End()
end = posMax(end, assEnd)
}
if len(s.Redirs) > 0 {
redEnd := s.Redirs[len(s.Redirs)-1].End()
end = posMax(end, redEnd)
}
return end
}
// Command represents all nodes that are simple commands, which are
// directly placed in a Stmt.
type Command interface {
Node
commandNode()
}
func (*CallExpr) commandNode() {}
func (*IfClause) commandNode() {}
func (*WhileClause) commandNode() {}
func (*UntilClause) commandNode() {}
func (*ForClause) commandNode() {}
func (*CaseClause) commandNode() {}
func (*Block) commandNode() {}
func (*Subshell) commandNode() {}
func (*BinaryCmd) commandNode() {}
func (*FuncDecl) commandNode() {}
func (*ArithmCmd) commandNode() {}
func (*TestClause) commandNode() {}
func (*DeclClause) commandNode() {}
func (*EvalClause) commandNode() {}
func (*LetClause) commandNode() {}
func (*CoprocClause) commandNode() {}
// Assign represents an assignment to a variable.
type Assign struct {
Append bool
Name *Lit
Value Word
}
func (a *Assign) Pos() Pos {
if a.Name == nil {
return a.Value.Pos()
}
return a.Name.Pos()
}
func (a *Assign) End() Pos {
if a.Name != nil {
return posMax(a.Name.End(), a.Value.End())
}
return a.Value.End()
}
// Redirect represents an input/output redirection.
type Redirect struct {
OpPos Pos
Op RedirOperator
N *Lit
Word, Hdoc Word
}
func (r *Redirect) Pos() Pos {
if r.N != nil {
return r.N.Pos()
}
return r.OpPos
}
func (r *Redirect) End() Pos { return r.Word.End() }
// CallExpr represents a command execution or function call.
type CallExpr struct {
Args []Word
}
func (c *CallExpr) Pos() Pos { return c.Args[0].Pos() }
func (c *CallExpr) End() Pos { return c.Args[len(c.Args)-1].End() }
// Subshell represents a series of commands that should be executed in a
// nested shell environment.
type Subshell struct {
Lparen, Rparen Pos
Stmts []*Stmt
}
func (s *Subshell) Pos() Pos { return s.Lparen }
func (s *Subshell) End() Pos { return posAdd(s.Rparen, 1) }
// Block represents a series of commands that should be executed in a
// nested scope.
type Block struct {
Lbrace, Rbrace Pos
Stmts []*Stmt
}
func (b *Block) Pos() Pos { return b.Rbrace }
func (b *Block) End() Pos { return posAdd(b.Rbrace, 1) }
// IfClause represents an if statement.
type IfClause struct {
If, Then, Fi Pos
CondStmts []*Stmt
ThenStmts []*Stmt
Elifs []*Elif
Else Pos
ElseStmts []*Stmt
}
func (c *IfClause) Pos() Pos { return c.If }
func (c *IfClause) End() Pos { return posAdd(c.Fi, 2) }
// Elif represents an "else if" case in an if clause.
type Elif struct {
Elif, Then Pos
CondStmts []*Stmt
ThenStmts []*Stmt
}
// WhileClause represents a while clause.
type WhileClause struct {
While, Do, Done Pos
CondStmts []*Stmt
DoStmts []*Stmt
}
func (w *WhileClause) Pos() Pos { return w.While }
func (w *WhileClause) End() Pos { return posAdd(w.Done, 4) }
// UntilClause represents an until clause.
type UntilClause struct {
Until, Do, Done Pos
CondStmts []*Stmt
DoStmts []*Stmt
}
func (u *UntilClause) Pos() Pos { return u.Until }
func (u *UntilClause) End() Pos { return posAdd(u.Done, 4) }
// ForClause represents a for clause.
type ForClause struct {
For, Do, Done Pos
Loop Loop
DoStmts []*Stmt
}
func (f *ForClause) Pos() Pos { return f.For }
func (f *ForClause) End() Pos { return posAdd(f.Done, 4) }
// Loop represents all nodes that can be loops in a for clause.
type Loop interface {
Node
loopNode()
}
func (*WordIter) loopNode() {}
func (*CStyleLoop) loopNode() {}
// WordIter represents the iteration of a variable over a series of
// words in a for clause.
type WordIter struct {
Name Lit
List []Word
}
func (w *WordIter) Pos() Pos { return w.Name.Pos() }
func (w *WordIter) End() Pos { return posMax(w.Name.End(), wordLastEnd(w.List)) }
// CStyleLoop represents the behaviour of a for clause similar to the C
// language.
//
// This node will never appear when in PosixConformant mode.
type CStyleLoop struct {
Lparen, Rparen Pos
Init, Cond, Post ArithmExpr
}
func (c *CStyleLoop) Pos() Pos { return c.Lparen }
func (c *CStyleLoop) End() Pos { return posAdd(c.Rparen, 2) }
// BinaryCmd represents a binary expression between two statements.
type BinaryCmd struct {
OpPos Pos
Op BinCmdOperator
X, Y *Stmt
}
func (b *BinaryCmd) Pos() Pos { return b.X.Pos() }
func (b *BinaryCmd) End() Pos { return b.Y.End() }
// FuncDecl represents the declaration of a function.
type FuncDecl struct {
Position Pos
BashStyle bool
Name Lit
Body *Stmt
}
func (f *FuncDecl) Pos() Pos { return f.Position }
func (f *FuncDecl) End() Pos { return f.Body.End() }
// Word represents a list of nodes that are contiguous to each other.
// The word is delimeted by word boundaries.
type Word struct {
Parts []WordPart
}
func (w *Word) Pos() Pos { return partsFirstPos(w.Parts) }
func (w *Word) End() Pos { return partsLastEnd(w.Parts) }
// WordPart represents all nodes that can form a word.
type WordPart interface {
Node
wordPartNode()
}
func (*Lit) wordPartNode() {}
func (*SglQuoted) wordPartNode() {}
func (*DblQuoted) wordPartNode() {}
func (*ParamExp) wordPartNode() {}
func (*CmdSubst) wordPartNode() {}
func (*ArithmExp) wordPartNode() {}
func (*ProcSubst) wordPartNode() {}
func (*ArrayExpr) wordPartNode() {}
func (*ExtGlob) wordPartNode() {}
// Lit represents an unquoted string consisting of characters that were
// not tokenized.
type Lit struct {
ValuePos Pos
Value string
}
func (l *Lit) Pos() Pos { return l.ValuePos }
func (l *Lit) End() Pos { return posAddStr(l.ValuePos, l.Value) }
// SglQuoted represents a string within single quotes.
type SglQuoted struct {
Position Pos
Dollar bool
Value string
}
func (q *SglQuoted) Pos() Pos { return q.Position }
func (q *SglQuoted) End() Pos {
pos := posAdd(q.Position, 2+len(q.Value))
if pos > 0 && q.Dollar {
pos++
}
return pos
}
// DblQuoted represents a list of nodes within double quotes.
type DblQuoted struct {
Position Pos
Dollar bool
Parts []WordPart
}
func (q *DblQuoted) Pos() Pos { return q.Position }
func (q *DblQuoted) End() Pos {
if q.Position == 0 {
return defaultPos
}
end := q.Position
if len(q.Parts) > 0 {
end = partsLastEnd(q.Parts)
} else if q.Dollar {
end += 2
} else {
end++
}
return posAdd(end, 1)
}
// CmdSubst represents a command substitution.
type CmdSubst struct {
Left, Right Pos
Stmts []*Stmt
}
func (c *CmdSubst) Pos() Pos { return c.Left }
func (c *CmdSubst) End() Pos { return posAdd(c.Right, 1) }
// ParamExp represents a parameter expansion.
type ParamExp struct {
Dollar, Rbrace Pos
Short, Length bool
Param Lit
Ind *Index
Slice *Slice
Repl *Replace
Exp *Expansion
}
func (p *ParamExp) Pos() Pos { return p.Dollar }
func (p *ParamExp) End() Pos {
if p.Rbrace > 0 {
return p.Rbrace + 1
}
return p.Param.End()
}
// Index represents access to an array via an index inside a ParamExp.
//
// This node will never appear when in PosixConformant mode.
type Index struct {
Word Word
}
// Slice represents character slicing inside a ParamExp.
//
// This node will never appear when in PosixConformant mode.
type Slice struct {
Offset, Length Word
}
// Replace represents a search and replace inside a ParamExp.
type Replace struct {
All bool
Orig, With Word
}
// Expansion represents string manipulation in a ParamExp other than
// those covered by Replace.
type Expansion struct {
Op ParExpOperator
Word Word
}
// ArithmExp represents an arithmetic expansion.
type ArithmExp struct {
Left, Right Pos
Bracket bool
X ArithmExpr
}
func (a *ArithmExp) Pos() Pos { return a.Left }
func (a *ArithmExp) End() Pos {
if a.Bracket {
return posAdd(a.Right, 1)
}
return posAdd(a.Right, 2)
}
// ArithmCmd represents an arithmetic command.
//
// This node will never appear when in PosixConformant mode.
type ArithmCmd struct {
Left, Right Pos
X ArithmExpr
}
func (a *ArithmCmd) Pos() Pos { return a.Left }
func (a *ArithmCmd) End() Pos { return posAdd(a.Right, 2) }
// ArithmExpr represents all nodes that form arithmetic expressions.
type ArithmExpr interface {
Node
arithmExprNode()
}
func (*BinaryArithm) arithmExprNode() {}
func (*UnaryArithm) arithmExprNode() {}
func (*ParenArithm) arithmExprNode() {}
func (*Word) arithmExprNode() {}
// BinaryArithm represents a binary expression between two arithmetic
// expression.
type BinaryArithm struct {
OpPos Pos
Op Token
X, Y ArithmExpr
}
func (b *BinaryArithm) Pos() Pos { return b.X.Pos() }
func (b *BinaryArithm) End() Pos { return b.Y.End() }
// UnaryArithm represents an unary expression over a node, either before
// or after it.
type UnaryArithm struct {
OpPos Pos
Op Token
Post bool
X ArithmExpr
}
func (u *UnaryArithm) Pos() Pos {
if u.Post {
return u.X.Pos()
}
return u.OpPos
}
func (u *UnaryArithm) End() Pos {
if u.Post {
return posAdd(u.OpPos, 2)
}
return u.X.End()
}
// ParenArithm represents an expression within parentheses inside an
// ArithmExp.
type ParenArithm struct {
Lparen, Rparen Pos
X ArithmExpr
}
func (p *ParenArithm) Pos() Pos { return p.Lparen }
func (p *ParenArithm) End() Pos { return posAdd(p.Rparen, 1) }
// CaseClause represents a case (switch) clause.
type CaseClause struct {
Case, Esac Pos
Word Word
List []*PatternList
}
func (c *CaseClause) Pos() Pos { return c.Case }
func (c *CaseClause) End() Pos { return posAdd(c.Esac, 4) }
// PatternList represents a pattern list (case) within a CaseClause.
type PatternList struct {
Op CaseOperator
OpPos Pos
Patterns []Word
Stmts []*Stmt
}
// TestClause represents a Bash extended test clause.
//
// This node will never appear when in PosixConformant mode.
type TestClause struct {
Left, Right Pos
X TestExpr
}
func (t *TestClause) Pos() Pos { return t.Left }
func (t *TestClause) End() Pos { return posAdd(t.Right, 2) }
// TestExpr represents all nodes that form arithmetic expressions.
type TestExpr interface {
Node
testExprNode()
}
func (*BinaryTest) testExprNode() {}
func (*UnaryTest) testExprNode() {}
func (*ParenTest) testExprNode() {}
func (*Word) testExprNode() {}
// BinaryTest represents a binary expression between two arithmetic
// expression.
type BinaryTest struct {
OpPos Pos
Op BinTestOperator
X, Y TestExpr
}
func (b *BinaryTest) Pos() Pos { return b.X.Pos() }
func (b *BinaryTest) End() Pos { return b.Y.End() }
// UnaryTest represents an unary expression over a node, either before
// or after it.
type UnaryTest struct {
OpPos Pos
Op UnTestOperator
X TestExpr
}
func (u *UnaryTest) Pos() Pos { return u.OpPos }
func (u *UnaryTest) End() Pos { return u.X.End() }
// ParenTest represents an expression within parentheses inside an
// TestExp.
type ParenTest struct {
Lparen, Rparen Pos
X TestExpr
}
func (p *ParenTest) Pos() Pos { return p.Lparen }
func (p *ParenTest) End() Pos { return posAdd(p.Rparen, 1) }
// DeclClause represents a Bash declare clause.
//
// This node will never appear when in PosixConformant mode.
type DeclClause struct {
Position Pos
Variant string
Opts []Word
Assigns []*Assign
}
func (d *DeclClause) Pos() Pos { return d.Position }
func (d *DeclClause) End() Pos {
end := wordLastEnd(d.Opts)
if len(d.Assigns) > 0 {
assignEnd := d.Assigns[len(d.Assigns)-1].End()
end = posMax(end, assignEnd)
}
return end
}
// ArrayExpr represents a Bash array expression.
//
// This node will never appear when in PosixConformant mode.
type ArrayExpr struct {
Lparen, Rparen Pos
List []Word
}
func (a *ArrayExpr) Pos() Pos { return a.Lparen }
func (a *ArrayExpr) End() Pos { return posAdd(a.Rparen, 1) }
// ExtGlob represents a Bash extended globbing expression. Note that
// these are parsed independently of whether shopt has been called or
// not.
//
// This node will never appear when in PosixConformant mode.
type ExtGlob struct {
Op GlobOperator
Pattern Lit
}
func (e *ExtGlob) Pos() Pos { return posAdd(e.Pattern.Pos(), -2) }
func (e *ExtGlob) End() Pos { return posAdd(e.Pattern.End(), 1) }
// ProcSubst represents a Bash process substitution.
//
// This node will never appear when in PosixConformant mode.
type ProcSubst struct {
OpPos, Rparen Pos
Op ProcOperator
Stmts []*Stmt
}
func (s *ProcSubst) Pos() Pos { return s.OpPos }
func (s *ProcSubst) End() Pos { return posAdd(s.Rparen, 1) }
// EvalClause represents a Bash eval clause.
//
// This node will never appear when in PosixConformant mode.
type EvalClause struct {
Eval Pos
Stmt *Stmt
}
func (e *EvalClause) Pos() Pos { return e.Eval }
func (e *EvalClause) End() Pos {
if e.Stmt == nil {
return posAdd(e.Eval, 4)
}
return e.Stmt.End()
}
// CoprocClause represents a Bash coproc clause.
//
// This node will never appear when in PosixConformant mode.
type CoprocClause struct {
Coproc Pos
Name *Lit
Stmt *Stmt
}
func (c *CoprocClause) Pos() Pos { return c.Coproc }
func (c *CoprocClause) End() Pos { return c.Stmt.End() }
// LetClause represents a Bash let clause.
//
// This node will never appear when in PosixConformant mode.
type LetClause struct {
Let Pos
Exprs []ArithmExpr
}
func (l *LetClause) Pos() Pos { return l.Let }
func (l *LetClause) End() Pos { return l.Exprs[len(l.Exprs)-1].End() }
func posAdd(pos Pos, n int) Pos {
if pos == defaultPos {
return pos
}
return pos + Pos(n)
}
func posAddStr(pos Pos, s string) Pos {
return posAdd(pos, len(s))
}
func stmtFirstPos(sts []*Stmt) Pos {
if len(sts) == 0 {
return defaultPos
}
return sts[0].Pos()
}
func stmtLastEnd(sts []*Stmt) Pos {
if len(sts) == 0 {
return defaultPos
}
return sts[len(sts)-1].End()
}
func partsFirstPos(ps []WordPart) Pos {
if len(ps) == 0 {
return defaultPos
}
return ps[0].Pos()
}
func partsLastEnd(ps []WordPart) Pos {
if len(ps) == 0 {
return defaultPos
}
return ps[len(ps)-1].End()
}
func wordLastEnd(ws []Word) Pos {
if len(ws) == 0 {
return defaultPos
}
return ws[len(ws)-1].End()
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,423 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package syntax
// Token is the set of lexical tokens and reserved words.
type Token int
// The list of all possible tokens and reserved words.
const (
illegalTok Token = iota
_EOF
_Lit
_LitWord
sglQuote // '
dblQuote // "
bckQuote // `
And // &
AndExpr // &&
OrExpr // ||
Or // |
pipeAll // |& - bash
dollar // $
dollSglQuote // $' - bash
dollDblQuote // $" - bash
dollBrace // ${
dollBrack // $[
dollParen // $(
dollDblParen // $((
leftBrack // [
leftParen // (
dblLeftParen // (( - bash
rightBrace // }
rightBrack // ]
rightParen // )
dblRightParen // ))
semicolon // ;
dblSemicolon // ;;
semiFall // ;& - bash
dblSemiFall // ;;& - bash
Mul // *
Not // !
Inc // ++
Dec // --
Pow // **
Eql // ==
Neq // !=
Leq // <=
Geq // >=
AddAssgn // +=
SubAssgn // -=
MulAssgn // *=
QuoAssgn // /=
RemAssgn // %=
AndAssgn // &=
OrAssgn // |=
XorAssgn // ^=
ShlAssgn // <<=
ShrAssgn // >>=
Gtr // >
Shr // >>
Lss // <
rdrInOut // <>
dplIn // <&
dplOut // >&
clbOut // >|
Shl // <<
dashHdoc // <<-
wordHdoc // <<< - bash
rdrAll // &> - bash
appAll // &>> - bash
cmdIn // <( - bash
cmdOut // >( - bash
Add // +
ColAdd // :+
Sub // -
ColSub // :-
Quest // ?
ColQuest // :?
Assgn // =
ColAssgn // :=
Rem // %
dblRem // %%
Hash // #
dblHash // ##
Xor // ^
dblXor // ^^ - bash
Comma // ,
dblComma // ,, - bash
Quo // /
dblQuo // //
Colon // :
tsNot // !
tsExists // -e
tsRegFile // -f
tsDirect // -d
tsCharSp // -c
tsBlckSp // -b
tsNmPipe // -p
tsSocket // -S
tsSmbLink // -L
tsGIDSet // -g
tsUIDSet // -u
tsRead // -r
tsWrite // -w
tsExec // -x
tsNoEmpty // -s
tsFdTerm // -t
tsEmpStr // -z
tsNempStr // -n
tsOptSet // -o
tsVarSet // -v
tsRefVar // -R
tsReMatch // =~
tsNewer // -nt
tsOlder // -ot
tsDevIno // -ef
tsEql // -eq
tsNeq // -ne
tsLeq // -le
tsGeq // -ge
tsLss // -lt
tsGtr // -gt
globQuest // ?(
globMul // *(
globAdd // +(
globAt // @(
globNot // !(
)
type RedirOperator Token
const (
RdrOut = RedirOperator(Gtr) + iota
AppOut
RdrIn
RdrInOut
DplIn
DplOut
ClbOut
Hdoc
DashHdoc
WordHdoc
RdrAll
AppAll
)
type ProcOperator Token
const (
CmdIn = ProcOperator(cmdIn) + iota
CmdOut
)
type GlobOperator Token
const (
GlobQuest = GlobOperator(globQuest) + iota
GlobMul
GlobAdd
GlobAt
GlobNot
)
type BinCmdOperator Token
const (
AndStmt = BinCmdOperator(AndExpr) + iota
OrStmt
Pipe
PipeAll
)
type CaseOperator Token
const (
DblSemicolon = CaseOperator(dblSemicolon) + iota
SemiFall
DblSemiFall
)
type ParExpOperator Token
const (
SubstAdd = ParExpOperator(Add) + iota
SubstColAdd
SubstSub
SubstColSub
SubstQuest
SubstColQuest
SubstAssgn
SubstColAssgn
RemSmallSuffix
RemLargeSuffix
RemSmallPrefix
RemLargePrefix
UpperFirst
UpperAll
LowerFirst
LowerAll
)
type UnTestOperator Token
const (
TsNot = UnTestOperator(tsNot) + iota
TsExists
TsRegFile
TsDirect
TsCharSp
TsBlckSp
TsNmPipe
TsSocket
TsSmbLink
TsGIDSet
TsUIDSet
TsRead
TsWrite
TsExec
TsNoEmpty
TsFdTerm
TsEmpStr
TsNempStr
TsOptSet
TsVarSet
TsRefVar
)
type BinTestOperator Token
const (
TsReMatch = BinTestOperator(tsReMatch) + iota
TsNewer
TsOlder
TsDevIno
TsEql
TsNeq
TsLeq
TsGeq
TsLss
TsGtr
AndTest = BinTestOperator(AndExpr)
OrTest = BinTestOperator(OrExpr)
TsAssgn = BinTestOperator(Assgn)
TsEqual = BinTestOperator(Eql)
TsNequal = BinTestOperator(Neq)
TsBefore = BinTestOperator(Lss)
TsAfter = BinTestOperator(Gtr)
)
func (o RedirOperator) String() string { return Token(o).String() }
func (o ProcOperator) String() string { return Token(o).String() }
func (o GlobOperator) String() string { return Token(o).String() }
func (o BinCmdOperator) String() string { return Token(o).String() }
func (o CaseOperator) String() string { return Token(o).String() }
func (o ParExpOperator) String() string { return Token(o).String() }
func (o UnTestOperator) String() string { return Token(o).String() }
func (o BinTestOperator) String() string { return Token(o).String() }
// Pos is the internal representation of a position within a source
// file.
type Pos int
var defaultPos Pos
const maxPos = Pos(^uint(0) >> 1)
// Position describes a position within a source file including the line
// and column location. A Position is valid if the line number is > 0.
type Position struct {
Offset int // byte offset, starting at 0
Line int // line number, starting at 1
Column int // column number, starting at 1 (in bytes)
}
var tokNames = map[Token]string{
illegalTok: "illegal",
_EOF: "EOF",
_Lit: "Lit",
_LitWord: "LitWord",
sglQuote: "'",
dblQuote: `"`,
bckQuote: "`",
And: "&",
AndExpr: "&&",
OrExpr: "||",
Or: "|",
pipeAll: "|&",
dollar: "$",
dollSglQuote: "$'",
dollDblQuote: `$"`,
dollBrace: "${",
dollBrack: "$[",
dollParen: "$(",
dollDblParen: "$((",
leftBrack: "[",
leftParen: "(",
dblLeftParen: "((",
rightBrace: "}",
rightBrack: "]",
rightParen: ")",
dblRightParen: "))",
semicolon: ";",
dblSemicolon: ";;",
semiFall: ";&",
dblSemiFall: ";;&",
Gtr: ">",
Shr: ">>",
Lss: "<",
rdrInOut: "<>",
dplIn: "<&",
dplOut: ">&",
clbOut: ">|",
Shl: "<<",
dashHdoc: "<<-",
wordHdoc: "<<<",
rdrAll: "&>",
appAll: "&>>",
cmdIn: "<(",
cmdOut: ">(",
Add: "+",
ColAdd: ":+",
Sub: "-",
ColSub: ":-",
Quest: "?",
ColQuest: ":?",
Assgn: "=",
ColAssgn: ":=",
Rem: "%",
dblRem: "%%",
Hash: "#",
dblHash: "##",
Xor: "^",
dblXor: "^^",
Comma: ",",
dblComma: ",,",
Quo: "/",
dblQuo: "//",
Colon: ":",
Mul: "*",
Not: "!",
Inc: "++",
Dec: "--",
Pow: "**",
Eql: "==",
Neq: "!=",
Leq: "<=",
Geq: ">=",
AddAssgn: "+=",
SubAssgn: "-=",
MulAssgn: "*=",
QuoAssgn: "/=",
RemAssgn: "%=",
AndAssgn: "&=",
OrAssgn: "|=",
XorAssgn: "^=",
ShlAssgn: "<<=",
ShrAssgn: ">>=",
tsNot: "!",
tsExists: "-e",
tsRegFile: "-f",
tsDirect: "-d",
tsCharSp: "-c",
tsBlckSp: "-b",
tsNmPipe: "-p",
tsSocket: "-S",
tsSmbLink: "-L",
tsGIDSet: "-g",
tsUIDSet: "-u",
tsRead: "-r",
tsWrite: "-w",
tsExec: "-x",
tsNoEmpty: "-s",
tsFdTerm: "-t",
tsEmpStr: "-z",
tsNempStr: "-n",
tsOptSet: "-o",
tsVarSet: "-v",
tsRefVar: "-R",
tsReMatch: "=~",
tsNewer: "-nt",
tsOlder: "-ot",
tsDevIno: "-ef",
tsEql: "-eq",
tsNeq: "-ne",
tsLeq: "-le",
tsGeq: "-ge",
tsLss: "-lt",
tsGtr: "-gt",
globQuest: "?(",
globMul: "*(",
globAdd: "+(",
globAt: "@(",
globNot: "!(",
}
func (t Token) String() string { return tokNames[t] }

View File

@@ -1,189 +0,0 @@
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package syntax
import "fmt"
// Visitor holds a Visit method which is invoked for each node
// encountered by Walk. If the result visitor w is not nil, Walk visits
// each of the children of node with the visitor w, followed by a call
// of w.Visit(nil).
type Visitor interface {
Visit(node Node) (w Visitor)
}
func walkStmts(v Visitor, stmts []*Stmt) {
for _, s := range stmts {
Walk(v, s)
}
}
func walkWords(v Visitor, words []Word) {
for i := range words {
Walk(v, &words[i])
}
}
// Walk traverses an AST in depth-first order: It starts by calling
// v.Visit(node); node must not be nil. If the visitor w returned by
// v.Visit(node) is not nil, Walk is invoked recursively with visitor w
// for each of the non-nil children of node, followed by a call of
// w.Visit(nil).
func Walk(v Visitor, node Node) {
if v = v.Visit(node); v == nil {
return
}
switch x := node.(type) {
case *File:
walkStmts(v, x.Stmts)
case *Stmt:
if x.Cmd != nil {
Walk(v, x.Cmd)
}
for _, a := range x.Assigns {
Walk(v, a)
}
for _, r := range x.Redirs {
Walk(v, r)
}
case *Assign:
if x.Name != nil {
Walk(v, x.Name)
}
Walk(v, &x.Value)
case *Redirect:
if x.N != nil {
Walk(v, x.N)
}
Walk(v, &x.Word)
if len(x.Hdoc.Parts) > 0 {
Walk(v, &x.Hdoc)
}
case *CallExpr:
walkWords(v, x.Args)
case *Subshell:
walkStmts(v, x.Stmts)
case *Block:
walkStmts(v, x.Stmts)
case *IfClause:
walkStmts(v, x.CondStmts)
walkStmts(v, x.ThenStmts)
for _, elif := range x.Elifs {
walkStmts(v, elif.CondStmts)
walkStmts(v, elif.ThenStmts)
}
walkStmts(v, x.ElseStmts)
case *WhileClause:
walkStmts(v, x.CondStmts)
walkStmts(v, x.DoStmts)
case *UntilClause:
walkStmts(v, x.CondStmts)
walkStmts(v, x.DoStmts)
case *ForClause:
Walk(v, x.Loop)
walkStmts(v, x.DoStmts)
case *WordIter:
Walk(v, &x.Name)
walkWords(v, x.List)
case *CStyleLoop:
if x.Init != nil {
Walk(v, x.Init)
}
if x.Cond != nil {
Walk(v, x.Cond)
}
if x.Post != nil {
Walk(v, x.Post)
}
case *BinaryCmd:
Walk(v, x.X)
Walk(v, x.Y)
case *FuncDecl:
Walk(v, &x.Name)
Walk(v, x.Body)
case *Word:
for _, wp := range x.Parts {
Walk(v, wp)
}
case *Lit:
case *SglQuoted:
case *DblQuoted:
for _, wp := range x.Parts {
Walk(v, wp)
}
case *CmdSubst:
walkStmts(v, x.Stmts)
case *ParamExp:
Walk(v, &x.Param)
if x.Ind != nil {
Walk(v, &x.Ind.Word)
}
if x.Repl != nil {
Walk(v, &x.Repl.Orig)
Walk(v, &x.Repl.With)
}
if x.Exp != nil {
Walk(v, &x.Exp.Word)
}
case *ArithmExp:
if x.X != nil {
Walk(v, x.X)
}
case *ArithmCmd:
if x.X != nil {
Walk(v, x.X)
}
case *BinaryArithm:
Walk(v, x.X)
Walk(v, x.Y)
case *BinaryTest:
Walk(v, x.X)
Walk(v, x.Y)
case *UnaryArithm:
Walk(v, x.X)
case *UnaryTest:
Walk(v, x.X)
case *ParenArithm:
Walk(v, x.X)
case *ParenTest:
Walk(v, x.X)
case *CaseClause:
Walk(v, &x.Word)
for _, pl := range x.List {
walkWords(v, pl.Patterns)
walkStmts(v, pl.Stmts)
}
case *TestClause:
Walk(v, x.X)
case *DeclClause:
walkWords(v, x.Opts)
for _, a := range x.Assigns {
Walk(v, a)
}
case *ArrayExpr:
walkWords(v, x.List)
case *ExtGlob:
Walk(v, &x.Pattern)
case *ProcSubst:
walkStmts(v, x.Stmts)
case *EvalClause:
if x.Stmt != nil {
Walk(v, x.Stmt)
}
case *CoprocClause:
if x.Name != nil {
Walk(v, x.Name)
}
Walk(v, x.Stmt)
case *LetClause:
for _, expr := range x.Exprs {
Walk(v, expr)
}
default:
panic(fmt.Sprintf("ast.Walk: unexpected node type %T", x))
}
v.Visit(nil)
}

32
vendor/github.com/weaveworks/common/user/id.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package user
import (
"fmt"
"golang.org/x/net/context"
)
// UserIDContextKey is the key used in contexts to find the userid
type contextKey int
const userIDContextKey contextKey = 0
// OrgIDHeaderName is a legacy from scope as a service.
const OrgIDHeaderName = "X-Scope-OrgID"
// LowerOrgIDHeaderName as gRPC / HTTP2.0 headers are lowercased.
const LowerOrgIDHeaderName = "x-scope-orgid"
// GetID returns the user
func GetID(ctx context.Context) (string, error) {
userid, ok := ctx.Value(userIDContextKey).(string)
if !ok {
return "", fmt.Errorf("no user id")
}
return userid, nil
}
// WithID returns a derived context containing the user ID.
func WithID(ctx context.Context, userID string) context.Context {
return context.WithValue(ctx, interface{}(userIDContextKey), userID)
}

2
vendor/manifest vendored
View File

@@ -1361,7 +1361,7 @@
"importpath": "github.com/weaveworks/common",
"repository": "https://github.com/weaveworks/common",
"vcs": "git",
"revision": "139d0313ac15170e9de8187b26e7df03b4cb910e",
"revision": "8da456c848efd2a13ffb5a3f9f4507c52c3f52e1",
"branch": "master",
"notests": true
},