<commit_before>package sarama

import (
	"errors"
	"math/rand"
)

// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics,
// brokers, configurations and ACLs. The minimum broker version required is 0.10.0.0.
// Methods with stricter requirements will specify the minimum broker version required.
// You MUST call Close() on a client to avoid leaks
type ClusterAdmin interface {
	// Creates a new topic. This operation is supported by brokers with version 0.10.1.0 or higher.
	// It may take several seconds after CreateTopic returns success for all the brokers
	// to become aware that the topic has been created. During this time, listTopics
	// may not return information about the new topic.The validateOnly option is supported from version 0.10.2.0.
	CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error

	// List the topics available in the cluster with the default options.
	ListTopics() (map[string]TopicDetail, error)

	// Delete a topic. It may take several seconds after the DeleteTopic to returns success
	// and for all the brokers to become aware that the topics are gone.
	// During this time, listTopics  may continue to return information about the deleted topic.
	// If delete.topic.enable is false on the brokers, deleteTopic will mark
	// the topic for deletion, but not actually delete them.
	// This operation is supported by brokers with version 0.10.1.0 or higher.
	DeleteTopic(topic string) error

	// Increase the number of partitions of the topics  according to the corresponding values.
	// If partitions are increased for a topic that has a key, the partition logic or ordering of
	// the messages will be affected. It may take several seconds after this method returns
	// success for all the brokers to become aware that the partitions have been created.
	// During this time, ClusterAdmin#describeTopics may not return information about the
	// new partitions. This operation is supported by brokers with version 1.0.0 or higher.
	CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error

	// Delete records whose offset is smaller than the given offset of the corresponding partition.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DeleteRecords(topic string, partitionOffsets map[int32]int64) error

	// Get the configuration for the specified resources.
	// The returned configuration includes default values and the Default is true
	// can be used to distinguish them from user supplied values.
	// Config entries where ReadOnly is true cannot be updated.
	// The value of config entries where Sensitive is true is always nil so
	// sensitive information is not disclosed.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DescribeConfig(resource ConfigResource) ([]ConfigEntry, error)

	// Update the configuration for the specified resources with the default options.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	// The resources with their configs (topic is the only resource type with configs
	// that can be updated currently Updates are not transactional so they may succeed
	// for some resources while fail for others. The configs for a particular resource are updated automatically.
	AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error

	// Creates access control lists (ACLs) which are bound to specific resources.
	// This operation is not transactional so it may succeed for some ACLs while fail for others.
	// If you attempt to add an ACL that duplicates an existing ACL, no error will be raised, but
	// no changes will be made. This operation is supported by brokers with version 0.11.0.0 or higher.
	CreateACL(resource Resource, acl Acl) error

	// Lists access control lists (ACLs) according to the supplied filter.
	// it may take some time for changes made by createAcls or deleteAcls to be reflected in the output of ListAcls
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	ListAcls(filter AclFilter) ([]ResourceAcls, error)

	// Deletes access control lists (ACLs) according to the supplied filters.
	// This operation is not transactional so it may succeed for some ACLs while fail for others.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error)

	// Close shuts down the admin and closes underlying client.
	Close() error
}

type clusterAdmin struct {
	client Client
	conf   *Config
}

// NewClusterAdmin creates a new ClusterAdmin using the given broker addresses and configuration.
func NewClusterAdmin(addrs []string, conf *Config) (ClusterAdmin, error) {
	client, err := NewClient(addrs, conf)
	if err != nil {
		return nil, err
	}

	//make sure we can retrieve the controller
	_, err = client.Controller()
	if err != nil {
		return nil, err
	}

	ca := &clusterAdmin{
		client: client,
		conf:   client.Config(),
	}
	return ca, nil
}

func (ca *clusterAdmin) Close() error {
	return ca.client.Close()
}

func (ca *clusterAdmin) Controller() (*Broker, error) {
	return ca.client.Controller()
}

