summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/goamz/goamz/ec2/ec2test/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/goamz/goamz/ec2/ec2test/server.go')
-rw-r--r--vendor/github.com/goamz/goamz/ec2/ec2test/server.go993
1 files changed, 0 insertions, 993 deletions
diff --git a/vendor/github.com/goamz/goamz/ec2/ec2test/server.go b/vendor/github.com/goamz/goamz/ec2/ec2test/server.go
deleted file mode 100644
index e25d4ea20..000000000
--- a/vendor/github.com/goamz/goamz/ec2/ec2test/server.go
+++ /dev/null
@@ -1,993 +0,0 @@
-// The ec2test package implements a fake EC2 provider with
-// the capability of inducing errors on any given operation,
-// and retrospectively determining what operations have been
-// carried out.
-package ec2test
-
-import (
- "encoding/base64"
- "encoding/xml"
- "fmt"
- "github.com/goamz/goamz/ec2"
- "io"
- "net"
- "net/http"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- "sync"
-)
-
-var b64 = base64.StdEncoding
-
-// Action represents a request that changes the ec2 state.
-type Action struct {
- RequestId string
-
- // Request holds the requested action as a url.Values instance
- Request url.Values
-
- // If the action succeeded, Response holds the value that
- // was marshalled to build the XML response for the request.
- Response interface{}
-
- // If the action failed, Err holds an error giving details of the failure.
- Err *ec2.Error
-}
-
-// TODO possible other things:
-// - some virtual time stamp interface, so a client
-// can ask for all actions after a certain virtual time.
-
-// Server implements an EC2 simulator for use in testing.
-type Server struct {
- url string
- listener net.Listener
- mu sync.Mutex
- reqs []*Action
-
- instances map[string]*Instance // id -> instance
- reservations map[string]*reservation // id -> reservation
- groups map[string]*securityGroup // id -> group
- maxId counter
- reqId counter
- reservationId counter
- groupId counter
- initialInstanceState ec2.InstanceState
-}
-
-// reservation holds a simulated ec2 reservation.
-type reservation struct {
- id string
- instances map[string]*Instance
- groups []*securityGroup
-}
-
-// instance holds a simulated ec2 instance
-type Instance struct {
- // UserData holds the data that was passed to the RunInstances request
- // when the instance was started.
- UserData []byte
- id string
- imageId string
- reservation *reservation
- instType string
- state ec2.InstanceState
-}
-
-// permKey represents permission for a given security
-// group or IP address (but not both) to access a given range of
-// ports. Equality of permKeys is used in the implementation of
-// permission sets, relying on the uniqueness of securityGroup
-// instances.
-type permKey struct {
- protocol string
- fromPort int
- toPort int
- group *securityGroup
- ipAddr string
-}
-
-// securityGroup holds a simulated ec2 security group.
-// Instances of securityGroup should only be created through
-// Server.createSecurityGroup to ensure that groups can be
-// compared by pointer value.
-type securityGroup struct {
- id string
- name string
- description string
-
- perms map[permKey]bool
-}
-
-func (g *securityGroup) ec2SecurityGroup() ec2.SecurityGroup {
- return ec2.SecurityGroup{
- Name: g.name,
- Id: g.id,
- }
-}
-
-func (g *securityGroup) matchAttr(attr, value string) (ok bool, err error) {
- switch attr {
- case "description":
- return g.description == value, nil
- case "group-id":
- return g.id == value, nil
- case "group-name":
- return g.name == value, nil
- case "ip-permission.cidr":
- return g.hasPerm(func(k permKey) bool { return k.ipAddr == value }), nil
- case "ip-permission.group-name":
- return g.hasPerm(func(k permKey) bool {
- return k.group != nil && k.group.name == value
- }), nil
- case "ip-permission.from-port":
- port, err := strconv.Atoi(value)
- if err != nil {
- return false, err
- }
- return g.hasPerm(func(k permKey) bool { return k.fromPort == port }), nil
- case "ip-permission.to-port":
- port, err := strconv.Atoi(value)
- if err != nil {
- return false, err
- }
- return g.hasPerm(func(k permKey) bool { return k.toPort == port }), nil
- case "ip-permission.protocol":
- return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil
- case "owner-id":
- return value == ownerId, nil
- }
- return false, fmt.Errorf("unknown attribute %q", attr)
-}
-
-func (g *securityGroup) hasPerm(test func(k permKey) bool) bool {
- for k := range g.perms {
- if test(k) {
- return true
- }
- }
- return false
-}
-
-// ec2Perms returns the list of EC2 permissions granted
-// to g. It groups permissions by port range and protocol.
-func (g *securityGroup) ec2Perms() (perms []ec2.IPPerm) {
- // The grouping is held in result. We use permKey for convenience,
- // (ensuring that the group and ipAddr of each key is zero). For
- // each protocol/port range combination, we build up the permission
- // set in the associated value.
- result := make(map[permKey]*ec2.IPPerm)
- for k := range g.perms {
- groupKey := k
- groupKey.group = nil
- groupKey.ipAddr = ""
-
- ec2p := result[groupKey]
- if ec2p == nil {
- ec2p = &ec2.IPPerm{
- Protocol: k.protocol,
- FromPort: k.fromPort,
- ToPort: k.toPort,
- }
- result[groupKey] = ec2p
- }
- if k.group != nil {
- ec2p.SourceGroups = append(ec2p.SourceGroups,
- ec2.UserSecurityGroup{
- Id: k.group.id,
- Name: k.group.name,
- OwnerId: ownerId,
- })
- } else {
- ec2p.SourceIPs = append(ec2p.SourceIPs, k.ipAddr)
- }
- }
- for _, ec2p := range result {
- perms = append(perms, *ec2p)
- }
- return
-}
-
-var actions = map[string]func(*Server, http.ResponseWriter, *http.Request, string) interface{}{
- "RunInstances": (*Server).runInstances,
- "TerminateInstances": (*Server).terminateInstances,
- "DescribeInstances": (*Server).describeInstances,
- "CreateSecurityGroup": (*Server).createSecurityGroup,
- "DescribeSecurityGroups": (*Server).describeSecurityGroups,
- "DeleteSecurityGroup": (*Server).deleteSecurityGroup,
- "AuthorizeSecurityGroupIngress": (*Server).authorizeSecurityGroupIngress,
- "RevokeSecurityGroupIngress": (*Server).revokeSecurityGroupIngress,
-}
-
-const ownerId = "9876"
-
-// newAction allocates a new action and adds it to the
-// recorded list of server actions.
-func (srv *Server) newAction() *Action {
- srv.mu.Lock()
- defer srv.mu.Unlock()
-
- a := new(Action)
- srv.reqs = append(srv.reqs, a)
- return a
-}
-
-// NewServer returns a new server.
-func NewServer() (*Server, error) {
- srv := &Server{
- instances: make(map[string]*Instance),
- groups: make(map[string]*securityGroup),
- reservations: make(map[string]*reservation),
- initialInstanceState: Pending,
- }
-
- // Add default security group.
- g := &securityGroup{
- name: "default",
- description: "default group",
- id: fmt.Sprintf("sg-%d", srv.groupId.next()),
- }
- g.perms = map[permKey]bool{
- permKey{
- protocol: "icmp",
- fromPort: -1,
- toPort: -1,
- group: g,
- }: true,
- permKey{
- protocol: "tcp",
- fromPort: 0,
- toPort: 65535,
- group: g,
- }: true,
- permKey{
- protocol: "udp",
- fromPort: 0,
- toPort: 65535,
- group: g,
- }: true,
- }
- srv.groups[g.id] = g
-
- l, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- return nil, fmt.Errorf("cannot listen on localhost: %v", err)
- }
- srv.listener = l
-
- srv.url = "http://" + l.Addr().String()
-
- // we use HandlerFunc rather than *Server directly so that we
- // can avoid exporting HandlerFunc from *Server.
- go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- srv.serveHTTP(w, req)
- }))
- return srv, nil
-}
-
-// Quit closes down the server.
-func (srv *Server) Quit() {
- srv.listener.Close()
-}
-
-// SetInitialInstanceState sets the state that any new instances will be started in.
-func (srv *Server) SetInitialInstanceState(state ec2.InstanceState) {
- srv.mu.Lock()
- srv.initialInstanceState = state
- srv.mu.Unlock()
-}
-
-// URL returns the URL of the server.
-func (srv *Server) URL() string {
- return srv.url
-}
-
-// serveHTTP serves the EC2 protocol.
-func (srv *Server) serveHTTP(w http.ResponseWriter, req *http.Request) {
- req.ParseForm()
-
- a := srv.newAction()
- a.RequestId = fmt.Sprintf("req%d", srv.reqId.next())
- a.Request = req.Form
-
- // Methods on Server that deal with parsing user data
- // may fail. To save on error handling code, we allow these
- // methods to call fatalf, which will panic with an *ec2.Error
- // which will be caught here and returned
- // to the client as a properly formed EC2 error.
- defer func() {
- switch err := recover().(type) {
- case *ec2.Error:
- a.Err = err
- err.RequestId = a.RequestId
- writeError(w, err)
- case nil:
- default:
- panic(err)
- }
- }()
-
- f := actions[req.Form.Get("Action")]
- if f == nil {
- fatalf(400, "InvalidParameterValue", "Unrecognized Action")
- }
-
- response := f(srv, w, req, a.RequestId)
- a.Response = response
-
- w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`)
- xmlMarshal(w, response)
-}
-
-// Instance returns the instance for the given instance id.
-// It returns nil if there is no such instance.
-func (srv *Server) Instance(id string) *Instance {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- return srv.instances[id]
-}
-
-// writeError writes an appropriate error response.
-// TODO how should we deal with errors when the
-// error itself is potentially generated by backend-agnostic
-// code?
-func writeError(w http.ResponseWriter, err *ec2.Error) {
- // Error encapsulates an error returned by EC2.
- // TODO merge with ec2.Error when xml supports ignoring a field.
- type ec2error struct {
- Code string // EC2 error code ("UnsupportedOperation", ...)
- Message string // The human-oriented error message
- RequestId string
- }
-
- type Response struct {
- RequestId string
- Errors []ec2error `xml:"Errors>Error"`
- }
-
- w.Header().Set("Content-Type", `xml version="1.0" encoding="UTF-8"`)
- w.WriteHeader(err.StatusCode)
- xmlMarshal(w, Response{
- RequestId: err.RequestId,
- Errors: []ec2error{{
- Code: err.Code,
- Message: err.Message,
- }},
- })
-}
-
-// xmlMarshal is the same as xml.Marshal except that
-// it panics on error. The marshalling should not fail,
-// but we want to know if it does.
-func xmlMarshal(w io.Writer, x interface{}) {
- if err := xml.NewEncoder(w).Encode(x); err != nil {
- panic(fmt.Errorf("error marshalling %#v: %v", x, err))
- }
-}
-
-// formToGroups parses a set of SecurityGroup form values
-// as found in a RunInstances request, and returns the resulting
-// slice of security groups.
-// It calls fatalf if a group is not found.
-func (srv *Server) formToGroups(form url.Values) []*securityGroup {
- var groups []*securityGroup
- for name, values := range form {
- switch {
- case strings.HasPrefix(name, "SecurityGroupId."):
- if g := srv.groups[values[0]]; g != nil {
- groups = append(groups, g)
- } else {
- fatalf(400, "InvalidGroup.NotFound", "unknown group id %q", values[0])
- }
- case strings.HasPrefix(name, "SecurityGroup."):
- var found *securityGroup
- for _, g := range srv.groups {
- if g.name == values[0] {
- found = g
- }
- }
- if found == nil {
- fatalf(400, "InvalidGroup.NotFound", "unknown group name %q", values[0])
- }
- groups = append(groups, found)
- }
- }
- return groups
-}
-
-// runInstances implements the EC2 RunInstances entry point.
-func (srv *Server) runInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- min := atoi(req.Form.Get("MinCount"))
- max := atoi(req.Form.Get("MaxCount"))
- if min < 0 || max < 1 {
- fatalf(400, "InvalidParameterValue", "bad values for MinCount or MaxCount")
- }
- if min > max {
- fatalf(400, "InvalidParameterCombination", "MinCount is greater than MaxCount")
- }
- var userData []byte
- if data := req.Form.Get("UserData"); data != "" {
- var err error
- userData, err = b64.DecodeString(data)
- if err != nil {
- fatalf(400, "InvalidParameterValue", "bad UserData value: %v", err)
- }
- }
-
- // TODO attributes still to consider:
- // ImageId: accept anything, we can verify later
- // KeyName ?
- // InstanceType ?
- // KernelId ?
- // RamdiskId ?
- // AvailabilityZone ?
- // GroupName tag
- // Monitoring ignore?
- // SubnetId ?
- // DisableAPITermination bool
- // ShutdownBehavior string
- // PrivateIPAddress string
-
- srv.mu.Lock()
- defer srv.mu.Unlock()
-
- // make sure that form fields are correct before creating the reservation.
- instType := req.Form.Get("InstanceType")
- imageId := req.Form.Get("ImageId")
-
- r := srv.newReservation(srv.formToGroups(req.Form))
-
- var resp ec2.RunInstancesResp
- resp.RequestId = reqId
- resp.ReservationId = r.id
- resp.OwnerId = ownerId
-
- for i := 0; i < max; i++ {
- inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)
- inst.UserData = userData
- resp.Instances = append(resp.Instances, inst.ec2instance())
- }
- return &resp
-}
-
-func (srv *Server) group(group ec2.SecurityGroup) *securityGroup {
- if group.Id != "" {
- return srv.groups[group.Id]
- }
- for _, g := range srv.groups {
- if g.name == group.Name {
- return g
- }
- }
- return nil
-}
-
-// NewInstances creates n new instances in srv with the given instance type,
-// image ID, initial state and security groups. If any group does not already
-// exist, it will be created. NewInstances returns the ids of the new instances.
-func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
- srv.mu.Lock()
- defer srv.mu.Unlock()
-
- rgroups := make([]*securityGroup, len(groups))
- for i, group := range groups {
- g := srv.group(group)
- if g == nil {
- fatalf(400, "InvalidGroup.NotFound", "no such group %v", g)
- }
- rgroups[i] = g
- }
- r := srv.newReservation(rgroups)
-
- ids := make([]string, n)
- for i := 0; i < n; i++ {
- inst := srv.newInstance(r, instType, imageId, state)
- ids[i] = inst.id
- }
- return ids
-}
-
-func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {
- inst := &Instance{
- id: fmt.Sprintf("i-%d", srv.maxId.next()),
- instType: instType,
- imageId: imageId,
- state: state,
- reservation: r,
- }
- srv.instances[inst.id] = inst
- r.instances[inst.id] = inst
- return inst
-}
-
-func (srv *Server) newReservation(groups []*securityGroup) *reservation {
- r := &reservation{
- id: fmt.Sprintf("r-%d", srv.reservationId.next()),
- instances: make(map[string]*Instance),
- groups: groups,
- }
-
- srv.reservations[r.id] = r
- return r
-}
-
-func (srv *Server) terminateInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- var resp ec2.TerminateInstancesResp
- resp.RequestId = reqId
- var insts []*Instance
- for attr, vals := range req.Form {
- if strings.HasPrefix(attr, "InstanceId.") {
- id := vals[0]
- inst := srv.instances[id]
- if inst == nil {
- fatalf(400, "InvalidInstanceID.NotFound", "no such instance id %q", id)
- }
- insts = append(insts, inst)
- }
- }
- for _, inst := range insts {
- resp.StateChanges = append(resp.StateChanges, inst.terminate())
- }
- return &resp
-}
-
-func (inst *Instance) terminate() (d ec2.InstanceStateChange) {
- d.PreviousState = inst.state
- inst.state = ShuttingDown
- d.CurrentState = inst.state
- d.InstanceId = inst.id
- return d
-}
-
-func (inst *Instance) ec2instance() ec2.Instance {
- return ec2.Instance{
- InstanceId: inst.id,
- InstanceType: inst.instType,
- ImageId: inst.imageId,
- DNSName: fmt.Sprintf("%s.example.com", inst.id),
- // TODO the rest
- }
-}
-
-func (inst *Instance) matchAttr(attr, value string) (ok bool, err error) {
- switch attr {
- case "architecture":
- return value == "i386", nil
- case "instance-id":
- return inst.id == value, nil
- case "group-id":
- for _, g := range inst.reservation.groups {
- if g.id == value {
- return true, nil
- }
- }
- return false, nil
- case "group-name":
- for _, g := range inst.reservation.groups {
- if g.name == value {
- return true, nil
- }
- }
- return false, nil
- case "image-id":
- return value == inst.imageId, nil
- case "instance-state-code":
- code, err := strconv.Atoi(value)
- if err != nil {
- return false, err
- }
- return code&0xff == inst.state.Code, nil
- case "instance-state-name":
- return value == inst.state.Name, nil
- }
- return false, fmt.Errorf("unknown attribute %q", attr)
-}
-
-var (
- Pending = ec2.InstanceState{0, "pending"}
- Running = ec2.InstanceState{16, "running"}
- ShuttingDown = ec2.InstanceState{32, "shutting-down"}
- Terminated = ec2.InstanceState{16, "terminated"}
- Stopped = ec2.InstanceState{16, "stopped"}
-)
-
-func (srv *Server) createSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- name := req.Form.Get("GroupName")
- if name == "" {
- fatalf(400, "InvalidParameterValue", "empty security group name")
- }
- srv.mu.Lock()
- defer srv.mu.Unlock()
- if srv.group(ec2.SecurityGroup{Name: name}) != nil {
- fatalf(400, "InvalidGroup.Duplicate", "group %q already exists", name)
- }
- g := &securityGroup{
- name: name,
- description: req.Form.Get("GroupDescription"),
- id: fmt.Sprintf("sg-%d", srv.groupId.next()),
- perms: make(map[permKey]bool),
- }
- srv.groups[g.id] = g
- // we define a local type for this because ec2.CreateSecurityGroupResp
- // contains SecurityGroup, but the response to this request
- // should not contain the security group name.
- type CreateSecurityGroupResponse struct {
- RequestId string `xml:"requestId"`
- Return bool `xml:"return"`
- GroupId string `xml:"groupId"`
- }
- r := &CreateSecurityGroupResponse{
- RequestId: reqId,
- Return: true,
- GroupId: g.id,
- }
- return r
-}
-
-func (srv *Server) notImplemented(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- fatalf(500, "InternalError", "not implemented")
- panic("not reached")
-}
-
-func (srv *Server) describeInstances(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- insts := make(map[*Instance]bool)
- for name, vals := range req.Form {
- if !strings.HasPrefix(name, "InstanceId.") {
- continue
- }
- inst := srv.instances[vals[0]]
- if inst == nil {
- fatalf(400, "InvalidInstanceID.NotFound", "instance %q not found", vals[0])
- }
- insts[inst] = true
- }
-
- f := newFilter(req.Form)
-
- var resp ec2.DescribeInstancesResp
- resp.RequestId = reqId
- for _, r := range srv.reservations {
- var instances []ec2.Instance
- for _, inst := range r.instances {
- if len(insts) > 0 && !insts[inst] {
- continue
- }
- ok, err := f.ok(inst)
- if ok {
- instances = append(instances, inst.ec2instance())
- } else if err != nil {
- fatalf(400, "InvalidParameterValue", "describe instances: %v", err)
- }
- }
- if len(instances) > 0 {
- var groups []ec2.SecurityGroup
- for _, g := range r.groups {
- groups = append(groups, g.ec2SecurityGroup())
- }
- resp.Reservations = append(resp.Reservations, ec2.Reservation{
- ReservationId: r.id,
- OwnerId: ownerId,
- Instances: instances,
- SecurityGroups: groups,
- })
- }
- }
- return &resp
-}
-
-func (srv *Server) describeSecurityGroups(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- // BUG similar bug to describeInstances, but for GroupName and GroupId
- srv.mu.Lock()
- defer srv.mu.Unlock()
-
- var groups []*securityGroup
- for name, vals := range req.Form {
- var g ec2.SecurityGroup
- switch {
- case strings.HasPrefix(name, "GroupName."):
- g.Name = vals[0]
- case strings.HasPrefix(name, "GroupId."):
- g.Id = vals[0]
- default:
- continue
- }
- sg := srv.group(g)
- if sg == nil {
- fatalf(400, "InvalidGroup.NotFound", "no such group %v", g)
- }
- groups = append(groups, sg)
- }
- if len(groups) == 0 {
- for _, g := range srv.groups {
- groups = append(groups, g)
- }
- }
-
- f := newFilter(req.Form)
- var resp ec2.SecurityGroupsResp
- resp.RequestId = reqId
- for _, group := range groups {
- ok, err := f.ok(group)
- if ok {
- resp.Groups = append(resp.Groups, ec2.SecurityGroupInfo{
- OwnerId: ownerId,
- SecurityGroup: group.ec2SecurityGroup(),
- Description: group.description,
- IPPerms: group.ec2Perms(),
- })
- } else if err != nil {
- fatalf(400, "InvalidParameterValue", "describe security groups: %v", err)
- }
- }
- return &resp
-}
-
-func (srv *Server) authorizeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- g := srv.group(ec2.SecurityGroup{
- Name: req.Form.Get("GroupName"),
- Id: req.Form.Get("GroupId"),
- })
- if g == nil {
- fatalf(400, "InvalidGroup.NotFound", "group not found")
- }
- perms := srv.parsePerms(req)
-
- for _, p := range perms {
- if g.perms[p] {
- fatalf(400, "InvalidPermission.Duplicate", "Permission has already been authorized on the specified group")
- }
- }
- for _, p := range perms {
- g.perms[p] = true
- }
- return &ec2.SimpleResp{
- XMLName: xml.Name{"", "AuthorizeSecurityGroupIngressResponse"},
- RequestId: reqId,
- }
-}
-
-func (srv *Server) revokeSecurityGroupIngress(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- g := srv.group(ec2.SecurityGroup{
- Name: req.Form.Get("GroupName"),
- Id: req.Form.Get("GroupId"),
- })
- if g == nil {
- fatalf(400, "InvalidGroup.NotFound", "group not found")
- }
- perms := srv.parsePerms(req)
-
- // Note EC2 does not give an error if asked to revoke an authorization
- // that does not exist.
- for _, p := range perms {
- delete(g.perms, p)
- }
- return &ec2.SimpleResp{
- XMLName: xml.Name{"", "RevokeSecurityGroupIngressResponse"},
- RequestId: reqId,
- }
-}
-
-var secGroupPat = regexp.MustCompile(`^sg-[a-z0-9]+$`)
-var ipPat = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$`)
-var ownerIdPat = regexp.MustCompile(`^[0-9]+$`)
-
-// parsePerms returns a slice of permKey values extracted
-// from the permission fields in req.
-func (srv *Server) parsePerms(req *http.Request) []permKey {
- // perms maps an index found in the form to its associated
- // IPPerm. For instance, the form value with key
- // "IpPermissions.3.FromPort" will be stored in perms[3].FromPort
- perms := make(map[int]ec2.IPPerm)
-
- type subgroupKey struct {
- id1, id2 int
- }
- // Each IPPerm can have many source security groups. The form key
- // for a source security group contains two indices: the index
- // of the IPPerm and the sub-index of the security group. The
- // sourceGroups map maps from a subgroupKey containing these
- // two indices to the associated security group. For instance,
- // the form value with key "IPPermissions.3.Groups.2.GroupName"
- // will be stored in sourceGroups[subgroupKey{3, 2}].Name.
- sourceGroups := make(map[subgroupKey]ec2.UserSecurityGroup)
-
- // For each value in the form we store its associated information in the
- // above maps. The maps are necessary because the form keys may
- // arrive in any order, and the indices are not
- // necessarily sequential or even small.
- for name, vals := range req.Form {
- val := vals[0]
- var id1 int
- var rest string
- if x, _ := fmt.Sscanf(name, "IpPermissions.%d.%s", &id1, &rest); x != 2 {
- continue
- }
- ec2p := perms[id1]
- switch {
- case rest == "FromPort":
- ec2p.FromPort = atoi(val)
- case rest == "ToPort":
- ec2p.ToPort = atoi(val)
- case rest == "IpProtocol":
- switch val {
- case "tcp", "udp", "icmp":
- ec2p.Protocol = val
- default:
- // check it's a well formed number
- atoi(val)
- ec2p.Protocol = val
- }
- case strings.HasPrefix(rest, "Groups."):
- k := subgroupKey{id1: id1}
- if x, _ := fmt.Sscanf(rest[len("Groups."):], "%d.%s", &k.id2, &rest); x != 2 {
- continue
- }
- g := sourceGroups[k]
- switch rest {
- case "UserId":
- // BUG if the user id is blank, this does not conform to the
- // way that EC2 handles it - a specified but blank owner id
- // can cause RevokeSecurityGroupIngress to fail with
- // "group not found" even if the security group id has been
- // correctly specified.
- // By failing here, we ensure that we fail early in this case.
- if !ownerIdPat.MatchString(val) {
- fatalf(400, "InvalidUserID.Malformed", "Invalid user ID: %q", val)
- }
- g.OwnerId = val
- case "GroupName":
- g.Name = val
- case "GroupId":
- if !secGroupPat.MatchString(val) {
- fatalf(400, "InvalidGroupId.Malformed", "Invalid group ID: %q", val)
- }
- g.Id = val
- default:
- fatalf(400, "UnknownParameter", "unknown parameter %q", name)
- }
- sourceGroups[k] = g
- case strings.HasPrefix(rest, "IpRanges."):
- var id2 int
- if x, _ := fmt.Sscanf(rest[len("IpRanges."):], "%d.%s", &id2, &rest); x != 2 {
- continue
- }
- switch rest {
- case "CidrIp":
- if !ipPat.MatchString(val) {
- fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val)
- }
- ec2p.SourceIPs = append(ec2p.SourceIPs, val)
- default:
- fatalf(400, "UnknownParameter", "unknown parameter %q", name)
- }
- default:
- fatalf(400, "UnknownParameter", "unknown parameter %q", name)
- }
- perms[id1] = ec2p
- }
- // Associate each set of source groups with its IPPerm.
- for k, g := range sourceGroups {
- p := perms[k.id1]
- p.SourceGroups = append(p.SourceGroups, g)
- perms[k.id1] = p
- }
-
- // Now that we have built up the IPPerms we need, we check for
- // parameter errors and build up a permKey for each permission,
- // looking up security groups from srv as we do so.
- var result []permKey
- for _, p := range perms {
- if p.FromPort > p.ToPort {
- fatalf(400, "InvalidParameterValue", "invalid port range")
- }
- k := permKey{
- protocol: p.Protocol,
- fromPort: p.FromPort,
- toPort: p.ToPort,
- }
- for _, g := range p.SourceGroups {
- if g.OwnerId != "" && g.OwnerId != ownerId {
- fatalf(400, "InvalidGroup.NotFound", "group %q not found", g.Name)
- }
- var ec2g ec2.SecurityGroup
- switch {
- case g.Id != "":
- ec2g.Id = g.Id
- case g.Name != "":
- ec2g.Name = g.Name
- }
- k.group = srv.group(ec2g)
- if k.group == nil {
- fatalf(400, "InvalidGroup.NotFound", "group %v not found", g)
- }
- result = append(result, k)
- }
- k.group = nil
- for _, ip := range p.SourceIPs {
- k.ipAddr = ip
- result = append(result, k)
- }
- }
- return result
-}
-
-func (srv *Server) deleteSecurityGroup(w http.ResponseWriter, req *http.Request, reqId string) interface{} {
- srv.mu.Lock()
- defer srv.mu.Unlock()
- g := srv.group(ec2.SecurityGroup{
- Name: req.Form.Get("GroupName"),
- Id: req.Form.Get("GroupId"),
- })
- if g == nil {
- fatalf(400, "InvalidGroup.NotFound", "group not found")
- }
- for _, r := range srv.reservations {
- for _, h := range r.groups {
- if h == g && r.hasRunningMachine() {
- fatalf(500, "InvalidGroup.InUse", "group is currently in use by a running instance")
- }
- }
- }
- for _, sg := range srv.groups {
- // If a group refers to itself, it's ok to delete it.
- if sg == g {
- continue
- }
- for k := range sg.perms {
- if k.group == g {
- fatalf(500, "InvalidGroup.InUse", "group is currently in use by group %q", sg.id)
- }
- }
- }
-
- delete(srv.groups, g.id)
- return &ec2.SimpleResp{
- XMLName: xml.Name{"", "DeleteSecurityGroupResponse"},
- RequestId: reqId,
- }
-}
-
-func (r *reservation) hasRunningMachine() bool {
- for _, inst := range r.instances {
- if inst.state.Code != ShuttingDown.Code && inst.state.Code != Terminated.Code {
- return true
- }
- }
- return false
-}
-
-type counter int
-
-func (c *counter) next() (i int) {
- i = int(*c)
- (*c)++
- return
-}
-
-// atoi is like strconv.Atoi but is fatal if the
-// string is not well formed.
-func atoi(s string) int {
- i, err := strconv.Atoi(s)
- if err != nil {
- fatalf(400, "InvalidParameterValue", "bad number: %v", err)
- }
- return i
-}
-
-func fatalf(statusCode int, code string, f string, a ...interface{}) {
- panic(&ec2.Error{
- StatusCode: statusCode,
- Code: code,
- Message: fmt.Sprintf(f, a...),
- })
-}