<commit_before>package openstack

import (
	"bytes"
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
)

func resourceComputeSecGroupV2() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeSecGroupV2Create,
		Read:   resourceComputeSecGroupV2Read,
		Update: resourceComputeSecGroupV2Update,
		Delete: resourceComputeSecGroupV2Delete,

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"rule": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"from_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
							ForceNew: false,
						},
						"to_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
							ForceNew: false,
						},
						"ip_protocol": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
							ForceNew: false,
						},
						"cidr": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
						"from_group_id": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
						"self": &schema.Schema{
							Type:     schema.TypeBool,
							Optional: true,
							Default:  false,
							ForceNew: false,
						},
					},
				},
			},
		},
	}
}

func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	createOpts := secgroups.CreateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
	}

	log.Printf("[DEBUG] Create Options: %#v", createOpts)
	sg, err := secgroups.Create(computeClient, createOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error creating OpenStack security group: %s", err)
	}

	d.SetId(sg.ID)

	createRuleOptsList := resourceSecGroupRulesV2(d)
	for _, createRuleOpts := range createRuleOptsList {
		_, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
		if err != nil {
			return fmt.Errorf("Error creating OpenStack security group rule: %s", err)
		}
	}

	return resourceComputeSecGroupV2Read(d, meta)
}

func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	sg, err := secgroups.Get(computeClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "security group")
	}

	d.Set("name", sg.Name)
	d.Set("description", sg.Description)
	rtm := rulesToMap(sg.Rules)
	for _, v := range rtm {
		if v["group"] == d.Get("name") {
			v["self"] = "1"
		} else {
			v["self"] = "0"
		}
	}
	log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm)
	d.Set("rule", rtm)

	return nil
}

func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	updateOpts := secgroups.UpdateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
	}

	log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts)

	_, err = secgroups.Update(computeClient, d.Id(), updateOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err)
	}

	if d.HasChange("rule") {
		oldSGRaw, newSGRaw := d.GetChange("rule")
		oldSGRSlice, newSGRSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{})
		oldSGRSet := schema.NewSet(secgroupRuleV2Hash, oldSGRSlice)
		newSGRSet := schema.NewSet(secgroupRuleV2Hash, newSGRSlice)
		secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet)
		secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet)

		log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd)

		log.Printf("[DEBUG] Security groups rules to remove: %v", secgrouprulesToRemove)

		for _, rawRule := range secgrouprulesToAdd.List() {
			createRuleOpts := resourceSecGroupRuleCreateOptsV2(d, rawRule)
			rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
			if err != nil {
				return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err)
			}
			log.Printf("[DEBUG] Added rule (%s) to OpenStack security group (%s) ", rule.ID, d.Id())
		}

		for _, r := range secgrouprulesToRemove.List() {
			rule := resourceSecGroupRuleV2(d, r)
			err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
			if err != nil {
				errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
				if !ok {
					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
				}
				if errCode.Actual == 404 {
					continue
				} else {
					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
				}
			} else {
				log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
			}
		}
	}

	return resourceComputeSecGroupV2Read(d, meta)
}

func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	err = secgroups.Delete(computeClient, d.Id()).ExtractErr()
	if err != nil {
		return fmt.Errorf("Error deleting OpenStack security group: %s", err)
	}
	d.SetId("")
	return nil
}

func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts {
	rawRules := d.Get("rule").([]interface{})
	createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules))
	for i, raw := range rawRules {
		rawMap := raw.(map[string]interface{})
		groupId := rawMap["from_group_id"].(string)
		if rawMap["self"].(bool) {
			groupId = d.Id()
		}
		createRuleOptsList[i] = secgroups.CreateRuleOpts{
			ParentGroupID: d.Id(),
			FromPort:      rawMap["from_port"].(int),
			ToPort:        rawMap["to_port"].(int),
			IPProtocol:    rawMap["ip_protocol"].(string),
			CIDR:          rawMap["cidr"].(string),
			FromGroupID:   groupId,
		}
	}
	return createRuleOptsList
}

func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts {
	rawMap := raw.(map[string]interface{})
	groupId := rawMap["from_group_id"].(string)
	if rawMap["self"].(bool) {
		groupId = d.Id()
	}
	return secgroups.CreateRuleOpts{
		ParentGroupID: d.Id(),
		FromPort:      rawMap["from_port"].(int),
		ToPort:        rawMap["to_port"].(int),
		IPProtocol:    rawMap["ip_protocol"].(string),
		CIDR:          rawMap["cidr"].(string),
		FromGroupID:   groupId,
	}
}

func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.Rule {
	rawMap := raw.(map[string]interface{})
	return secgroups.Rule{
		ID:            rawMap["id"].(string),
		ParentGroupID: d.Id(),
		FromPort:      rawMap["from_port"].(int),
		ToPort:        rawMap["to_port"].(int),
		IPProtocol:    rawMap["ip_protocol"].(string),
		IPRange:       secgroups.IPRange{CIDR: rawMap["cidr"].(string)},
	}
}

func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} {
	sgrMap := make([]map[string]interface{}, len(sgrs))
	for i, sgr := range sgrs {
		sgrMap[i] = map[string]interface{}{
			"id":          sgr.ID,
			"from_port":   sgr.FromPort,
			"to_port":     sgr.ToPort,
			"ip_protocol": sgr.IPProtocol,
			"cidr":        sgr.IPRange.CIDR,
			"group":       sgr.Group.Name,
		}
	}
	return sgrMap
}

func secgroupRuleV2Hash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
	buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
	buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string)))

	return hashcode.String(buf.String())
}
<commit_msg>provider/openstack: Safe SecGroup Delete<commit_after>package openstack

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
)

func resourceComputeSecGroupV2() *schema.Resource {
	return &schema.Resource{
		Create: resourceComputeSecGroupV2Create,
		Read:   resourceComputeSecGroupV2Read,
		Update: resourceComputeSecGroupV2Update,
		Delete: resourceComputeSecGroupV2Delete,

		Schema: map[string]*schema.Schema{
			"region": &schema.Schema{
				Type:        schema.TypeString,
				Required:    true,
				ForceNew:    true,
				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: false,
			},
			"rule": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"id": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},
						"from_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
							ForceNew: false,
						},
						"to_port": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
							ForceNew: false,
						},
						"ip_protocol": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
							ForceNew: false,
						},
						"cidr": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
						"from_group_id": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							ForceNew: false,
						},
						"self": &schema.Schema{
							Type:     schema.TypeBool,
							Optional: true,
							Default:  false,
							ForceNew: false,
						},
					},
				},
			},
		},
	}
}

func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	createOpts := secgroups.CreateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
	}

	log.Printf("[DEBUG] Create Options: %#v", createOpts)
	sg, err := secgroups.Create(computeClient, createOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error creating OpenStack security group: %s", err)
	}

	d.SetId(sg.ID)

	createRuleOptsList := resourceSecGroupRulesV2(d)
	for _, createRuleOpts := range createRuleOptsList {
		_, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
		if err != nil {
			return fmt.Errorf("Error creating OpenStack security group rule: %s", err)
		}
	}

	return resourceComputeSecGroupV2Read(d, meta)
}

func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	sg, err := secgroups.Get(computeClient, d.Id()).Extract()
	if err != nil {
		return CheckDeleted(d, err, "security group")
	}

	d.Set("name", sg.Name)
	d.Set("description", sg.Description)
	rtm := rulesToMap(sg.Rules)
	for _, v := range rtm {
		if v["group"] == d.Get("name") {
			v["self"] = "1"
		} else {
			v["self"] = "0"
		}
	}
	log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm)
	d.Set("rule", rtm)

	return nil
}

func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	updateOpts := secgroups.UpdateOpts{
		Name:        d.Get("name").(string),
		Description: d.Get("description").(string),
	}

	log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts)

	_, err = secgroups.Update(computeClient, d.Id(), updateOpts).Extract()
	if err != nil {
		return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err)
	}

	if d.HasChange("rule") {
		oldSGRaw, newSGRaw := d.GetChange("rule")
		oldSGRSlice, newSGRSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{})
		oldSGRSet := schema.NewSet(secgroupRuleV2Hash, oldSGRSlice)
		newSGRSet := schema.NewSet(secgroupRuleV2Hash, newSGRSlice)
		secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet)
		secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet)

		log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd)

		log.Printf("[DEBUG] Security groups rules to remove: %v", secgrouprulesToRemove)

		for _, rawRule := range secgrouprulesToAdd.List() {
			createRuleOpts := resourceSecGroupRuleCreateOptsV2(d, rawRule)
			rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
			if err != nil {
				return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err)
			}
			log.Printf("[DEBUG] Added rule (%s) to OpenStack security group (%s) ", rule.ID, d.Id())
		}

		for _, r := range secgrouprulesToRemove.List() {
			rule := resourceSecGroupRuleV2(d, r)
			err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
			if err != nil {
				errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
				if !ok {
					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
				}
				if errCode.Actual == 404 {
					continue
				} else {
					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
				}
			} else {
				log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
			}
		}
	}

	return resourceComputeSecGroupV2Read(d, meta)
}

func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)
	computeClient, err := config.computeV2Client(d.Get("region").(string))
	if err != nil {
		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
	}

	stateConf := &resource.StateChangeConf{
		Pending:    []string{"ACTIVE"},
		Target:     "DELETED",
		Refresh:    SecGroupV2StateRefreshFunc(computeClient, d),
		Timeout:    10 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	_, err = stateConf.WaitForState()
	if err != nil {
		return fmt.Errorf("Error deleting OpenStack security group: %s", err)
	}

	d.SetId("")
	return nil
}

func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts {
	rawRules := d.Get("rule").([]interface{})
	createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules))
	for i, raw := range rawRules {
		rawMap := raw.(map[string]interface{})
		groupId := rawMap["from_group_id"].(string)
		if rawMap["self"].(bool) {
			groupId = d.Id()
		}
		createRuleOptsList[i] = secgroups.CreateRuleOpts{
			ParentGroupID: d.Id(),
			FromPort:      rawMap["from_port"].(int),
			ToPort:        rawMap["to_port"].(int),
			IPProtocol:    rawMap["ip_protocol"].(string),
			CIDR:          rawMap["cidr"].(string),
			FromGroupID:   groupId,
		}
	}
	return createRuleOptsList
}

