blob: 3ebbf89d2c4b78a138c80c49dc57991f7575f95b [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
Ash Wilson4bf41a32015-02-12 15:52:44 -050083 _, result.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{
84 JSONResponse: &result.Body,
85 JSONBody: &reqBody,
86 OkCodes: []int{200},
Jamie Hannaforda493e642014-11-19 12:40:30 +010087 })
88
89 return result
90}
Jamie Hannafordb38dd312014-11-19 13:02:11 +010091
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +010092// UpdateOpts is the struct responsible for updating an existing security group.
Jamie Hannaford30c74662014-11-19 15:37:34 +010093type UpdateOpts GroupOpts
94
Jamie Hannaford04abbc72014-11-21 11:27:57 +010095// UpdateOptsBuilder builds the update options into a serializable format.
96type UpdateOptsBuilder interface {
97 ToSecGroupUpdateMap() (map[string]interface{}, error)
98}
99
100// ToSecGroupUpdateMap builds the update options into a serializable format.
101func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
102 sg := make(map[string]interface{})
103
Jamie Hannaford0e750962014-11-24 16:04:38 +0100104 if opts.Name == "" {
105 return sg, errName
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100106 }
Jamie Hannaford0e750962014-11-24 16:04:38 +0100107 if opts.Description == "" {
108 return sg, errDesc
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100109 }
110
Jamie Hannaford0e750962014-11-24 16:04:38 +0100111 sg["name"] = opts.Name
112 sg["description"] = opts.Description
113
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100114 return map[string]interface{}{"security_group": sg}, nil
115}
116
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100117// Update will modify the mutable properties of a security group, notably its
118// name and description.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100119func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
Jamie Hannaford30c74662014-11-19 15:37:34 +0100120 var result UpdateResult
121
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100122 reqBody, err := opts.ToSecGroupUpdateMap()
123 if err != nil {
124 result.Err = err
125 return result
126 }
Jamie Hannaford30c74662014-11-19 15:37:34 +0100127
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100128 _, result.Err = perigee.Request("PUT", resourceURL(client, id), perigee.Options{
Jamie Hannaford30c74662014-11-19 15:37:34 +0100129 Results: &result.Body,
130 ReqBody: &reqBody,
131 MoreHeaders: client.AuthenticatedHeaders(),
132 OkCodes: []int{200},
133 })
134
135 return result
136}
137
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100138// Get will return details for a particular security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100139func Get(client *gophercloud.ServiceClient, id string) GetResult {
Jamie Hannafordb38dd312014-11-19 13:02:11 +0100140 var result GetResult
141
142 _, result.Err = perigee.Request("GET", resourceURL(client, id), perigee.Options{
143 Results: &result.Body,
144 MoreHeaders: client.AuthenticatedHeaders(),
145 OkCodes: []int{200},
146 })
147
148 return result
149}
Jamie Hannafordd276e612014-11-19 13:56:28 +0100150
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100151// Delete will permanently delete a security group from the project.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100152func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannafordd276e612014-11-19 13:56:28 +0100153 var result gophercloud.ErrResult
154
155 _, result.Err = perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
156 MoreHeaders: client.AuthenticatedHeaders(),
157 OkCodes: []int{202},
158 })
159
160 return result
161}
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100162
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100163// CreateRuleOpts represents the configuration for adding a new rule to an
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100164// existing security group.
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100165type CreateRuleOpts struct {
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100166 // Required - the ID of the group that this rule will be added to.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100167 ParentGroupID string `json:"parent_group_id"`
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100168
169 // Required - the lower bound of the port range that will be opened.
170 FromPort int `json:"from_port"`
171
172 // Required - the upper bound of the port range that will be opened.
173 ToPort int `json:"to_port"`
174
175 // Required - the protocol type that will be allowed, e.g. TCP.
176 IPProtocol string `json:"ip_protocol"`
177
178 // ONLY required if FromGroupID is blank. This represents the IP range that
179 // will be the source of network traffic to your security group. Use
180 // 0.0.0.0/0 to allow all IP addresses.
181 CIDR string `json:"cidr,omitempty"`
182
183 // ONLY required if CIDR is blank. This value represents the ID of a group
184 // that forwards traffic to the parent group. So, instead of accepting
185 // network traffic from an entire IP range, you can instead refine the
186 // inbound source by an existing security group.
187 FromGroupID string `json:"group_id,omitempty"`
188}
189
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100190// CreateRuleOptsBuilder builds the create rule options into a serializable format.
191type CreateRuleOptsBuilder interface {
192 ToRuleCreateMap() (map[string]interface{}, error)
193}
194
195// ToRuleCreateMap builds the create rule options into a serializable format.
196func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
197 rule := make(map[string]interface{})
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100198
Jamie Hannaford2f226172014-11-25 11:52:25 +0100199 if opts.ParentGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100200 return rule, errors.New("A ParentGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100201 }
202 if opts.FromPort == 0 {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100203 return rule, errors.New("A FromPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100204 }
205 if opts.ToPort == 0 {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100206 return rule, errors.New("A ToPort must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100207 }
208 if opts.IPProtocol == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100209 return rule, errors.New("A IPProtocol must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100210 }
211 if opts.CIDR == "" && opts.FromGroupID == "" {
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100212 return rule, errors.New("A CIDR or FromGroupID must be set")
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100213 }
214
Jamie Hannaford04abbc72014-11-21 11:27:57 +0100215 rule["parent_group_id"] = opts.ParentGroupID
216 rule["from_port"] = opts.FromPort
217 rule["to_port"] = opts.ToPort
218 rule["ip_protocol"] = opts.IPProtocol
219
220 if opts.CIDR != "" {
221 rule["cidr"] = opts.CIDR
222 }
223 if opts.FromGroupID != "" {
224 rule["from_group_id"] = opts.FromGroupID
225 }
226
227 return map[string]interface{}{"security_group_rule": rule}, nil
228}
229
230// CreateRule will add a new rule to an existing security group (whose ID is
231// specified in CreateRuleOpts). You have the option of controlling inbound
232// traffic from either an IP range (CIDR) or from another security group.
233func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
234 var result CreateRuleResult
235
236 reqBody, err := opts.ToRuleCreateMap()
237 if err != nil {
238 result.Err = err
239 return result
240 }
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100241
Ash Wilson4bf41a32015-02-12 15:52:44 -0500242 _, result.Err = client.Request("POST", rootRuleURL(client), gophercloud.RequestOpts{
243 JSONResponse: &result.Body,
244 JSONBody: &reqBody,
245 OkCodes: []int{200},
Jamie Hannaford8badf1e2014-11-19 14:39:26 +0100246 })
247
248 return result
249}
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100250
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100251// DeleteRule will permanently delete a rule from a security group.
Jamie Hannaford2f226172014-11-25 11:52:25 +0100252func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
Jamie Hannaford61f81ca2014-11-19 14:44:33 +0100253 var result gophercloud.ErrResult
254
255 _, result.Err = perigee.Request("DELETE", resourceRuleURL(client, id), perigee.Options{
256 MoreHeaders: client.AuthenticatedHeaders(),
257 OkCodes: []int{202},
258 })
259
260 return result
261}
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100262
263func actionMap(prefix, groupName string) map[string]map[string]string {
264 return map[string]map[string]string{
265 prefix + "SecurityGroup": map[string]string{"name": groupName},
266 }
267}
268
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100269// AddServerToGroup will associate a server and a security group, enforcing the
270// rules of the group on the server.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100271func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
272 var result gophercloud.ErrResult
273
274 _, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
275 Results: &result.Body,
276 ReqBody: actionMap("add", groupName),
277 MoreHeaders: client.AuthenticatedHeaders(),
Jamie Hannafordd8ac5bb2014-11-20 12:01:37 +0100278 OkCodes: []int{202},
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100279 })
280
281 return result
282}
283
Jamie Hannaford7f34d8e2014-11-20 12:24:55 +0100284// RemoveServerFromGroup will disassociate a server from a security group.
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100285func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
286 var result gophercloud.ErrResult
287
288 _, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
289 Results: &result.Body,
290 ReqBody: actionMap("remove", groupName),
291 MoreHeaders: client.AuthenticatedHeaders(),
Jamie Hannafordd8ac5bb2014-11-20 12:01:37 +0100292 OkCodes: []int{202},
Jamie Hannaford740e4a32014-11-19 16:13:30 +0100293 })
294
295 return result
296}