diff --git a/httpbakery/agent/agent.go b/httpbakery/agent/agent.go index 8850f61..fdc6e12 100644 --- a/httpbakery/agent/agent.go +++ b/httpbakery/agent/agent.go @@ -7,8 +7,11 @@ package agent import ( + "encoding/json" "errors" + "io/ioutil" "net/url" + "os" "strings" "github.com/juju/loggo" @@ -40,6 +43,32 @@ type Agent struct { Username string `json:"username" yaml:"username"` } +var ErrNoAuthInfo = errgo.New("no bakery agent info found in environment") + +// AuthInfoFromEnvironment returns an AuthInfo derived +// from environment variables. +// +// It recognizes the following variable: +// BAKERY_AGENT_FILE - path to a file containing agent authentication +// info in JSON format (as marshaled by the AuthInfo type). +// +// If BAKERY_AGENT_FILE is not set, ErrNoAuthInfo will be returned. +func AuthInfoFromEnvironment() (*AuthInfo, error) { + agentFile := os.Getenv("BAKERY_AGENT_FILE") + var ai AuthInfo + data, err := ioutil.ReadFile(agentFile) + if err != nil { + return nil, errgo.Mask(err) + } + if err := json.Unmarshal(data, &ai); err != nil { + return nil, errgo.Notef(err, "cannot unmarshal agent information from %q: %v", agentFile) + } + if ai.Key == nil { + return nil, errgo.Newf("no private key found in %q", agentFile) + } + return &ai, nil +} + // SetUpAuth sets up agent authentication on the given client. // If this is called several times on the same client, earlier // calls will take precedence over later calls when there's diff --git a/httpbakery/agent/agent_test.go b/httpbakery/agent/agent_test.go index 21578fb..4c71e5d 100644 --- a/httpbakery/agent/agent_test.go +++ b/httpbakery/agent/agent_test.go @@ -1,9 +1,13 @@ package agent_test import ( + "encoding/json" + "io/ioutil" "net/http" + "os" "github.com/juju/testing" + jc "github.com/juju/testing/checkers" "golang.org/x/net/context" gc "gopkg.in/check.v1" "gopkg.in/errgo.v1" @@ -117,6 +121,36 @@ func (s *agentSuite) TestSetUpAuth(c *gc.C) { c.Assert(err, gc.Equals, nil) } +func (s *agentSuite) TestAuthInfoFromEnvironment(c *gc.C) { + defer os.Setenv("BAKERY_AGENT_FILE", "") + + f, err := ioutil.TempFile("", "") + c.Assert(err, gc.Equals, nil) + defer os.Remove(f.Name()) + defer f.Close() + + authInfo := &agent.AuthInfo{ + Key: bakery.MustGenerateKey(), + Agents: []agent.Agent{{ + URL: "https://0.1.2.3/x", + Username: "bob", + }, { + URL: "https://0.2.3.4", + Username: "charlie", + }}, + } + data, err := json.Marshal(authInfo) + _, err = f.Write(data) + c.Assert(err, gc.Equals, nil) + f.Close() + + os.Setenv("BAKERY_AGENT_FILE", f.Name()) + + authInfo1, err := agent.AuthInfoFromEnvironment() + c.Assert(err, gc.Equals, nil) + c.Assert(authInfo1, jc.DeepEquals, authInfo) +} + func AgentHandlers(h AgentHandler) []httprequest.Handler { return reqServer.Handlers(func(p httprequest.Params) (agentHandlers, context.Context, error) { return agentHandlers{h}, p.Context, nil