func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts {
	rawMap := raw.(map[string]interface{})
	groupId := rawMap["from_group_id"].(string)
	if rawMap["self"].(bool) {
		groupId = d.Id()
	}
	return secgroups.CreateRuleOpts{
		ParentGroupID: d.Id(),
		FromPort:      rawMap["from_port"].(int),
		ToPort:        rawMap["to_port"].(int),
		IPProtocol:    rawMap["ip_protocol"].(string),
		CIDR:          rawMap["cidr"].(string),
		FromGroupID:   groupId,
	}
}

func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.Rule {
	rawMap := raw.(map[string]interface{})
	return secgroups.Rule{
		ID:            rawMap["id"].(string),
		ParentGroupID: d.Id(),
		FromPort:      rawMap["from_port"].(int),
		ToPort:        rawMap["to_port"].(int),
		IPProtocol:    rawMap["ip_protocol"].(string),
		IPRange:       secgroups.IPRange{CIDR: rawMap["cidr"].(string)},
	}
}

func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} {
	sgrMap := make([]map[string]interface{}, len(sgrs))
	for i, sgr := range sgrs {
		sgrMap[i] = map[string]interface{}{
			"id":          sgr.ID,
			"from_port":   sgr.FromPort,
			"to_port":     sgr.ToPort,
			"ip_protocol": sgr.IPProtocol,
			"cidr":        sgr.IPRange.CIDR,
			"group":       sgr.Group.Name,
		}
	}
	return sgrMap
}

func secgroupRuleV2Hash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
	buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
	buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string)))
	buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string)))

	return hashcode.String(buf.String())
}

func SecGroupV2StateRefreshFunc(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		log.Printf("[DEBUG] Attempting to delete Security Group %s.\n", d.Id())

		err := secgroups.Delete(computeClient, d.Id()).ExtractErr()
		if err != nil {
			return nil, "", err
		}

		s, err := secgroups.Get(computeClient, d.Id()).Extract()
		if err != nil {
			err = CheckDeleted(d, err, "Security Group")
			if err != nil {
				return s, "", err
			} else {
				log.Printf("[DEBUG] Successfully deleted Security Group %s", d.Id())
				return s, "DELETED", nil
			}
		}

		log.Printf("[DEBUG] Security Group %s still active.\n", d.Id())
		return s, "ACTIVE", nil
	}
}
<|endoftext|>


<commit_before>package api

import "testing"

func Test_Parse(t *testing.T) {
	testXml := []byte(`<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
		<!DOCTYPE GANGLIA_XML [
		<!ELEMENT GANGLIA_XML (GRID|CLUSTER|HOST)*>
		<!ATTLIST GANGLIA_XML VERSION CDATA #REQUIRED>
		<!ATTLIST GANGLIA_XML SOURCE CDATA #REQUIRED>
		<!ELEMENT GRID (CLUSTER | GRID | HOSTS | METRICS)*>
		<!ATTLIST GRID NAME CDATA #REQUIRED>
		<!ATTLIST GRID AUTHORITY CDATA #REQUIRED>
		<!ATTLIST GRID LOCALTIME CDATA #IMPLIED>
		<!ELEMENT CLUSTER (HOST | HOSTS | METRICS)*>
		<!ATTLIST CLUSTER NAME CDATA #REQUIRED>
		<!ATTLIST CLUSTER OWNER CDATA #IMPLIED>
		<!ATTLIST CLUSTER LATLONG CDATA #IMPLIED>
		<!ATTLIST CLUSTER URL CDATA #IMPLIED>
		<!ATTLIST CLUSTER LOCALTIME CDATA #REQUIRED>
		<!ELEMENT HOST (METRIC)*>
		<!ATTLIST HOST NAME CDATA #REQUIRED>
		<!ATTLIST HOST IP CDATA #REQUIRED>
		<!ATTLIST HOST LOCATION CDATA #IMPLIED>
		<!ATTLIST HOST TAGS CDATA #IMPLIED>
		<!ATTLIST HOST REPORTED CDATA #REQUIRED>
		<!ATTLIST HOST TN CDATA #IMPLIED>
		<!ATTLIST HOST TMAX CDATA #IMPLIED>
		<!ATTLIST HOST DMAX CDATA #IMPLIED>
		<!ATTLIST HOST GMOND_STARTED CDATA #IMPLIED>
		<!ELEMENT METRIC (EXTRA_DATA*)>
		<!ATTLIST METRIC NAME CDATA #REQUIRED>
		<!ATTLIST METRIC VAL CDATA #REQUIRED>
		<!ATTLIST METRIC TYPE (string | int8 | uint8 | int16 | uint16 | int32 | uint32 | float | double | timestamp) #REQUIRED>
		<!ATTLIST METRIC UNITS CDATA #IMPLIED>
		<!ATTLIST METRIC TN CDATA #IMPLIED>
		<!ATTLIST METRIC TMAX CDATA #IMPLIED>
		<!ATTLIST METRIC DMAX CDATA #IMPLIED>
		<!ATTLIST METRIC SLOPE (zero | positive | negative | both | unspecified) #IMPLIED>
		<!ATTLIST METRIC SOURCE (gmond) 'gmond'>
		<!ELEMENT EXTRA_DATA (EXTRA_ELEMENT*)>
		<!ELEMENT EXTRA_ELEMENT EMPTY>
		<!ATTLIST EXTRA_ELEMENT NAME CDATA #REQUIRED>
		<!ATTLIST EXTRA_ELEMENT VAL CDATA #REQUIRED>
		<!ELEMENT HOSTS EMPTY>
		<!ATTLIST HOSTS UP CDATA #REQUIRED>
		<!ATTLIST HOSTS DOWN CDATA #REQUIRED>
		<!ATTLIST HOSTS SOURCE (gmond | gmetad) #REQUIRED>
		<!ELEMENT METRICS (EXTRA_DATA*)>
		<!ATTLIST METRICS NAME CDATA #REQUIRED>
		<!ATTLIST METRICS SUM CDATA #REQUIRED>
		<!ATTLIST METRICS NUM CDATA #REQUIRED>
		<!ATTLIST METRICS TYPE (string | int8 | uint8 | int16 | uint16 | int32 | uint32 | float | double | timestamp) #REQUIRED>
		<!ATTLIST METRICS UNITS CDATA #IMPLIED>
		<!ATTLIST METRICS SLOPE (zero | positive | negative | both | unspecified) #IMPLIED>
		<!ATTLIST METRICS SOURCE (gmond) 'gmond'>
		]>
		<GANGLIA_XML VERSION="3.6.0" SOURCE="gmetad">
		<GRID NAME="unspecified" AUTHORITY="http://monitor.example.com/ganglia/" LOCALTIME="1436989284">
		<CLUSTER NAME="cluster-example.com" LOCALTIME="1436989282" OWNER="example" LATLONG="100" URL="200">
		<HOST NAME="example.com" IP="127.0.0.1" REPORTED="1436989274" TN="9" TMAX="20" DMAX="0" LOCATION="unspecified" GMOND_STARTED="1411930720" TAGS="unspecified">
		<METRIC NAME="disk_free" VAL="1106.528" TYPE="double" UNITS="GB" TN="117" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total free disk space"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Disk Space Available"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="bytes_out" VAL="7167.18" TYPE="float" UNITS="bytes/sec" TN="277" TMAX="300" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="network"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Number of bytes out per second"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Bytes Sent"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="disk_free" VAL="945.966" TYPE="double" UNITS="GB" TN="80" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total free disk space"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Disk Space Available"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="swap_total" VAL="2102460" TYPE="float" UNITS="KB" TN="21" TMAX="1200" DMAX="0" SLOPE="zero" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="memory"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total amount of swap space displayed in KBs"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Swap Space Total"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="part_max_used" VAL="60.5" TYPE="float" UNITS="%" TN="80" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Maximum percent used for all partitions"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Maximum Disk Space Used"/>
		</EXTRA_DATA>
		</METRIC>
		</HOST>
		</CLUSTER>
		</GRID>
		</GANGLIA_XML>`)
	resXml := ParseXML(testXml)
	if resXml.Version != "3.6.0" {
		t.Error("Wrong Version")
	}
	if resXml.Source != "gmetad" {
		t.Error("Wrong source")
	}
	if len(resXml.Grids) != 1 {
		t.Error("Wrong number of grids in the cluster")
	}
	grid := resXml.Grids[0]
	if grid.Name != "unspecified" {
		t.Error("Wrong name of the grid")
	}
	if grid.Authority != "http://monitor.example.com/ganglia/" {
		t.Error("Wrong Authority of the grid")
	}
}
<commit_msg>imroved tests<commit_after>package api

import "testing"