func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	if detail == nil {
		return errors.New("You must specify topic details")
	}

	topicDetails := make(map[string]*TopicDetail)
	topicDetails[topic] = detail

	request := &CreateTopicsRequest{
		TopicDetails: topicDetails,
		ValidateOnly: validateOnly,
		Timeout:      ca.conf.Admin.Timeout,
	}

	if ca.conf.Version.IsAtLeast(V0_11_0_0) {
		request.Version = 1
	}
	if ca.conf.Version.IsAtLeast(V1_0_0_0) {
		request.Version = 2
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.CreateTopics(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicErrors[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr.Err != ErrNoError {
		return topicErr.Err
	}

	return nil
}

func (ca *clusterAdmin) findAnyBroker() (*Broker, error) {
	brokers := ca.client.Brokers()
	if len(brokers) > 0 {
		index := rand.Intn(len(brokers))
		return brokers[index], nil
	}
	return nil, errors.New("no available broker")
}

func (ca *clusterAdmin) ListTopics() (map[string]TopicDetail, error) {
	// In order to build TopicDetails we need to first get the list of all
	// topics using a MetadataRequest and then get their configs using a
	// DescribeConfigsRequest request. To avoid sending many requests to the
	// broker, we use a single DescribeConfigsRequest.

	// Send the all-topic MetadataRequest
	b, err := ca.findAnyBroker()
	if err != nil {
		return nil, err
	}

	metadataReq := &MetadataRequest{}
	metadataResp, err := b.GetMetadata(metadataReq)
	if err != nil {
		return nil, err
	}

	topicsDetailsMap := make(map[string]TopicDetail)

	var describeConfigsResources []*ConfigResource

	for _, topic := range metadataResp.Topics {
		topicDetails := TopicDetail{
			NumPartitions: int32(len(topic.Partitions)),
		}
		topicsDetailsMap[topic.Name] = topicDetails

		// we populate the resources we want to describe from the MetadataResponse
		topicResource := ConfigResource{
			Type: TopicResource,
			Name: topic.Name,
		}
		describeConfigsResources = append(describeConfigsResources, &topicResource)
	}

	// Send the DescribeConfigsRequest
	describeConfigsReq := &DescribeConfigsRequest{
		Resources: describeConfigsResources,
	}
	describeConfigsResp, err := b.DescribeConfigs(describeConfigsReq)
	if err != nil {
		return nil, err
	}

	for _, resource := range describeConfigsResp.Resources {
		topicDetails := topicsDetailsMap[resource.Name]
		topicDetails.ConfigEntries = make(map[string]*string)

		for _, entry := range resource.Configs {
			// only include non-default non-sensitive config
			// (don't actually think topic config will ever be sensitive)
			if entry.Default || entry.Sensitive {
				continue
			}
			topicDetails.ConfigEntries[entry.Name] = &entry.Value
		}

		topicsDetailsMap[resource.Name] = topicDetails
	}

	return topicsDetailsMap, nil
}

func (ca *clusterAdmin) DeleteTopic(topic string) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	request := &DeleteTopicsRequest{
		Topics:  []string{topic},
		Timeout: ca.conf.Admin.Timeout,
	}

	if ca.conf.Version.IsAtLeast(V0_11_0_0) {
		request.Version = 1
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.DeleteTopics(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicErrorCodes[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr != ErrNoError {
		return topicErr
	}
	return nil
}

func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error {
	if topic == "" {
		return ErrInvalidTopic
	}

	topicPartitions := make(map[string]*TopicPartition)
	topicPartitions[topic] = &TopicPartition{Count: count, Assignment: assignment}

	request := &CreatePartitionsRequest{
		TopicPartitions: topicPartitions,
		Timeout:         ca.conf.Admin.Timeout,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.CreatePartitions(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicPartitionErrors[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr.Err != ErrNoError {
		return topicErr.Err
	}

	return nil
}

func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	topics := make(map[string]*DeleteRecordsRequestTopic)
	topics[topic] = &DeleteRecordsRequestTopic{PartitionOffsets: partitionOffsets}
	request := &DeleteRecordsRequest{
		Topics:  topics,
		Timeout: ca.conf.Admin.Timeout,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.DeleteRecords(request)
	if err != nil {
		return err
	}

	_, ok := rsp.Topics[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	//todo since we are dealing with couple of partitions it would be good if we return slice of errors
	//for each partition instead of one error
	return nil
}

func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) {

	var entries []ConfigEntry
	var resources []*ConfigResource
	resources = append(resources, &resource)

	request := &DescribeConfigsRequest{
		Resources: resources,
	}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DescribeConfigs(request)
	if err != nil {
		return nil, err
	}

	for _, rspResource := range rsp.Resources {
		if rspResource.Name == resource.Name {
			if rspResource.ErrorMsg != "" {
				return nil, errors.New(rspResource.ErrorMsg)
			}
			for _, cfgEntry := range rspResource.Configs {
				entries = append(entries, *cfgEntry)
			}
		}
	}
	return entries, nil
}

func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error {

	var resources []*AlterConfigsResource
	resources = append(resources, &AlterConfigsResource{
		Type:          resourceType,
		Name:          name,
		ConfigEntries: entries,
	})

	request := &AlterConfigsRequest{
		Resources:    resources,
		ValidateOnly: validateOnly,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.AlterConfigs(request)
	if err != nil {
		return err
	}

	for _, rspResource := range rsp.Resources {
		if rspResource.Name == name {
			if rspResource.ErrorMsg != "" {
				return errors.New(rspResource.ErrorMsg)
			}
		}
	}
	return nil
}

func (ca *clusterAdmin) CreateACL(resource Resource, acl Acl) error {
	var acls []*AclCreation
	acls = append(acls, &AclCreation{resource, acl})
	request := &CreateAclsRequest{AclCreations: acls}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	_, err = b.CreateAcls(request)
	return err
}

func (ca *clusterAdmin) ListAcls(filter AclFilter) ([]ResourceAcls, error) {

	request := &DescribeAclsRequest{AclFilter: filter}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DescribeAcls(request)
	if err != nil {
		return nil, err
	}

	var lAcls []ResourceAcls
	for _, rAcl := range rsp.ResourceAcls {
		lAcls = append(lAcls, *rAcl)
	}
	return lAcls, nil
}

func (ca *clusterAdmin) DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) {
	var filters []*AclFilter
	filters = append(filters, &filter)
	request := &DeleteAclsRequest{Filters: filters}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DeleteAcls(request)
	if err != nil {
		return nil, err
	}

	var mAcls []MatchingAcl
	for _, fr := range rsp.FilterResponses {
		for _, mACL := range fr.MatchingAcls {
			mAcls = append(mAcls, *mACL)
		}

	}
	return mAcls, nil
}
<commit_msg>Add missing broker Open() call<commit_after>package sarama

import (
	"errors"
	"math/rand"
)

// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics,
// brokers, configurations and ACLs. The minimum broker version required is 0.10.0.0.
// Methods with stricter requirements will specify the minimum broker version required.
// You MUST call Close() on a client to avoid leaks
type ClusterAdmin interface {
	// Creates a new topic. This operation is supported by brokers with version 0.10.1.0 or higher.
	// It may take several seconds after CreateTopic returns success for all the brokers
	// to become aware that the topic has been created. During this time, listTopics
	// may not return information about the new topic.The validateOnly option is supported from version 0.10.2.0.
	CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error

	// List the topics available in the cluster with the default options.
	ListTopics() (map[string]TopicDetail, error)

	// Delete a topic. It may take several seconds after the DeleteTopic to returns success
	// and for all the brokers to become aware that the topics are gone.
	// During this time, listTopics  may continue to return information about the deleted topic.
	// If delete.topic.enable is false on the brokers, deleteTopic will mark
	// the topic for deletion, but not actually delete them.
	// This operation is supported by brokers with version 0.10.1.0 or higher.
	DeleteTopic(topic string) error

	// Increase the number of partitions of the topics  according to the corresponding values.
	// If partitions are increased for a topic that has a key, the partition logic or ordering of
	// the messages will be affected. It may take several seconds after this method returns
	// success for all the brokers to become aware that the partitions have been created.
	// During this time, ClusterAdmin#describeTopics may not return information about the
	// new partitions. This operation is supported by brokers with version 1.0.0 or higher.
	CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error

	// Delete records whose offset is smaller than the given offset of the corresponding partition.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DeleteRecords(topic string, partitionOffsets map[int32]int64) error

	// Get the configuration for the specified resources.
	// The returned configuration includes default values and the Default is true
	// can be used to distinguish them from user supplied values.
	// Config entries where ReadOnly is true cannot be updated.
	// The value of config entries where Sensitive is true is always nil so
	// sensitive information is not disclosed.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DescribeConfig(resource ConfigResource) ([]ConfigEntry, error)

	// Update the configuration for the specified resources with the default options.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	// The resources with their configs (topic is the only resource type with configs
	// that can be updated currently Updates are not transactional so they may succeed
	// for some resources while fail for others. The configs for a particular resource are updated automatically.
	AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error

	// Creates access control lists (ACLs) which are bound to specific resources.
	// This operation is not transactional so it may succeed for some ACLs while fail for others.
	// If you attempt to add an ACL that duplicates an existing ACL, no error will be raised, but
	// no changes will be made. This operation is supported by brokers with version 0.11.0.0 or higher.
	CreateACL(resource Resource, acl Acl) error

	// Lists access control lists (ACLs) according to the supplied filter.
	// it may take some time for changes made by createAcls or deleteAcls to be reflected in the output of ListAcls
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	ListAcls(filter AclFilter) ([]ResourceAcls, error)

	// Deletes access control lists (ACLs) according to the supplied filters.
	// This operation is not transactional so it may succeed for some ACLs while fail for others.
	// This operation is supported by brokers with version 0.11.0.0 or higher.
	DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error)

	// Close shuts down the admin and closes underlying client.
	Close() error
}

type clusterAdmin struct {
	client Client
	conf   *Config
}

// NewClusterAdmin creates a new ClusterAdmin using the given broker addresses and configuration.
func NewClusterAdmin(addrs []string, conf *Config) (ClusterAdmin, error) {
	client, err := NewClient(addrs, conf)
	if err != nil {
		return nil, err
	}

	//make sure we can retrieve the controller
	_, err = client.Controller()
	if err != nil {
		return nil, err
	}

	ca := &clusterAdmin{
		client: client,
		conf:   client.Config(),
	}
	return ca, nil
}

func (ca *clusterAdmin) Close() error {
	return ca.client.Close()
}

func (ca *clusterAdmin) Controller() (*Broker, error) {
	return ca.client.Controller()
}

func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	if detail == nil {
		return errors.New("You must specify topic details")
	}

	topicDetails := make(map[string]*TopicDetail)
	topicDetails[topic] = detail

	request := &CreateTopicsRequest{
		TopicDetails: topicDetails,
		ValidateOnly: validateOnly,
		Timeout:      ca.conf.Admin.Timeout,
	}

	if ca.conf.Version.IsAtLeast(V0_11_0_0) {
		request.Version = 1
	}
	if ca.conf.Version.IsAtLeast(V1_0_0_0) {
		request.Version = 2
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.CreateTopics(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicErrors[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr.Err != ErrNoError {
		return topicErr.Err
	}

	return nil
}

func (ca *clusterAdmin) findAnyBroker() (*Broker, error) {
	brokers := ca.client.Brokers()
	if len(brokers) > 0 {
		index := rand.Intn(len(brokers))
		return brokers[index], nil
	}
	return nil, errors.New("no available broker")
}

func (ca *clusterAdmin) ListTopics() (map[string]TopicDetail, error) {
	// In order to build TopicDetails we need to first get the list of all
	// topics using a MetadataRequest and then get their configs using a
	// DescribeConfigsRequest request. To avoid sending many requests to the
	// broker, we use a single DescribeConfigsRequest.

	// Send the all-topic MetadataRequest
	b, err := ca.findAnyBroker()
	if err != nil {
		return nil, err
	}
	b.Open(ca.client.Config())

	metadataReq := &MetadataRequest{}
	metadataResp, err := b.GetMetadata(metadataReq)
	if err != nil {
		return nil, err
	}

	topicsDetailsMap := make(map[string]TopicDetail)

	var describeConfigsResources []*ConfigResource

	for _, topic := range metadataResp.Topics {
		topicDetails := TopicDetail{
			NumPartitions: int32(len(topic.Partitions)),
		}
		topicsDetailsMap[topic.Name] = topicDetails

		// we populate the resources we want to describe from the MetadataResponse
		topicResource := ConfigResource{
			Type: TopicResource,
			Name: topic.Name,
		}
		describeConfigsResources = append(describeConfigsResources, &topicResource)
	}

	// Send the DescribeConfigsRequest
	describeConfigsReq := &DescribeConfigsRequest{
		Resources: describeConfigsResources,
	}
	describeConfigsResp, err := b.DescribeConfigs(describeConfigsReq)
	if err != nil {
		return nil, err
	}

	for _, resource := range describeConfigsResp.Resources {
		topicDetails := topicsDetailsMap[resource.Name]
		topicDetails.ConfigEntries = make(map[string]*string)

		for _, entry := range resource.Configs {
			// only include non-default non-sensitive config
			// (don't actually think topic config will ever be sensitive)
			if entry.Default || entry.Sensitive {
				continue
			}
			topicDetails.ConfigEntries[entry.Name] = &entry.Value
		}

		topicsDetailsMap[resource.Name] = topicDetails
	}

	return topicsDetailsMap, nil
}

func (ca *clusterAdmin) DeleteTopic(topic string) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	request := &DeleteTopicsRequest{
		Topics:  []string{topic},
		Timeout: ca.conf.Admin.Timeout,
	}

	if ca.conf.Version.IsAtLeast(V0_11_0_0) {
		request.Version = 1
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.DeleteTopics(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicErrorCodes[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr != ErrNoError {
		return topicErr
	}
	return nil
}

func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error {
	if topic == "" {
		return ErrInvalidTopic
	}

	topicPartitions := make(map[string]*TopicPartition)
	topicPartitions[topic] = &TopicPartition{Count: count, Assignment: assignment}

	request := &CreatePartitionsRequest{
		TopicPartitions: topicPartitions,
		Timeout:         ca.conf.Admin.Timeout,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.CreatePartitions(request)
	if err != nil {
		return err
	}

	topicErr, ok := rsp.TopicPartitionErrors[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	if topicErr.Err != ErrNoError {
		return topicErr.Err
	}

	return nil
}

func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error {

	if topic == "" {
		return ErrInvalidTopic
	}

	topics := make(map[string]*DeleteRecordsRequestTopic)
	topics[topic] = &DeleteRecordsRequestTopic{PartitionOffsets: partitionOffsets}
	request := &DeleteRecordsRequest{
		Topics:  topics,
		Timeout: ca.conf.Admin.Timeout,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.DeleteRecords(request)
	if err != nil {
		return err
	}

	_, ok := rsp.Topics[topic]
	if !ok {
		return ErrIncompleteResponse
	}

	//todo since we are dealing with couple of partitions it would be good if we return slice of errors
	//for each partition instead of one error
	return nil
}

func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) {

	var entries []ConfigEntry
	var resources []*ConfigResource
	resources = append(resources, &resource)

	request := &DescribeConfigsRequest{
		Resources: resources,
	}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DescribeConfigs(request)
	if err != nil {
		return nil, err
	}

	for _, rspResource := range rsp.Resources {
		if rspResource.Name == resource.Name {
			if rspResource.ErrorMsg != "" {
				return nil, errors.New(rspResource.ErrorMsg)
			}
			for _, cfgEntry := range rspResource.Configs {
				entries = append(entries, *cfgEntry)
			}
		}
	}
	return entries, nil
}

func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error {

	var resources []*AlterConfigsResource
	resources = append(resources, &AlterConfigsResource{
		Type:          resourceType,
		Name:          name,
		ConfigEntries: entries,
	})

	request := &AlterConfigsRequest{
		Resources:    resources,
		ValidateOnly: validateOnly,
	}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	rsp, err := b.AlterConfigs(request)
	if err != nil {
		return err
	}

	for _, rspResource := range rsp.Resources {
		if rspResource.Name == name {
			if rspResource.ErrorMsg != "" {
				return errors.New(rspResource.ErrorMsg)
			}
		}
	}
	return nil
}

func (ca *clusterAdmin) CreateACL(resource Resource, acl Acl) error {
	var acls []*AclCreation
	acls = append(acls, &AclCreation{resource, acl})
	request := &CreateAclsRequest{AclCreations: acls}

	b, err := ca.Controller()
	if err != nil {
		return err
	}

	_, err = b.CreateAcls(request)
	return err
}

func (ca *clusterAdmin) ListAcls(filter AclFilter) ([]ResourceAcls, error) {

	request := &DescribeAclsRequest{AclFilter: filter}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DescribeAcls(request)
	if err != nil {
		return nil, err
	}

	var lAcls []ResourceAcls
	for _, rAcl := range rsp.ResourceAcls {
		lAcls = append(lAcls, *rAcl)
	}
	return lAcls, nil
}

func (ca *clusterAdmin) DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) {
	var filters []*AclFilter
	filters = append(filters, &filter)
	request := &DeleteAclsRequest{Filters: filters}

	b, err := ca.Controller()
	if err != nil {
		return nil, err
	}

	rsp, err := b.DeleteAcls(request)
	if err != nil {
		return nil, err
	}

	var mAcls []MatchingAcl
	for _, fr := range rsp.FilterResponses {
		for _, mACL := range fr.MatchingAcls {
			mAcls = append(mAcls, *mACL)
		}

	}
	return mAcls, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"log"
	"strings"
	"time"

	docker_client "github.com/moby/moby/client"
)

// CreateAgentOptions represents the agent Create() options
type CreateAgentOptions struct {
	Hostname string   `json:"hostname"`
	Labels   []string `json:"labels,omitempty"`
}

// PingAgentOptions represents the agent Ping() options
type PingAgentOptions struct {
	RunningTasks     []string `json:"running_tasks,omitempty"`
	RunningTerminals []string `json:"running_terminals,omitempty"`
}

// Agent describes the agent
type Agent struct {
	Client       *Client
	DockerClient *docker_client.Client
	ID           string   `json:"aid"`
	PingURL      string   `json:"ping_url"`
	TTL          int      `json:"ttl"`
	Hostname     string   `json:"hostname"`
	Labels       []string `json:"labels"`
}

func makeLabels(labels string) []string {
	return strings.Split(labels, ",")
}

// Create an agent
func (agent *Agent) Create(opt *CreateAgentOptions) error {
	status, err := agent.Client.Post("run/agents/", opt, &agent)
	if err != nil {
		return err
	}
	if status != 200 {
		return fmt.Errorf("Failed to create agent http code: %d", status)
	}
	log.Printf("Agent %s registered with hostname %s\n", agent.ID, agent.Hostname)
	return nil
}

func (agent *Agent) infinitePing() {
	for {
		err := agent.Ping()
		if err != nil {
			log.Printf("Cannot ping %+v", err)
			log.Println("Trying to register lost agent")
			newAgentOpts := CreateAgentOptions{
				Hostname: agent.Hostname,
				Labels:   agent.Labels,
			}
			agent.Create(&newAgentOpts)
			agent.syncDockerContainers(syncDockerContainersOptions{})
			info, err := agent.DockerClient.Info(context.Background())
			if err != nil {
				log.Printf("Cannot fetch docker info %+v", err)
			} else {
				agent.syncDockerInfo(info)
			}

		}
		time.Sleep(3 * time.Second)
	}
}

// Ping agent
func (agent *Agent) Ping() error {
	pingURI := fmt.Sprintf("run/agents/%s/ping/", agent.ID)
	opts := PingAgentOptions{
		RunningTasks: runningTasksList,
	}
	for t := range runningTerminalsList {
		opts.RunningTerminals = append(opts.RunningTerminals, t.Task.ID)
	}
	status, err := agent.Client.Post(pingURI, opts, nil)
	if err != nil {
		return err
	}
	if status != 200 {
		return fmt.Errorf("Ping to %s returns %d\n", pingURI, status)
	}
	log.Printf("Ping OK (%d running task%s, %d running terminal%s)", len(opts.RunningTasks), pluralize(len(opts.RunningTasks)), len(opts.RunningTerminals), pluralize(len(opts.RunningTerminals)))
	return nil
}

func (agent *Agent) ackTask(task *Task, taskACK *TaskACK) error {
	ackURI := fmt.Sprintf("run/agents/%s/tasks/unack/%s/", agent.ID, task.ID)
	_, err := agent.Client.Delete(ackURI, &taskACK, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (agent *Agent) infinitePullTasks() {
	for {
		tasks, err := agent.pullTasks()
		if err != nil {
			log.Println(err)
		}
		if len(tasks) > 0 {
			log.Printf("Got %d new tasks", len(tasks))
		}
		for _, task := range tasks {
			if task.NeedACK {
				runningTasksList = append(runningTasksList, task.ID)
			}
			ackTask, err := task.Do()
			if err != nil {
				log.Printf("Unable to do task %s: %s", task.ID, err)
			}
			log.Printf("task %s done!", task.ID)
			if task.NeedACK {
				err := agent.ackTask(&task, &ackTask)
				if err != nil {
					log.Printf("Unable to ack task %s: %s", task.ID, err)
				} else {
					log.Printf("task %s ACKed", task.ID)
				}
				deleteRunningTasks(task.ID)
			}
		}
		time.Sleep(3 * time.Second)
	}
}

func (agent *Agent) pullTasks() ([]Task, error) {
	tasksURI := fmt.Sprintf("run/agents/%s/tasks/ready/?requeue=false&size=50", agent.ID)
	tasks := []Task{}
	err := agent.Client.Get(tasksURI, &tasks)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	return tasks, nil
}
<commit_msg>ping with agent local time<commit_after>package main

import (
	"context"
	"fmt"
	"log"
	"strings"
	"time"

	docker_client "github.com/moby/moby/client"
)

// CreateAgentOptions represents the agent Create() options
type CreateAgentOptions struct {
	Hostname  string   `json:"hostname"`
	Labels    []string `json:"labels,omitempty"`
	Localtime int32    `json:"localtime"`
}

// PingAgentOptions represents the agent Ping() options
type PingAgentOptions struct {
	RunningTasks     []string `json:"running_tasks,omitempty"`
	RunningTerminals []string `json:"running_terminals,omitempty"`
	Localtime        int32    `json:"localtime"`
}

// Agent describes the agent
type Agent struct {
	Client       *Client
	DockerClient *docker_client.Client
	ID           string   `json:"aid"`
	PingURL      string   `json:"ping_url"`
	TTL          int      `json:"ttl"`
	Hostname     string   `json:"hostname"`
	Labels       []string `json:"labels"`
	Localtime    int32    `json:"localtime"`
}

func localtime() int32 {
	return int32(time.Now().Unix())
}

func makeLabels(labels string) []string {
	return strings.Split(labels, ",")
}

// Create an agent
func (agent *Agent) Create(opt *CreateAgentOptions) error {
	status, err := agent.Client.Post("run/agents/", opt, &agent)
	if err != nil {
		return err
	}
	if status != 200 {
		return fmt.Errorf("Failed to create agent http code: %d", status)
	}
	log.Printf("Agent %s registered with hostname %s\n", agent.ID, agent.Hostname)
	return nil
}

func (agent *Agent) infinitePing() {
	for {
		err := agent.Ping()
		if err != nil {
			log.Printf("Cannot ping %+v", err)
			log.Println("Trying to register lost agent")
			newAgentOpts := CreateAgentOptions{
				Hostname:  agent.Hostname,
				Labels:    agent.Labels,
				Localtime: localtime(),
			}
			agent.Create(&newAgentOpts)
			agent.syncDockerContainers(syncDockerContainersOptions{})
			info, err := agent.DockerClient.Info(context.Background())
			if err != nil {
				log.Printf("Cannot fetch docker info %+v", err)
			} else {
				agent.syncDockerInfo(info)
			}

		}
		time.Sleep(3 * time.Second)
	}
}

// Ping agent
func (agent *Agent) Ping() error {
	pingURI := fmt.Sprintf("run/agents/%s/ping/", agent.ID)
	opts := PingAgentOptions{
		RunningTasks: runningTasksList,
		Localtime:    localtime(),
	}
	for t := range runningTerminalsList {
		opts.RunningTerminals = append(opts.RunningTerminals, t.Task.ID)
	}
	status, err := agent.Client.Post(pingURI, opts, nil)
	if err != nil {
		return err
	}
	if status != 200 {
		return fmt.Errorf("Ping to %s returns %d\n", pingURI, status)
	}
	log.Printf("Ping OK (%d running task%s, %d running terminal%s)", len(opts.RunningTasks), pluralize(len(opts.RunningTasks)), len(opts.RunningTerminals), pluralize(len(opts.RunningTerminals)))
	return nil
}

func (agent *Agent) ackTask(task *Task, taskACK *TaskACK) error {
	ackURI := fmt.Sprintf("run/agents/%s/tasks/unack/%s/", agent.ID, task.ID)
	_, err := agent.Client.Delete(ackURI, &taskACK, nil)
	if err != nil {
		log.Println(err)
		return err
	}
	return nil
}

func (agent *Agent) infinitePullTasks() {
	for {
		tasks, err := agent.pullTasks()
		if err != nil {
			log.Println(err)
		}
		if len(tasks) > 0 {
			log.Printf("Got %d new tasks", len(tasks))
		}
		for _, task := range tasks {
			if task.NeedACK {
				runningTasksList = append(runningTasksList, task.ID)
			}
			ackTask, err := task.Do()
			if err != nil {
				log.Printf("Unable to do task %s: %s", task.ID, err)
			}
			log.Printf("task %s done!", task.ID)
			if task.NeedACK {
				err := agent.ackTask(&task, &ackTask)
				if err != nil {
					log.Printf("Unable to ack task %s: %s", task.ID, err)
				} else {
					log.Printf("task %s ACKed", task.ID)
				}
				deleteRunningTasks(task.ID)
			}
		}
		time.Sleep(3 * time.Second)
	}
}

func (agent *Agent) pullTasks() ([]Task, error) {
	tasksURI := fmt.Sprintf("run/agents/%s/tasks/ready/?requeue=false&size=50", agent.ID)
	tasks := []Task{}
	err := agent.Client.Get(tasksURI, &tasks)
	if err != nil {
		log.Println(err)
		return nil, err
	}
	return tasks, nil
}
<|endoftext|>


<commit_before>package gorelic

import (
	"errors"
	"fmt"
	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/newrelic_platform_go"
	"log"
	"net/http"
)

const (
	// DefaultNewRelicPollInterval - how often we will report metrics to NewRelic.
	// Recommended values is 60 seconds
	DefaultNewRelicPollInterval = 60

	// DefaultGcPollIntervalInSeconds - how often we will get garbage collector run statistic
	// Default value is - every 10 seconds
	// During GC stat pooling - mheap will be locked, so be carefull changing this value
	DefaultGcPollIntervalInSeconds = 10

	// DefaultMemoryAllocatorPollIntervalInSeconds - how often we will get memory allocator statistic.
	// Default value is - every 60 seconds
	// During this process stoptheword() is called, so be carefull changing this value
	DefaultMemoryAllocatorPollIntervalInSeconds = 60

	//DefaultAgentGuid is plugin ID in NewRelic.
	//You should not change it unless you want to create your own plugin.
	DefaultAgentGuid = "com.github.yvasiyarov.GoRelic"

	//CurrentAgentVersion is plugin version
	CurrentAgentVersion = "0.0.6"

	//DefaultAgentName in NewRelic GUI. You can change it.
	DefaultAgentName = "Go daemon"
)

//Agent - is NewRelic agent implementation.
//Agent start separate go routine which will report data to NewRelic
type Agent struct {
	NewrelicName                string
	NewrelicLicense             string
	NewrelicPollInterval        int
	Verbose                     bool
	CollectGcStat               bool
	CollectMemoryStat           bool
	CollectHTTPStat             bool
	GCPollInterval              int
	MemoryAllocatorPollInterval int
	AgentGUID                   string
	AgentVersion                string
	plugin                      *newrelic_platform_go.NewrelicPlugin
	HTTPTimer                   metrics.Timer
	Tracer                      *Tracer
}

//NewAgent build new Agent objects.
func NewAgent() *Agent {
	agent := &Agent{
		NewrelicName:                DefaultAgentName,
		NewrelicPollInterval:        DefaultNewRelicPollInterval,
		Verbose:                     false,
		CollectGcStat:               true,
		CollectMemoryStat:           true,
		GCPollInterval:              DefaultGcPollIntervalInSeconds,
		MemoryAllocatorPollInterval: DefaultMemoryAllocatorPollIntervalInSeconds,
		AgentGUID:                   DefaultAgentGuid,
		AgentVersion:                CurrentAgentVersion,
		Tracer:                      nil,
	}
	return agent
}

//WrapHTTPHandlerFunc  instrument HTTP handler functions to collect HTTP metrics
func (agent *Agent) WrapHTTPHandlerFunc(h tHTTPHandlerFunc) tHTTPHandlerFunc {
	agent.initTimer()
	return func(w http.ResponseWriter, req *http.Request) {
		proxy := newHTTPHandlerFunc(h)
		proxy.timer = agent.HTTPTimer
		proxy.ServeHTTP(w, req)
	}
}

//WrapHTTPHandler  instrument HTTP handler object to collect HTTP metrics
func (agent *Agent) WrapHTTPHandler(h http.Handler) http.Handler {
	agent.initTimer()

	proxy := newHTTPHandler(h)
	proxy.timer = agent.HTTPTimer
	return proxy
}

//Run initialize Agent instance and start harvest go routine
func (agent *Agent) Run() error {
	if agent.NewrelicLicense == "" {
		return errors.New("please, pass a valid newrelic license key")
	}

	agent.plugin = newrelic_platform_go.NewNewrelicPlugin(agent.AgentVersion, agent.NewrelicLicense, agent.NewrelicPollInterval)
	component := newrelic_platform_go.NewPluginComponent(agent.NewrelicName, agent.AgentGUID)
	agent.plugin.AddComponent(component)

	addRuntimeMericsToComponent(component)

	agent.Tracer = newTracer(component)

	if agent.CollectGcStat {
		addGCMericsToComponent(component, agent.GCPollInterval)
		agent.debug(fmt.Sprintf("Init GC metrics collection. Poll interval %d seconds.", agent.GCPollInterval))
	}
	if agent.CollectMemoryStat {
		addMemoryMericsToComponent(component, agent.MemoryAllocatorPollInterval)
		agent.debug(fmt.Sprintf("Init memory allocator metrics collection. Poll interval %d seconds.", agent.MemoryAllocatorPollInterval))
	}

	if agent.CollectHTTPStat {
		agent.initTimer()
		addHTTPMericsToComponent(component, agent.HTTPTimer)
		agent.debug(fmt.Sprintf("Init HTTP metrics collection."))
	}

	agent.plugin.Verbose = agent.Verbose
	go agent.plugin.Run()
	return nil
}

//Initialize global metrics.Timer object, used to collect HTTP metrics
func (agent *Agent) initTimer() {
	if agent.HTTPTimer == nil {
		agent.HTTPTimer = metrics.NewTimer()
	}

	agent.CollectHTTPStat = true
}

//Print debug messages
func (agent *Agent) debug(msg string) {
	if agent.Verbose {
		log.Println(msg)
	}
}
<commit_msg>Added support for HTTP proxies<commit_after>package gorelic

import (
	"errors"
	"fmt"
	"log"
	"net/http"

	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/newrelic_platform_go"
)

const (
	// DefaultNewRelicPollInterval - how often we will report metrics to NewRelic.
	// Recommended values is 60 seconds
	DefaultNewRelicPollInterval = 60

	// DefaultGcPollIntervalInSeconds - how often we will get garbage collector run statistic
	// Default value is - every 10 seconds
	// During GC stat pooling - mheap will be locked, so be carefull changing this value
	DefaultGcPollIntervalInSeconds = 10

	// DefaultMemoryAllocatorPollIntervalInSeconds - how often we will get memory allocator statistic.
	// Default value is - every 60 seconds
	// During this process stoptheword() is called, so be carefull changing this value
	DefaultMemoryAllocatorPollIntervalInSeconds = 60

	//DefaultAgentGuid is plugin ID in NewRelic.
	//You should not change it unless you want to create your own plugin.
	DefaultAgentGuid = "com.github.yvasiyarov.GoRelic"

	//CurrentAgentVersion is plugin version
	CurrentAgentVersion = "0.0.6"

	//DefaultAgentName in NewRelic GUI. You can change it.
	DefaultAgentName = "Go daemon"
)

//Agent - is NewRelic agent implementation.
//Agent start separate go routine which will report data to NewRelic
type Agent struct {
	NewrelicName                string
	NewrelicLicense             string
	NewrelicPollInterval        int
	Verbose                     bool
	CollectGcStat               bool
	CollectMemoryStat           bool
	CollectHTTPStat             bool
	GCPollInterval              int
	MemoryAllocatorPollInterval int
	AgentGUID                   string
	AgentVersion                string
	plugin                      *newrelic_platform_go.NewrelicPlugin
	HTTPTimer                   metrics.Timer
	Tracer                      *Tracer

	// All HTTP requests will be done using this client. Change it if you need
	// to use a proxy.
	Client http.Client
}

//NewAgent build new Agent objects.
func NewAgent() *Agent {
	agent := &Agent{
		NewrelicName:                DefaultAgentName,
		NewrelicPollInterval:        DefaultNewRelicPollInterval,
		Verbose:                     false,
		CollectGcStat:               true,
		CollectMemoryStat:           true,
		GCPollInterval:              DefaultGcPollIntervalInSeconds,
		MemoryAllocatorPollInterval: DefaultMemoryAllocatorPollIntervalInSeconds,
		AgentGUID:                   DefaultAgentGuid,
		AgentVersion:                CurrentAgentVersion,
		Tracer:                      nil,
	}
	return agent
}

//WrapHTTPHandlerFunc  instrument HTTP handler functions to collect HTTP metrics
func (agent *Agent) WrapHTTPHandlerFunc(h tHTTPHandlerFunc) tHTTPHandlerFunc {
	agent.initTimer()
	return func(w http.ResponseWriter, req *http.Request) {
		proxy := newHTTPHandlerFunc(h)
		proxy.timer = agent.HTTPTimer
		proxy.ServeHTTP(w, req)
	}
}

//WrapHTTPHandler  instrument HTTP handler object to collect HTTP metrics
func (agent *Agent) WrapHTTPHandler(h http.Handler) http.Handler {
	agent.initTimer()

	proxy := newHTTPHandler(h)
	proxy.timer = agent.HTTPTimer
	return proxy
}

//Run initialize Agent instance and start harvest go routine
func (agent *Agent) Run() error {
	if agent.NewrelicLicense == "" {
		return errors.New("please, pass a valid newrelic license key")
	}

	agent.plugin = newrelic_platform_go.NewNewrelicPlugin(agent.AgentVersion, agent.NewrelicLicense, agent.NewrelicPollInterval)
	agent.plugin.Client = agent.Client
	component := newrelic_platform_go.NewPluginComponent(agent.NewrelicName, agent.AgentGUID)
	agent.plugin.AddComponent(component)

	addRuntimeMericsToComponent(component)

	agent.Tracer = newTracer(component)

	if agent.CollectGcStat {
		addGCMericsToComponent(component, agent.GCPollInterval)
		agent.debug(fmt.Sprintf("Init GC metrics collection. Poll interval %d seconds.", agent.GCPollInterval))
	}
	if agent.CollectMemoryStat {
		addMemoryMericsToComponent(component, agent.MemoryAllocatorPollInterval)
		agent.debug(fmt.Sprintf("Init memory allocator metrics collection. Poll interval %d seconds.", agent.MemoryAllocatorPollInterval))
	}

	if agent.CollectHTTPStat {
		agent.initTimer()
		addHTTPMericsToComponent(component, agent.HTTPTimer)
		agent.debug(fmt.Sprintf("Init HTTP metrics collection."))
	}

	agent.plugin.Verbose = agent.Verbose
	go agent.plugin.Run()
	return nil
}

//Initialize global metrics.Timer object, used to collect HTTP metrics
func (agent *Agent) initTimer() {
	if agent.HTTPTimer == nil {
		agent.HTTPTimer = metrics.NewTimer()
	}

	agent.CollectHTTPStat = true
}

//Print debug messages
func (agent *Agent) debug(msg string) {
	if agent.Verbose {
		log.Println(msg)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	log "github.com/Lafeng/deblocus/golang/glog"
	t "github.com/Lafeng/deblocus/tunnel"
	"os"
	"os/signal"
	"syscall"
)

var context = &bootContext{}
var sigChan = make(chan os.Signal)

func waitSignal() {
	USR2 := syscall.Signal(12) // fake signal-USR2 for windows
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, USR2)
	for sig := range sigChan {
		switch sig {
		case t.Bye:
			log.Exitln("Exiting.")
			return
		case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM:
			log.Exitln("Terminated by", sig)
			return
		case USR2:
			context.doStats()
		default:
			log.Infoln("Ingore signal", sig)
		}
	}
}

func main() {
	var output, logDir string
	var showVersion bool
	flag.Usage = showUsage
	flag.StringVar(&context.config, "config", "", "indicate Config path if it in nontypical path")
	flag.StringVar(&output, "o", "", "output file")
	flag.BoolVar(&context.csc, "csc", false, "Server;;Create Server Config")
	flag.BoolVar(&context.ccc, "ccc", false, "Server;;Issue Client Credential for user//-ccc <ServerAddress:Port> <User1> <User2>...")
	flag.BoolVar(&context.isServ, "serv", false, "Server;;run as Server explicitly")
	flag.BoolVar(&showVersion, "V", false, "show Version")
	flag.StringVar(&context.verbosity, "v", "", "Verbose log level")
	flag.StringVar(&logDir, "logdir", "", "write log into the directory")
	flag.BoolVar(&context.debug, "debug", false, "debug")
	flag.Parse()

	if showVersion {
		fmt.Println(versionString())
		return
	}

	context.parse()
	// toStd bool, logDir string
	log.Set_output(true, logDir)

	if context.csc {
		var rsaParam string
		switch flag.NArg() {
		case 0:
		case 1:
			rsaParam = flag.Arg(0)
		default:
			fmt.Println("unknown args")
		}
		t.Generate_d5sFile(output, rsaParam)
		return
	}

	if context.ccc {
		context.ccc_process(output)
		return
	}

	if context.isServ {
		go context.startServer()
	} else {
		go context.startClient()
	}
	waitSignal()
}
<commit_msg>ccc fix<commit_after>package main

import (
	"flag"
	"fmt"
	log "github.com/Lafeng/deblocus/golang/glog"
	t "github.com/Lafeng/deblocus/tunnel"
	"os"
	"os/signal"
	"syscall"
)

var context = &bootContext{}
var sigChan = make(chan os.Signal)

func waitSignal() {
	USR2 := syscall.Signal(12) // fake signal-USR2 for windows
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, USR2)
	for sig := range sigChan {
		switch sig {
		case t.Bye:
			log.Exitln("Exiting.")
			return
		case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM:
			log.Exitln("Terminated by", sig)
			return
		case USR2:
			context.doStats()
		default:
			log.Infoln("Ingore signal", sig)
		}
	}
}

func main() {
	var output, logDir string
	var showVersion bool
	flag.Usage = showUsage
	flag.StringVar(&context.config, "config", "", "indicate Config path if it in nontypical path")
	flag.StringVar(&output, "o", "", "output file")
	flag.BoolVar(&context.csc, "csc", false, "Server;;Create Server Config")
	flag.BoolVar(&context.ccc, "ccc", false, "Server;;Create Client Config for user//-ccc <ServerAddress:Port> <User1> <User2>...")
	flag.BoolVar(&context.isServ, "serv", false, "Server;;run as Server explicitly")
	flag.BoolVar(&showVersion, "V", false, "show Version")
	flag.StringVar(&context.verbosity, "v", "", "Verbose log level")
	flag.StringVar(&logDir, "logdir", "", "write log into the directory")
	flag.BoolVar(&context.debug, "debug", false, "debug")
	flag.Parse()

	if showVersion {
		fmt.Println(versionString())
		return
	}

	context.parse()
	// toStd bool, logDir string
	log.Set_output(true, logDir)

	if context.csc {
		var rsaParam string
		switch flag.NArg() {
		case 0:
		case 1:
			rsaParam = flag.Arg(0)
		default:
			fmt.Println("unknown args")
		}
		t.Generate_d5sFile(output, rsaParam)
		return
	}

	if context.ccc {
		context.ccc_process(output)
		return
	}

	if context.isServ {
		go context.startServer()
	} else {
		go context.startClient()
	}
	waitSignal()
}
<|endoftext|>


<commit_before>package battleship

import (
	"errors"
	"math/rand"
	"time"
)

const (
	AvailableSpace byte = 0
	HitSpace       byte = 1
	MissSpace      byte = 2
)

type Decision struct {
	shipGrid *GameGrid
	hitGrid  *GameGrid
}

func NewDecision(shipGrid, hitGrid *GameGrid) *Decision {
	return &Decision{shipGrid: shipGrid, hitGrid: hitGrid}
}

func (grid GameGrid) scoreYAxis(y, x uint8, inc int, scoreGrid *GameGrid, accessible bool) {
	ny := uint8(int(y) + inc)
	negY := uint8(int(y) + (-1 * inc))
	if grid[ny][x] == AvailableSpace {
		scoreGrid[ny][x]++
	}
	if accessible && grid[negY][x] == HitSpace {
		scoreGrid[ny][x] += 2
	}
}

func (grid GameGrid) scoreXAxis(y, x uint8, inc int, scoreGrid *GameGrid, accessible bool) {
	nx := uint8(int(x) + inc)
	negX := uint8(int(x) + (-1 * inc))
	if grid[y][nx] == AvailableSpace {
		scoreGrid[y][nx]++
	}
	if accessible && grid[y][negX] == HitSpace {
		scoreGrid[y][nx] += 2
	}
}

func (d Decision) Make() ([2]uint8, error) {
	var scoredGrid GameGrid
	var shipSpacesDiscovered uint8
	var result [2]uint8

	d.hitGrid.Iterate(func(y, x uint8, val *byte) {
		switch *val {
		case AvailableSpace:
			scoredGrid[y][x]++
		case HitSpace:
			shipSpacesDiscovered++
			scoredGrid[y][x] = 0
			if y < 9 {
				d.hitGrid.scoreYAxis(y, x, 1, &scoredGrid, y > 0)
			}
			if y > 0 {
				d.hitGrid.scoreYAxis(y, x, -1, &scoredGrid, y < 9)
			}
			if x < 9 {
				d.hitGrid.scoreXAxis(y, x, 1, &scoredGrid, x > 0)
			}
			if x > 0 {
				d.hitGrid.scoreXAxis(y, x, -1, &scoredGrid, x < 9)
			}
		default:
			scoredGrid[y][x] = 0
		}
	})

	ratedMoves := make([][2]uint8, 0)
	var highestScore uint8 = 0
	scoredGrid.Iterate(func(y, x uint8, val *byte) {
		if *val > highestScore {
			highestScore = scoredGrid[y][x]
			ratedMoves = make([][2]uint8, 0)                // Reset rated moves
			ratedMoves = append(ratedMoves, [2]uint8{y, x}) // Add to rated moves
		} else if *val == highestScore {
			ratedMoves = append(ratedMoves, [2]uint8{y, x}) // Add to rated moves
		}
	})

	if highestScore == 0 {
		return result, errors.New("No available moves")
	} else {
		rand.Seed(time.Now().UnixNano())
		result = ratedMoves[rand.Intn(len(ratedMoves))]
	}

	return result, nil
}
<commit_msg> Removed unused variable<commit_after>package battleship

import (
	"errors"
	"math/rand"
	"time"
)

const (
	AvailableSpace byte = 0
	HitSpace       byte = 1
	MissSpace      byte = 2
)

type Decision struct {
	shipGrid *GameGrid
	hitGrid  *GameGrid
}

func NewDecision(shipGrid, hitGrid *GameGrid) *Decision {
	return &Decision{shipGrid: shipGrid, hitGrid: hitGrid}
}

func (grid GameGrid) scoreYAxis(y, x uint8, inc int, scoreGrid *GameGrid, accessible bool) {
	ny := uint8(int(y) + inc)
	negY := uint8(int(y) + (-1 * inc))
	if grid[ny][x] == AvailableSpace {
		scoreGrid[ny][x]++
	}
	if accessible && grid[negY][x] == HitSpace {
		scoreGrid[ny][x] += 2
	}
}

func (grid GameGrid) scoreXAxis(y, x uint8, inc int, scoreGrid *GameGrid, accessible bool) {
	nx := uint8(int(x) + inc)
	negX := uint8(int(x) + (-1 * inc))
	if grid[y][nx] == AvailableSpace {
		scoreGrid[y][nx]++
	}
	if accessible && grid[y][negX] == HitSpace {
		scoreGrid[y][nx] += 2
	}
}

func (d Decision) Make() ([2]uint8, error) {
	var scoredGrid GameGrid
	var result [2]uint8

	d.hitGrid.Iterate(func(y, x uint8, val *byte) {
		switch *val {
		case AvailableSpace:
			scoredGrid[y][x]++
		case HitSpace:
			scoredGrid[y][x] = 0
			if y < 9 {
				d.hitGrid.scoreYAxis(y, x, 1, &scoredGrid, y > 0)
			}
			if y > 0 {
				d.hitGrid.scoreYAxis(y, x, -1, &scoredGrid, y < 9)
			}
			if x < 9 {
				d.hitGrid.scoreXAxis(y, x, 1, &scoredGrid, x > 0)
			}
			if x > 0 {
				d.hitGrid.scoreXAxis(y, x, -1, &scoredGrid, x < 9)
			}
		default:
			scoredGrid[y][x] = 0
		}
	})

	ratedMoves := make([][2]uint8, 0)
	var highestScore uint8 = 0
	scoredGrid.Iterate(func(y, x uint8, val *byte) {
		if *val > highestScore {
			highestScore = scoredGrid[y][x]
			ratedMoves = make([][2]uint8, 0)                // Reset rated moves
			ratedMoves = append(ratedMoves, [2]uint8{y, x}) // Add to rated moves
		} else if *val == highestScore {
			ratedMoves = append(ratedMoves, [2]uint8{y, x}) // Add to rated moves
		}
	})

	if highestScore == 0 {
		return result, errors.New("No available moves")
	} else {
		rand.Seed(time.Now().UnixNano())
		result = ratedMoves[rand.Intn(len(ratedMoves))]
	}

	return result, nil
}
<|endoftext|>


<commit_before>/*
agorn is a wrapper around gorename for use with Acme.
It renames the entity under the cursor.

Usage:
	agorn name

Example:
	agorn Foo
renames the entity under the cursor with 'Foo'.

gorename must be installed:
	% go get golang.org/x/tools/cmd/gorename
*/
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"code.google.com/p/goplan9/plan9/acme"
)

type bodyReader struct{ *acme.Win }

func (r bodyReader) Read(data []byte) (int, error) {
	return r.Win.Read("body", data)
}

type window struct {
	win    *acme.Win
	offset int
	name   string
}

func currentAcmeWin() (*acme.Win, error) {
	id, err := strconv.Atoi(os.Getenv("winid"))
	if err != nil {
		return nil, err
	}
	return acme.Open(id, nil)
}

func currentWindow() (*window, error) {
	win, err := currentAcmeWin()
	if err != nil {
		return nil, err
	}
	_, _, err = win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	err = win.Ctl("addr=dot")
	if err != nil {
		return nil, fmt.Errorf("cannot set addr=dot: %v", err)
	}
	q0, _, err := win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	b, err := win.ReadAll("tag")
	if err != nil {
		return nil, fmt.Errorf("cannot read tag: %v", err)
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return nil, fmt.Errorf("tag with no spaces")
	}
	off, err := byteOffset(bufio.NewReader(&bodyReader{win}), q0)
	if err != nil {
		return nil, fmt.Errorf("cannot read body: %v", err)
	}
	return &window{win: win, name: tag[0:i], offset: off}, nil
}

func byteOffset(r io.RuneReader, off int) (bo int, err error) {
	for i := 0; i != off; i++ {
		_, s, err := r.ReadRune()
		if err != nil {
			return 0, err
		}
		bo += s
	}
	return
}

func (w *window) showAddr(addr string) {
	w.win.Fprintf("addr", addr)
	w.win.Ctl("dot=addr\nshow")
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "agorn: %v", err)
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		fail(fmt.Errorf("usage: agorn name\n"))
	}
	to := os.Args[1]

	win, err := currentWindow()
	if err != nil {
		fail(err)
	}

	c := exec.Command("gorename", "-offset", fmt.Sprintf("%s:#%d", win.name, win.offset), "-to", to)
	b := new(bytes.Buffer)
	c.Stderr = b
	err = c.Run()
	if err != nil {
		fail(fmt.Errorf(b.String()))
	}

	win.win.Ctl("get")
	win.showAddr(fmt.Sprintf("#%d", win.offset))
}
<commit_msg>Rename functions.<commit_after>/*
agorn is a wrapper around gorename for use with Acme.
It renames the entity under the cursor.

Usage:
	agorn name

Example:
	agorn Foo
renames the entity under the cursor with 'Foo'.

gorename must be installed:
	% go get golang.org/x/tools/cmd/gorename
*/
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"code.google.com/p/goplan9/plan9/acme"
)

type bodyReader struct{ *acme.Win }

func (r bodyReader) Read(data []byte) (int, error) {
	return r.Win.Read("body", data)
}

type window struct {
	win    *acme.Win
	offset int
	name   string
}

func openWin() (*acme.Win, error) {
	id, err := strconv.Atoi(os.Getenv("winid"))
	if err != nil {
		return nil, err
	}
	return acme.Open(id, nil)
}

func selection() (*window, error) {
	win, err := openWin()
	if err != nil {
		return nil, err
	}
	_, _, err = win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	err = win.Ctl("addr=dot")
	if err != nil {
		return nil, fmt.Errorf("cannot set addr=dot: %v", err)
	}
	q0, _, err := win.ReadAddr()
	if err != nil {
		return nil, fmt.Errorf("cannot read address: %v", err)
	}
	b, err := win.ReadAll("tag")
	if err != nil {
		return nil, fmt.Errorf("cannot read tag: %v", err)
	}
	tag := string(b)
	i := strings.Index(tag, " ")
	if i == -1 {
		return nil, fmt.Errorf("tag with no spaces")
	}
	off, err := byteOffset(bufio.NewReader(&bodyReader{win}), q0)
	if err != nil {
		return nil, fmt.Errorf("cannot read body: %v", err)
	}
	return &window{win: win, name: tag[0:i], offset: off}, nil
}

func byteOffset(r io.RuneReader, off int) (bo int, err error) {
	for i := 0; i != off; i++ {
		_, s, err := r.ReadRune()
		if err != nil {
			return 0, err
		}
		bo += s
	}
	return
}

func (w *window) showAddr(addr string) {
	w.win.Fprintf("addr", addr)
	w.win.Ctl("dot=addr\nshow")
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "agorn: %v", err)
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		fail(fmt.Errorf("usage: agorn name\n"))
	}
	to := os.Args[1]

	win, err := selection()
	if err != nil {
		fail(err)
	}

	c := exec.Command("gorename", "-offset", fmt.Sprintf("%s:#%d", win.name, win.offset), "-to", to)
	b := new(bytes.Buffer)
	c.Stderr = b
	err = c.Run()
	if err != nil {
		fail(fmt.Errorf(b.String()))
	}

	win.win.Ctl("get")
	win.showAddr(fmt.Sprintf("#%d", win.offset))
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018 Johannes Kohnen <wjkohnen@users.noreply.github.com>
 *
 * 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 airac

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	format        = "2006-01-02"
	cycleDuration = 24192e11 // 4 weeks
)

var (
	epoch = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
)

// AIRAC represents an Aeronautical Information Regulation And Control (AIRAC) cycle.
type AIRAC uint16

type Airac = AIRAC

// Effective returns the effective date of this AIRAC cycle.
func (a AIRAC) Effective() time.Time {
	return epoch.Add(time.Duration(a) * cycleDuration)
}

// Year returns the year for this AIRAC cycle's identifier.
func (a AIRAC) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a AIRAC) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is
// year 2192.
func FromDate(date time.Time) AIRAC {
	a := date.Sub(epoch) / cycleDuration
	return AIRAC(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>,
// i.e. the last two digits of the year and the ordinal, each with leading
// zeros. This works for years between 1964 and 2063. Identifiers between
// "6401" and "9913" are interpreted as AIRAC cycles between the years 1964
// and 1999 inclusive. AIRAC cycles between "0001" and "6313" are
// interpreted as AIRAC cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (AIRAC, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return 0, err
	}

	lastAiracOfPreviousYear := FromDate(time.Date(year-1, time.December, 31, 0, 0, 0, 0, time.UTC))
	airac := lastAiracOfPreviousYear + AIRAC(ordinal)

	if airac.Year() != year {
		return 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	yyoo = strings.TrimSpace(yyoo)
	if len(yyoo) != 4 {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}
	yyooInt, err := strconv.Atoi(yyoo)
	if err != nil {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	year, ordinal = (yyooInt/100)+1900, yyooInt%100
	if year <= 1963 {
		year += 100
	}
	return year, ordinal, nil
}

// FromStringMust returns an AIRAC cycle that matches the identifier <yyoo>
// like FromString, but does not return an error. If there is an error it will
// panic instead.
func FromStringMust(yyoo string) AIRAC {
	airac, err := FromString(yyoo)
	if err != nil {
		panic(err)
	}
	return airac
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a AIRAC) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a AIRAC) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// ByChrono is an []AIRAC wrapper, that satisfies sort.Interface and can be used
// to chronologically sort AIRAC instances.
type ByChrono []AIRAC

// Len ist the number of elements in the collection.
func (c ByChrono) Len() int { return len(c) }

// Less reports whether the element with
// index i should sort before the element with index j.
func (c ByChrono) Less(i, j int) bool { return c[i] < c[j] }

// Swap swaps the elements with indexes i and j.
func (c ByChrono) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

// static assert
var _ sort.Interface = ByChrono{}
<commit_msg>Document Airac type alias<commit_after>/*
 * Copyright (c) 2018 Johannes Kohnen <wjkohnen@users.noreply.github.com>
 *
 * 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 airac

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	format        = "2006-01-02"
	cycleDuration = 24192e11 // 4 weeks
)

var (
	epoch = time.Date(1901, time.January, 10, 0, 0, 0, 0, time.UTC)
)

type (
	// AIRAC represents an Aeronautical Information Regulation And Control (AIRAC) cycle.
	AIRAC uint16

	// Airac is a deprecated alias to AIRAC.
	Airac = AIRAC
)

// Effective returns the effective date of this AIRAC cycle.
func (a AIRAC) Effective() time.Time {
	return epoch.Add(time.Duration(a) * cycleDuration)
}

// Year returns the year for this AIRAC cycle's identifier.
func (a AIRAC) Year() int {
	return a.Effective().Year()
}

// Ordinal returns the ordinal for this AIRAC cycle's identifier.
func (a AIRAC) Ordinal() int {
	return (a.Effective().YearDay()-1)/28 + 1
}

// FromDate returns the AIRAC cycle that occurred at date. A date before the
// internal epoch (1901-01-10) may return wrong data. The upper limit is
// year 2192.
func FromDate(date time.Time) AIRAC {
	a := date.Sub(epoch) / cycleDuration
	return AIRAC(a)
}

// FromString returns an AIRAC cycle that matches the identifier <yyoo>,
// i.e. the last two digits of the year and the ordinal, each with leading
// zeros. This works for years between 1964 and 2063. Identifiers between
// "6401" and "9913" are interpreted as AIRAC cycles between the years 1964
// and 1999 inclusive. AIRAC cycles between "0001" and "6313" are
// interpreted as AIRAC cycles between the years 2000 and 2063 inclusive.
func FromString(yyoo string) (AIRAC, error) {
	year, ordinal, err := parseIdentifier(yyoo)
	if err != nil {
		return 0, err
	}

	lastAiracOfPreviousYear := FromDate(time.Date(year-1, time.December, 31, 0, 0, 0, 0, time.UTC))
	airac := lastAiracOfPreviousYear + AIRAC(ordinal)

	if airac.Year() != year {
		return 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	return airac, nil
}

func parseIdentifier(yyoo string) (year, ordinal int, err error) {
	yyoo = strings.TrimSpace(yyoo)
	if len(yyoo) != 4 {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}
	yyooInt, err := strconv.Atoi(yyoo)
	if err != nil {
		return 0, 0, fmt.Errorf("illegal AIRAC id %q", yyoo)
	}

	year, ordinal = (yyooInt/100)+1900, yyooInt%100
	if year <= 1963 {
		year += 100
	}
	return year, ordinal, nil
}

// FromStringMust returns an AIRAC cycle that matches the identifier <yyoo>
// like FromString, but does not return an error. If there is an error it will
// panic instead.
func FromStringMust(yyoo string) AIRAC {
	airac, err := FromString(yyoo)
	if err != nil {
		panic(err)
	}
	return airac
}

// String returns a short representation of this AIRAC cycle. "YYOO"
func (a AIRAC) String() string {
	return fmt.Sprintf("%02d%02d", a.Year()%100, a.Ordinal())
}

// LongString returns a verbose representation of this AIRAC cycle.
// "YYOO (effective: YYYY-MM-DD; expires: YYYY-MM-DD)"
func (a AIRAC) LongString() string {
	n := a + 1
	return fmt.Sprintf("%02d%02d (effective: %s; expires: %s)",
		a.Year()%100,
		a.Ordinal(),
		a.Effective().Format(format),
		n.Effective().Add(-1).Format(format),
	)
}

// ByChrono is an []AIRAC wrapper, that satisfies sort.Interface and can be used
// to chronologically sort AIRAC instances.
type ByChrono []AIRAC

// Len ist the number of elements in the collection.
func (c ByChrono) Len() int { return len(c) }

// Less reports whether the element with
// index i should sort before the element with index j.
func (c ByChrono) Less(i, j int) bool { return c[i] < c[j] }

// Swap swaps the elements with indexes i and j.
func (c ByChrono) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

// static assert
var _ sort.Interface = ByChrono{}
<|endoftext|>


<commit_before>package monitor

import (
	"bufio"
	"fmt"
	"io"
	_ "io/ioutil"
	"log"
	"os"
	"os/exec"
	_ "regexp"
)

/* The Monitor interface defines a series of methods which will be defined on
 * monitor structs. The Start method takes a channel to send messages over,
 * back to the configurator. The Stop method kills the process which is
 * performing the actual monitoring.
 */
type Monitor interface {
	Start(messages chan<- []byte, dockerComposeName string)
	Stop()
}

/* Store the MonitorName, which is the name of the program to execute, the
 * DockerDirs which are the directories to monitor which our Docker containers
 * of interest live in, and a pointer to the exec.Cmd struct which describes
 * the running command.
 */
type FSMonitor struct {
	MonitorName   string
	DockerDirs    []string
	fsWatcherProc *exec.Cmd
}

/* This function is going to need some comments describing why we chose this
 * approach, because it will be hairy.
 * FIXME: Break out functionality into multiple functions so we can test this
 * easily!
 */
func (m FSMonitor) getDockerFSDirectory(dockerComposeName string) []string {
	dockerComposeCommand := exec.Command(dockerComposeName, "ps", "-q")
	outpipe, err := dockerComposeCommand.StdoutPipe()
	if err != nil {
		panic(err)
	}

	dockerComposeCommand.Start()
	defer dockerComposeCommand.Wait()

	ids := []string{}
	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		if string(line) != "" {
			ids = append(ids, string(line))
		}
	}

	/*
		dockerInfoCommand := exec.Command("docker", "info")
		infoOutPipe, err := dockerInfoCommand.StdoutPipe()

		if err != nil {
			panic(err)
		}

		dockerInfoCommand.Start()
		defer dockerInfoCommand.Wait()

		infoBuf, err := ioutil.ReadAll(infoOutPipe)

		if err != nil {
			panic(err)
		}

		info := string(infoBuf)

		re := regexp.MustCompile("Docker Root Dir: (.*)")

		submatch := re.FindStringSubmatch(info)

		if len(submatch) < 2 {
			fmt.Println(submatch, info)
			panic("Couldn't find the docker root directory")
		}
		dockerRootPath := submatch[1]
	*/
	dockerRootPath := "/var/lib/docker/aufs"

	for i := 0; i < len(ids); i++ {
		ids[i] = dockerRootPath + "/mnt/" + ids[i]
	}
	return ids
}

/* Start the process running on the honeypot host to monitor the Docker
 * container. The Docker container's filesysem is mounted on the host. Find
 * the location of this filesysem with the getDockerFSDirectory function and
 * store it in the struct. Then create and start the process and forward
 * the output of the process on to the messages channel.
 */
func (m FSMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	m.DockerDirs = m.getDockerFSDirectory(dockerComposeName)
	fmt.Println(m.MonitorName)
	m.fsWatcherProc = exec.Command(m.MonitorName, m.DockerDirs...)
	defer m.fsWatcherProc.Wait()

	outpipe, err := m.fsWatcherProc.StdoutPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stdout pipe")
		panic(err)
	}
	stderr, err := m.fsWatcherProc.StderrPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stderr pipe")
		panic(err)
	}
	go io.Copy(os.Stderr, stderr)

	m.fsWatcherProc.Start()

	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err != nil {
			panic(err)
		}
		sb := line
		messages <- sb

	}
}

/* Stop the filesystem monitor. Kill the process monitoring the Docker
 * container's filesysem.
 */
func (m FSMonitor) Stop() {
	err := m.fsWatcherProc.Process.Kill()
	if err != nil {
		log.Fatal("Could not kill ", m.MonitorName, err)
	}
}
<commit_msg>Initial implementation of network monitor<commit_after>package monitor

import (
	"bufio"
	"fmt"
	"io"
	_ "io/ioutil"
	"log"
	"os"
	"os/exec"
	_ "regexp"
)

/* The Monitor interface defines a series of methods which will be defined on
 * monitor structs. The Start method takes a channel to send messages over,
 * back to the configurator. The Stop method kills the process which is
 * performing the actual monitoring.
 */
type Monitor interface {
	Start(messages chan<- []byte, dockerComposeName string)
	Stop()
}

/* Store the MonitorName, which is the name of the program to execute, the
 * DockerDirs which are the directories to monitor which our Docker containers
 * of interest live in, and a pointer to the exec.Cmd struct which describes
 * the running command.
 */
type FSMonitor struct {
	MonitorName   string
	DockerDirs    []string
	fsWatcherProc *exec.Cmd
}

type NetMonitor struct {
	MonitorName string
	ContainerIds []string
}

// Memoize this. It's kind of expensive to get.
var dockerContainerIds = []string{}

func getDockerContainerIds(dockerComposeName string) []string {
	if len(dockerContainerIds) != 0 {
		return dockerContainerIds
	}
	dockerComposeCommand := exec.Command(dockerComposeName, "ps", "-q")
	outpipe, err := dockerComposeCommand.StdoutPipe()
	if err != nil {
		panic(err)
	}

	dockerComposeCommand.Start()
	defer dockerComposeCommand.Wait()

	ids := []string{}
	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		if string(line) != "" {
			ids = append(ids, string(line))
		}
	}
	dockerContainerIds = ids
	return ids
}

func (n NetMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	ids := getDockerContainerIds(dockerComposeName)
	arguments := []string{"inspect", "-f", "'{{ .State.Pid }}'"}
	arguments = append(arguments, ids...)
	dockerComposeCommand := exec.Command("docker", arguments...)
	outpipe, err := dockerComposeCommand.StdoutPipe()
	if err != nil {
		panic(err)
	}

	dockerComposeCommand.Start()
	defer dockerComposeCommand.Wait()

	procIds := []string{}
	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		if string(line) != "" {
			procIds = append(procIds, string(line))
			err = setSymlink(string(line), "/var/run/netns/")
			if err != nil {
				panic(err)
			}
			go startIPProcess(messages, string(line), "tcpdump", "")
		}
	}
}

