vendor: Update to a new version of InfraKit

This pulls in another slew of other packages.

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
This commit is contained in:
Rolf Neugebauer
2017-04-04 16:07:53 +01:00
parent c0e416a2a5
commit 2ab909fcbd
106 changed files with 25124 additions and 229 deletions

17
vendor/github.com/docker/infrakit/pkg/cli/cli.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
package cli
import (
"github.com/spf13/cobra"
)
const (
// CliDirEnvVar is the environment variable that points to where the cli config folders are.
CliDirEnvVar = "INFRAKIT_CLI_DIR"
)
// Modules provides access to CLI module discovery
type Modules interface {
// List returns a list of preconfigured commands
List() ([]*cobra.Command, error)
}

View File

@@ -1,16 +1,32 @@
package cli
import log "github.com/Sirupsen/logrus"
import (
"github.com/spf13/pflag"
"github.com/Sirupsen/logrus"
logutil "github.com/docker/infrakit/pkg/log"
)
// DefaultLogLevel is the default log level value.
var DefaultLogLevel = len(log.AllLevels) - 2
var DefaultLogLevel = len(logrus.AllLevels) - 2
// SetLogLevel adjusts the logrus level.
func SetLogLevel(level int) {
if level > len(log.AllLevels)-1 {
level = len(log.AllLevels) - 1
if level > len(logrus.AllLevels)-1 {
level = len(logrus.AllLevels) - 1
} else if level < 0 {
level = 0
}
log.SetLevel(log.AllLevels[level])
logrus.SetLevel(logrus.AllLevels[level])
}
// Flags returns the set of logging flags
func Flags(o *logutil.Options) *pflag.FlagSet {
f := pflag.NewFlagSet("logging", pflag.ExitOnError)
f.IntVar(&o.Level, "log", o.Level, "log level")
f.BoolVar(&o.Stdout, "log-stdout", o.Stdout, "log to stdout")
f.BoolVar(&o.CallFunc, "log-caller", o.CallFunc, "include caller function")
f.BoolVar(&o.CallStack, "log-stack", o.CallStack, "include caller stack")
f.StringVar(&o.Format, "log-format", o.Format, "log format: logfmt|term|json")
return f
}

View File

@@ -6,8 +6,8 @@ import (
"os"
"path"
log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/discovery"
"github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/discovery/local"
"github.com/docker/infrakit/pkg/rpc/server"
)
@@ -20,7 +20,7 @@ func EnsureDirExists(dir string) {
// The plugin should conform to the rpc call convention as implemented in the rpc package.
func RunPlugin(name string, plugin server.VersionedInterface, more ...server.VersionedInterface) {
dir := discovery.Dir()
dir := local.Dir()
EnsureDirExists(dir)
socketPath := path.Join(dir, name)
@@ -28,20 +28,20 @@ func RunPlugin(name string, plugin server.VersionedInterface, more ...server.Ver
stoppable, err := server.StartPluginAtPath(socketPath, plugin, more...)
if err != nil {
log.Error(err)
logrus.Error(err)
}
// write PID file
err = ioutil.WriteFile(pidPath, []byte(fmt.Sprintf("%v", os.Getpid())), 0644)
if err != nil {
log.Error(err)
logrus.Error(err)
}
log.Infoln("PID file at", pidPath)
logrus.Infoln("PID file at", pidPath)
if stoppable != nil {
stoppable.AwaitStopped()
}
// clean up
os.Remove(pidPath)
log.Infoln("Removed PID file at", pidPath)
logrus.Infoln("Removed PID file at", pidPath)
}

40
vendor/github.com/docker/infrakit/pkg/cli/util.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package cli
import (
"os"
logutil "github.com/docker/infrakit/pkg/log"
"github.com/spf13/cobra"
)
var log = logutil.New("module", "cli/core")
// UpTree traverses up the command tree and starts executing the do function in the order from top
// of the command tree to the bottom. Cobra commands executes only one level of PersistentPreRunE
// in reverse order. This breaks our model of setting log levels at the very top and have the log level
// set throughout the entire hierarchy of command execution.
func UpTree(c *cobra.Command, do func(*cobra.Command, []string) error) error {
if p := c.Parent(); p != nil {
return UpTree(p, do)
}
return do(c, c.Flags().Args())
}
// EnsurePersistentPreRunE works around a limit of COBRA where only the persistent runE is executed at the
// parent of the leaf node.
func EnsurePersistentPreRunE(c *cobra.Command) error {
return UpTree(c, func(x *cobra.Command, argv []string) error {
if x.PersistentPreRunE != nil {
return x.PersistentPreRunE(x, argv)
}
return nil
})
}
// MustNotNil checks the object, if nil , exits and logs message
func MustNotNil(object interface{}, message string, ctx ...string) {
if object == nil {
log.Crit(message, ctx)
os.Exit(-1)
}
}

View File

@@ -28,7 +28,7 @@ func RegisterInfo(key string, data map[string]interface{}) {
func VersionCommand() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "print build version information",
Short: "Print build version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("\n%-24s: %v", "Version", Version)
fmt.Printf("\n%-24s: %v", "Revision", Revision)

View File

@@ -1,106 +0,0 @@
package discovery
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/plugin"
)
type errNotUnixSocket string
func (e errNotUnixSocket) Error() string {
return string(e)
}
// IsErrNotUnixSocket returns true if the error is due to the file not being a valid unix socket.
func IsErrNotUnixSocket(e error) bool {
_, is := e.(errNotUnixSocket)
return is
}
type dirPluginDiscovery struct {
dir string
lock sync.Mutex
}
// Find returns a plugin by name
func (r *dirPluginDiscovery) Find(name plugin.Name) (*plugin.Endpoint, error) {
lookup, _ := name.GetLookupAndType()
plugins, err := r.List()
if err != nil {
return nil, err
}
p, exists := plugins[lookup]
if !exists {
return nil, fmt.Errorf("Plugin not found: %s (looked up using %s)", name, lookup)
}
return p, nil
}
// newDirPluginDiscovery creates a registry instance with the given file directory path.
func newDirPluginDiscovery(dir string) (*dirPluginDiscovery, error) {
d := &dirPluginDiscovery{dir: dir}
// Perform a dummy read to catch obvious issues early (such as the directory not existing).
_, err := d.List()
return d, err
}
func (r *dirPluginDiscovery) dirLookup(entry os.FileInfo) (*plugin.Endpoint, error) {
if entry.Mode()&os.ModeSocket != 0 {
socketPath := filepath.Join(r.dir, entry.Name())
return &plugin.Endpoint{
Protocol: "unix",
Address: socketPath,
Name: entry.Name(),
}, nil
}
return nil, errNotUnixSocket(fmt.Sprintf("File is not a socket: %s", entry))
}
// List returns a list of plugins known, keyed by the name
func (r *dirPluginDiscovery) List() (map[string]*plugin.Endpoint, error) {
r.lock.Lock()
defer r.lock.Unlock()
log.Debugln("Opening:", r.dir)
entries, err := ioutil.ReadDir(r.dir)
if err != nil {
return nil, err
}
plugins := map[string]*plugin.Endpoint{}
for _, entry := range entries {
if !entry.IsDir() {
instance, err := r.dirLookup(entry)
if err != nil {
if !IsErrNotUnixSocket(err) {
log.Warningln("Loading plugin err=", err)
}
continue
}
if instance == nil {
log.Warningln("Plugin in nil=")
continue
}
log.Debugln("Discovered plugin at", instance.Address)
plugins[instance.Name] = instance
}
}
return plugins, nil
}

View File

@@ -2,9 +2,6 @@ package discovery
import (
"fmt"
"os"
"os/user"
"path"
"github.com/docker/infrakit/pkg/plugin"
)
@@ -21,40 +18,28 @@ const (
PluginDirEnvVar = "INFRAKIT_PLUGINS_DIR"
)
// Dir returns the directory to use for plugin discovery, which may be customized by the environment.
func Dir() string {
if pluginDir := os.Getenv(PluginDirEnvVar); pluginDir != "" {
return pluginDir
}
// ErrNotUnixSocket is the error raised when the file is not a unix socket
type ErrNotUnixSocket string
home := os.Getenv("HOME")
if usr, err := user.Current(); err == nil {
home = usr.HomeDir
}
return path.Join(home, ".infrakit/plugins")
func (e ErrNotUnixSocket) Error() string {
return fmt.Sprintf("not a unix socket:%s", string(e))
}
// NewPluginDiscovery creates a plugin discovery based on the environment configuration.
func NewPluginDiscovery() (Plugins, error) {
return NewPluginDiscoveryWithDirectory(Dir())
// IsErrNotUnixSocket returns true if the error is due to the file not being a valid unix socket.
func IsErrNotUnixSocket(e error) bool {
_, is := e.(ErrNotUnixSocket)
return is
}
// NewPluginDiscoveryWithDirectory creates a plugin discovery based on the directory given.
func NewPluginDiscoveryWithDirectory(pluginDir string) (Plugins, error) {
stat, err := os.Stat(pluginDir)
if err == nil {
if !stat.IsDir() {
return nil, fmt.Errorf("Plugin dir %s is a file", pluginDir)
}
} else {
if os.IsNotExist(err) {
if err := os.MkdirAll(pluginDir, 0700); err != nil {
return nil, fmt.Errorf("Failed to create plugin dir %s: %s", pluginDir, err)
}
} else {
return nil, fmt.Errorf("Failed to access plugin dir %s: %s", pluginDir, err)
}
}
// ErrNotFound is the error raised when the plugin is not found
type ErrNotFound string
return newDirPluginDiscovery(pluginDir)
func (e ErrNotFound) Error() string {
return fmt.Sprintf("plugin not found:%s", string(e))
}
// IsErrNotFound returns true if the error is due to a plugin not found.
func IsErrNotFound(e error) bool {
_, is := e.(ErrNotFound)
return is
}

View File

@@ -0,0 +1,132 @@
package local
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"sync"
"github.com/docker/infrakit/pkg/discovery"
"github.com/docker/infrakit/pkg/plugin"
)
// Dir returns the directory to use for plugin discovery, which may be customized by the environment.
func Dir() string {
if pluginDir := os.Getenv(discovery.PluginDirEnvVar); pluginDir != "" {
return pluginDir
}
home := os.Getenv("HOME")
if usr, err := user.Current(); err == nil {
home = usr.HomeDir
}
return filepath.Join(home, ".infrakit/plugins")
}
// NewPluginDiscovery creates a plugin discovery based on the environment configuration.
func NewPluginDiscovery() (discovery.Plugins, error) {
return NewPluginDiscoveryWithDirectory(Dir())
}
// NewPluginDiscoveryWithDirectory creates a plugin discovery based on the directory given.
func NewPluginDiscoveryWithDirectory(pluginDir string) (discovery.Plugins, error) {
stat, err := os.Stat(pluginDir)
if err == nil {
if !stat.IsDir() {
return nil, fmt.Errorf("Plugin dir %s is a file", pluginDir)
}
} else {
if os.IsNotExist(err) {
if err := os.MkdirAll(pluginDir, 0700); err != nil {
return nil, fmt.Errorf("Failed to create plugin dir %s: %s", pluginDir, err)
}
} else {
return nil, fmt.Errorf("Failed to access plugin dir %s: %s", pluginDir, err)
}
}
return newDirPluginDiscovery(pluginDir)
}
type dirPluginDiscovery struct {
dir string
lock sync.Mutex
}
// Find returns a plugin by name
func (r *dirPluginDiscovery) Find(name plugin.Name) (*plugin.Endpoint, error) {
lookup, _ := name.GetLookupAndType()
plugins, err := r.List()
if err != nil {
return nil, err
}
p, exists := plugins[lookup]
if !exists {
return nil, discovery.ErrNotFound(string(name))
}
return p, nil
}
// newDirPluginDiscovery creates a registry instance with the given file directory path.
func newDirPluginDiscovery(dir string) (*dirPluginDiscovery, error) {
d := &dirPluginDiscovery{dir: dir}
// Perform a dummy read to catch obvious issues early (such as the directory not existing).
_, err := d.List()
return d, err
}
func (r *dirPluginDiscovery) dirLookup(entry os.FileInfo) (*plugin.Endpoint, error) {
socketPath := filepath.Join(r.dir, entry.Name())
if entry.Mode()&os.ModeSocket != 0 {
return &plugin.Endpoint{
Protocol: "unix",
Address: socketPath,
Name: entry.Name(),
}, nil
}
return nil, discovery.ErrNotUnixSocket(socketPath)
}
// List returns a list of plugins known, keyed by the name
func (r *dirPluginDiscovery) List() (map[string]*plugin.Endpoint, error) {
r.lock.Lock()
defer r.lock.Unlock()
entries, err := ioutil.ReadDir(r.dir)
if err != nil {
return nil, err
}
plugins := map[string]*plugin.Endpoint{}
for _, entry := range entries {
if !entry.IsDir() {
instance, err := r.dirLookup(entry)
if err != nil {
if !discovery.IsErrNotUnixSocket(err) {
log.Warn("Err loading plugin", "err", err)
}
continue
}
if instance == nil {
log.Warn("Plugin is nil")
continue
}
log.Debug("Discovered plugin", "address", instance.Address)
plugins[instance.Name] = instance
}
}
return plugins, nil
}

View File

@@ -0,0 +1,33 @@
package local
import (
"fmt"
"github.com/docker/infrakit/pkg/discovery"
logutil "github.com/docker/infrakit/pkg/log"
"github.com/spf13/afero"
)
// Setup sets up the necessary environment for running this module -- ie make sure
// the CLI module directories are present, etc.
func Setup() error {
dir := Dir()
if dir == "" {
return fmt.Errorf("Env not set:%s", discovery.PluginDirEnvVar)
}
fs := afero.NewOsFs()
exists, err := afero.Exists(fs, dir)
if err != nil {
return err
}
if !exists {
log.Warn("Creating directory", "dir", dir)
err = fs.MkdirAll(dir, 0600)
if err != nil {
return err
}
}
return nil
}
var log = logutil.New("module", "discovery/local")

114
vendor/github.com/docker/infrakit/pkg/log/log.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
package log
import (
"flag"
"os"
"github.com/Sirupsen/logrus"
"gopkg.in/inconshreveable/log15.v2"
)
// DefaultLogLevel is the default log level value.
var DefaultLogLevel = len(logrus.AllLevels) - 2
// SetLogLevel adjusts the logrus level.
func SetLogLevel(level int) {
if level > len(logrus.AllLevels)-1 {
level = len(logrus.AllLevels) - 1
} else if level < 0 {
level = 0
}
logrus.SetLevel(logrus.AllLevels[level])
}
// Options capture the logging configuration
type Options struct {
Level int
Stdout bool
Format string
CallFunc bool
CallStack bool
}
// DevDefaults is the default options for development
var DevDefaults = Options{
Level: 5,
Stdout: false,
Format: "json",
CallStack: true,
}
// ProdDefaults is the default options for production
var ProdDefaults = Options{
Level: 4,
Stdout: false,
Format: "term",
CallFunc: true,
}
func init() {
Configure(&DevDefaults)
}
// New returns a logger of given context
func New(ctx ...interface{}) log15.Logger {
return log15.Root().New(ctx...)
}
// Root returns the process's root logger
func Root() log15.Logger {
return log15.Root()
}
// Configure configures the logging
func Configure(options *Options) {
SetLogLevel(options.Level)
var f log15.Format
switch options.Format {
case "term":
f = log15.TerminalFormat()
case "json":
f = log15.JsonFormatEx(true, true)
case "logfmt":
fallthrough
default:
f = log15.LogfmtFormat()
}
var h log15.Handler
if options.Stdout {
h = log15.StreamHandler(os.Stdout, f)
} else {
h = log15.StreamHandler(os.Stderr, f)
}
if options.CallFunc {
h = log15.CallerFuncHandler(h)
}
if options.CallStack {
h = log15.CallerStackHandler("%+v", h)
}
switch options.Level {
case 0:
h = log15.DiscardHandler() // no output
case 1:
h = log15.LvlFilterHandler(log15.LvlCrit, h)
case 2:
h = log15.LvlFilterHandler(log15.LvlError, h)
case 3:
h = log15.LvlFilterHandler(log15.LvlWarn, h)
case 4:
h = log15.LvlFilterHandler(log15.LvlInfo, h)
case 5:
h = log15.LvlFilterHandler(log15.LvlDebug, h)
default:
h = log15.LvlFilterHandler(log15.LvlInfo, h)
}
log15.Root().SetHandler(h)
// Necessary to stop glog from complaining / noisy logs
flag.CommandLine.Parse([]string{})
}

View File

@@ -2,29 +2,38 @@ package client
import (
"bytes"
log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/spi"
"github.com/gorilla/rpc/v2/json2"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/docker/infrakit/pkg/spi"
"github.com/gorilla/rpc/v2/json2"
)
type client struct {
http http.Client
http *http.Client
addr string
url *url.URL
}
// New creates a new Client that communicates with a unix socket and validates the remote API.
func New(socketPath string, api spi.InterfaceSpec) (Client, error) {
dialUnix := func(proto, addr string) (conn net.Conn, err error) {
return net.Dial("unix", socketPath)
func New(address string, api spi.InterfaceSpec) (Client, error) {
u, err := url.Parse(address)
if err != nil {
return nil, err
}
unvalidatedClient := &client{addr: socketPath, http: http.Client{Transport: &http.Transport{Dial: dialUnix}}}
cl := &handshakingClient{client: unvalidatedClient, iface: api, lock: &sync.Mutex{}}
u, httpC, err := parseAddress(address)
if err != nil {
return nil, err
}
unvalidatedClient := &client{addr: address, http: httpC, url: u}
cl := &handshakingClient{client: unvalidatedClient, iface: api, lock: &sync.Mutex{}}
// check handshake
if err := cl.handshake(); err != nil {
// Note - we still return the client with the possibility of doing a handshake later on
@@ -35,6 +44,30 @@ func New(socketPath string, api spi.InterfaceSpec) (Client, error) {
return cl, nil
}
func parseAddress(address string) (*url.URL, *http.Client, error) {
u, err := url.Parse(address)
if err != nil {
return nil, nil, err
}
switch u.Scheme {
case "", "unix", "file":
// Socket case
u.Scheme = "http"
u.Host = "h"
u.Path = "" // clear it since it's a file path and we are using it to connect.
return u, &http.Client{Transport: &http.Transport{
Dial: func(proto, addr string) (conn net.Conn, err error) {
return net.Dial("unix", address)
},
}}, nil
case "http", "https", "tcp":
return u, &http.Client{}, nil
default:
}
return nil, nil, fmt.Errorf("invalid address %v", address)
}
func (c client) Addr() string {
return c.addr
}
@@ -45,7 +78,7 @@ func (c client) Call(method string, arg interface{}, result interface{}) error {
return err
}
req, err := http.NewRequest("POST", "http://a/", bytes.NewReader(message))
req, err := http.NewRequest(http.MethodPost, c.url.String(), bytes.NewReader(message))
if err != nil {
return err
}

View File

@@ -62,9 +62,9 @@ func (c client) Destroy(instance instance.ID) error {
}
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
func (c client) DescribeInstances(tags map[string]string) ([]instance.Description, error) {
func (c client) DescribeInstances(tags map[string]string, properties bool) ([]instance.Description, error) {
_, instanceType := c.name.GetLookupAndType()
req := DescribeInstancesRequest{Tags: tags, Type: instanceType}
req := DescribeInstancesRequest{Tags: tags, Type: instanceType, Properties: properties}
resp := DescribeInstancesResponse{}
err := c.client.Call("Instance.DescribeInstances", req, &resp)

View File

@@ -145,7 +145,7 @@ func (p *Instance) DescribeInstances(_ *http.Request, req *DescribeInstancesRequ
if c == nil {
return fmt.Errorf("no-plugin:%s", req.Type)
}
desc, err := c.DescribeInstances(req.Tags)
desc, err := c.DescribeInstances(req.Tags, req.Properties)
if err != nil {
return err
}

View File

@@ -56,8 +56,9 @@ type DestroyResponse struct {
// DescribeInstancesRequest is the rpc wrapper for DescribeInstances request
type DescribeInstancesRequest struct {
Type string
Tags map[string]string
Type string
Tags map[string]string
Properties bool
}
// DescribeInstancesResponse is the rpc wrapper for the DescribeInstances response

View File

@@ -24,6 +24,7 @@ import (
type Stoppable interface {
Stop()
AwaitStopped()
Wait() <-chan struct{}
}
type stoppableServer struct {
@@ -34,6 +35,10 @@ func (s *stoppableServer) Stop() {
s.server.Stop(10 * time.Second)
}
func (s *stoppableServer) Wait() <-chan struct{} {
return s.server.StopChan()
}
func (s *stoppableServer) AwaitStopped() {
<-s.server.StopChan()
}

View File

@@ -38,5 +38,5 @@ type Publisher interface {
type Subscriber interface {
// SubscribeOn returns the channel for the topic
SubscribeOn(topic types.Path) (<-chan *Event, error)
SubscribeOn(topic types.Path) (<-chan *Event, chan<- struct{}, error)
}

View File

@@ -103,3 +103,12 @@ func (event *Event) FromAny(any *types.Any) *Event {
}
return event
}
// Bytes returns the bytes representation
func (event *Event) Bytes() ([]byte, error) {
v, err := types.AnyValue(event)
if err != nil {
return nil, err
}
return v.Bytes(), nil
}

View File

@@ -8,7 +8,7 @@ import (
// InterfaceSpec is the current name and version of the Instance API.
var InterfaceSpec = spi.InterfaceSpec{
Name: "Instance",
Version: "0.3.0",
Version: "0.5.0",
}
// Plugin is a vendor-agnostic API used to create and manage resources with an infrastructure provider.
@@ -26,5 +26,6 @@ type Plugin interface {
Destroy(instance ID) error
// DescribeInstances returns descriptions of all instances matching all of the provided tags.
DescribeInstances(labels map[string]string) ([]Description, error)
// The properties flag indicates the client is interested in receiving details about each instance.
DescribeInstances(labels map[string]string, properties bool) ([]Description, error)
}

View File

@@ -12,6 +12,10 @@ type Description struct {
ID ID
LogicalID *LogicalID
Tags map[string]string
// Properties carry the opaque, platform specific blob about the resource.
// It can represent the current state of the resource.
Properties *types.Any `json:",omitempty" yaml:",omitempty"`
}
// LogicalID is the logical identifier to associate with an instance.

View File

@@ -7,7 +7,6 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
)
// Fetch fetchs content from the given URL string. Supported schemes are http:// https:// file:// unix://
@@ -21,34 +20,42 @@ func Fetch(s string, opt Options) ([]byte, error) {
return ioutil.ReadFile(u.Path)
case "http", "https":
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
return doHTTPGet(u, opt.CustomizeFetch, &http.Client{})
case "unix":
// unix: will look for a socket that matches the host name at a
// directory path set by environment variable.
c, err := socketClient(u, opt.SocketDir)
c, err := socketClient(u)
if err != nil {
return nil, err
}
u.Scheme = "http"
resp, err := c.Get(u.String())
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
return doHTTPGet(u, opt.CustomizeFetch, c)
}
return nil, fmt.Errorf("unsupported url:%s", s)
}
func socketClient(u *url.URL, socketDir string) (*http.Client, error) {
socketPath := filepath.Join(socketDir, u.Host)
func doHTTPGet(u *url.URL, customize func(*http.Request), client *http.Client) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
}
if customize != nil {
customize(req)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func socketClient(u *url.URL) (*http.Client, error) {
socketPath := u.Path
if f, err := os.Stat(socketPath); err != nil {
return nil, err
} else if f.Mode()&os.ModeSocket == 0 {

View File

@@ -4,12 +4,16 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strings"
"time"
"github.com/docker/infrakit/pkg/types"
"github.com/ghodss/yaml"
"github.com/jmespath/go-jmespath"
"github.com/vaughan0/go-ini"
)
// DeepCopyObject makes a deep copy of the argument, using encoding/gob encode/decode.
@@ -80,6 +84,47 @@ func ToJSONFormat(prefix, indent string, o interface{}) (string, error) {
return string(buff), err
}
// FromYAML decode the input YAML encoded as string or byte slice into a map.
func FromYAML(o interface{}) (interface{}, error) {
var ret interface{}
switch o := o.(type) {
case string:
err := yaml.Unmarshal([]byte(o), &ret)
return ret, err
case []byte:
err := yaml.Unmarshal(o, &ret)
return ret, err
case *types.Any:
err := yaml.Unmarshal(o.Bytes(), &ret)
return ret, err
}
return ret, fmt.Errorf("not-supported-value-type")
}
// ToYAML encodes the input struct into a YAML string.
func ToYAML(o interface{}) (string, error) {
buff, err := yaml.Marshal(o)
return string(buff), err
}
// FromINI decodes content formatted in INI format at path
func FromINI(v string) (map[string]interface{}, error) {
buff := bytes.NewBufferString(v)
file, err := ini.Load(buff)
if err != nil {
return nil, err
}
out := map[string]interface{}{}
for n, section := range file {
m := map[string]interface{}{}
for k, v := range section {
m[k] = v
}
out[n] = m
}
return out, nil
}
// FromMap decodes map into raw struct
func FromMap(m map[string]interface{}, raw interface{}) error {
// The safest way, but the slowest, is to just marshal and unmarshal back
@@ -134,8 +179,45 @@ func IndexOf(srch interface{}, array interface{}, strictOptional ...bool) int {
return -1
}
// given optional args in a template function call, extra headers and the context
func headersAndContext(opt ...interface{}) (headers map[string][]string, context interface{}) {
if len(opt) == 0 {
return
}
// scan through all the args and if it's a string of the form x=y, then use as header
// the element that doesn't follow the form is the context
headers = map[string][]string{}
for _, v := range opt {
if vv, is := v.(string); is && strings.Index(vv, "=") > 0 {
kv := strings.Split(vv, "=")
key := kv[0]
value := ""
if len(kv) == 2 {
value = kv[1]
}
if _, has := headers[key]; !has {
headers[key] = []string{value}
} else {
headers[key] = append(headers[key], value)
}
} else {
context = v
}
}
return
}
func setHeaders(req *http.Request, headers map[string][]string) {
for k, vv := range headers {
for _, v := range vv {
req.Header.Add(k, v)
}
}
}
// DefaultFuncs returns a list of default functions for binding in the template
func (t *Template) DefaultFuncs() []Function {
return []Function{
{
Name: "source",
@@ -143,13 +225,11 @@ func (t *Template) DefaultFuncs() []Function {
"Source / evaluate the template at the input location (as URL).",
"This will make all of the global variables declared there visible in this template's context.",
"Similar to 'source' in bash, sourcing another template means applying it in the same context ",
"as the calling template. The context (e.g. variables) of the calling template as a result can be mutated.",
"as the calling template. The context (e.g. variables) of the calling template as a result can",
"be mutated.",
},
Func: func(p string, opt ...interface{}) (string, error) {
var o interface{}
if len(opt) > 0 {
o = opt[0]
}
headers, context := headersAndContext(opt...)
loc := p
if strings.Index(loc, "str://") == -1 {
buff, err := getURL(t.url, p)
@@ -158,6 +238,14 @@ func (t *Template) DefaultFuncs() []Function {
}
loc = buff
}
prev := t.options.CustomizeFetch
t.options.CustomizeFetch = func(req *http.Request) {
setHeaders(req, headers)
if prev != nil {
prev(req)
}
}
sourced, err := NewTemplate(loc, t.options)
if err != nil {
return "", err
@@ -167,11 +255,11 @@ func (t *Template) DefaultFuncs() []Function {
sourced.forkFrom(t)
sourced.context = t.context
if o == nil {
o = sourced.context
if context == nil {
context = sourced.context
}
// TODO(chungers) -- let the sourced template define new functions that can be called in the parent.
return sourced.Render(o)
return sourced.Render(context)
},
},
{
@@ -184,10 +272,7 @@ func (t *Template) DefaultFuncs() []Function {
"be visible in the calling template's context.",
},
Func: func(p string, opt ...interface{}) (string, error) {
var o interface{}
if len(opt) > 0 {
o = opt[0]
}
headers, context := headersAndContext(opt...)
loc := p
if strings.Index(loc, "str://") == -1 {
buff, err := getURL(t.url, p)
@@ -196,6 +281,15 @@ func (t *Template) DefaultFuncs() []Function {
}
loc = buff
}
prev := t.options.CustomizeFetch
t.options.CustomizeFetch = func(req *http.Request) {
setHeaders(req, headers)
if prev != nil {
prev(req)
}
}
included, err := NewTemplate(loc, t.options)
if err != nil {
return "", err
@@ -206,11 +300,11 @@ func (t *Template) DefaultFuncs() []Function {
}
included.context = dotCopy
if o == nil {
o = included.context
if context == nil {
context = included.context
}
return included.Render(o)
return included.Render(context)
},
},
{
@@ -278,30 +372,31 @@ func (t *Template) DefaultFuncs() []Function {
Func: QueryObject,
},
{
Name: "to_json",
Name: "yamlEncode",
Description: []string{
"Encodes the input as a JSON string",
"This is useful for taking an object (interface{}) and render it inline as proper JSON.",
"Example: {{ include \"https://httpbin.org/get\" | from_json | to_json }}",
"Encodes the input as a YAML string",
"This is useful for taking an object (interface{}) and render it inline as proper YAML.",
"Example: {{ include \"https://httpbin.org/get\" | jsonDecode | yamlEncode }}",
},
Func: ToJSON,
Func: ToYAML,
},
{
Name: "yamlDecode",
Description: []string{
"Decodes the input YAML (first arg) into a structure (a map[string]interface{} or []interface{}).",
"This is useful for parsing arbitrary resources in YAML format as object. The object is the queryable via 'q'",
},
Func: FromYAML,
},
{
Name: "jsonEncode",
Description: []string{
"Encodes the input as a JSON string",
"This is useful for taking an object (interface{}) and render it inline as proper JSON.",
"Example: {{ include \"https://httpbin.org/get\" | from_json | to_json }}",
"Example: {{ include \"https://httpbin.org/get\" | jsonDecode | jsonEncode }}",
},
Func: ToJSON,
},
{
Name: "to_json_format",
Description: []string{
"Encodes the input as a JSON string with first arg as prefix, second arg the indentation, then the object",
},
Func: ToJSONFormat,
},
{
Name: "jsonEncodeIndent",
Description: []string{
@@ -309,21 +404,12 @@ func (t *Template) DefaultFuncs() []Function {
},
Func: ToJSONFormat,
},
{
Name: "from_json",
Description: []string{
"Decodes the input (first arg) into a structure (a map[string]interface{} or []interface{}).",
"This is useful for parsing arbitrary resources in JSON format as object. The object is the queryable via 'q'",
"For example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of request.",
},
Func: FromJSON,
},
{
Name: "jsonDecode",
Description: []string{
"Decodes the input (first arg) into a structure (a map[string]interface{} or []interface{}).",
"This is useful for parsing arbitrary resources in JSON format as object. The object is the queryable via 'q'",
"For example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of request.",
"For example: {{ include \"https://httpbin.org/get\" | jsonDecode | q \"origin\" }} returns the origin of request.",
},
Func: FromJSON,
},
@@ -357,5 +443,67 @@ func (t *Template) DefaultFuncs() []Function {
},
Func: IndexOf,
},
{
Name: "iniDecode",
Description: []string{
"Decodes the input INI into a structure (a map[string]interface{}).",
"This is useful for parsing arbitrary resources in INI format as object. The object is the queryable via 'q'",
},
Func: FromINI,
},
{
Name: "k",
Description: []string{
"Get value from dictionary by key. First arg is the key, second must be a map[string]interface{}",
},
Func: // MapIndex gets the value of key from map
func(k interface{}, m map[string]interface{}) interface{} {
return m[fmt.Sprintf("%v", k)]
},
},
{
Name: "echo",
Description: []string{
"Print the args to stderr. This does not affect the evaluation of the template and result is not in the template.",
},
Func: // echo out to stderr
func(args ...interface{}) string {
var out io.Writer
if t.options.Stderr != nil {
out = t.options.Stderr()
}
if out != nil {
fmt.Fprintln(out, args...)
}
return ""
},
},
// Deprecated
{
Name: "to_json",
Description: []string{
"Encodes the input as a JSON string",
"This is useful for taking an object (interface{}) and render it inline as proper JSON.",
"Example: {{ include \"https://httpbin.org/get\" | from_json | to_json }}",
},
Func: ToJSON,
},
{
Name: "to_json_format",
Description: []string{
"Encodes the input as a JSON string with first arg as prefix, second arg the indentation, then the object",
},
Func: ToJSONFormat,
},
{
Name: "from_json",
Description: []string{
"Decodes the input (first arg) into a structure (a map[string]interface{} or []interface{}).",
"This is useful for parsing arbitrary resources in JSON format as object. The object is the queryable via 'q'",
"For example: {{ include \"https://httpbin.org/get\" | from_json | q \"origin\" }} returns the origin of request.",
},
Func: FromJSON,
},
}
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"net/http"
"reflect"
"strings"
"sync"
@@ -52,9 +53,16 @@ type Context interface {
// Options contains parameters for customizing the behavior of the engine
type Options struct {
// SocketDir is the directory for locating the socket file for
// a template URL of the form unix://socket_file/path/to/resource
SocketDir string
// DelimLeft is the left delimiter, default is {{
DelimLeft string
// DelimRight is the right delimiter, default is }}
DelimRight string
// CustomizeFetch allows setting of http request header, etc. during fetch
CustomizeFetch func(*http.Request)
Stderr func() io.Writer
}
type defaultValue struct {
@@ -298,7 +306,12 @@ func (t *Template) build(context Context) error {
t.registered = registered
parsed, err := template.New(t.url).Funcs(fm).Parse(string(t.body))
tt := template.New(t.url).Funcs(fm)
if t.options.DelimLeft != "" && t.options.DelimRight != "" {
tt.Delims(t.options.DelimLeft, t.options.DelimRight)
}
parsed, err := tt.Parse(string(t.body))
if err != nil {
return err
}