github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/ports.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package description 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/schema" 9 ) 10 11 type versionedOpenedPorts struct { 12 Version int `yaml:"version"` 13 OpenedPorts_ []*openedPorts `yaml:"opened-ports"` 14 } 15 16 type openedPorts struct { 17 SubnetID_ string `yaml:"subnet-id"` 18 OpenedPorts_ *portRanges `yaml:"opened-ports"` 19 } 20 21 // OpenedPortsArgs is an argument struct used to add a set of opened port ranges 22 // to a machine. 23 type OpenedPortsArgs struct { 24 SubnetID string 25 OpenedPorts []PortRangeArgs 26 } 27 28 func newOpenedPorts(args OpenedPortsArgs) *openedPorts { 29 result := &openedPorts{SubnetID_: args.SubnetID} 30 result.setOpenedPorts(nil) 31 for _, pargs := range args.OpenedPorts { 32 result.OpenedPorts_.add(pargs) 33 } 34 return result 35 } 36 37 // SubnetID implements OpenedPorts. 38 func (p *openedPorts) SubnetID() string { 39 return p.SubnetID_ 40 } 41 42 // OpenPorts implements OpenedPorts. 43 func (p *openedPorts) OpenPorts() []PortRange { 44 var result []PortRange 45 for _, pr := range p.OpenedPorts_.OpenedPorts_ { 46 result = append(result, pr) 47 } 48 return result 49 } 50 51 func (p *openedPorts) setOpenedPorts(ports []*portRange) { 52 p.OpenedPorts_ = &portRanges{ 53 Version: 1, 54 OpenedPorts_: ports, 55 } 56 } 57 58 func importOpenedPorts(source map[string]interface{}) ([]*openedPorts, error) { 59 checker := versionedChecker("opened-ports") 60 coerced, err := checker.Coerce(source, nil) 61 if err != nil { 62 return nil, errors.Annotatef(err, "opened-ports version schema check failed") 63 } 64 valid := coerced.(map[string]interface{}) 65 66 version := int(valid["version"].(int64)) 67 importFunc, ok := openedPortsDeserializationFuncs[version] 68 if !ok { 69 return nil, errors.NotValidf("version %d", version) 70 } 71 sourceList := valid["opened-ports"].([]interface{}) 72 return importOpenedPortsList(sourceList, importFunc) 73 } 74 75 func importOpenedPortsList(sourceList []interface{}, importFunc openedPortsDeserializationFunc) ([]*openedPorts, error) { 76 result := make([]*openedPorts, 0, len(sourceList)) 77 for i, value := range sourceList { 78 source, ok := value.(map[string]interface{}) 79 if !ok { 80 return nil, errors.Errorf("unexpected value for opened-ports %d, %T", i, value) 81 } 82 ports, err := importFunc(source) 83 if err != nil { 84 return nil, errors.Annotatef(err, "opened-ports %d", i) 85 } 86 result = append(result, ports) 87 } 88 return result, nil 89 } 90 91 type openedPortsDeserializationFunc func(map[string]interface{}) (*openedPorts, error) 92 93 var openedPortsDeserializationFuncs = map[int]openedPortsDeserializationFunc{ 94 1: importOpenedPortsV1, 95 } 96 97 func importOpenedPortsV1(source map[string]interface{}) (*openedPorts, error) { 98 fields := schema.Fields{ 99 "subnet-id": schema.String(), 100 "opened-ports": schema.StringMap(schema.Any()), 101 } 102 103 checker := schema.FieldMap(fields, nil) // no defaults 104 105 coerced, err := checker.Coerce(source, nil) 106 if err != nil { 107 return nil, errors.Annotatef(err, "opened-ports v1 schema check failed") 108 } 109 valid := coerced.(map[string]interface{}) 110 // From here we know that the map returned from the schema coercion 111 // contains fields of the right type. 112 113 ports, err := importPortRanges(valid["opened-ports"].(map[string]interface{})) 114 if err != nil { 115 return nil, errors.Trace(err) 116 } 117 result := &openedPorts{ 118 SubnetID_: valid["subnet-id"].(string), 119 } 120 result.setOpenedPorts(ports) 121 return result, nil 122 } 123 124 type portRanges struct { 125 Version int `yaml:"version"` 126 OpenedPorts_ []*portRange `yaml:"opened-ports"` 127 } 128 129 type portRange struct { 130 UnitName_ string `yaml:"unit-name"` 131 FromPort_ int `yaml:"from-port"` 132 ToPort_ int `yaml:"to-port"` 133 Protocol_ string `yaml:"protocol"` 134 } 135 136 // PortRangeArgs is an argument struct used to create a PortRange. This is only 137 // done as part of creating OpenedPorts for a Machine. 138 type PortRangeArgs struct { 139 UnitName string 140 FromPort int 141 ToPort int 142 Protocol string 143 } 144 145 func newPortRange(args PortRangeArgs) *portRange { 146 return &portRange{ 147 UnitName_: args.UnitName, 148 FromPort_: args.FromPort, 149 ToPort_: args.ToPort, 150 Protocol_: args.Protocol, 151 } 152 } 153 154 func (p *portRanges) add(args PortRangeArgs) { 155 p.OpenedPorts_ = append(p.OpenedPorts_, newPortRange(args)) 156 } 157 158 // UnitName implements PortRange. 159 func (p *portRange) UnitName() string { 160 return p.UnitName_ 161 } 162 163 // FromPort implements PortRange. 164 func (p *portRange) FromPort() int { 165 return p.FromPort_ 166 } 167 168 // ToPort implements PortRange. 169 func (p *portRange) ToPort() int { 170 return p.ToPort_ 171 } 172 173 // Protocol implements PortRange. 174 func (p *portRange) Protocol() string { 175 return p.Protocol_ 176 } 177 178 func importPortRanges(source map[string]interface{}) ([]*portRange, error) { 179 checker := versionedChecker("opened-ports") 180 coerced, err := checker.Coerce(source, nil) 181 if err != nil { 182 return nil, errors.Annotatef(err, "port-range version schema check failed") 183 } 184 valid := coerced.(map[string]interface{}) 185 186 version := int(valid["version"].(int64)) 187 importFunc, ok := portRangeDeserializationFuncs[version] 188 if !ok { 189 return nil, errors.NotValidf("version %d", version) 190 } 191 sourceList := valid["opened-ports"].([]interface{}) 192 return importPortRangeList(sourceList, importFunc) 193 } 194 195 func importPortRangeList(sourceList []interface{}, importFunc portRangeDeserializationFunc) ([]*portRange, error) { 196 result := make([]*portRange, 0, len(sourceList)) 197 for i, value := range sourceList { 198 source, ok := value.(map[string]interface{}) 199 if !ok { 200 return nil, errors.Errorf("unexpected value for port-range %d, %T", i, value) 201 } 202 ports, err := importFunc(source) 203 if err != nil { 204 return nil, errors.Annotatef(err, "port-range %d", i) 205 } 206 result = append(result, ports) 207 } 208 return result, nil 209 } 210 211 type portRangeDeserializationFunc func(map[string]interface{}) (*portRange, error) 212 213 var portRangeDeserializationFuncs = map[int]portRangeDeserializationFunc{ 214 1: importPortRangeV1, 215 } 216 217 func importPortRangeV1(source map[string]interface{}) (*portRange, error) { 218 fields := schema.Fields{ 219 "unit-name": schema.String(), 220 "from-port": schema.Int(), 221 "to-port": schema.Int(), 222 "protocol": schema.String(), 223 } 224 225 checker := schema.FieldMap(fields, nil) // no defaults 226 227 coerced, err := checker.Coerce(source, nil) 228 if err != nil { 229 return nil, errors.Annotatef(err, "port-range v1 schema check failed") 230 } 231 valid := coerced.(map[string]interface{}) 232 // From here we know that the map returned from the schema coercion 233 // contains fields of the right type. 234 235 return &portRange{ 236 UnitName_: valid["unit-name"].(string), 237 FromPort_: int(valid["from-port"].(int64)), 238 ToPort_: int(valid["to-port"].(int64)), 239 Protocol_: valid["protocol"].(string), 240 }, nil 241 }