func startIPProcess(messages chan<- []byte, procId string, watcherName string,
		watcherArgs... string) {
	arguments := []string{"netns", "exec", procId, watcherName}
	arguments = append(arguments, watcherArgs...)
	netWatcherCommand := exec.Command("ip", arguments...)
	outpipe, err := netWatcherCommand.StdoutPipe()
	if err != nil {
		panic(err)
	}

	netWatcherCommand.Start()
	defer netWatcherCommand.Wait()

	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		messages <- line
	}
}

func setSymlink(procId string, destination string) error {
	err := os.Symlink("/proc/" + procId + "/ns/net", "/var/run/netns/" + procId)
	return err
}

/* This function is going to need some comments describing why we chose this
 * approach, because it will be hairy.
 * FIXME: Break out functionality into multiple functions so we can test this
 * easily!
 */
func (m FSMonitor) getDockerFSDirectory(dockerComposeName string) []string {
	ids := getDockerContainerIds(dockerComposeName)
	/*
		dockerInfoCommand := exec.Command("docker", "info")
		infoOutPipe, err := dockerInfoCommand.StdoutPipe()

		if err != nil {
			panic(err)
		}

		dockerInfoCommand.Start()
		defer dockerInfoCommand.Wait()

		infoBuf, err := ioutil.ReadAll(infoOutPipe)

		if err != nil {
			panic(err)
		}

		info := string(infoBuf)

		re := regexp.MustCompile("Docker Root Dir: (.*)")

		submatch := re.FindStringSubmatch(info)

		if len(submatch) < 2 {
			fmt.Println(submatch, info)
			panic("Couldn't find the docker root directory")
		}
		dockerRootPath := submatch[1]
	*/
	dockerRootPath := "/var/lib/docker/aufs"

	for i := 0; i < len(ids); i++ {
		ids[i] = dockerRootPath + "/mnt/" + ids[i]
	}
	return ids
}