var (
	testXml = []byte(`<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
		<!DOCTYPE GANGLIA_XML [
		<!ELEMENT GANGLIA_XML (GRID|CLUSTER|HOST)*>
		<!ATTLIST GANGLIA_XML VERSION CDATA #REQUIRED>
		<!ATTLIST GANGLIA_XML SOURCE CDATA #REQUIRED>
		<!ELEMENT GRID (CLUSTER | GRID | HOSTS | METRICS)*>
		<!ATTLIST GRID NAME CDATA #REQUIRED>
		<!ATTLIST GRID AUTHORITY CDATA #REQUIRED>
		<!ATTLIST GRID LOCALTIME CDATA #IMPLIED>
		<!ELEMENT CLUSTER (HOST | HOSTS | METRICS)*>
		<!ATTLIST CLUSTER NAME CDATA #REQUIRED>
		<!ATTLIST CLUSTER OWNER CDATA #IMPLIED>
		<!ATTLIST CLUSTER LATLONG CDATA #IMPLIED>
		<!ATTLIST CLUSTER URL CDATA #IMPLIED>
		<!ATTLIST CLUSTER LOCALTIME CDATA #REQUIRED>
		<!ELEMENT HOST (METRIC)*>
		<!ATTLIST HOST NAME CDATA #REQUIRED>
		<!ATTLIST HOST IP CDATA #REQUIRED>
		<!ATTLIST HOST LOCATION CDATA #IMPLIED>
		<!ATTLIST HOST TAGS CDATA #IMPLIED>
		<!ATTLIST HOST REPORTED CDATA #REQUIRED>
		<!ATTLIST HOST TN CDATA #IMPLIED>
		<!ATTLIST HOST TMAX CDATA #IMPLIED>
		<!ATTLIST HOST DMAX CDATA #IMPLIED>
		<!ATTLIST HOST GMOND_STARTED CDATA #IMPLIED>
		<!ELEMENT METRIC (EXTRA_DATA*)>
		<!ATTLIST METRIC NAME CDATA #REQUIRED>
		<!ATTLIST METRIC VAL CDATA #REQUIRED>
		<!ATTLIST METRIC TYPE (string | int8 | uint8 | int16 | uint16 | int32 | uint32 | float | double | timestamp) #REQUIRED>
		<!ATTLIST METRIC UNITS CDATA #IMPLIED>
		<!ATTLIST METRIC TN CDATA #IMPLIED>
		<!ATTLIST METRIC TMAX CDATA #IMPLIED>
		<!ATTLIST METRIC DMAX CDATA #IMPLIED>
		<!ATTLIST METRIC SLOPE (zero | positive | negative | both | unspecified) #IMPLIED>
		<!ATTLIST METRIC SOURCE (gmond) 'gmond'>
		<!ELEMENT EXTRA_DATA (EXTRA_ELEMENT*)>
		<!ELEMENT EXTRA_ELEMENT EMPTY>
		<!ATTLIST EXTRA_ELEMENT NAME CDATA #REQUIRED>
		<!ATTLIST EXTRA_ELEMENT VAL CDATA #REQUIRED>
		<!ELEMENT HOSTS EMPTY>
		<!ATTLIST HOSTS UP CDATA #REQUIRED>
		<!ATTLIST HOSTS DOWN CDATA #REQUIRED>
		<!ATTLIST HOSTS SOURCE (gmond | gmetad) #REQUIRED>
		<!ELEMENT METRICS (EXTRA_DATA*)>
		<!ATTLIST METRICS NAME CDATA #REQUIRED>
		<!ATTLIST METRICS SUM CDATA #REQUIRED>
		<!ATTLIST METRICS NUM CDATA #REQUIRED>
		<!ATTLIST METRICS TYPE (string | int8 | uint8 | int16 | uint16 | int32 | uint32 | float | double | timestamp) #REQUIRED>
		<!ATTLIST METRICS UNITS CDATA #IMPLIED>
		<!ATTLIST METRICS SLOPE (zero | positive | negative | both | unspecified) #IMPLIED>
		<!ATTLIST METRICS SOURCE (gmond) 'gmond'>
		]>
		<GANGLIA_XML VERSION="3.6.0" SOURCE="gmetad">
		<GRID NAME="unspecified" AUTHORITY="http://monitor.example.com/ganglia/" LOCALTIME="1436989284">
		<CLUSTER NAME="cluster-example.com" LOCALTIME="1436989282" OWNER="example" LATLONG="100" URL="200">
		<HOST NAME="example.com" IP="127.0.0.1" REPORTED="1436989274" TN="9" TMAX="20" DMAX="0" LOCATION="unspecified" GMOND_STARTED="1411930720" TAGS="unspecified">
		<METRIC NAME="disk_free" VAL="1106.528" TYPE="double" UNITS="GB" TN="117" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total free disk space"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Disk Space Available"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="bytes_out" VAL="7167.18" TYPE="float" UNITS="bytes/sec" TN="277" TMAX="300" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="network"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Number of bytes out per second"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Bytes Sent"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="disk_free" VAL="945.966" TYPE="double" UNITS="GB" TN="80" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total free disk space"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Disk Space Available"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="swap_total" VAL="2102460" TYPE="float" UNITS="KB" TN="21" TMAX="1200" DMAX="0" SLOPE="zero" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="memory"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Total amount of swap space displayed in KBs"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Swap Space Total"/>
		</EXTRA_DATA>
		</METRIC>
		<METRIC NAME="part_max_used" VAL="60.5" TYPE="float" UNITS="%" TN="80" TMAX="180" DMAX="0" SLOPE="both" SOURCE="gmond">
		<EXTRA_DATA>
		<EXTRA_ELEMENT NAME="GROUP" VAL="disk"/>
		<EXTRA_ELEMENT NAME="DESC" VAL="Maximum percent used for all partitions"/>
		<EXTRA_ELEMENT NAME="TITLE" VAL="Maximum Disk Space Used"/>
		</EXTRA_DATA>
		</METRIC>
		</HOST>
		</CLUSTER>
		</GRID>
		</GANGLIA_XML>`)
)

func Test_ParseXML(t *testing.T) {
	resXml := ParseXML(testXml)
	if resXml.Version != "3.6.0" {
		t.Error("Wrong Version")
	}
	if resXml.Source != "gmetad" {
		t.Error("Wrong source")
	}
	if len(resXml.Grids) != 1 {
		t.Error("Wrong number of grids in the cluster")
	}
	grid := resXml.Grids[0]
	if grid.Name != "unspecified" {
		t.Error("Wrong name of the grid")
	}
	if grid.Authority != "http://monitor.example.com/ganglia/" {
		t.Error("Wrong Authority of the grid")
	}
}

func Test_Parse(t *testing.T) {

}
<|endoftext|>


<commit_before>package intcode

import (
	"fmt"
	"strconv"

	"github.com/bewuethr/advent-of-code/go/convert"
	"github.com/bewuethr/advent-of-code/go/log"
)

const (
	// Instructions
	add         = 1
	mult        = 2
	input       = 3
	output      = 4
	jumpIfTrue  = 5
	jumpIfFalse = 6
	lessThan    = 7
	equals      = 8
	halt        = 99

	// Modes
	positionMode  = 0
	immediateMode = 1
)

// nargs maps instructions to the number of arguments they use.
var nargs = map[int]int{
	add:         3,
	mult:        3,
	input:       1,
	output:      1,
	jumpIfTrue:  2,
	jumpIfFalse: 2,
	lessThan:    3,
	equals:      3,
	halt:        0,
}

// methodMap is a map from instructions to the corresponding Computer methods.
var methodMap = map[int]func(*Computer, []int){
	add:         (*Computer).add,
	mult:        (*Computer).mult,
	input:       (*Computer).input,
	output:      (*Computer).output,
	jumpIfTrue:  (*Computer).jumpIfTrue,
	jumpIfFalse: (*Computer).jumpIfFalse,
	lessThan:    (*Computer).lessThan,
	equals:      (*Computer).equals,
}

// Computer is an opcode computer.
type Computer struct {
	opcodes   []int
	inputVals []int
	instrPtr  int
}

// NewComputer returns an opcode computer with its memory initalized to opcodes.
func NewComputer(opcodes []int) *Computer {
	return &Computer{
		opcodes:  opcodes,
		instrPtr: 0,
	}
}

// RunProgram executes the program in the memory of the computer.
func (c *Computer) RunProgram(inputVals ...int) error {
	c.inputVals = inputVals
	for {
		code, params := c.parseInstruction(c.opcodes[c.instrPtr])

		if code == halt {
			return nil
		}

		methodMap[code](c, params)
	}
}

func (c *Computer) add(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+3]] = params[0] + params[1]
	c.instrPtr += nargs[add] + 1
}

func (c *Computer) mult(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+3]] = params[0] * params[1]
	c.instrPtr += nargs[mult] + 1
}

func (c *Computer) input(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+1]] = c.inputVals[0]
	c.inputVals = c.inputVals[1:]
	c.instrPtr += nargs[input] + 1
}

func (c *Computer) output(params []int) {
	fmt.Println(params[0])
	c.instrPtr += nargs[output] + 1
}

func (c *Computer) jumpIfTrue(params []int) {
	if params[0] != 0 {
		c.instrPtr = params[1]
	} else {
		c.instrPtr += nargs[jumpIfTrue] + 1
	}
}

func (c *Computer) jumpIfFalse(params []int) {
	if params[0] == 0 {
		c.instrPtr = params[1]
	} else {
		c.instrPtr += nargs[jumpIfFalse] + 1
	}
}

func (c *Computer) lessThan(params []int) {
	if params[0] < params[1] {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 1
	} else {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 0
	}
	c.instrPtr += nargs[lessThan] + 1
}

func (c *Computer) equals(params []int) {
	if params[0] == params[1] {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 1
	} else {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 0
	}
	c.instrPtr += nargs[equals] + 1
}

// parseInstruction reads a value  from memory and extracts the opcode as well
// as the parameter values for the instruction, taking the parameter mode into
// account.
func (c *Computer) parseInstruction(val int) (code int, params []int) {
	code = val % 100
	var modes []int
	if valStr := strconv.Itoa(val); len(valStr) > 2 {
		valStr = valStr[:len(valStr)-2]

		var modesStr []string
		for _, m := range valStr {
			modesStr = append([]string{string(m)}, modesStr...)
		}

		var err error
		modes, err = convert.StrSliceToInt(modesStr)
		if err != nil {
			log.Die("converting modes to int", err)
		}
	}

	for len(modes) < nargs[code] {
		modes = append(modes, 0)
	}

	return code, c.getParams(modes)
}

// getParams takes a slice of parameter modes and returns the corresponding
// parameter values based on the current value of the instruction pointer.
func (c *Computer) getParams(modes []int) []int {
	var params []int

	for i := 0; i < len(modes); i++ {
		var param int
		if modes[i] == immediateMode {
			param = c.opcodes[c.instrPtr+i+1]
		} else {
			param = c.opcodes[c.opcodes[c.instrPtr+i+1]]
		}
		params = append(params, param)
	}

	return params
}
<commit_msg>Remove log dependency from intcode package<commit_after>// Package intcode implements an intocde computer.
package intcode

import (
	"fmt"
	"strconv"

	"github.com/bewuethr/advent-of-code/go/convert"
)

const (
	// Instructions
	add         = 1
	mult        = 2
	input       = 3
	output      = 4
	jumpIfTrue  = 5
	jumpIfFalse = 6
	lessThan    = 7
	equals      = 8
	halt        = 99

	// Modes
	positionMode  = 0
	immediateMode = 1
)

// nargs maps instructions to the number of arguments they use.
var nargs = map[int]int{
	add:         3,
	mult:        3,
	input:       1,
	output:      1,
	jumpIfTrue:  2,
	jumpIfFalse: 2,
	lessThan:    3,
	equals:      3,
	halt:        0,
}

// methodMap is a map from instructions to the corresponding Computer methods.
var methodMap = map[int]func(*Computer, []int){
	add:         (*Computer).add,
	mult:        (*Computer).mult,
	input:       (*Computer).input,
	output:      (*Computer).output,
	jumpIfTrue:  (*Computer).jumpIfTrue,
	jumpIfFalse: (*Computer).jumpIfFalse,
	lessThan:    (*Computer).lessThan,
	equals:      (*Computer).equals,
}

// Computer is an opcode computer.
type Computer struct {
	opcodes   []int
	inputVals []int
	instrPtr  int
}

// NewComputer returns an opcode computer with its memory initalized to opcodes.
func NewComputer(opcodes []int) *Computer {
	return &Computer{
		opcodes:  opcodes,
		instrPtr: 0,
	}
}

