code: func ExtractHiera(fname string) ([]*TestPlan, error) {
  	b, err := ioutil.ReadFile(fname)
  	if err != nil {
  		return nil, err
  	}
  	var hs hieraSets
  	if err := json.Unmarshal(b, &hs); err != nil {
  		return nil, err
  	}
  	var plans []*TestPlan
  	for addr := range hs.Iptables.Listeners.Members {
  		sp := strings.Split(addr, ":")
  		if len(sp) != 2 {
  			return nil, fmt.Errorf("unrecognized hiera address: %s", addr)
  		}
  		port, err := strconv.Atoi(sp[1])
  		if err != nil {
  			return nil, fmt.Errorf("bad hiera port %s in %s", sp[1], addr)
  		}
  		sp = strings.Split(sp[0], ",")
  		if len(sp) != 2 {
  			return nil, fmt.Errorf("unrecognized hiera address: %s", addr)
  		}
  		ip, scheme := sp[0], sp[1]
  		plans = append(plans, &TestPlan{
  			Label: addr,
  			URL:   fmt.Sprintf("%s://%s:%d", scheme, ip, port),
  		})
  	}
  	return plans, nil
  }
docs: /*
  ExtractHiera returns listener members from a hiera sets.json file. For
  example, the following file would create three entries for the given IPs
  and ports. Other items present in the file are ignored.
  
  	{
  	  "iptables::sets::sets": {
  	    "listeners": {
  	      "members": {
  	        "10.0.1.2,tcp:80": {
  	          "comment": "first load balancer"
  	        },
  	        "10.0.3.4,tcp:80": {
  	          "comment": "second load balancer"
  	        },
  	        "10.0.7.8,tcp:25": {
  	          "comment": "mail servers"
  	        }
  	      }
  	    }
  	  }
  	}
  
  */