/* Start the process running on the honeypot host to monitor the Docker
 * container. The Docker container's filesysem is mounted on the host. Find
 * the location of this filesysem with the getDockerFSDirectory function and
 * store it in the struct. Then create and start the process and forward
 * the output of the process on to the messages channel.
 */
func (m FSMonitor) Start(messages chan<- []byte, dockerComposeName string) {
	m.DockerDirs = m.getDockerFSDirectory(dockerComposeName)
	fmt.Println(m.MonitorName)
	m.fsWatcherProc = exec.Command(m.MonitorName, m.DockerDirs...)
	defer m.fsWatcherProc.Wait()

	outpipe, err := m.fsWatcherProc.StdoutPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stdout pipe")
		panic(err)
	}
	stderr, err := m.fsWatcherProc.StderrPipe()
	if err != nil {
		log.Println("Could not open the ", m.MonitorName, "stderr pipe")
		panic(err)
	}
	go io.Copy(os.Stderr, stderr)

	m.fsWatcherProc.Start()

	stdoutReader := bufio.NewReader(outpipe)
	for {
		line, _, err := stdoutReader.ReadLine()
		if err != nil {
			panic(err)
		}
		sb := line
		messages <- sb

	}
}

/* Stop the filesystem monitor. Kill the process monitoring the Docker
 * container's filesysem.
 */