// RunProgram executes the program in the memory of the computer.
func (c *Computer) RunProgram(inputVals ...int) error {
	c.inputVals = inputVals
	for {
		code, params, err := c.parseInstruction(c.opcodes[c.instrPtr])
		if err != nil {
			return err
		}

		if code == halt {
			return nil
		}

		methodMap[code](c, params)
	}
}

func (c *Computer) add(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+3]] = params[0] + params[1]
	c.instrPtr += nargs[add] + 1
}

func (c *Computer) mult(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+3]] = params[0] * params[1]
	c.instrPtr += nargs[mult] + 1
}

func (c *Computer) input(params []int) {
	c.opcodes[c.opcodes[c.instrPtr+1]] = c.inputVals[0]
	c.inputVals = c.inputVals[1:]
	c.instrPtr += nargs[input] + 1
}

func (c *Computer) output(params []int) {
	fmt.Println(params[0])
	c.instrPtr += nargs[output] + 1
}

func (c *Computer) jumpIfTrue(params []int) {
	if params[0] != 0 {
		c.instrPtr = params[1]
	} else {
		c.instrPtr += nargs[jumpIfTrue] + 1
	}
}

func (c *Computer) jumpIfFalse(params []int) {
	if params[0] == 0 {
		c.instrPtr = params[1]
	} else {
		c.instrPtr += nargs[jumpIfFalse] + 1
	}
}

func (c *Computer) lessThan(params []int) {
	if params[0] < params[1] {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 1
	} else {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 0
	}
	c.instrPtr += nargs[lessThan] + 1
}

func (c *Computer) equals(params []int) {
	if params[0] == params[1] {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 1
	} else {
		c.opcodes[c.opcodes[c.instrPtr+3]] = 0
	}
	c.instrPtr += nargs[equals] + 1
}

// parseInstruction reads a value  from memory and extracts the opcode as well
// as the parameter values for the instruction, taking the parameter mode into
// account.
func (c *Computer) parseInstruction(val int) (code int, params []int, err error) {
	code = val % 100
	var modes []int
	if valStr := strconv.Itoa(val); len(valStr) > 2 {
		valStr = valStr[:len(valStr)-2]

		var modesStr []string
		for _, m := range valStr {
			modesStr = append([]string{string(m)}, modesStr...)
		}

		var err error
		modes, err = convert.StrSliceToInt(modesStr)
		if err != nil {
			return 0, nil, fmt.Errorf("converting modes %v to int: %w", modesStr, err)
		}
	}

	for len(modes) < nargs[code] {
		modes = append(modes, 0)
	}

	return code, c.getParams(modes), nil
}

// getParams takes a slice of parameter modes and returns the corresponding
// parameter values based on the current value of the instruction pointer.
func (c *Computer) getParams(modes []int) []int {
	var params []int

	for i := 0; i < len(modes); i++ {
		var param int
		if modes[i] == immediateMode {
			param = c.opcodes[c.instrPtr+i+1]
		} else {
			param = c.opcodes[c.opcodes[c.instrPtr+i+1]]
		}
		params = append(params, param)
	}

	return params
}
<|endoftext|>


<commit_before>package paddlecloud

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/PaddlePaddle/cloud/go/utils/restclient"
	"github.com/google/subcommands"
)

// GetCommand exports get subcommand for fetching status
type GetCommand struct {
	a bool
}

// Name is subcommands name
func (*GetCommand) Name() string { return "get" }

// Synopsis is subcommands synopsis
func (*GetCommand) Synopsis() string { return "Print resources" }

// Usage is subcommands usage
func (*GetCommand) Usage() string {
	return `get [jobs|workers|registry [jobname]|quota]:
	Print resources.
`
}

// SetFlags registers subcommands flags
func (p *GetCommand) SetFlags(f *flag.FlagSet) {
	f.BoolVar(&p.a, "a", false, "Get all resources.")
}

// Execute get command
func (p *GetCommand) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() < 1 || f.NArg() > 2 {
		f.Usage()
		return subcommands.ExitFailure
	}
	if f.Arg(0) == "jobs" {
		jobs()
	} else if f.Arg(0) == "quota" {
		quota()
	} else if f.Arg(0) == "registry" {
		registry()
	} else if f.Arg(0) == "workers" {
		if f.NArg() != 2 {
			f.Usage()
			return subcommands.ExitFailure
		}
		workers(f.Arg(1))
	} else {
		f.Usage()
		return subcommands.ExitFailure
	}

	return subcommands.ExitSuccess
}

func workers(jobname string) error {
	queryMap := url.Values{}
	queryMap.Add("jobname", jobname)
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/workers/", queryMap)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting workers: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		fmt.Fprintf(os.Stderr, "bad server return: %s", respBody)
		return err
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)

	fmt.Fprintln(w, "NAME\tSTATUS\tSTART\tEXIT_CODE\tMSG\t")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing: %s", err)
		return err
	}
	for _, item := range respObj.(map[string]interface{})["items"].([]interface{}) {
		var exitCode, msg interface{}
		terminateState := item.(map[string]interface{})["status"].(map[string]interface{})["container_statuses"].([]interface{})[0].(map[string]interface{})["state"].(map[string]interface{})["terminated"]

		if terminateState != nil {
			exitCode = terminateState.(map[string]interface{})["exit_code"]
			msg = terminateState.(map[string]interface{})["message"]
		}

		fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t\n",
			item.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string),
			item.(map[string]interface{})["status"].(map[string]interface{})["phase"].(string),
			item.(map[string]interface{})["status"].(map[string]interface{})["start_time"],
			exitCode, msg)
	}
	w.Flush()
	return nil
}
func registry() error {
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/registry/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err getting registry secret: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	items := respObj.(map[string]interface{})["msg"].(map[string]interface{})["items"].([]interface{})
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	if len(items) >= 0 {
		fmt.Fprintf(w, "ID\tNAME\tDATA\n")
	}
	idx := 0
	for _, r := range items {
		metadata := r.(map[string]interface{})["metadata"].(map[string]interface{})
		name := RegistryName(metadata["name"].(string))
		if len(name) != 0 {
			cTime := metadata["creation_timestamp"].(string)
			fmt.Fprintf(w, "%d\t%s\t%s\n", idx, name, cTime)
			idx++
		}
	}
	w.Flush()
	return err
}

func jobs() error {
	// NOTE: a job include pserver replicaset and a trainers job, display them
	// get pserver replicaset
	//          "status": {
	//              "available_replicas": 1,
	//              "conditions": null,
	//              "fully_labeled_replicas": 1,
	//              "observed_generation": 1,
	//              "ready_replicas": 1,
	//              "replicas": 1
	var respObj interface{}

	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/pservers/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting pservers: %v\n", err)
		return err
	}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	pserverItems := respObj.(map[string]interface{})["items"].([]interface{})

	// get kubernetes jobs info
	respBody, err = restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/jobs/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting jobs: %v\n", err)
		return err
	}

	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	items := respObj.(map[string]interface{})["items"].([]interface{})

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	if len(items) >= 0 {
		fmt.Fprintf(w, "NAME\tACTIVE\tSUCC\tFAIL\tSTART\tCOMP\tPS_READY\tPS_TOTAL\t\n")
	}
	for _, j := range items {
		jobnameTrainer := j.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
		jobnameParts := strings.Split(jobnameTrainer, "-")
		jobname := strings.Join(jobnameParts[0:len(jobnameParts)-1], "-")
		// get info for job pservers
		var psrsname string
		var readyReplicas, replicas interface{}
		for _, psrs := range pserverItems {
			psrsname = psrs.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
			if psrsname == jobname+"-pserver" {
				readyReplicas = psrs.(map[string]interface{})["status"].(map[string]interface{})["ready_replicas"]
				replicas = psrs.(map[string]interface{})["status"].(map[string]interface{})["replicas"]
				break
			}
		}

		fmt.Fprintf(w, "%s\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t\n",
			jobname,
			j.(map[string]interface{})["status"].(map[string]interface{})["active"],
			j.(map[string]interface{})["status"].(map[string]interface{})["succeeded"],
			j.(map[string]interface{})["status"].(map[string]interface{})["failed"],
			j.(map[string]interface{})["status"].(map[string]interface{})["start_time"],
			j.(map[string]interface{})["status"].(map[string]interface{})["completion_time"],
			readyReplicas, replicas)
	}
	w.Flush()

	return err
}

func quota() error {
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/quota/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting quota: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	fmt.Fprintf(w, "RESOURCE\tLIMIT\t\n")
	for _, item := range respObj.(map[string]interface{})["items"].([]interface{}) {
		fmt.Fprintf(w, "-----\t-----\t\n")
		hardLimits := item.(map[string]interface{})["status"].(map[string]interface{})["hard"].(map[string]interface{})
		for k, v := range hardLimits {
			fmt.Fprintf(w, "%s\t%s\t\n", k, v.(string))
		}
	}
	w.Flush()
	return nil
}
<commit_msg>Format quota print (#205)<commit_after>package paddlecloud

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/PaddlePaddle/cloud/go/utils/restclient"
	"github.com/google/subcommands"
)

// GetCommand exports get subcommand for fetching status
type GetCommand struct {
	a bool
}

// Name is subcommands name
func (*GetCommand) Name() string { return "get" }

// Synopsis is subcommands synopsis
func (*GetCommand) Synopsis() string { return "Print resources" }

// Usage is subcommands usage
func (*GetCommand) Usage() string {
	return `get [jobs|workers|registry [jobname]|quota]:
	Print resources.
`
}

// SetFlags registers subcommands flags
func (p *GetCommand) SetFlags(f *flag.FlagSet) {
	f.BoolVar(&p.a, "a", false, "Get all resources.")
}

// Execute get command
func (p *GetCommand) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() < 1 || f.NArg() > 2 {
		f.Usage()
		return subcommands.ExitFailure
	}
	if f.Arg(0) == "jobs" {
		jobs()
	} else if f.Arg(0) == "quota" {
		quota()
	} else if f.Arg(0) == "registry" {
		registry()
	} else if f.Arg(0) == "workers" {
		if f.NArg() != 2 {
			f.Usage()
			return subcommands.ExitFailure
		}
		workers(f.Arg(1))
	} else {
		f.Usage()
		return subcommands.ExitFailure
	}

	return subcommands.ExitSuccess
}

