blob: 09503d715ecaeab8fabd32413e519d70bedf925b [file] [log] [blame]
Jamie Hannaford924c09d2014-11-19 12:05:38 +01001package secgroups
2
3import (
Jamie Hannaford8badf1e2014-11-19 14:39:26 +01004 "errors"
5
Jamie Hannaforda493e642014-11-19 12:40:30 +01006 "github.com/racker/perigee"
7
Jamie Hannaford924c09d2014-11-19 12:05:38 +01008 "github.com/rackspace/gophercloud"
9 "github.com/rackspace/gophercloud/pagination"
10)
11
Jamie Hannaford19151792014-11-19 12:46:47 +010012func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
Jamie Hannaford924c09d2014-11-19 12:05:38 +010013 createPage := func(r pagination.PageResult) pagination.Page {
14 return SecurityGroupPage{pagination.SinglePageBase(r)}
15 }
16
Jamie Hannaford19151792014-11-19 12:46:47 +010017 return pagination.NewPager(client, url, createPage)
18}
19
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010020// List will return a collection of all the security groups for a particular
21// tenant.
Jamie Hannaford19151792014-11-19 12:46:47 +010022func List(client *gophercloud.ServiceClient) pagination.Pager {
23 return commonList(client, rootURL(client))
24}
25
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010026// ListByServer will return a collection of all the security groups which are
27// associated with a particular server.
Jamie Hannaford19151792014-11-19 12:46:47 +010028func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
29 return commonList(client, listByServerURL(client, serverID))
Jamie Hannaford924c09d2014-11-19 12:05:38 +010030}
Jamie Hannaforda493e642014-11-19 12:40:30 +010031
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010032// GroupOpts is the underlying struct responsible for creating or updating
33// security groups. It therefore represents the mutable attributes of a
34// security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010035type GroupOpts struct {
Jamie Hannaford415ff942014-11-25 15:25:57 +010036 // Required - the name of your security group.
Jamie Hannaford0e750962014-11-24 16:04:38 +010037 Name string `json:"name"`
Jamie Hannaforda493e642014-11-19 12:40:30 +010038
Jamie Hannaford415ff942014-11-25 15:25:57 +010039 // Required - the description of your security group.
Jamie Hannaford0e750962014-11-24 16:04:38 +010040 Description string `json:"description"`
Jamie Hannaforda493e642014-11-19 12:40:30 +010041}
42
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010043// CreateOpts is the struct responsible for creating a security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010044type CreateOpts GroupOpts
45
Jamie Hannaford04abbc72014-11-21 11:27:57 +010046// CreateOptsBuilder builds the create options into a serializable format.
47type CreateOptsBuilder interface {
48 ToSecGroupCreateMap() (map[string]interface{}, error)
49}
50
Jamie Hannaford0e750962014-11-24 16:04:38 +010051var (
52 errName = errors.New("Name is a required field")
53 errDesc = errors.New("Description is a required field")
54)
55
Jamie Hannaford04abbc72014-11-21 11:27:57 +010056// ToSecGroupCreateMap builds the create options into a serializable format.
57func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
58 sg := make(map[string]interface{})
59
Jamie Hannaford0e750962014-11-24 16:04:38 +010060 if opts.Name == "" {
61 return sg, errName
Jamie Hannaford04abbc72014-11-21 11:27:57 +010062 }
Jamie Hannaford0e750962014-11-24 16:04:38 +010063 if opts.Description == "" {
64 return sg, errDesc
Jamie Hannaford04abbc72014-11-21 11:27:57 +010065 }
66
Jamie Hannaford0e750962014-11-24 16:04:38 +010067 sg["name"] = opts.Name
68 sg["description"] = opts.Description
69
Jamie Hannaford04abbc72014-11-21 11:27:57 +010070 return map[string]interface{}{"security_group": sg}, nil
71}
72
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010073// Create will create a new security group.
Jamie Hannaford04abbc72014-11-21 11:27:57 +010074func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
Jamie Hannaforda493e642014-11-19 12:40:30 +010075 var result CreateResult
76
Jamie Hannaford04abbc72014-11-21 11:27:57 +010077 reqBody, err := opts.ToSecGroupCreateMap()
78 if err != nil {
79 result.Err = err
80 return result
81 }
Jamie Hannaforda493e642014-11-19 12:40:30 +010082
83 _, result.Err = perigee.Request("POST", rootURL(client), perigee.Options{
84 Results: &result.Body,
85 ReqBody: &reqBody,
86 MoreHeaders: client.AuthenticatedHeaders(),
87 OkCodes: []int{200},
88 })
89
90 return result
91}
Jamie Hannafordb38dd312014-11-19 13:02:11 +010092
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010093// UpdateOpts is the struct responsible for updating an existing security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010094type UpdateOpts GroupOpts
95
Jamie Hannaford04abbc72014-11-21 11:27:57 +010096// UpdateOptsBuilder builds the update options into a serializable format.
97type UpdateOptsBuilder interface {
98 ToSecGroupUpdateMap() (map[string]interface{}, error)
99}
100
101// ToSecGroupUpdateMap builds the update options into a serializable format.
102func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
103 sg := make(map[string]interface{})
104
Jamie Hannaford0e750962014-11-24 16:04:38 +0100105 if opts.Name == "" {
106 return sg, errName
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100107 }
Jamie Hannaford0e750962014-11-24 16:04:38 +0100108 if opts.Description == "" {
109 return sg, errDesc
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100110 }
111
Jamie Hannaford0e750962014-11-24 16:04:38 +0100112 sg["name"] = opts.Name
113 sg["description"] = opts.Description
114
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100115 return map[string]interface{}{"security_group": sg}, nil
116}
117
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100118// Update will modify the mutable properties of a security group, notably its
119// name and description.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100120func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
Jamie Hannaford30c74662014-11-19 15:37:34 +0100121 var result UpdateResult
122
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100123 reqBody, err := opts.ToSecGroupUpdateMap()
124 if err != nil {
125 result.Err = err
126 return result
127 }
Jamie Hannaford30c74662014-11-19 15:37:34 +0100128
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100129 _, result.Err = perigee.Request("PUT", resourceURL(client, id), perigee.Options{
Jamie Hannaford30c74662014-11-19 15:37:34 +0100130 Results: &result.Body,
131 ReqBody: &reqBody,
132 MoreHeaders: client.AuthenticatedHeaders(),
133 OkCodes: []int{200},
134 })
135
136 return result
137}
138
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100139// Get will return details for a particular security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100140func Get(client *gophercloud.ServiceClient, id string) GetResult {
Jamie Hannafordb38dd312014-11-19 13:02:11 +0100141 var result GetResult
142
143 _, result.Err = perigee.Request("GET", resourceURL(client, id), perigee.Options{
144 Results: &result.Body,
145 MoreHeaders: client.AuthenticatedHeaders(),
146 OkCodes: []int{200},
147 })
148
149 return result
150}
Jamie Hannafordd276e612014-11-19 13:56:28 +0100151
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100152// Delete will permanently delete a security group from the project.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100153func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannafordd276e612014-11-19 13:56:28 +0100154 var result gophercloud.ErrResult
155
156 _, result.Err = perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
157 MoreHeaders: client.AuthenticatedHeaders(),
158 OkCodes: []int{202},
159 })
160
161 return result
162}
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100163
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100164// CreateRuleOpts represents the configuration for adding a new rule to an
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100165// existing security group.
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100166type CreateRuleOpts struct {
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100167 // Required - the ID of the group that this rule will be added to.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100168 ParentGroupID string `json:"parent_group_id"`
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100169
170 // Required - the lower bound of the port range that will be opened.
171 FromPort int `json:"from_port"`
172
173 // Required - the upper bound of the port range that will be opened.
174 ToPort int `json:"to_port"`
175
176 // Required - the protocol type that will be allowed, e.g. TCP.
177 IPProtocol string `json:"ip_protocol"`
178
179 // ONLY required if FromGroupID is blank. This represents the IP range that
180 // will be the source of network traffic to your security group. Use
181 // 0.0.0.0/0 to allow all IP addresses.
182 CIDR string `json:"cidr,omitempty"`
183
184 // ONLY required if CIDR is blank. This value represents the ID of a group
185 // that forwards traffic to the parent group. So, instead of accepting
186 // network traffic from an entire IP range, you can instead refine the
187 // inbound source by an existing security group.
188 FromGroupID string `json:"group_id,omitempty"`
189}
190
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100191// CreateRuleOptsBuilder builds the create rule options into a serializable format.
192type CreateRuleOptsBuilder interface {
193 ToRuleCreateMap() (map[string]interface{}, error)
194}
195
196// ToRuleCreateMap builds the create rule options into a serializable format.
197func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
198 rule := make(map[string]interface{})
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100199
Jamie Hannaford2f226172014-11-25 11:52:25 +0100200 if opts.ParentGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100201 return rule, errors.New("A ParentGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100202 }
203 if opts.FromPort == 0 {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100204 return rule, errors.New("A FromPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100205 }
206 if opts.ToPort == 0 {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100207 return rule, errors.New("A ToPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100208 }
209 if opts.IPProtocol == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100210 return rule, errors.New("A IPProtocol must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100211 }
212 if opts.CIDR == "" && opts.FromGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100213 return rule, errors.New("A CIDR or FromGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100214 }
215
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100216 rule["parent_group_id"] = opts.ParentGroupID
217 rule["from_port"] = opts.FromPort
218 rule["to_port"] = opts.ToPort
219 rule["ip_protocol"] = opts.IPProtocol
220
221 if opts.CIDR != "" {
222 rule["cidr"] = opts.CIDR
223 }
224 if opts.FromGroupID != "" {
225 rule["from_group_id"] = opts.FromGroupID
226 }
227
228 return map[string]interface{}{"security_group_rule": rule}, nil
229}
230
231// CreateRule will add a new rule to an existing security group (whose ID is
232// specified in CreateRuleOpts). You have the option of controlling inbound
233// traffic from either an IP range (CIDR) or from another security group.
234func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
235 var result CreateRuleResult
236
237 reqBody, err := opts.ToRuleCreateMap()
238 if err != nil {
239 result.Err = err
240 return result
241 }
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100242
243 _, result.Err = perigee.Request("POST", rootRuleURL(client), perigee.Options{
244 Results: &result.Body,
245 ReqBody: &reqBody,
246 MoreHeaders: client.AuthenticatedHeaders(),
247 OkCodes: []int{200},
248 })
249
250 return result
251}
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100252
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100253// DeleteRule will permanently delete a rule from a security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100254func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100255 var result gophercloud.ErrResult
256
257 _, result.Err = perigee.Request("DELETE", resourceRuleURL(client, id), perigee.Options{
258 MoreHeaders: client.AuthenticatedHeaders(),
259 OkCodes: []int{202},
260 })
261
262 return result
263}
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100264
265func actionMap(prefix, groupName string) map[string]map[string]string {
266 return map[string]map[string]string{
267 prefix + "SecurityGroup": map[string]string{"name": groupName},
268 }
269}
270
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100271// AddServerToGroup will associate a server and a security group, enforcing the
272// rules of the group on the server.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100273func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
274 var result gophercloud.ErrResult
275
276 _, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
277 Results: &result.Body,
278 ReqBody: actionMap("add", groupName),
279 MoreHeaders: client.AuthenticatedHeaders(),
Jamie Hannafordd8ac5bb2014-11-20 12:01:37 +0100280 OkCodes: []int{202},
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100281 })
282
283 return result
284}
285
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100286// RemoveServerFromGroup will disassociate a server from a security group.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100287func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
288 var result gophercloud.ErrResult
289
290 _, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
291 Results: &result.Body,
292 ReqBody: actionMap("remove", groupName),
293 MoreHeaders: client.AuthenticatedHeaders(),
Jamie Hannafordd8ac5bb2014-11-20 12:01:37 +0100294 OkCodes: []int{202},
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100295 })
296
297 return result
298}