func (m FSMonitor) Stop() {
	err := m.fsWatcherProc.Process.Kill()
	if err != nil {
		log.Fatal("Could not kill ", m.MonitorName, err)
	}
}
<|endoftext|>


<commit_before>// Package alien is a lightweight http router from outer space.
package alien

import (
	"errors"
	"net/http"
	"path"
	"strings"
	"sync"
)

var (
	eof              = rune(0)
	errRouteNotFound = errors.New("route not found")
	errBadPattern    = errors.New("bad pattern")
	errUnknownMethod = errors.New("unkown http method")
	headerName       = "_alien"
)

type nodeType int

const (
	nodeRoot nodeType = iota
	nodeParam
	nodeNormal
	nodeCatchAll
	nodeEnd
)

type node struct {
	key      rune
	typ      nodeType
	mu       sync.RWMutex
	value    *route
	children []*node
}

func (n *node) branch(key rune, val *route, typ ...nodeType) *node {
	child := &node{
		key:   key,
		value: val,
	}
	if len(typ) > 0 {
		child.typ = typ[0]
	}
	n.children = append(n.children, child)
	return child
}

func (n *node) findChild(key rune) *node {
	for _, v := range n.children {
		if v.key == key {
			return v
		}
	}
	return nil
}

func (n *node) insert(pattern string, val *route) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.typ != nodeRoot {
		return errors.New("inserting on a non root node")
	}
	var level *node
	var child *node

	if pattern == "" {
		return errors.New("empty pattern is not supported")
	}

	for k, ch := range pattern {
		if k == 0 {
			if ch != '/' {
				return errors.New("path must start with slash ")
			}
			level = n
		}

		child = level.findChild(ch)
		switch level.typ {
		case nodeParam:
			if k < len(pattern) && ch != '/' {
				continue
			}
		}
		if child != nil {
			level = child
			continue
		}
		switch ch {
		case ':':
			level = level.branch(ch, nil, nodeParam)
		case '*':
			level = level.branch(ch, nil, nodeCatchAll)
		default:
			level = level.branch(ch, nil, nodeNormal)
		}
	}
	level.branch(eof, val, nodeEnd)
	return nil
}