func workers(jobname string) error {
	queryMap := url.Values{}
	queryMap.Add("jobname", jobname)
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/workers/", queryMap)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting workers: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		fmt.Fprintf(os.Stderr, "bad server return: %s", respBody)
		return err
	}

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)

	fmt.Fprintln(w, "NAME\tSTATUS\tSTART\tEXIT_CODE\tMSG\t")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing: %s", err)
		return err
	}
	for _, item := range respObj.(map[string]interface{})["items"].([]interface{}) {
		var exitCode, msg interface{}
		terminateState := item.(map[string]interface{})["status"].(map[string]interface{})["container_statuses"].([]interface{})[0].(map[string]interface{})["state"].(map[string]interface{})["terminated"]

		if terminateState != nil {
			exitCode = terminateState.(map[string]interface{})["exit_code"]
			msg = terminateState.(map[string]interface{})["message"]
		}

		fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t\n",
			item.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string),
			item.(map[string]interface{})["status"].(map[string]interface{})["phase"].(string),
			item.(map[string]interface{})["status"].(map[string]interface{})["start_time"],
			exitCode, msg)
	}
	w.Flush()
	return nil
}
func registry() error {
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/registry/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err getting registry secret: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	items := respObj.(map[string]interface{})["msg"].(map[string]interface{})["items"].([]interface{})
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	if len(items) >= 0 {
		fmt.Fprintf(w, "ID\tNAME\tDATA\n")
	}
	idx := 0
	for _, r := range items {
		metadata := r.(map[string]interface{})["metadata"].(map[string]interface{})
		name := RegistryName(metadata["name"].(string))
		if len(name) != 0 {
			cTime := metadata["creation_timestamp"].(string)
			fmt.Fprintf(w, "%d\t%s\t%s\n", idx, name, cTime)
			idx++
		}
	}
	w.Flush()
	return err
}

func jobs() error {
	// NOTE: a job include pserver replicaset and a trainers job, display them
	// get pserver replicaset
	//          "status": {
	//              "available_replicas": 1,
	//              "conditions": null,
	//              "fully_labeled_replicas": 1,
	//              "observed_generation": 1,
	//              "ready_replicas": 1,
	//              "replicas": 1
	var respObj interface{}

	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/pservers/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting pservers: %v\n", err)
		return err
	}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	pserverItems := respObj.(map[string]interface{})["items"].([]interface{})

	// get kubernetes jobs info
	respBody, err = restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/jobs/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting jobs: %v\n", err)
		return err
	}

	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	items := respObj.(map[string]interface{})["items"].([]interface{})

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	if len(items) >= 0 {
		fmt.Fprintf(w, "NAME\tACTIVE\tSUCC\tFAIL\tSTART\tCOMP\tPS_READY\tPS_TOTAL\t\n")
	}
	for _, j := range items {
		jobnameTrainer := j.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
		jobnameParts := strings.Split(jobnameTrainer, "-")
		jobname := strings.Join(jobnameParts[0:len(jobnameParts)-1], "-")
		// get info for job pservers
		var psrsname string
		var readyReplicas, replicas interface{}
		for _, psrs := range pserverItems {
			psrsname = psrs.(map[string]interface{})["metadata"].(map[string]interface{})["name"].(string)
			if psrsname == jobname+"-pserver" {
				readyReplicas = psrs.(map[string]interface{})["status"].(map[string]interface{})["ready_replicas"]
				replicas = psrs.(map[string]interface{})["status"].(map[string]interface{})["replicas"]
				break
			}
		}

		fmt.Fprintf(w, "%s\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t\n",
			jobname,
			j.(map[string]interface{})["status"].(map[string]interface{})["active"],
			j.(map[string]interface{})["status"].(map[string]interface{})["succeeded"],
			j.(map[string]interface{})["status"].(map[string]interface{})["failed"],
			j.(map[string]interface{})["status"].(map[string]interface{})["start_time"],
			j.(map[string]interface{})["status"].(map[string]interface{})["completion_time"],
			readyReplicas, replicas)
	}
	w.Flush()

	return err
}

func quota() error {
	respBody, err := restclient.GetCall(Config.ActiveConfig.Endpoint+"/api/v1/quota/", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error getting quota: %v\n", err)
		return err
	}
	var respObj interface{}
	err = json.Unmarshal(respBody, &respObj)
	if err != nil {
		return err
	}
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
	fmt.Fprintf(w, "RESOURCE\tLIMIT\t\n")
	for _, item := range respObj.(map[string]interface{})["items"].([]interface{}) {
		fmt.Fprintf(w, "-----\t-----\t\n")
		hardLimits := item.(map[string]interface{})["status"].(map[string]interface{})["hard"].(map[string]interface{})
		fmt.Fprintf(w, "Memory\t%s\t\n", hardLimits["requests.memory"])
		fmt.Fprintf(w, "CPU\t%s\t\n", hardLimits["requests.cpu"])
		fmt.Fprintf(w, "GPU\t%s\t\n", hardLimits["alpha.kubernetes.io/nvidia-gpu"])
		fmt.Fprintf(w, "MaxParallism\t%s\t\n", hardLimits["pods"])
	}
	w.Flush()
	return nil
}
<|endoftext|>


<commit_before>package update

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPullImage(t *testing.T) {
	// pull is OK
	msgStream1 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}`
	// pull errored
	msgStream2 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"error": "something bad happened"}`
	// msg stream is cut off
	msgStream3 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "ba`

	testImageRepo := "quay.io/protonet/dummy"

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/images/create" {
			http.Error(w, "Not found.", http.StatusNotFound)
		}

		// https://docs.docker.com/engine/reference/api/docker_remote_api_v1.22/#/create-an-image
		query := r.URL.Query()
		fromImage := query.Get("fromImage")
		tag := query.Get("tag")

		if fromImage == "" || tag == "" {
			http.Error(w, "Missing parameter 'fromImage' or 'tag'", http.StatusBadRequest)
		}

		if fromImage != testImageRepo {
			http.Error(w, "Not found.", http.StatusNotFound)
		}

		switch tag {
		case "v1":
			_, err := w.Write([]byte(msgStream1))
			assert.Nil(t, err)
		case "v2":
			_, err := w.Write([]byte(msgStream2))
			assert.Nil(t, err)
		case "v3":
			_, err := w.Write([]byte(msgStream3))
			assert.Nil(t, err)
		default:
			http.Error(w, "Not found.", http.StatusNotFound)
		}
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	defer os.Setenv("DOCKER_HOST", "")

	// pull is OK
	err := pullImage(testImageRepo, "v1")
	assert.Nil(t, err)

	// pull errored
	err = pullImage(testImageRepo, "v2")
	assert.EqualError(t, err, "Docker error: something bad happened")

	// msg stream is cut off
	err = pullImage(testImageRepo, "v3")
	assert.Equal(t, err, io.ErrUnexpectedEOF)
}

// TestExportDockerImage tests the functionality of streaming the data
// from a image's rootfs by exporting a pristine container
func TestExportDockerImage(t *testing.T) {
	testContainerID := "b99a1defb349"
	testTARBase64 := "H4sICJJWblgAA2FyY2hpdmUudGFyAO2VUQ7CIAxA+fYUvcFaCuw86Fhi5tS4mez4wrbol0uWCGrG++lPQ0sfpLYQ0UHEUmsYo5kiSjXFGSBGZqVlyQRIUkkpQMdvTYh719ubb6WpLq09d83xTZ5Pq+uFc+Z7POOfYIt99Bewwj+jUcG/UZz9pyD4ryLX8PMwSi34J/36/0Z6/4xEAjByXyMb9+8G215PDno39LtvN5NJzuHn9n857n/O+z8JLkGNNfufiIN/jVqAXJr3p9i4/0wms10eO0nUugAQAAA="
	testTARBase64Buffer := bytes.NewBufferString(testTARBase64)
	testTARBuffer := bytes.NewBuffer([]byte{})

	dec := base64.NewDecoder(base64.StdEncoding, testTARBase64Buffer)
	gunzip, err := gzip.NewReader(dec)
	assert.Nil(t, err)
	_, err = io.Copy(testTARBuffer, gunzip)
	assert.Nil(t, err)

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "POST":
			if r.URL.Path == "/containers/create" {
				response := struct {
					ID       string `json:"Id"`
					Warnings []string
				}{ID: testContainerID}
				json.NewEncoder(w).Encode(&response)
				return
			}
			break
		case "GET":
			if r.URL.Path == fmt.Sprintf("/containers/%s/export", testContainerID) {
				w.Header().Add("Content-Type", "application/octet-stream")
				w.Write(testTARBuffer.Bytes())
				return
			}
			break
		case "DELETE":
			if r.URL.Path == fmt.Sprintf("/containers/%s", testContainerID) {
				w.WriteHeader(http.StatusNoContent)
				return
			}
			break
		}

		http.Error(w, "Not found.", http.StatusNotFound)
		assert.Fail(t, "test tried to access a wrong path", "%s %+v", r.Method, r.URL)
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	os.Setenv("DOCKER_API_VERSION", "1.22")
	defer os.Setenv("DOCKER_HOST", "")
	defer os.Setenv("DOCKER_API_VERSION", "")

	// direct export to buffer
	imageBuf := bytes.NewBuffer([]byte{})
	err = exportDockerImage("repository", "tag", imageBuf)
	assert.Nil(t, err)
	assert.EqualValues(t, testTARBuffer.Bytes(), imageBuf.Bytes())

	// export through pipe
	imageBuf2 := bytes.NewBuffer([]byte{})
	pipeReader, pipeWriter := io.Pipe()

	var wg sync.WaitGroup
	wg.Add(1)
	var extractErr error
	go func() {
		extractErr = exportDockerImage("repository", "tag", bufio.NewWriter(pipeWriter))
		// needs to be closed, or io.Copy from the other end will be stuck forever
		pipeWriter.Close()
		wg.Done()
	}()

	io.Copy(bufio.NewWriter(imageBuf2), bufio.NewReader(pipeReader))
	wg.Wait()
}

