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  }