func (n *node) find(path string) (*route, error) {
	n.mu.RLock()
	defer n.mu.RUnlock()
	if n.typ != nodeRoot {
		return nil, errors.New("non node search")
	}
	var level *node
	var isParam bool
	for k, ch := range path {
		if k == 0 {
			level = n
		}
		c := level.findChild(ch)
		if isParam {
			if k < len(path) && ch != '/' {
				continue
			}
			isParam = false
		}
		param := level.findChild(':')
		if param != nil {
			level = param
			isParam = true
			continue
		}
		catchAll := level.findChild('*')
		if catchAll != nil {
			level = catchAll
			break
		}
		if c != nil {
			level = c
			continue
		}
		return nil, errRouteNotFound
	}
	if level != nil {
		end := level.findChild(eof)
		if end != nil {
			return end.value, nil
		}
		if slash := level.findChild('/'); slash != nil {
			end = slash.findChild(eof)
			if end != nil {
				return end.value, nil
			}
		}
	}
	return nil, errRouteNotFound
}

type route struct {
	path       string
	middleware []func(http.Handler) http.Handler
	handler    func(http.ResponseWriter, *http.Request)
}

func (r *route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var base http.Handler
	if base == nil {
		base = http.HandlerFunc(r.handler)
	}
	for _, m := range r.middleware {
		base = m(base)
	}
	base.ServeHTTP(w, req)
}