func TestExtractDockerImage(t *testing.T) {
	testContainerID := "b99a1defb349"
	testTARBase64 := "H4sICJJWblgAA2FyY2hpdmUudGFyAO2VUQ7CIAxA+fYUvcFaCuw86Fhi5tS4mez4wrbol0uWCGrG++lPQ0sfpLYQ0UHEUmsYo5kiSjXFGSBGZqVlyQRIUkkpQMdvTYh719ubb6WpLq09d83xTZ5Pq+uFc+Z7POOfYIt99Bewwj+jUcG/UZz9pyD4ryLX8PMwSi34J/36/0Z6/4xEAjByXyMb9+8G215PDno39LtvN5NJzuHn9n857n/O+z8JLkGNNfufiIN/jVqAXJr3p9i4/0wms10eO0nUugAQAAA="
	testTARBase64Buffer := bytes.NewBufferString(testTARBase64)
	testTARBuffer := bytes.NewBuffer([]byte{})

	dec := base64.NewDecoder(base64.StdEncoding, testTARBase64Buffer)
	gunzip, err := gzip.NewReader(dec)
	assert.Nil(t, err)
	_, err = io.Copy(testTARBuffer, gunzip)
	assert.Nil(t, err)

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "POST":
			if r.URL.Path == "/containers/create" {
				response := struct {
					ID       string `json:"Id"`
					Warnings []string
				}{ID: testContainerID}
				json.NewEncoder(w).Encode(&response)
				return
			}
			break
		case "GET":
			if r.URL.Path == fmt.Sprintf("/containers/%s/export", testContainerID) {
				w.Header().Add("Content-Type", "application/octet-stream")
				w.Write(testTARBuffer.Bytes())
				return
			}
			break
		case "DELETE":
			if r.URL.Path == fmt.Sprintf("/containers/%s", testContainerID) {
				w.WriteHeader(http.StatusNoContent)
				return
			}
			break
		}

		http.Error(w, "Not found.", http.StatusNotFound)
		assert.Fail(t, "test tried to access a wrong path", "%s %+v", r.Method, r.URL)
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	os.Setenv("DOCKER_API_VERSION", "1.22")
	defer os.Setenv("DOCKER_HOST", "")
	defer os.Setenv("DOCKER_API_VERSION", "")

	tempDir, err := ioutil.TempDir("", "platconf-unittest-")
	assert.Nil(t, err)
	defer os.RemoveAll(tempDir)

	err = extractDockerImage("repository", "tag", tempDir)
	assert.Nil(t, err)

	rootInfo, err := ioutil.ReadDir(tempDir)
	assert.Nil(t, err)
	assert.Len(t, rootInfo, 3)

	// 'a'
	assert.Equal(t, "a", rootInfo[0].Name())
	assert.True(t, rootInfo[0].IsDir())
	aInfo, err := ioutil.ReadDir(path.Join(tempDir, "a"))
	assert.Nil(t, err)
	assert.Len(t, aInfo, 1)

	// 'a/b'
	assert.Equal(t, "b", aInfo[0].Name())
	assert.True(t, aInfo[0].IsDir())
	abInfo, err := ioutil.ReadDir(path.Join(tempDir, "a", "b"))
	assert.Nil(t, err)
	assert.Len(t, abInfo, 1)

	// 'a/b/d'
	assert.Equal(t, "d", abInfo[0].Name())
	assert.False(t, abInfo[0].IsDir())
	dContent, err := ioutil.ReadFile(path.Join(tempDir, "a", "b", "d"))
	assert.Nil(t, err)
	assert.Equal(t, "example text\n", string(dContent))

	// 'c'
	assert.Equal(t, "c", rootInfo[1].Name())
	assert.True(t, rootInfo[1].IsDir())
	cInfo, err := ioutil.ReadDir(path.Join(tempDir, "c"))
	assert.Nil(t, err)
	assert.Len(t, cInfo, 0)

	// 'e'
	assert.Equal(t, "e", rootInfo[2].Name())
	assert.Equal(t, "Lrwxr-xr-x", rootInfo[2].Mode().String())
}
<commit_msg>fix checking symlink status in TestExtractDockerImage<commit_after>package update

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPullImage(t *testing.T) {
	// pull is OK
	msgStream1 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}`
	// pull errored
	msgStream2 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"error": "something bad happened"}`
	// msg stream is cut off
	msgStream3 := `{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "bar"}{"foo": "ba`

	testImageRepo := "quay.io/protonet/dummy"

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/images/create" {
			http.Error(w, "Not found.", http.StatusNotFound)
		}

		// https://docs.docker.com/engine/reference/api/docker_remote_api_v1.22/#/create-an-image
		query := r.URL.Query()
		fromImage := query.Get("fromImage")
		tag := query.Get("tag")

		if fromImage == "" || tag == "" {
			http.Error(w, "Missing parameter 'fromImage' or 'tag'", http.StatusBadRequest)
		}

		if fromImage != testImageRepo {
			http.Error(w, "Not found.", http.StatusNotFound)
		}

		switch tag {
		case "v1":
			_, err := w.Write([]byte(msgStream1))
			assert.Nil(t, err)
		case "v2":
			_, err := w.Write([]byte(msgStream2))
			assert.Nil(t, err)
		case "v3":
			_, err := w.Write([]byte(msgStream3))
			assert.Nil(t, err)
		default:
			http.Error(w, "Not found.", http.StatusNotFound)
		}
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	defer os.Setenv("DOCKER_HOST", "")

	// pull is OK
	err := pullImage(testImageRepo, "v1")
	assert.Nil(t, err)

	// pull errored
	err = pullImage(testImageRepo, "v2")
	assert.EqualError(t, err, "Docker error: something bad happened")

	// msg stream is cut off
	err = pullImage(testImageRepo, "v3")
	assert.Equal(t, err, io.ErrUnexpectedEOF)
}

// TestExportDockerImage tests the functionality of streaming the data
// from a image's rootfs by exporting a pristine container
func TestExportDockerImage(t *testing.T) {
	testContainerID := "b99a1defb349"
	testTARBase64 := "H4sICJJWblgAA2FyY2hpdmUudGFyAO2VUQ7CIAxA+fYUvcFaCuw86Fhi5tS4mez4wrbol0uWCGrG++lPQ0sfpLYQ0UHEUmsYo5kiSjXFGSBGZqVlyQRIUkkpQMdvTYh719ubb6WpLq09d83xTZ5Pq+uFc+Z7POOfYIt99Bewwj+jUcG/UZz9pyD4ryLX8PMwSi34J/36/0Z6/4xEAjByXyMb9+8G215PDno39LtvN5NJzuHn9n857n/O+z8JLkGNNfufiIN/jVqAXJr3p9i4/0wms10eO0nUugAQAAA="
	testTARBase64Buffer := bytes.NewBufferString(testTARBase64)
	testTARBuffer := bytes.NewBuffer([]byte{})

	dec := base64.NewDecoder(base64.StdEncoding, testTARBase64Buffer)
	gunzip, err := gzip.NewReader(dec)
	assert.Nil(t, err)
	_, err = io.Copy(testTARBuffer, gunzip)
	assert.Nil(t, err)

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "POST":
			if r.URL.Path == "/containers/create" {
				response := struct {
					ID       string `json:"Id"`
					Warnings []string
				}{ID: testContainerID}
				json.NewEncoder(w).Encode(&response)
				return
			}
			break
		case "GET":
			if r.URL.Path == fmt.Sprintf("/containers/%s/export", testContainerID) {
				w.Header().Add("Content-Type", "application/octet-stream")
				w.Write(testTARBuffer.Bytes())
				return
			}
			break
		case "DELETE":
			if r.URL.Path == fmt.Sprintf("/containers/%s", testContainerID) {
				w.WriteHeader(http.StatusNoContent)
				return
			}
			break
		}

		http.Error(w, "Not found.", http.StatusNotFound)
		assert.Fail(t, "test tried to access a wrong path", "%s %+v", r.Method, r.URL)
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	os.Setenv("DOCKER_API_VERSION", "1.22")
	defer os.Setenv("DOCKER_HOST", "")
	defer os.Setenv("DOCKER_API_VERSION", "")

	// direct export to buffer
	imageBuf := bytes.NewBuffer([]byte{})
	err = exportDockerImage("repository", "tag", imageBuf)
	assert.Nil(t, err)
	assert.EqualValues(t, testTARBuffer.Bytes(), imageBuf.Bytes())

	// export through pipe
	imageBuf2 := bytes.NewBuffer([]byte{})
	pipeReader, pipeWriter := io.Pipe()

	var wg sync.WaitGroup
	wg.Add(1)
	var extractErr error
	go func() {
		extractErr = exportDockerImage("repository", "tag", bufio.NewWriter(pipeWriter))
		// needs to be closed, or io.Copy from the other end will be stuck forever
		pipeWriter.Close()
		wg.Done()
	}()

	io.Copy(bufio.NewWriter(imageBuf2), bufio.NewReader(pipeReader))
	wg.Wait()
}

