// flushGlobalPorts opens and closes global ports in the environment. // It keeps a reference count for ports so that only 0-to-1 and 1-to-0 events // modify the environment. func (fw *Firewaller) flushGlobalPorts(rawOpen, rawClose []instance.Port) error { // Filter which ports are really to open or close. var toOpen, toClose []instance.Port for _, port := range rawOpen { if fw.globalPortRef[port] == 0 { toOpen = append(toOpen, port) } fw.globalPortRef[port]++ } for _, port := range rawClose { fw.globalPortRef[port]-- if fw.globalPortRef[port] == 0 { toClose = append(toClose, port) delete(fw.globalPortRef, port) } } // Open and close the ports. if len(toOpen) > 0 { if err := fw.environ.OpenPorts(toOpen); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toOpen) log.Infof("worker/firewaller: opened ports %v in environment", toOpen) } if len(toClose) > 0 { if err := fw.environ.ClosePorts(toClose); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toClose) log.Infof("worker/firewaller: closed ports %v in environment", toClose) } return nil }
func (*StateSuite) TestSortPorts(c *C) { for _, t := range sortPortsTests { p := make([]state.Port, len(t.have)) copy(p, t.have) state.SortPorts(p) c.Check(p, DeepEquals, t.want) state.SortPorts(p) c.Check(p, DeepEquals, t.want) } }
// reconcileInstances compares the initially started watcher for machines, // units and services with the opened and closed ports of the instances and // opens and closes the appropriate ports for each instance. func (fw *Firewaller) reconcileInstances() error { for _, machined := range fw.machineds { m, err := machined.machine() if errors.IsNotFoundError(err) { if err := fw.forgetMachine(machined); err != nil { return err } continue } else if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err == environs.ErrNoInstances { return nil } else if err != nil { return err } initialPorts, err := instances[0].Ports(machined.id) if err != nil { return err } // Check which ports to open or to close. toOpen := Diff(machined.ports, initialPorts) toClose := Diff(initialPorts, machined.ports) if len(toOpen) > 0 { log.Infof("worker/firewaller: opening instance ports %v for machine %s", toOpen, machined.id) if err := instances[0].OpenPorts(machined.id, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toOpen) } if len(toClose) > 0 { log.Infof("worker/firewaller: closing instance ports %v for machine %s", toClose, machined.id) if err := instances[0].ClosePorts(machined.id, toClose); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toClose) } } return nil }
// flushGlobalPorts opens and closes ports global on the machine. func (fw *Firewaller) flushInstancePorts(machined *machineData, toOpen, toClose []instance.Port) error { // If there's nothing to do, do nothing. // This is important because when a machine is first created, // it will have no instance id but also no open ports - // InstanceId will fail but we don't care. if len(toOpen) == 0 && len(toClose) == 0 { return nil } m, err := machined.machine() if errors.IsNotFoundError(err) { return nil } if err != nil { return err } instanceId, err := m.InstanceId() if err != nil { return err } instances, err := fw.environ.Instances([]instance.Id{instanceId}) if err != nil { return err } // Open and close the ports. if len(toOpen) > 0 { if err := instances[0].OpenPorts(machined.id, toOpen); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toOpen) log.Infof("worker/firewaller: opened ports %v on machine %s", toOpen, machined.id) } if len(toClose) > 0 { if err := instances[0].ClosePorts(machined.id, toClose); err != nil { // TODO(mue) Add local retry logic. return err } state.SortPorts(toClose) log.Infof("worker/firewaller: closed ports %v on machine %s", toClose, machined.id) } return nil }
// assertEnvironPorts retrieves the open ports of environment and compares them // to the expected. func (s *FirewallerSuite) assertEnvironPorts(c *C, expected []instance.Port) { s.State.StartSync() start := time.Now() for { got, err := s.Conn.Environ.Ports() if err != nil { c.Fatal(err) return } state.SortPorts(got) state.SortPorts(expected) if reflect.DeepEqual(got, expected) { c.Succeed() return } if time.Since(start) > coretesting.LongWait { c.Fatalf("timed out: expected %q; got %q", expected, got) return } time.Sleep(coretesting.ShortWait) } }
func (e *environ) Ports() (ports []instance.Port, err error) { e.state.mu.Lock() defer e.state.mu.Unlock() if e.state.firewallMode != config.FwGlobal { return nil, fmt.Errorf("invalid firewall mode for retrieving ports from environment: %q", e.state.firewallMode) } for p := range e.state.globalPorts { ports = append(ports, p) } state.SortPorts(ports) return }
// assertEnvironPorts retrieves the open ports of environment and compares them // to the expected. func (s *FirewallerSuite) assertEnvironPorts(c *C, expected []state.Port) { s.State.StartSync() start := time.Now() for { got, err := s.Conn.Environ.Ports() if err != nil { c.Fatal(err) return } state.SortPorts(got) state.SortPorts(expected) if reflect.DeepEqual(got, expected) { c.Succeed() return } if time.Since(start) > 5*time.Second { c.Fatalf("timed out: expected %q; got %q", expected, got) return } time.Sleep(50 * time.Millisecond) } panic("unreachable") }
// assertPorts retrieves the open ports of the instance and compares them // to the expected. func (s *FirewallerSuite) assertPorts(c *C, inst instance.Instance, machineId string, expected []instance.Port) { s.State.StartSync() start := time.Now() for { got, err := inst.Ports(machineId) if err != nil { c.Fatal(err) return } state.SortPorts(got) state.SortPorts(expected) if reflect.DeepEqual(got, expected) { c.Succeed() return } if time.Since(start) > coretesting.LongWait { c.Fatalf("timed out: expected %q; got %q", expected, got) return } time.Sleep(coretesting.ShortWait) } panic("unreachable") }
// reconcileGlobal compares the initially started watcher for machines, // units and services with the opened and closed ports globally and // opens and closes the appropriate ports for the whole environment. func (fw *Firewaller) reconcileGlobal() error { initialPorts, err := fw.environ.Ports() if err != nil { return err } collector := make(map[instance.Port]bool) for _, unitd := range fw.unitds { if unitd.serviced.exposed { for _, port := range unitd.ports { collector[port] = true } } } wantedPorts := []instance.Port{} for port := range collector { wantedPorts = append(wantedPorts, port) } // Check which ports to open or to close. toOpen := Diff(wantedPorts, initialPorts) toClose := Diff(initialPorts, wantedPorts) if len(toOpen) > 0 { log.Infof("worker/firewaller: opening global ports %v", toOpen) if err := fw.environ.OpenPorts(toOpen); err != nil { return err } state.SortPorts(toOpen) } if len(toClose) > 0 { log.Infof("worker/firewaller: closing global ports %v", toClose) if err := fw.environ.ClosePorts(toClose); err != nil { return err } state.SortPorts(toClose) } return nil }
// Ports is specified in the Instance interface. func (azInstance *azureInstance) Ports(machineId string) ([]instance.Port, error) { env := azInstance.environ context, err := env.getManagementAPI() if err != nil { return nil, err } defer env.releaseManagementAPI(context) ports, err := azInstance.listPorts(context) if err != nil { return nil, err } state.SortPorts(ports) return ports, nil }
func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { group, err := e.nova().SecurityGroupByName(name) if err != nil { return nil, err } for _, p := range (*group).Rules { for i := *p.FromPort; i <= *p.ToPort; i++ { ports = append(ports, instance.Port{ Protocol: *p.IPProtocol, Number: i, }) } } state.SortPorts(ports) return ports, nil }
func (inst *dummyInstance) Ports(machineId string) (ports []instance.Port, err error) { defer delay() if inst.state.firewallMode != config.FwInstance { return nil, fmt.Errorf("invalid firewall mode for retrieving ports from instance: %q", inst.state.firewallMode) } if inst.machineId != machineId { panic(fmt.Errorf("Ports with mismatched machine id, expected %q got %q", inst.machineId, machineId)) } inst.state.mu.Lock() defer inst.state.mu.Unlock() for p := range inst.ports { ports = append(ports, p) } state.SortPorts(ports) return }
func (e *environ) portsInGroup(name string) (ports []instance.Port, err error) { g := ec2.SecurityGroup{Name: name} resp, err := e.ec2().SecurityGroups([]ec2.SecurityGroup{g}, nil) if err != nil { return nil, err } if len(resp.Groups) != 1 { return nil, fmt.Errorf("expected one security group, got %d", len(resp.Groups)) } for _, p := range resp.Groups[0].IPPerms { if len(p.SourceIPs) != 1 { log.Warningf("environs/ec2: unexpected IP permission found: %v", p) continue } for i := p.FromPort; i <= p.ToPort; i++ { ports = append(ports, instance.Port{ Protocol: p.Protocol, Number: i, }) } } state.SortPorts(ports) return ports, nil }