// ParseParams parses params found in mateched from pattern. There are two kinds
// of params, one to capture a segment which starts with : and a nother to
// capture everything( a.k.a catch all) whis starts with *.
//
// For instance
//   pattern:="/hello/:name"
//   matched:="/hello/world"
// Will result into name:world. this function captures the named params and
// theri coreesponding values, returning them in a comma separated  string of a
// key:value nature. please see the tests for more details.
func ParseParams(matched, pattern string) (result string, err error) {
	if strings.Contains(pattern, ":") || strings.Contains(pattern, "*") {
		p1 := strings.Split(matched, "/")
		p2 := strings.Split(pattern, "/")
		s1 := len(p1)
		s2 := len(p2)
		if s1 < s2 {
			err = errBadPattern
			return
		}
		for k, v := range p2 {
			if len(v) > 0 {
				switch v[0] {
				case ':':
					if len(result) == 0 {
						result = v[1:] + ":" + p1[k]
						continue
					}
					result = result + "," + v[1:] + ":" + p1[k]
				case '*':
					name := "catch"
					if k != s2-1 {
						err = errBadPattern
						return
					}
					if len(v) > 1 {
						name = v[1:]
					}
					if len(result) == 0 {
						result = name + ":" + strings.Join(p1[k:], "/")
						return
					}
					result = result + "," + name + ":" + strings.Join(p1[k:], "/")
					return
				}
			}
		}
	}
	return
}

// Params stores route params.
type Params map[string]string

// Load loads params found in src into p.
func (p Params) Load(src string) {
	s := strings.Split(src, ",")
	var vars []string
	for _, v := range s {
		vars = strings.Split(v, ":")
		if len(vars) != 2 {
			continue
		}
		p[vars[0]] = vars[1]
	}
}

// Get returns value associated with key.
func (p Params) Get(key string) string {
	return p[key]
}

// GetParams returrns route params stored in r.
func GetParams(r *http.Request) Params {
	c := r.Header.Get(headerName)
	if c != "" {
		p := make(Params)
		p.Load(c)
		return p
	}
	return nil
}

type router struct {
	get, post, patch, put, head     *node
	connect, options, trace, delete *node
}

func (r *router) addRoute(method, path string, h func(http.ResponseWriter, *http.Request), wares ...func(http.Handler) http.Handler) error {
	newRoute := &route{path: path, handler: h}
	if len(wares) > 0 {
		newRoute.middleware = append(newRoute.middleware, wares...)
	}
	switch method {
	case "GET":
		if r.get == nil {
			r.get = &node{typ: nodeRoot}
		}
		return r.get.insert(path, newRoute)
	case "POST":
		if r.post == nil {
			r.post = &node{typ: nodeRoot}
		}
		return r.post.insert(path, newRoute)
	case "PUT":
		if r.put == nil {
			r.put = &node{typ: nodeRoot}
		}
		return r.put.insert(path, newRoute)
	case "PATCH":
		if r.patch == nil {
			r.patch = &node{typ: nodeRoot}
		}
		return r.patch.insert(path, newRoute)
	case "HEAD":
		if r.head == nil {
			r.head = &node{typ: nodeRoot}
		}
		return r.head.insert(path, newRoute)
	case "CONNECT":
		if r.connect == nil {
			r.connect = &node{typ: nodeRoot}
		}
		return r.connect.insert(path, newRoute)
	case "OPTIONS":
		if r.options == nil {
			r.options = &node{typ: nodeRoot}
		}
		return r.options.insert(path, newRoute)
	case "TRACE":
		if r.trace == nil {
			r.trace = &node{typ: nodeRoot}
		}
		return r.trace.insert(path, newRoute)
	case "DELETE":
		if r.delete == nil {
			r.delete = &node{typ: nodeRoot}
		}
		return r.delete.insert(path, newRoute)
	}
	return errUnknownMethod
}

func (r *router) find(method, path string) (*route, error) {
	switch method {
	case "GET":
		if r.get != nil {
			return r.get.find(path)
		}
	case "POST":
		if r.post != nil {
			return r.post.find(path)
		}
	case "PUT":
		if r.put != nil {
			return r.put.find(path)
		}
	case "PATCH":
		if r.patch != nil {
			return r.patch.find(path)
		}
	case "HEAD":
		if r.head != nil {
			return r.head.find(path)
		}
	case "CONNECT":
		if r.connect != nil {
			return r.connect.find(path)
		}
	case "OPTIONS":
		if r.options != nil {
			return r.options.find(path)
		}
	case "TRACE":
		if r.trace != nil {
			return r.trace.find(path)
		}
	case "DELETE":
		if r.delete != nil {
			return r.delete.find(path)
		}
	}
	return nil, errRouteNotFound
}

// Mux is a http multiplexer that allows matching of http requests to the
// registered http handlers.
//
// Mux supports named parameters in urls like
//   /hello/:name
// will match
//   /hello/world
// where by inside the request passed to the handler, the param with key name and
// value world will be passed.
//
// Mux supports catch all parameters too
//   /hello/*whatever
// will match
//   /hello/world
//   /hello/world/tanzania
//   /hello/world/afica/tanzania.png
// where by inside the request passed to the handler, the param with key
// whatever will be set and value will be
//   world
//   world/tanzania
//   world/afica/tanzania.png
//
// If you dont specify a name in a catch all route, then the default name "catch"
// will be ussed.
type Mux struct {
	prefix     string
	middleware []func(http.Handler) http.Handler
	notFound   http.Handler
	*router
}

// New returns a new *Mux instance with default handler for mismatched routes.
func New() *Mux {
	m := &Mux{}
	m.router = &router{}
	m.notFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errRouteNotFound.Error(), http.StatusNotFound)
	})
	return m
}

// AddRoute registers h with pattern and method. If there is a path prefix
// created via the Group method) it will be set.
func (m *Mux) AddRoute(method, pattern string, h func(http.ResponseWriter, *http.Request)) error {
	if m.prefix != "" {
		pattern = path.Join(m.prefix, pattern)
	}
	return m.addRoute(method, pattern, h, m.middleware...)
}

// Get registers h with pattern and method GET.
func (m *Mux) Get(pattern string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("GET", pattern, h)
}

// Put registers h with pattern and method PUT.
func (m *Mux) Put(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PUT", path, h)
}

// Post registers h with pattern and method POST.
func (m *Mux) Post(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("POST", path, h)
}

// Patch registers h with pattern and method PATCH.
func (m *Mux) Patch(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PATCH", path, h)
}

// Head registers h with pattern and method HEAD.
func (m *Mux) Head(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("HEAD", path, h)
}

// Options registers h with pattern and method OPTIONS.
func (m *Mux) Options(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("OPTIONS", path, h)
}

// Connect  registers h with pattern and method CONNECT.
func (m *Mux) Connect(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("CONNECT", path, h)
}

// Trace registers h with pattern and method TRACE.
func (m *Mux) Trace(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("TRACE", path, h)
}

// Delete registers h with pattern and method DELETE.
func (m *Mux) Delete(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("DELETE", path, h)
}

// NotFoundHandler is executed when the request route is not found.
func (m *Mux) NotFoundHandler(h http.Handler) {
	m.notFound = h
}

// ServeHTTP implements http.Handler interface. It muliplexes http requests
// against registered handlers.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := path.Clean(r.URL.Path)
	h, err := m.find(r.Method, p)
	if err != nil {
		m.notFound.ServeHTTP(w, r)
		return
	}
	params, _ := ParseParams(p, h.path) // check if there is any url params
	if params != "" {
		r.Header.Set(headerName, params)
	}
	h.ServeHTTP(w, r)
}

// Group creates a path prefix group for pattern, all routes registered using
// the returned Mux will only match if the request path starts with pattern. For
// instance .
//   m:=New()
//   home:=m.Group("/home")
//   home.Get("/alone",myHandler)
// will match
//   /home/alone
func (m *Mux) Group(pattern string) *Mux {
	return &Mux{
		prefix:     pattern,
		router:     m.router,
		middleware: m.middleware,
		notFound:   m.notFound,
	}

}