func TestExtractDockerImage(t *testing.T) {
	testContainerID := "b99a1defb349"
	testTARBase64 := "H4sICJJWblgAA2FyY2hpdmUudGFyAO2VUQ7CIAxA+fYUvcFaCuw86Fhi5tS4mez4wrbol0uWCGrG++lPQ0sfpLYQ0UHEUmsYo5kiSjXFGSBGZqVlyQRIUkkpQMdvTYh719ubb6WpLq09d83xTZ5Pq+uFc+Z7POOfYIt99Bewwj+jUcG/UZz9pyD4ryLX8PMwSi34J/36/0Z6/4xEAjByXyMb9+8G215PDno39LtvN5NJzuHn9n857n/O+z8JLkGNNfufiIN/jVqAXJr3p9i4/0wms10eO0nUugAQAAA="
	testTARBase64Buffer := bytes.NewBufferString(testTARBase64)
	testTARBuffer := bytes.NewBuffer([]byte{})

	dec := base64.NewDecoder(base64.StdEncoding, testTARBase64Buffer)
	gunzip, err := gzip.NewReader(dec)
	assert.Nil(t, err)
	_, err = io.Copy(testTARBuffer, gunzip)
	assert.Nil(t, err)

	var testHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "POST":
			if r.URL.Path == "/containers/create" {
				response := struct {
					ID       string `json:"Id"`
					Warnings []string
				}{ID: testContainerID}
				json.NewEncoder(w).Encode(&response)
				return
			}
			break
		case "GET":
			if r.URL.Path == fmt.Sprintf("/containers/%s/export", testContainerID) {
				w.Header().Add("Content-Type", "application/octet-stream")
				w.Write(testTARBuffer.Bytes())
				return
			}
			break
		case "DELETE":
			if r.URL.Path == fmt.Sprintf("/containers/%s", testContainerID) {
				w.WriteHeader(http.StatusNoContent)
				return
			}
			break
		}

		http.Error(w, "Not found.", http.StatusNotFound)
		assert.Fail(t, "test tried to access a wrong path", "%s %+v", r.Method, r.URL)
	}

	srv := httptest.NewServer(testHandler)

	os.Setenv("DOCKER_HOST", srv.URL)
	os.Setenv("DOCKER_API_VERSION", "1.22")
	defer os.Setenv("DOCKER_HOST", "")
	defer os.Setenv("DOCKER_API_VERSION", "")

	tempDir, err := ioutil.TempDir("", "platconf-unittest-")
	assert.Nil(t, err)
	defer os.RemoveAll(tempDir)

	err = extractDockerImage("repository", "tag", tempDir)
	assert.Nil(t, err)

	rootInfo, err := ioutil.ReadDir(tempDir)
	assert.Nil(t, err)
	assert.Len(t, rootInfo, 3)

	// 'a'
	assert.Equal(t, "a", rootInfo[0].Name())
	assert.True(t, rootInfo[0].IsDir())
	aInfo, err := ioutil.ReadDir(path.Join(tempDir, "a"))
	assert.Nil(t, err)
	assert.Len(t, aInfo, 1)

	// 'a/b'
	assert.Equal(t, "b", aInfo[0].Name())
	assert.True(t, aInfo[0].IsDir())
	abInfo, err := ioutil.ReadDir(path.Join(tempDir, "a", "b"))
	assert.Nil(t, err)
	assert.Len(t, abInfo, 1)

	// 'a/b/d'
	assert.Equal(t, "d", abInfo[0].Name())
	assert.False(t, abInfo[0].IsDir())
	dContent, err := ioutil.ReadFile(path.Join(tempDir, "a", "b", "d"))
	assert.Nil(t, err)
	assert.Equal(t, "example text\n", string(dContent))

	// 'c'
	assert.Equal(t, "c", rootInfo[1].Name())
	assert.True(t, rootInfo[1].IsDir())
	cInfo, err := ioutil.ReadDir(path.Join(tempDir, "c"))
	assert.Nil(t, err)
	assert.Len(t, cInfo, 0)

	// 'e'
	assert.Equal(t, "e", rootInfo[2].Name())
	assert.NotZero(t, rootInfo[2].Mode()|os.ModeSymlink, "the file 'e' is not a symlink")
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"time"

	"github.com/hyperledger/fabric/gossip/common"
)

// SecurityAdvisor defines an external auxiliary object
// that provides security and identity related capabilities
type SecurityAdvisor interface {
	// IsInMyOrg returns whether the given peer's certificate represents
	// a peer in the invoker's organization
	IsInMyOrg(PeerIdentityType) bool

	// Verify verifies a JoinChannelMessage, returns nil on success,
	// and an error on failure
	Verify(JoinChannelMessage) error
}

// ChannelNotifier is implemented by the gossip component and is used for the peer
// layer to notify the gossip component of a JoinChannel event
type ChannelNotifier interface {
	JoinChannel(joinMsg JoinChannelMessage, chainID common.ChainID)
}

// JoinChannelMessage is the message that asserts a creation or mutation
// of a channel's membership list, and is the message that is gossipped
// among the peers
type JoinChannelMessage interface {

	// GetTimestamp returns the timestamp of the message's creation
	GetTimestamp() time.Time

	// Members returns all the peers that are in the channel
	Members() []ChannelMember
}

// ChannelMember is a peer's certificate and endpoint (host:port)
type ChannelMember struct {
	Cert PeerIdentityType // PeerIdentityType defines the certificate of the remote peer
	Host string           // Host is the hostname/ip address of the remote peer
	Port int              // Port is the port the remote peer is listening on
}
<commit_msg>FAB-1018 MultiChannel API fabric<-->gossip<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"time"

	"github.com/hyperledger/fabric/gossip/common"
)

// SecurityAdvisor defines an external auxiliary object
// that provides security and identity related capabilities
type SecurityAdvisor interface {
	// OrgByPeerIdentity returns the OrgIdentityType
	// of a given peer identity
	OrgByPeerIdentity(PeerIdentityType) OrgIdentityType

	// Verify verifies a JoinChannelMessage, returns nil on success,
	// and an error on failure
	Verify(JoinChannelMessage) error

}

// ChannelNotifier is implemented by the gossip component and is used for the peer
// layer to notify the gossip component of a JoinChannel event
type ChannelNotifier interface {
	JoinChannel(joinMsg JoinChannelMessage, chainID common.ChainID)
}

// JoinChannelMessage is the message that asserts a creation or mutation
// of a channel's membership list, and is the message that is gossipped
// among the peers
type JoinChannelMessage interface {

	// GetTimestamp returns the timestamp of the message's creation
	GetTimestamp() time.Time

	// AnchorPeers returns all the anchor peers that are in the channel
	AnchorPeers() []AnchorPeer
}

// AnchorPeer is an anchor peer's certificate and endpoint (host:port)
type AnchorPeer struct {
	Cert PeerIdentityType // Cert defines the certificate of the remote peer
	Host string           // Host is the hostname/ip address of the remote peer
	Port int              // Port is the port the remote peer is listening on
}

// OrgIdentityType defines the identity of an organization
type OrgIdentityType []byte
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements export filtering of an AST.

package doc

import (
	"go/ast"
	"go/token"
)

// filterIdentList removes unexported names from list in place
// and returns the resulting list.
//
func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident {
	j := 0
	for _, x := range list {
		if ast.IsExported(x.Name) || (blankOk && x.Name == "_") {
			list[j] = x
			j++
		}
	}
	return list[0:j]
}

// removeErrorField removes anonymous fields named "error" from an interface.
// This is called when "error" has been determined to be a local name,
// not the predeclared type.
//
func removeErrorField(ityp *ast.InterfaceType) {
	list := ityp.Methods.List // we know that ityp.Methods != nil
	j := 0
	for _, field := range list {
		keepField := true
		if n := len(field.Names); n == 0 {
			// anonymous field
			if fname, _ := baseTypeName(field.Type); fname == "error" {
				keepField = false
			}
		}
		if keepField {
			list[j] = field
			j++
		}
	}
	if j < len(list) {
		ityp.Incomplete = true
	}
	ityp.Methods.List = list[0:j]
}

// filterFieldList removes unexported fields (field names) from the field list
// in place and returns true if fields were removed. Anonymous fields are
// recorded with the parent type. filterType is called with the types of
// all remaining fields.
//
func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
	if fields == nil {
		return
	}
	list := fields.List
	j := 0
	for _, field := range list {
		keepField := false
		if n := len(field.Names); n == 0 {
			// anonymous field
			fname := r.recordAnonymousField(parent, field.Type)
			if ast.IsExported(fname) {
				keepField = true
			} else if ityp != nil && fname == "error" {
				// possibly the predeclared error interface; keep
				// it for now but remember this interface so that
				// it can be fixed if error is also defined locally
				keepField = true
				r.remember(ityp)
			}
		} else {
			field.Names = filterIdentList(field.Names, false)
			if len(field.Names) < n {
				removedFields = true
			}
			if len(field.Names) > 0 {
				keepField = true
			}
		}
		if keepField {
			r.filterType(nil, field.Type)
			list[j] = field
			j++
		}
	}
	if j < len(list) {
		removedFields = true
	}
	fields.List = list[0:j]
	return
}

// filterParamList applies filterType to each parameter type in fields.
//
func (r *reader) filterParamList(fields *ast.FieldList) {
	if fields != nil {
		for _, f := range fields.List {
			r.filterType(nil, f.Type)
		}
	}
}

// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
// struct or interface type has the Incomplete field set to true.
//
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
	switch t := typ.(type) {
	case *ast.Ident:
		// nothing to do
	case *ast.ParenExpr:
		r.filterType(nil, t.X)
	case *ast.ArrayType:
		r.filterType(nil, t.Elt)
	case *ast.StructType:
		if r.filterFieldList(parent, t.Fields, nil) {
			t.Incomplete = true
		}
	case *ast.FuncType:
		r.filterParamList(t.Params)
		r.filterParamList(t.Results)
	case *ast.InterfaceType:
		if r.filterFieldList(parent, t.Methods, t) {
			t.Incomplete = true
		}
	case *ast.MapType:
		r.filterType(nil, t.Key)
		r.filterType(nil, t.Value)
	case *ast.ChanType:
		r.filterType(nil, t.Value)
	}
}

func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
	switch s := spec.(type) {
	case *ast.ImportSpec:
		// always keep imports so we can collect them
		return true
	case *ast.ValueSpec:
		s.Names = filterIdentList(s.Names, tok == token.CONST)
		if len(s.Names) > 0 {
			r.filterType(nil, s.Type)
			return true
		}
	case *ast.TypeSpec:
		if name := s.Name.Name; ast.IsExported(name) {
			r.filterType(r.lookupType(s.Name.Name), s.Type)
			return true
		} else if name == "error" {
			// special case: remember that error is declared locally
			r.errorDecl = true
		}
	}
	return false
}

func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
	j := 0
	for _, s := range list {
		if r.filterSpec(s, tok) {
			list[j] = s
			j++
		}
	}
	return list[0:j]
}

func (r *reader) filterDecl(decl ast.Decl) bool {
	switch d := decl.(type) {
	case *ast.GenDecl:
		d.Specs = r.filterSpecList(d.Specs, d.Tok)
		return len(d.Specs) > 0
	case *ast.FuncDecl:
		// ok to filter these methods early because any
		// conflicting method will be filtered here, too -
		// thus, removing these methods early will not lead
		// to the false removal of possible conflicts
		return ast.IsExported(d.Name.Name)
	}
	return false
}

// fileExports removes unexported declarations from src in place.
//
func (r *reader) fileExports(src *ast.File) {
	j := 0
	for _, d := range src.Decls {
		if r.filterDecl(d) {
			src.Decls[j] = d
			j++
		}
	}
	src.Decls = src.Decls[0:j]
}
<commit_msg>go/doc: document rationale for recent change<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements export filtering of an AST.

package doc

import (
	"go/ast"
	"go/token"
)

// filterIdentList removes unexported names from list in place
// and returns the resulting list. If blankOk is set, blank
// identifiers are considered exported names.
//
func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident {
	j := 0
	for _, x := range list {
		if ast.IsExported(x.Name) || (blankOk && x.Name == "_") {
			list[j] = x
			j++
		}
	}
	return list[0:j]
}

// removeErrorField removes anonymous fields named "error" from an interface.
// This is called when "error" has been determined to be a local name,
// not the predeclared type.
//
func removeErrorField(ityp *ast.InterfaceType) {
	list := ityp.Methods.List // we know that ityp.Methods != nil
	j := 0
	for _, field := range list {
		keepField := true
		if n := len(field.Names); n == 0 {
			// anonymous field
			if fname, _ := baseTypeName(field.Type); fname == "error" {
				keepField = false
			}
		}
		if keepField {
			list[j] = field
			j++
		}
	}
	if j < len(list) {
		ityp.Incomplete = true
	}
	ityp.Methods.List = list[0:j]
}

