github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runner/context/ports.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package context 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 "gopkg.in/juju/names.v2" 11 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/network" 14 ) 15 16 // PortRangeInfo contains information about a pending open- or 17 // close-port operation for a port range. This is only exported for 18 // testing. 19 type PortRangeInfo struct { 20 ShouldOpen bool 21 RelationTag names.RelationTag 22 } 23 24 // PortRange contains a port range and a relation id. Used as key to 25 // pendingRelations and is only exported for testing. 26 type PortRange struct { 27 Ports network.PortRange 28 RelationId int 29 } 30 31 func validatePortRange(protocol string, fromPort, toPort int) (network.PortRange, error) { 32 // Validate the given range. 33 newRange := network.PortRange{ 34 Protocol: strings.ToLower(protocol), 35 FromPort: fromPort, 36 ToPort: toPort, 37 } 38 if err := newRange.Validate(); err != nil { 39 return network.PortRange{}, err 40 } 41 return newRange, nil 42 } 43 44 func tryOpenPorts( 45 protocol string, 46 fromPort, toPort int, 47 unitTag names.UnitTag, 48 machinePorts map[network.PortRange]params.RelationUnit, 49 pendingPorts map[PortRange]PortRangeInfo, 50 ) error { 51 // TODO(dimitern) Once port ranges are linked to relations in 52 // addition to networks, refactor this functions and test it 53 // better to ensure it handles relations properly. 54 relationId := -1 55 56 //Validate the given range. 57 newRange, err := validatePortRange(protocol, fromPort, toPort) 58 if err != nil { 59 return err 60 } 61 rangeKey := PortRange{ 62 Ports: newRange, 63 RelationId: relationId, 64 } 65 66 rangeInfo, isKnown := pendingPorts[rangeKey] 67 if isKnown { 68 if !rangeInfo.ShouldOpen { 69 // If the same range is already pending to be closed, just 70 // mark is pending to be opened. 71 rangeInfo.ShouldOpen = true 72 pendingPorts[rangeKey] = rangeInfo 73 } 74 return nil 75 } 76 77 // Ensure there are no conflicts with existing ports on the 78 // machine. 79 for portRange, relUnit := range machinePorts { 80 relUnitTag, err := names.ParseUnitTag(relUnit.Unit) 81 if err != nil { 82 return errors.Annotatef( 83 err, 84 "machine ports %v contain invalid unit tag", 85 portRange, 86 ) 87 } 88 if newRange.ConflictsWith(portRange) { 89 if portRange == newRange && relUnitTag == unitTag { 90 // The same unit trying to open the same range is just 91 // ignored. 92 return nil 93 } 94 return errors.Errorf( 95 "cannot open %v (unit %q): conflicts with existing %v (unit %q)", 96 newRange, unitTag.Id(), portRange, relUnitTag.Id(), 97 ) 98 } 99 } 100 // Ensure other pending port ranges do not conflict with this one. 101 for rangeKey, rangeInfo := range pendingPorts { 102 if newRange.ConflictsWith(rangeKey.Ports) && rangeInfo.ShouldOpen { 103 return errors.Errorf( 104 "cannot open %v (unit %q): conflicts with %v requested earlier", 105 newRange, unitTag.Id(), rangeKey.Ports, 106 ) 107 } 108 } 109 110 rangeInfo = pendingPorts[rangeKey] 111 rangeInfo.ShouldOpen = true 112 pendingPorts[rangeKey] = rangeInfo 113 return nil 114 } 115 116 func tryClosePorts( 117 protocol string, 118 fromPort, toPort int, 119 unitTag names.UnitTag, 120 machinePorts map[network.PortRange]params.RelationUnit, 121 pendingPorts map[PortRange]PortRangeInfo, 122 ) error { 123 // TODO(dimitern) Once port ranges are linked to relations in 124 // addition to networks, refactor this functions and test it 125 // better to ensure it handles relations properly. 126 relationId := -1 127 128 // Validate the given range. 129 newRange, err := validatePortRange(protocol, fromPort, toPort) 130 if err != nil { 131 return err 132 } 133 rangeKey := PortRange{ 134 Ports: newRange, 135 RelationId: relationId, 136 } 137 138 rangeInfo, isKnown := pendingPorts[rangeKey] 139 if isKnown { 140 if rangeInfo.ShouldOpen { 141 // If the same range is already pending to be opened, just 142 // remove it from pending. 143 delete(pendingPorts, rangeKey) 144 } 145 return nil 146 } 147 148 // Ensure the range we're trying to close is opened on the 149 // machine. 150 relUnit, found := machinePorts[newRange] 151 if !found { 152 // Trying to close a range which is not open is ignored. 153 return nil 154 } else if relUnit.Unit != unitTag.String() { 155 relUnitTag, err := names.ParseUnitTag(relUnit.Unit) 156 if err != nil { 157 return errors.Annotatef( 158 err, 159 "machine ports %v contain invalid unit tag", 160 newRange, 161 ) 162 } 163 return errors.Errorf( 164 "cannot close %v (opened by %q) from %q", 165 newRange, relUnitTag.Id(), unitTag.Id(), 166 ) 167 } 168 169 rangeInfo = pendingPorts[rangeKey] 170 rangeInfo.ShouldOpen = false 171 pendingPorts[rangeKey] = rangeInfo 172 return nil 173 }