// Package order manages the bookkeeping and utilies required // for users to create an 'order' meaning they have requested // delegations for a certain resource. // // Copyright (c) 2016 CloudFlare, Inc. package order import ( "crypto/rand" "encoding/hex" "fmt" "net/url" "strconv" "time" "github.com/cloudflare/redoctober/hipchat" ) const ( NewOrder = "%s has created an order for the label %s. requesting %d delegations for %s" NewOrderLink = "@%s - https://%s?%s" OrderFulfilled = "%s has had order %s fulfilled." NewDelegation = "%s has delegated the label %s to %s (per order %s) for %s" ) type Order struct { Creator string Users []string Num string TimeRequested time.Time DurationRequested time.Duration Delegated int OwnersDelegated []string Owners []string Labels []string } type OrderIndex struct { OrderFor string OrderId string OrderOwners []string } // Orders represents a mapping of Order IDs to Orders. This structure // is useful for looking up information about individual Orders and // whether or not an order has been fulfilled. Orders that have been // fulfilled will be removed from the structure. type Orderer struct { Orders map[string]Order Hipchat hipchat.HipchatClient AlternateName string } func CreateOrder(name, orderNum string, time time.Time, duration time.Duration, adminsDelegated, contacts, users, labels []string, numDelegated int) (ord Order) { ord.Creator = name ord.Num = orderNum ord.Labels = labels ord.TimeRequested = time ord.DurationRequested = duration ord.OwnersDelegated = adminsDelegated ord.Owners = contacts ord.Delegated = numDelegated ord.Users = users return } func GenerateNum() (num string) { b := make([]byte, 12) rand.Read(b) return hex.EncodeToString(b) } // NewOrder will create a new map of Orders func NewOrderer(hipchatClient hipchat.HipchatClient) (o Orderer) { o.Orders = make(map[string]Order) o.Hipchat = hipchatClient o.AlternateName = "HipchatName" return } // notify is a generic function for using a notifier, but it checks to make // sure that there is a notifier available, since there won't always be. func notify(o *Orderer, msg, color string) { o.Hipchat.Notify(msg, color) } func (o *Orderer) NotifyNewOrder(duration, orderNum string, names, labels []string, uses int, owners map[string]string) { labelList := "" for i, label := range labels { if i == 0 { labelList += label } else { // Never include spaces in something go URI encodes. Go will // add a + to the string, instead of a %20 labelList += "," + label } } nameList := "" for i, name := range names { if i == 0 { nameList += name } else { // Never include spaces in something go URI encodes. Go will // add a + to the string, instead of a %20 nameList += "," + name } } n := fmt.Sprintf(NewOrder, nameList, labelList, uses, duration) notify(o, n, hipchat.RedBackground) for owner, hipchatName := range owners { queryParams := url.Values{ "delegator": {owner}, "label": {labelList}, "duration": {duration}, "uses": {strconv.Itoa(uses)}, "ordernum": {orderNum}, "delegatee": {nameList}, }.Encode() notify(o, fmt.Sprintf(NewOrderLink, hipchatName, o.Hipchat.RoHost, queryParams), hipchat.GreenBackground) } } func (o *Orderer) NotifyDelegation(delegator, delegatee, orderNum, duration string, labels []string) { labelList := "" for i, label := range labels { if i == 0 { labelList += label } else { labelList += ", " + label } } n := fmt.Sprintf(NewDelegation, delegator, labelList, delegatee, orderNum, duration) notify(o, n, hipchat.YellowBackground) } func (o *Orderer) NotifyOrderFulfilled(name, orderNum string) { n := fmt.Sprintf(OrderFulfilled, name, orderNum) notify(o, n, hipchat.PurpleBackground) } func (o *Orderer) FindOrder(user string, labels []string) (string, bool) { for key, order := range o.Orders { foundLabel := false foundUser := false for _, orderUser := range order.Users { if orderUser == user { foundUser = true } } if !foundUser { continue } for _, ol := range order.Labels { foundLabel = false for _, il := range labels { if il == ol { foundLabel = true } } } if !foundLabel { continue } return key, true } return "", false }