// filterFieldList removes unexported fields (field names) from the field list
// in place and returns true if fields were removed. Anonymous fields are
// recorded with the parent type. filterType is called with the types of
// all remaining fields.
//
func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) {
	if fields == nil {
		return
	}
	list := fields.List
	j := 0
	for _, field := range list {
		keepField := false
		if n := len(field.Names); n == 0 {
			// anonymous field
			fname := r.recordAnonymousField(parent, field.Type)
			if ast.IsExported(fname) {
				keepField = true
			} else if ityp != nil && fname == "error" {
				// possibly the predeclared error interface; keep
				// it for now but remember this interface so that
				// it can be fixed if error is also defined locally
				keepField = true
				r.remember(ityp)
			}
		} else {
			field.Names = filterIdentList(field.Names, false)
			if len(field.Names) < n {
				removedFields = true
			}
			if len(field.Names) > 0 {
				keepField = true
			}
		}
		if keepField {
			r.filterType(nil, field.Type)
			list[j] = field
			j++
		}
	}
	if j < len(list) {
		removedFields = true
	}
	fields.List = list[0:j]
	return
}

// filterParamList applies filterType to each parameter type in fields.
//
func (r *reader) filterParamList(fields *ast.FieldList) {
	if fields != nil {
		for _, f := range fields.List {
			r.filterType(nil, f.Type)
		}
	}
}

// filterType strips any unexported struct fields or method types from typ
// in place. If fields (or methods) have been removed, the corresponding
// struct or interface type has the Incomplete field set to true.
//
func (r *reader) filterType(parent *namedType, typ ast.Expr) {
	switch t := typ.(type) {
	case *ast.Ident:
		// nothing to do
	case *ast.ParenExpr:
		r.filterType(nil, t.X)
	case *ast.ArrayType:
		r.filterType(nil, t.Elt)
	case *ast.StructType:
		if r.filterFieldList(parent, t.Fields, nil) {
			t.Incomplete = true
		}
	case *ast.FuncType:
		r.filterParamList(t.Params)
		r.filterParamList(t.Results)
	case *ast.InterfaceType:
		if r.filterFieldList(parent, t.Methods, t) {
			t.Incomplete = true
		}
	case *ast.MapType:
		r.filterType(nil, t.Key)
		r.filterType(nil, t.Value)
	case *ast.ChanType:
		r.filterType(nil, t.Value)
	}
}

func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
	switch s := spec.(type) {
	case *ast.ImportSpec:
		// always keep imports so we can collect them
		return true
	case *ast.ValueSpec:
		// special case: consider blank constants as exported
		// (work-around for issue 5397)
		s.Names = filterIdentList(s.Names, tok == token.CONST)
		if len(s.Names) > 0 {
			r.filterType(nil, s.Type)
			return true
		}
	case *ast.TypeSpec:
		if name := s.Name.Name; ast.IsExported(name) {
			r.filterType(r.lookupType(s.Name.Name), s.Type)
			return true
		} else if name == "error" {
			// special case: remember that error is declared locally
			r.errorDecl = true
		}
	}
	return false
}

func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
	j := 0
	for _, s := range list {
		if r.filterSpec(s, tok) {
			list[j] = s
			j++
		}
	}
	return list[0:j]
}

func (r *reader) filterDecl(decl ast.Decl) bool {
	switch d := decl.(type) {
	case *ast.GenDecl:
		d.Specs = r.filterSpecList(d.Specs, d.Tok)
		return len(d.Specs) > 0
	case *ast.FuncDecl:
		// ok to filter these methods early because any
		// conflicting method will be filtered here, too -
		// thus, removing these methods early will not lead
		// to the false removal of possible conflicts
		return ast.IsExported(d.Name.Name)
	}
	return false
}

// fileExports removes unexported declarations from src in place.
//
func (r *reader) fileExports(src *ast.File) {
	j := 0
	for _, d := range src.Decls {
		if r.filterDecl(d) {
			src.Decls[j] = d
			j++
		}
	}
	src.Decls = src.Decls[0:j]
}
<|endoftext|>


<commit_before>/*
 * Primitives are basic shapes which can be directly drawn to screen.
 *
 */

package graphics

import "image/color"
import "github.com/banthar/Go-SDL/sdl"

// A Primitive is a basic shape which can be drawn directly by the artist.
type Primitive interface {
	draw(s *sdl.Surface)
}

// A Point is as it sounds, a single point in space.
type Point struct {
	x, y int
	c    color.Color
}

// Points are drawn by setting a single corresponding pixel.
func (p Point) draw(s *sdl.Surface) {
	safeSet(s, p.x, p.y, p.c)
}

// A Rectangle is... a rectangle.
type Rectangle struct {
	x, y int16
	w, h uint16
	c    color.Color
}

// Rectangles are drawn by directly calling FillRect on the surface.
func (r Rectangle) draw(s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(r.c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)
	s.FillRect(&sdl.Rect{r.x, r.y, r.w, r.h}, colorVal)
}

// Circles are, you guessed it. Circles.
type Circle struct {
	x, y int16       // Location on screen
	r    uint16      // Radius
	b    int         // Border thickness. For now only controls if there IS a border or not, not actually it's thickness.
	c    color.Color // Color
}

// Circles may be filled or not.
func (c Circle) draw(s *sdl.Surface) {
	if c.b == 0 {
		drawFilledCircle(c.x, c.y, c.r, c.c, s)
	} else {
		drawOutlineCircle(c.x, c.y, c.r, c.c, s)
	}
}

// drawFilledCircle uses the integer midpoint circle algorithm to draw a filled
// circle to the given surface.
func drawFilledCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		s.FillRect(&sdl.Rect{-x + x0, y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-x + x0, -y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, x + y0, uint16(2 * y), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, -x + y0, uint16(2 * y), 1}, colorVal)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

// drawOutlineCircle uses the integer midpoint circle algorithm to draw the outline
// of a circle (1 px thick) to the given surface.
func drawOutlineCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	s.Lock()
	defer s.Unlock()

	color := sdl.ColorFromGoColor(c)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		safeSet(s, int(x+x0), int(y+y0), color)
		safeSet(s, int(x+x0), int(-y+y0), color)
		safeSet(s, int(-x+x0), int(y+y0), color)
		safeSet(s, int(-x+x0), int(-y+y0), color)
		safeSet(s, int(y+x0), int(x+y0), color)
		safeSet(s, int(y+x0), int(-x+y0), color)
		safeSet(s, int(-y+x0), int(x+y0), color)
		safeSet(s, int(-y+x0), int(-x+y0), color)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

func safeSet(s *sdl.Surface, x, y int, c sdl.Color) {
	if x >= 0 && y >= 0 && x < int(s.W) && y < int(s.H) {
		s.Set(x, y, c)
	}
}
<commit_msg>Fixed bug in primitive drawing. Was using wrong type of color.<commit_after>/*
 * Primitives are basic shapes which can be directly drawn to screen.
 *
 */

package graphics

import "image/color"
import "github.com/banthar/Go-SDL/sdl"

// A Primitive is a basic shape which can be drawn directly by the artist.
type Primitive interface {
	draw(s *sdl.Surface)
}

// A Point is as it sounds, a single point in space.
type Point struct {
	x, y int
	c    color.Color
}

// Points are drawn by setting a single corresponding pixel.
func (p Point) draw(s *sdl.Surface) {
	color := sdl.ColorFromGoColor(p.c)
	safeSet(s, p.x, p.y, color)
}

// A Rectangle is... a rectangle.
type Rectangle struct {
	x, y int16
	w, h uint16
	c    color.Color
}

// Rectangles are drawn by directly calling FillRect on the surface.
func (r Rectangle) draw(s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(r.c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)
	s.FillRect(&sdl.Rect{r.x, r.y, r.w, r.h}, colorVal)
}

// Circles are, you guessed it. Circles.
type Circle struct {
	x, y int16       // Location on screen
	r    uint16      // Radius
	b    int         // Border thickness. For now only controls if there IS a border or not, not actually it's thickness.
	c    color.Color // Color
}

// Circles may be filled or not.
func (c Circle) draw(s *sdl.Surface) {
	if c.b == 0 {
		drawFilledCircle(c.x, c.y, c.r, c.c, s)
	} else {
		drawOutlineCircle(c.x, c.y, c.r, c.c, s)
	}
}

// drawFilledCircle uses the integer midpoint circle algorithm to draw a filled
// circle to the given surface.
func drawFilledCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	format := sdl.GetVideoInfo().Vfmt
	color := sdl.ColorFromGoColor(c)
	colorVal := sdl.MapRGB(format, color.R, color.G, color.B)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		s.FillRect(&sdl.Rect{-x + x0, y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-x + x0, -y + y0, uint16(2 * x), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, x + y0, uint16(2 * y), 1}, colorVal)
		s.FillRect(&sdl.Rect{-y + x0, -x + y0, uint16(2 * y), 1}, colorVal)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

// drawOutlineCircle uses the integer midpoint circle algorithm to draw the outline
// of a circle (1 px thick) to the given surface.
func drawOutlineCircle(x0, y0 int16, r uint16, c color.Color, s *sdl.Surface) {
	s.Lock()
	defer s.Unlock()

	color := sdl.ColorFromGoColor(c)

	x := int16(r)
	y := int16(0)
	e := 1 - x

	for x >= y {
		safeSet(s, int(x+x0), int(y+y0), color)
		safeSet(s, int(x+x0), int(-y+y0), color)
		safeSet(s, int(-x+x0), int(y+y0), color)
		safeSet(s, int(-x+x0), int(-y+y0), color)
		safeSet(s, int(y+x0), int(x+y0), color)
		safeSet(s, int(y+x0), int(-x+y0), color)
		safeSet(s, int(-y+x0), int(x+y0), color)
		safeSet(s, int(-y+x0), int(-x+y0), color)

		y++

		if e < 0 {
			e += 2*y + 1
		} else {
			x--
			e += 2 * (y - x + 1)
		}
	}
}

func safeSet(s *sdl.Surface, x, y int, c sdl.Color) {
	if x >= 0 && y >= 0 && x < int(s.W) && y < int(s.H) {
		s.Set(x, y, c)
	}
}
<|endoftext|>