// Use assigns midlewares to the current *Mux. All routes registered by the *Mux
// after this call will have the middlewares assigned to them.
func (m *Mux) Use(middleware ...func(http.Handler) http.Handler) {
	if len(middleware) > 0 {
		m.middleware = append(m.middleware, middleware...)
	}
}
<commit_msg>Update alien.go<commit_after>// Package alien is a lightweight http router from outer space.
package alien

import (
	"errors"
	"net/http"
	"path"
	"strings"
	"sync"
)

var (
	eof              = rune(0)
	errRouteNotFound = errors.New("route not found")
	errBadPattern    = errors.New("bad pattern")
	errUnknownMethod = errors.New("unknown http method")
	headerName       = "_alien"
)

type nodeType int

const (
	nodeRoot nodeType = iota
	nodeParam
	nodeNormal
	nodeCatchAll
	nodeEnd
)

type node struct {
	key      rune
	typ      nodeType
	mu       sync.RWMutex
	value    *route
	children []*node
}

func (n *node) branch(key rune, val *route, typ ...nodeType) *node {
	child := &node{
		key:   key,
		value: val,
	}
	if len(typ) > 0 {
		child.typ = typ[0]
	}
	n.children = append(n.children, child)
	return child
}

func (n *node) findChild(key rune) *node {
	for _, v := range n.children {
		if v.key == key {
			return v
		}
	}
	return nil
}

func (n *node) insert(pattern string, val *route) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	if n.typ != nodeRoot {
		return errors.New("inserting on a non root node")
	}
	var level *node
	var child *node

	if pattern == "" {
		return errors.New("empty pattern is not supported")
	}

	for k, ch := range pattern {
		if k == 0 {
			if ch != '/' {
				return errors.New("path must start with slash ")
			}
			level = n
		}

		child = level.findChild(ch)
		switch level.typ {
		case nodeParam:
			if k < len(pattern) && ch != '/' {
				continue
			}
		}
		if child != nil {
			level = child
			continue
		}
		switch ch {
		case ':':
			level = level.branch(ch, nil, nodeParam)
		case '*':
			level = level.branch(ch, nil, nodeCatchAll)
		default:
			level = level.branch(ch, nil, nodeNormal)
		}
	}
	level.branch(eof, val, nodeEnd)
	return nil
}

func (n *node) find(path string) (*route, error) {
	n.mu.RLock()
	defer n.mu.RUnlock()
	if n.typ != nodeRoot {
		return nil, errors.New("non node search")
	}
	var level *node
	var isParam bool
	for k, ch := range path {
		if k == 0 {
			level = n
		}
		c := level.findChild(ch)
		if isParam {
			if k < len(path) && ch != '/' {
				continue
			}
			isParam = false
		}
		param := level.findChild(':')
		if param != nil {
			level = param
			isParam = true
			continue
		}
		catchAll := level.findChild('*')
		if catchAll != nil {
			level = catchAll
			break
		}
		if c != nil {
			level = c
			continue
		}
		return nil, errRouteNotFound
	}
	if level != nil {
		end := level.findChild(eof)
		if end != nil {
			return end.value, nil
		}
		if slash := level.findChild('/'); slash != nil {
			end = slash.findChild(eof)
			if end != nil {
				return end.value, nil
			}
		}
	}
	return nil, errRouteNotFound
}

type route struct {
	path       string
	middleware []func(http.Handler) http.Handler
	handler    func(http.ResponseWriter, *http.Request)
}

func (r *route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var base http.Handler
	if base == nil {
		base = http.HandlerFunc(r.handler)
	}
	for _, m := range r.middleware {
		base = m(base)
	}
	base.ServeHTTP(w, req)
}

// ParseParams parses params found in mateched from pattern. There are two kinds
// of params, one to capture a segment which starts with : and a nother to
// capture everything( a.k.a catch all) whis starts with *.
//
// For instance
//   pattern:="/hello/:name"
//   matched:="/hello/world"
// Will result into name:world. this function captures the named params and
// theri coreesponding values, returning them in a comma separated  string of a
// key:value nature. please see the tests for more details.
func ParseParams(matched, pattern string) (result string, err error) {
	if strings.Contains(pattern, ":") || strings.Contains(pattern, "*") {
		p1 := strings.Split(matched, "/")
		p2 := strings.Split(pattern, "/")
		s1 := len(p1)
		s2 := len(p2)
		if s1 < s2 {
			err = errBadPattern
			return
		}
		for k, v := range p2 {
			if len(v) > 0 {
				switch v[0] {
				case ':':
					if len(result) == 0 {
						result = v[1:] + ":" + p1[k]
						continue
					}
					result = result + "," + v[1:] + ":" + p1[k]
				case '*':
					name := "catch"
					if k != s2-1 {
						err = errBadPattern
						return
					}
					if len(v) > 1 {
						name = v[1:]
					}
					if len(result) == 0 {
						result = name + ":" + strings.Join(p1[k:], "/")
						return
					}
					result = result + "," + name + ":" + strings.Join(p1[k:], "/")
					return
				}
			}
		}
	}
	return
}

// Params stores route params.
type Params map[string]string

// Load loads params found in src into p.
func (p Params) Load(src string) {
	s := strings.Split(src, ",")
	var vars []string
	for _, v := range s {
		vars = strings.Split(v, ":")
		if len(vars) != 2 {
			continue
		}
		p[vars[0]] = vars[1]
	}
}

// Get returns value associated with key.
func (p Params) Get(key string) string {
	return p[key]
}

// GetParams returrns route params stored in r.
func GetParams(r *http.Request) Params {
	c := r.Header.Get(headerName)
	if c != "" {
		p := make(Params)
		p.Load(c)
		return p
	}
	return nil
}

type router struct {
	get, post, patch, put, head     *node
	connect, options, trace, delete *node
}

func (r *router) addRoute(method, path string, h func(http.ResponseWriter, *http.Request), wares ...func(http.Handler) http.Handler) error {
	newRoute := &route{path: path, handler: h}
	if len(wares) > 0 {
		newRoute.middleware = append(newRoute.middleware, wares...)
	}
	switch method {
	case "GET":
		if r.get == nil {
			r.get = &node{typ: nodeRoot}
		}
		return r.get.insert(path, newRoute)
	case "POST":
		if r.post == nil {
			r.post = &node{typ: nodeRoot}
		}
		return r.post.insert(path, newRoute)
	case "PUT":
		if r.put == nil {
			r.put = &node{typ: nodeRoot}
		}
		return r.put.insert(path, newRoute)
	case "PATCH":
		if r.patch == nil {
			r.patch = &node{typ: nodeRoot}
		}
		return r.patch.insert(path, newRoute)
	case "HEAD":
		if r.head == nil {
			r.head = &node{typ: nodeRoot}
		}
		return r.head.insert(path, newRoute)
	case "CONNECT":
		if r.connect == nil {
			r.connect = &node{typ: nodeRoot}
		}
		return r.connect.insert(path, newRoute)
	case "OPTIONS":
		if r.options == nil {
			r.options = &node{typ: nodeRoot}
		}
		return r.options.insert(path, newRoute)
	case "TRACE":
		if r.trace == nil {
			r.trace = &node{typ: nodeRoot}
		}
		return r.trace.insert(path, newRoute)
	case "DELETE":
		if r.delete == nil {
			r.delete = &node{typ: nodeRoot}
		}
		return r.delete.insert(path, newRoute)
	}
	return errnownMethod
}

func (r *router) find(method, path string) (*route, error) {
	switch method {
	case "GET":
		if r.get != nil {
			return r.get.find(path)
		}
	case "POST":
		if r.post != nil {
			return r.post.find(path)
		}
	case "PUT":
		if r.put != nil {
			return r.put.find(path)
		}
	case "PATCH":
		if r.patch != nil {
			return r.patch.find(path)
		}
	case "HEAD":
		if r.head != nil {
			return r.head.find(path)
		}
	case "CONNECT":
		if r.connect != nil {
			return r.connect.find(path)
		}
	case "OPTIONS":
		if r.options != nil {
			return r.options.find(path)
		}
	case "TRACE":
		if r.trace != nil {
			return r.trace.find(path)
		}
	case "DELETE":
		if r.delete != nil {
			return r.delete.find(path)
		}
	}
	return nil, errRouteNotFound
}

// Mux is a http multiplexer that allows matching of http requests to the
// registered http handlers.
//
// Mux supports named parameters in urls like
//   /hello/:name
// will match
//   /hello/world
// where by inside the request passed to the handler, the param with key name and
// value world will be passed.
//
// Mux supports catch all parameters too
//   /hello/*whatever
// will match
//   /hello/world
//   /hello/world/tanzania
//   /hello/world/afica/tanzania.png
// where by inside the request passed to the handler, the param with key
// whatever will be set and value will be
//   world
//   world/tanzania
//   world/afica/tanzania.png
//
// If you dont specify a name in a catch all route, then the default name "catch"
// will be ussed.
type Mux struct {
	prefix     string
	middleware []func(http.Handler) http.Handler
	notFound   http.Handler
	*router
}

// New returns a new *Mux instance with default handler for mismatched routes.
func New() *Mux {
	m := &Mux{}
	m.router = &router{}
	m.notFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, errRouteNotFound.Error(), http.StatusNotFound)
	})
	return m
}

// AddRoute registers h with pattern and method. If there is a path prefix
// created via the Group method) it will be set.
func (m *Mux) AddRoute(method, pattern string, h func(http.ResponseWriter, *http.Request)) error {
	if m.prefix != "" {
		pattern = path.Join(m.prefix, pattern)
	}
	return m.addRoute(method, pattern, h, m.middleware...)
}

// Get registers h with pattern and method GET.
func (m *Mux) Get(pattern string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("GET", pattern, h)
}

// Put registers h with pattern and method PUT.
func (m *Mux) Put(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PUT", path, h)
}

// Post registers h with pattern and method POST.
func (m *Mux) Post(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("POST", path, h)
}

// Patch registers h with pattern and method PATCH.
func (m *Mux) Patch(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("PATCH", path, h)
}

// Head registers h with pattern and method HEAD.
func (m *Mux) Head(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("HEAD", path, h)
}

// Options registers h with pattern and method OPTIONS.
func (m *Mux) Options(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("OPTIONS", path, h)
}

// Connect  registers h with pattern and method CONNECT.
func (m *Mux) Connect(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("CONNECT", path, h)
}

// Trace registers h with pattern and method TRACE.
func (m *Mux) Trace(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("TRACE", path, h)
}

// Delete registers h with pattern and method DELETE.
func (m *Mux) Delete(path string, h func(http.ResponseWriter, *http.Request)) error {
	return m.AddRoute("DELETE", path, h)
}

// NotFoundHandler is executed when the request route is not found.
func (m *Mux) NotFoundHandler(h http.Handler) {
	m.notFound = h
}

// ServeHTTP implements http.Handler interface. It muliplexes http requests
// against registered handlers.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p := path.Clean(r.URL.Path)
	h, err := m.find(r.Method, p)
	if err != nil {
		m.notFound.ServeHTTP(w, r)
		return
	}
	params, _ := ParseParams(p, h.path) // check if there is any url params
	if params != "" {
		r.Header.Set(headerName, params)
	}
	h.ServeHTTP(w, r)
}

// Group creates a path prefix group for pattern, all routes registered using
// the returned Mux will only match if the request path starts with pattern. For
// instance .
//   m:=New()
//   home:=m.Group("/home")
//   home.Get("/alone",myHandler)
// will match
//   /home/alone
func (m *Mux) Group(pattern string) *Mux {
	return &Mux{
		prefix:     pattern,
		router:     m.router,
		middleware: m.middleware,
		notFound:   m.notFound,
	}

}

// Use assigns midlewares to the current *Mux. All routes registered by the *Mux
// after this call will have the middlewares assigned to them.
func (m *Mux) Use(middleware ...func(http.Handler) http.Handler) {
	if len(middleware) > 0 {
		m.middleware = append(m.middleware, middleware...)
	}
}
<|endoftext|>