ducky/subscriptions
ducky/subscriptions/client/client.go
Create a listener that will create subscriptions. We need a listener (as discussed in c4cfceb2f2fb) that will create a Subscription whenever an auth.Profile is created. This is the beginning of that effort. It hasn't been tested, and all the pieces aren't in place, but it's a rough skeleton. We have a Dockerfile that will correctly build a minimal container for the listener. We have a build-docker.sh script that will correctly build a binary that will be used in the Dockerfile. We have a ca-certificates.crt, which are pulled from Ubuntu, and are necessary before we can safely consume SSL endpoints. We created a small consumer script that listens for events off NSQ, and calls the appropriate endpoint for our Subscriptions API. This is untested, and it doesn't build at the moment, but that's awaiting changes in the code.secondbit.org/auth.hg package. Finally, we have a wrapper.sh file that will expose the Stripe secret key being used from a Kubernetes secret file as an environment variable, instead.
| paddy@7 | 1 package client |
| paddy@7 | 2 |
| paddy@7 | 3 import ( |
| paddy@7 | 4 "bytes" |
| paddy@7 | 5 "encoding/json" |
| paddy@7 | 6 "errors" |
| paddy@7 | 7 "io" |
| paddy@7 | 8 "net/http" |
| paddy@7 | 9 "strings" |
| paddy@7 | 10 "time" |
| paddy@7 | 11 |
| paddy@7 | 12 "code.secondbit.org/uuid.hg" |
| paddy@7 | 13 |
| paddy@7 | 14 "code.secondbit.org/ducky/subscriptions.hg/api" |
| paddy@7 | 15 ) |
| paddy@7 | 16 |
| paddy@7 | 17 var ( |
| paddy@7 | 18 ErrNilClient = errors.New("nil client wrapper") |
| paddy@7 | 19 ErrNilHTTPClient = errors.New("nil client") |
| paddy@7 | 20 ) |
| paddy@7 | 21 |
| paddy@7 | 22 type Client struct { |
| paddy@7 | 23 client *http.Client |
| paddy@7 | 24 address string |
| paddy@7 | 25 ID uuid.ID |
| paddy@7 | 26 } |
| paddy@7 | 27 |
| paddy@7 | 28 func New(address string, id uuid.ID) *Client { |
| paddy@7 | 29 address = strings.TrimRight(address, "/") |
| paddy@7 | 30 return &Client{ |
| paddy@7 | 31 address: address, |
| paddy@7 | 32 client: &http.Client{}, |
| paddy@7 | 33 ID: id, |
| paddy@7 | 34 } |
| paddy@7 | 35 } |
| paddy@7 | 36 |
| paddy@7 | 37 func (c *Client) do(method, url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 38 if c == nil { |
| paddy@7 | 39 return api.Response{}, ErrNilClient |
| paddy@7 | 40 } |
| paddy@7 | 41 if c.client == nil { |
| paddy@7 | 42 return api.Response{}, ErrNilHTTPClient |
| paddy@7 | 43 } |
| paddy@7 | 44 var response api.Response |
| paddy@7 | 45 if !strings.HasPrefix(url, "http") { |
| paddy@7 | 46 url = strings.TrimLeft(url, "/") |
| paddy@7 | 47 url = "/" + url |
| paddy@7 | 48 url = c.address + url |
| paddy@7 | 49 } |
| paddy@7 | 50 var body io.Reader |
| paddy@7 | 51 if request != nil { |
| paddy@7 | 52 data, err := json.Marshal(request) |
| paddy@7 | 53 if err != nil { |
| paddy@7 | 54 return response, err |
| paddy@7 | 55 } |
| paddy@7 | 56 body = bytes.NewBuffer(data) |
| paddy@7 | 57 } |
| paddy@7 | 58 req, err := http.NewRequest(method, url, body) |
| paddy@7 | 59 if err != nil { |
| paddy@7 | 60 return response, err |
| paddy@7 | 61 } |
| paddy@7 | 62 req.Header.Set("Ducky-Scope", strings.Join(scopes, " ")) |
| paddy@7 | 63 req.Header.Set("Ducky-Issuer", c.ID.String()) |
| paddy@7 | 64 if subject != nil { |
| paddy@7 | 65 req.Header.Set("Ducky-Subject", subject.String()) |
| paddy@7 | 66 } |
| paddy@7 | 67 req.Header.Set("Ducky-Expires", time.Now().Add(time.Hour).String()) |
| paddy@7 | 68 req.Header.Set("Ducky-Issued-At", time.Now().String()) |
| paddy@7 | 69 req.Header.Set("Ducky-Not-Before", time.Now().Add(-5*time.Minute).String()) |
| paddy@7 | 70 resp, err := c.client.Do(req) |
| paddy@7 | 71 if err != nil { |
| paddy@7 | 72 return response, err |
| paddy@7 | 73 } |
| paddy@7 | 74 defer resp.Body.Close() |
| paddy@7 | 75 switch resp.Header.Get("Content-Type") { |
| paddy@7 | 76 case "application/json": |
| paddy@7 | 77 dec := json.NewDecoder(resp.Body) |
| paddy@7 | 78 err = dec.Decode(&response) |
| paddy@7 | 79 if err != nil { |
| paddy@7 | 80 return response, err |
| paddy@7 | 81 } |
| paddy@7 | 82 default: |
| paddy@7 | 83 dec := json.NewDecoder(resp.Body) |
| paddy@7 | 84 err = dec.Decode(&response) |
| paddy@7 | 85 if err != nil { |
| paddy@7 | 86 return response, err |
| paddy@7 | 87 } |
| paddy@7 | 88 } |
| paddy@7 | 89 return response, nil |
| paddy@7 | 90 } |
| paddy@7 | 91 |
| paddy@7 | 92 func (c *Client) Get(url string, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 93 return c.do("GET", url, nil, scopes, subject) |
| paddy@7 | 94 } |
| paddy@7 | 95 |
| paddy@7 | 96 func (c *Client) Post(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 97 return c.do("POST", url, request, scopes, subject) |
| paddy@7 | 98 } |
| paddy@7 | 99 |
| paddy@7 | 100 func (c *Client) Patch(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 101 return c.do("PATCH", url, request, scopes, subject) |
| paddy@7 | 102 } |