<commit_before>package main

import (
	"errors"
	"fmt"
	"regexp"
	"testing"
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"

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

var testPolicyConfig = `
  resource "baremetal_identity_policy" "p" {
    name = "%s"
    description = "%s"
    statements = %s
  }
`

type ResourceIdentityPolicyTestSuite struct {
	suite.Suite
	Client      *MockClient
	Provider    terraform.ResourceProvider
	Providers   map[string]terraform.ResourceProvider
	TimeCreated time.Time
	Config      string
	PolicyName  string
	Policy      *baremtlsdk.Policy
}

func (s *ResourceIdentityPolicyTestSuite) SetupTest() {
	s.Client = &MockClient{}
	s.Provider = Provider(func(d *schema.ResourceData) (interface{}, error) {
		return s.Client, nil
	},
	)
	s.Providers = map[string]terraform.ResourceProvider{
		"baremetal": s.Provider,
	}
	s.TimeCreated, _ = time.Parse("2006-Jan-02", "2006-Jan-02")
	s.Config = fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"desc",
		`["statementX","statementY"]`,
	)
	s.PolicyName = "baremetal_identity_policy.p"
	s.Policy = &baremtlsdk.Policy{
		Resource: baremtlsdk.Resource{
			ID:            "123",
			Name:          "pol",
			Description:   "desc",
			CompartmentID: "7",
			State:         baremtlsdk.ResourceCreated,
			TimeCreated:   s.TimeCreated,
			TimeModified:  s.TimeCreated,
		},
		Statements: []string{"statementX", "statementY"},
	}

	s.Client.On(
		"CreatePolicy",
		"pol",
		"desc",
		[]string{"statementX", "statementY"},
	).Return(
		s.Policy,
		nil,
	)

}

func (s *ResourceIdentityPolicyTestSuite) TestCreateResourceIdentityPolicy() {
	s.Client.On(
		"GetPolicy",
		s.Policy.ID,
	).Return(
		s.Policy,
		nil,
	)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "name", s.Policy.Name),
					resource.TestCheckResourceAttr(s.PolicyName, "description", s.Policy.Description),
					resource.TestCheckResourceAttr(s.PolicyName, "compartment_id", s.Policy.CompartmentID),
					resource.TestCheckResourceAttr(s.PolicyName, "state", s.Policy.State),
					resource.TestCheckResourceAttr(s.PolicyName, "time_created", s.Policy.TimeCreated.String()),
					resource.TestCheckResourceAttr(s.PolicyName, "time_modified", s.Policy.TimeModified.String()),
					testCheckAttributeTypeList(s.PolicyName, "statements", s.Policy.Statements),
				),
			},
		},
	},
	)
}

func (s *ResourceIdentityPolicyTestSuite) TestUpdateResourceIdentityPolicy() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil).Twice()

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"newdesc",
		`["statementA","statementY", "statementZ"]`,
	)

	updated := *s.Policy
	updated.Description = "newdesc"
	updated.Statements = []string{"statementA", "statementY", "statementZ"}
	updated.TimeModified = s.TimeCreated.Add(fiveMinutes)
	s.Client.On(
		"UpdatePolicy",
		updated.ID,
		updated.Description,
		updated.Statements,
	).Return(
		&updated,
		nil,
	)
	s.Client.On("GetPolicy",
		updated.ID,
	).Return(
		&updated,
		nil,
	)

	resource.UnitTest(s.T(),
		resource.TestCase{
			Providers: s.Providers,
			Steps: []resource.TestStep{
				resource.TestStep{
					Config: s.Config,
				},
				resource.TestStep{
					Config: config,
					Check: resource.ComposeTestCheckFunc(
						resource.TestCheckResourceAttr(s.PolicyName, "description", updated.Description),
						resource.TestCheckResourceAttr(s.PolicyName, "time_modified", updated.TimeModified.String()),
						testCheckAttributeTypeList(s.PolicyName, "statements", updated.Statements),
					),
				},
			},
		},
	)

}

func (s *ResourceIdentityPolicyTestSuite) TestFailedUpdateResourceIdentityPolicy() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil).Times(3)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"newdesc",
		`["statementA", "statementB"]`,
	)

	newStatements := []string{"statementA", "statementB"}

	s.Client.On("UpdatePolicy",
		s.Policy.ID,
		"newdesc",
		newStatements,
	).Return(
		nil,
		errors.New("FAILED")).Once()

	u := *s.Policy
	u.Description = "newdesc"
	u.TimeModified = s.Policy.TimeModified.Add(fiveMinutes)
	u.Statements = newStatements
	s.Client.On("UpdatePolicy", s.Policy.ID, "newdesc", newStatements).Return(&u, nil)
	s.Client.On("GetPolicy", s.Policy.ID).Return(&u, nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:      config,
				ExpectError: regexp.MustCompile(`FAILED`),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "description", "desc"),
					testCheckAttributeTypeList(s.PolicyName, "statements", s.Policy.Statements),
				),
			},
			resource.TestStep{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "description", "newdesc"),
					testCheckAttributeTypeList(s.PolicyName, "statements", u.Statements),
				),
			},
		},
	})
}

func (s *ResourceIdentityPolicyTestSuite) TestUpdateResourceIdentityPolicyNameShouldCreateNew() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"newname",
		"desc",
		`["statementX", "statementY"]`,
	)

	statements := []string{"statementX", "statementY"}

	u := *s.Policy
	u.ID = "999"
	u.Name = "newname"
	s.Client.On(
		"CreatePolicy",
		"newname",
		"desc",
		statements,
	).Return(&u, nil)
	s.Client.On("GetPolicy", "999").Return(&u, nil)
	s.Client.On("DeletePolicy", "999").Return(nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config: config,
				Check:  resource.TestCheckResourceAttr(s.PolicyName, "name", "newname"),
			},
		},
	})
}

func (s *ResourceIdentityPolicyTestSuite) TestDeleteResourceIdentityPolicy() {
	s.Client.On("GetPolicy", "123").Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:  s.Config,
				Destroy: true,
			},
		},
	})

	s.Client.AssertCalled(s.T(), "DeletePolicy", "123")
}

func (s *ResourceIdentityPolicyTestSuite) TestDeleteFailureResourceIdentityPolicy() {
	s.Client.On("GetPolicy", "123").Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		errors.New("XXX"),
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:      s.Config,
				ExpectError: regexp.MustCompile(`XXX`),
				Destroy:     true,
			},
		},
	})

	s.Client.AssertCalled(s.T(), "DeletePolicy", "123")

}

func TestResourceIdentityPolicyTestSuite(t *testing.T) {
	suite.Run(t, new(ResourceIdentityPolicyTestSuite))
}
<commit_msg>Added delete failure<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"testing"
	"time"

	"github.com/MustWin/baremtlclient"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"

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

var testPolicyConfig = `
  resource "baremetal_identity_policy" "p" {
    name = "%s"
    description = "%s"
    statements = %s
  }
`

type ResourceIdentityPolicyTestSuite struct {
	suite.Suite
	Client      *MockClient
	Provider    terraform.ResourceProvider
	Providers   map[string]terraform.ResourceProvider
	TimeCreated time.Time
	Config      string
	PolicyName  string
	Policy      *baremtlsdk.Policy
}

func (s *ResourceIdentityPolicyTestSuite) SetupTest() {
	s.Client = &MockClient{}
	s.Provider = Provider(func(d *schema.ResourceData) (interface{}, error) {
		return s.Client, nil
	},
	)
	s.Providers = map[string]terraform.ResourceProvider{
		"baremetal": s.Provider,
	}
	s.TimeCreated, _ = time.Parse("2006-Jan-02", "2006-Jan-02")
	s.Config = fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"desc",
		`["statementX","statementY"]`,
	)
	s.PolicyName = "baremetal_identity_policy.p"
	s.Policy = &baremtlsdk.Policy{
		Resource: baremtlsdk.Resource{
			ID:            "123",
			Name:          "pol",
			Description:   "desc",
			CompartmentID: "7",
			State:         baremtlsdk.ResourceCreated,
			TimeCreated:   s.TimeCreated,
			TimeModified:  s.TimeCreated,
		},
		Statements: []string{"statementX", "statementY"},
	}

	s.Client.On(
		"CreatePolicy",
		"pol",
		"desc",
		[]string{"statementX", "statementY"},
	).Return(
		s.Policy,
		nil,
	)

}

func (s *ResourceIdentityPolicyTestSuite) TestCreateResourceIdentityPolicy() {
	s.Client.On(
		"GetPolicy",
		s.Policy.ID,
	).Return(
		s.Policy,
		nil,
	)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "name", s.Policy.Name),
					resource.TestCheckResourceAttr(s.PolicyName, "description", s.Policy.Description),
					resource.TestCheckResourceAttr(s.PolicyName, "compartment_id", s.Policy.CompartmentID),
					resource.TestCheckResourceAttr(s.PolicyName, "state", s.Policy.State),
					resource.TestCheckResourceAttr(s.PolicyName, "time_created", s.Policy.TimeCreated.String()),
					resource.TestCheckResourceAttr(s.PolicyName, "time_modified", s.Policy.TimeModified.String()),
					testCheckAttributeTypeList(s.PolicyName, "statements", s.Policy.Statements),
				),
			},
		},
	},
	)
}

func (s *ResourceIdentityPolicyTestSuite) TestUpdateResourceIdentityPolicy() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil).Twice()

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"newdesc",
		`["statementA","statementY", "statementZ"]`,
	)

	updated := *s.Policy
	updated.Description = "newdesc"
	updated.Statements = []string{"statementA", "statementY", "statementZ"}
	updated.TimeModified = s.TimeCreated.Add(fiveMinutes)
	s.Client.On(
		"UpdatePolicy",
		updated.ID,
		updated.Description,
		updated.Statements,
	).Return(
		&updated,
		nil,
	)
	s.Client.On("GetPolicy",
		updated.ID,
	).Return(
		&updated,
		nil,
	)

	resource.UnitTest(s.T(),
		resource.TestCase{
			Providers: s.Providers,
			Steps: []resource.TestStep{
				resource.TestStep{
					Config: s.Config,
				},
				resource.TestStep{
					Config: config,
					Check: resource.ComposeTestCheckFunc(
						resource.TestCheckResourceAttr(s.PolicyName, "description", updated.Description),
						resource.TestCheckResourceAttr(s.PolicyName, "time_modified", updated.TimeModified.String()),
						testCheckAttributeTypeList(s.PolicyName, "statements", updated.Statements),
					),
				},
			},
		},
	)

}

func (s *ResourceIdentityPolicyTestSuite) TestFailedUpdateResourceIdentityPolicy() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil).Times(3)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"pol",
		"newdesc",
		`["statementA", "statementB"]`,
	)

	newStatements := []string{"statementA", "statementB"}

	s.Client.On("UpdatePolicy",
		s.Policy.ID,
		"newdesc",
		newStatements,
	).Return(
		nil,
		errors.New("FAILED")).Once()

	u := *s.Policy
	u.Description = "newdesc"
	u.TimeModified = s.Policy.TimeModified.Add(fiveMinutes)
	u.Statements = newStatements
	s.Client.On("UpdatePolicy", s.Policy.ID, "newdesc", newStatements).Return(&u, nil)
	s.Client.On("GetPolicy", s.Policy.ID).Return(&u, nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:      config,
				ExpectError: regexp.MustCompile(`FAILED`),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "description", "desc"),
					testCheckAttributeTypeList(s.PolicyName, "statements", s.Policy.Statements),
				),
			},
			resource.TestStep{
				Config: config,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(s.PolicyName, "description", "newdesc"),
					testCheckAttributeTypeList(s.PolicyName, "statements", u.Statements),
				),
			},
		},
	})
}

func (s *ResourceIdentityPolicyTestSuite) TestUpdateResourceIdentityPolicyNameShouldCreateNew() {
	s.Client.On("GetPolicy", s.Policy.ID).Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	config := fmt.Sprintf(testProviderConfig+testPolicyConfig,
		"newname",
		"desc",
		`["statementX", "statementY"]`,
	)

	statements := []string{"statementX", "statementY"}

	u := *s.Policy
	u.ID = "999"
	u.Name = "newname"
	s.Client.On(
		"CreatePolicy",
		"newname",
		"desc",
		statements,
	).Return(&u, nil)
	s.Client.On("GetPolicy", "999").Return(&u, nil)
	s.Client.On("DeletePolicy", "999").Return(nil)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config: config,
				Check:  resource.TestCheckResourceAttr(s.PolicyName, "name", "newname"),
			},
		},
	})
}

func (s *ResourceIdentityPolicyTestSuite) TestDeleteResourceIdentityPolicy() {
	s.Client.On("GetPolicy", "123").Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		nil,
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:  s.Config,
				Destroy: true,
			},
		},
	})

	s.Client.AssertCalled(s.T(), "DeletePolicy", "123")
}

func (s *ResourceIdentityPolicyTestSuite) TestDeleteFailureResourceIdentityPolicy() {
	s.Client.On("GetPolicy", "123").Return(s.Policy, nil)

	s.Client.On(
		"DeletePolicy",
		"123",
	).Return(
		errors.New("XXX"),
	)

	resource.UnitTest(s.T(), resource.TestCase{
		Providers: s.Providers,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: s.Config,
			},
			resource.TestStep{
				Config:      s.Config,
				ExpectError: regexp.MustCompile(`XXX`),
				Destroy:     true,
			},
			resource.TestStep{
				Config: s.Config,
			},
		},
	})

	s.Client.AssertCalled(s.T(), "DeletePolicy", "123")

}

func TestResourceIdentityPolicyTestSuite(t *testing.T) {
	suite.Run(t, new(ResourceIdentityPolicyTestSuite))
}
<|endoftext|>


<commit_before>package msg

import (
	"fmt"
	"strings"
	"testing"
)

func testDS(t *testing.T, ds *DataSet, expectNil bool) {
	// iterate through each valid dataset type
	for dstID := range DataSetType_name {
		dst := DataSetType(dstID)

		i, err := ds.FieldByType(dst)
		if err != nil {
			t.Error("DataSetByType returned error", err)
		}

		if i == nil && !expectNil {
			t.Error("did not expect nil dataset field")
		}

		if i != nil && expectNil {
			t.Errorf("got unexpected nil dataset field %#v", i)
		}

		if expectNil {
			return
		}

		switch dst {
		case CATEGORIZATION:
			r, ok := i.(*DataSet_Categorization)
			if !ok {
				t.Error("type of i not *DataSet_Categorization")
			}

			if r != ds.Categorization {
				t.Error("t != ds.Categorization")
			}
		case ADFRAUD:
			r, ok := i.(*DataSet_AdFraud)
			if !ok {
				t.Error("type of i not *DataSet_AdFraud")
			}

			if r != ds.Adfraud {
				t.Error("t != ds.Adfraud")
			}
		case MALICIOUS:
			r, ok := i.(*DataSet_Malicious)
			if !ok {
				t.Error("type of i not *DataSet_Malicious")
			}

			if r != ds.Malicious {
				t.Error("t != ds.Malicious")
			}
		case ECHO:
			r, ok := i.(*DataSet_Echo)
			if !ok {
				t.Error("type of i not *DataSet_Echo")
			}

			if r != ds.Echo {
				t.Error("t != ds.Echo")

			}
		case KEYWORD:
			_, ok := i.(*DataSet_Keyword)
			if !ok {
				t.Errorf("type of i not *DataSet_Keyword")
			}

		case SENTIMENT:
			_, ok := i.(*DataSet_Sentiment)
			if !ok {
				t.Errorf("type of i not *DataSet_Sentiment")
			}
		default:
			t.Errorf("unexpected dataset type: %s", dst)
		}
	}
}

func TestDataSetByType(t *testing.T) {
	testDS(t, &DataSet{
		Categorization: &DataSet_Categorization{},
		Adfraud:        &DataSet_AdFraud{},
		Malicious:      &DataSet_Malicious{},
		Echo:           &DataSet_Echo{},
		Keyword:        &DataSet_Keyword{},
		Sentiment:      &DataSet_Sentiment{},
	}, false)
}

func TestNilDataSetByType(t *testing.T) {
	testDS(t, &DataSet{}, true)
}

func TestDataSetByTypeErr(t *testing.T) {
	ds := &DataSet{}
	i, err := ds.FieldByType(DataSetType(-1))

	if err == nil {
		t.Error("DataSetByType didn't return error for invalid dataset type")
	}

	if i != nil {
		t.Error("expected DataSetByType to return nil interface when err != nil ")
	}

	e, ok := err.(errInvalidDataSetType)
	if !ok {
		t.Error("error was not of type errInvalidDataSetType")
	}

	const errMsg0 = "invalid dataset type: -1"
	if e.Error() != errMsg0 || err.Error() != errMsg0 {
		t.Error("error did not have expected message")
	}

	ds = nil
	i, err = ds.FieldByType(CATEGORIZATION)

	if err == nil {
		t.Error("DataSetByType didn't return error for nil dataset")
	}

	if i != nil {
		t.Error("expected DataSetByType to return nil interface when err != nil ")
	}

	if err != ErrNilDataSet {
		t.Error("unexpected error type")
	}
}

func ExampleDataSet_FieldByType() {
	ds := &DataSet{
		Categorization: &DataSet_Categorization{},
	}

	i, _ := ds.FieldByType(CATEGORIZATION)

	c := i.(*DataSet_Categorization)

	fmt.Printf("c == ds.Categorization => %v\n", c == ds.Categorization)
	// Output: c == ds.Categorization => true
}

func testDataSetType(t *testing.T, dstValue int32, dstName string) {
	dst, err := NewDataSetType(strings.ToLower(dstName))
	if err != nil {
		t.Error("unexpected error")
	}

	if DataSetType(dstValue) != dst {
		t.Error("unexpected dataset type value")
	}
}

func TestNewDataSetType(t *testing.T) {
	for dstValue, dstName := range DataSetType_name {
		testDataSetType(t, dstValue, dstName)
		testDataSetType(t, dstValue, strings.ToLower(dstName))
		testDataSetType(t, dstValue, strings.ToUpper(dstName))
	}

	dst, err := NewDataSetType("invalid dataset type name")
	if err == nil {
		t.Error("NewDataSetType should have returned an error")
	}

	if err != ErrInvalidDataSetType {
		t.Error("NewDataSetType returned the wrong error")
	}

	if dst != DataSetType(-1) {
		t.Error("NewDataSetType returned wrong response")
	}
}

func TestMergeDatasets(t *testing.T) {
	d1 := &DataSet{
		Categorization: &DataSet_Categorization{
			Values: []uint32{1},
		},
	}
	d2 := &DataSet{
		Adfraud: &DataSet_AdFraud{
			Verdict: true,
		},
	}

	if d1.Categorization == nil {
		t.Error("d1.Categorization should not be nil")
	}

	if d2.Adfraud == nil {
		t.Error("d2.Adfraud should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d1.Adfraud != nil {
		t.Error("d1.Adfraud should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization == nil {
		t.Error("d3.Categorization should not be nil")
	}

	if d3.Categorization.Values[0] != 1 {
		t.Error("d3.Categorization.Values[0] should equal 1")
	}

	if d3.Adfraud == nil {
		t.Error("d3.Adfraud should not be nil")
	}

	if !d3.Adfraud.Verdict {
		t.Error("d3.Adfraud.Verdict should be true")
	}
}

func TestMergeDatasetsOneEmpty(t *testing.T) {
	d1 := &DataSet{}
	d2 := &DataSet{
		Adfraud: &DataSet_AdFraud{
			Verdict: true,
		},
	}

	if d1.Categorization != nil {
		t.Error("d1.Categorization should be nil")
	}

	if d2.Adfraud == nil {
		t.Error("d2.Adfraud should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d1.Adfraud != nil {
		t.Error("d1.Adfraud should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Adfraud == nil {
		t.Error("d3.Adfraud should not be nil")
	}

	if !d3.Adfraud.Verdict {
		t.Error("d3.Adfraud.Verdict should be true")
	}
}

func TestMergeDatasetsBothEmpty(t *testing.T) {
	d1 := &DataSet{}
	d2 := &DataSet{}

	if d1.Categorization != nil {
		t.Error("d1.Categorization should be nil")
	}

	if d1.Adfraud != nil {
		t.Error("d1.Adfraud should be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d2.Adfraud != nil {
		t.Error("d2.Adfraud should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Adfraud != nil {
		t.Error("d3.Adfraud should be nil")
	}
}

func TestMergeDatasetsOneNil(t *testing.T) {
	var d1 *DataSet
	d2 := &DataSet{
		Adfraud: &DataSet_AdFraud{
			Verdict: true,
		},
	}

	if d2.Adfraud == nil {
		t.Error("d2.Adfraud should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Adfraud == nil {
		t.Error("d3.Adfraud should not be nil")
	}

	if !d3.Adfraud.Verdict {
		t.Error("d3.Adfraud.Verdict should be true")
	}
}

func TestMergeDatasetsBothNil(t *testing.T) {
	var d1 *DataSet
	var d2 *DataSet
	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Adfraud != nil {
		t.Error("d3.Adfraud should be nil")
	}
}
<commit_msg>fix tests<commit_after>package msg

import (
	"fmt"
	"strings"
	"testing"
)

func testDS(t *testing.T, ds *DataSet, expectNil bool) {
	// iterate through each valid dataset type
	for dstID := range DataSetType_name {
		dst := DataSetType(dstID)

		i, err := ds.FieldByType(dst)
		if err != nil {
			t.Error("DataSetByType returned error", err)
		}

		if i == nil && !expectNil {
			t.Error("did not expect nil dataset field")
		}

		if i != nil && expectNil {
			t.Errorf("got unexpected nil dataset field %#v", i)
		}

		if expectNil {
			return
		}

		switch dst {
		case CATEGORIZATION:
			r, ok := i.(*DataSet_Categorization)
			if !ok {
				t.Error("type of i not *DataSet_Categorization")
			}

			if r != ds.Categorization {
				t.Error("t != ds.Categorization")
			}
		case MALICIOUS:
			r, ok := i.(*DataSet_Malicious)
			if !ok {
				t.Error("type of i not *DataSet_Malicious")
			}

			if r != ds.Malicious {
				t.Error("t != ds.Malicious")
			}
		case ECHO:
			r, ok := i.(*DataSet_Echo)
			if !ok {
				t.Error("type of i not *DataSet_Echo")
			}

			if r != ds.Echo {
				t.Error("t != ds.Echo")

			}
		default:
			t.Errorf("unexpected dataset type: %s", dst)
		}
	}
}

func TestDataSetByType(t *testing.T) {
	testDS(t, &DataSet{
		Categorization: &DataSet_Categorization{},
		Malicious:      &DataSet_Malicious{},
		Echo:           &DataSet_Echo{},
	}, false)
}

func TestNilDataSetByType(t *testing.T) {
	testDS(t, &DataSet{}, true)
}

func TestDataSetByTypeErr(t *testing.T) {
	ds := &DataSet{}
	i, err := ds.FieldByType(DataSetType(-1))

	if err == nil {
		t.Error("DataSetByType didn't return error for invalid dataset type")
	}

	if i != nil {
		t.Error("expected DataSetByType to return nil interface when err != nil ")
	}

	e, ok := err.(errInvalidDataSetType)
	if !ok {
		t.Error("error was not of type errInvalidDataSetType")
	}

	const errMsg0 = "invalid dataset type: -1"
	if e.Error() != errMsg0 || err.Error() != errMsg0 {
		t.Error("error did not have expected message")
	}

	ds = nil
	i, err = ds.FieldByType(CATEGORIZATION)

	if err == nil {
		t.Error("DataSetByType didn't return error for nil dataset")
	}

	if i != nil {
		t.Error("expected DataSetByType to return nil interface when err != nil ")
	}

	if err != ErrNilDataSet {
		t.Error("unexpected error type")
	}
}

func ExampleDataSet_FieldByType() {
	ds := &DataSet{
		Categorization: &DataSet_Categorization{},
	}

	i, _ := ds.FieldByType(CATEGORIZATION)

	c := i.(*DataSet_Categorization)

	fmt.Printf("c == ds.Categorization => %v\n", c == ds.Categorization)
	// Output: c == ds.Categorization => true
}

func testDataSetType(t *testing.T, dstValue int32, dstName string) {
	dst, err := NewDataSetType(strings.ToLower(dstName))
	if err != nil {
		t.Error("unexpected error")
	}

	if DataSetType(dstValue) != dst {
		t.Error("unexpected dataset type value")
	}
}

func TestNewDataSetType(t *testing.T) {
	for dstValue, dstName := range DataSetType_name {
		testDataSetType(t, dstValue, dstName)
		testDataSetType(t, dstValue, strings.ToLower(dstName))
		testDataSetType(t, dstValue, strings.ToUpper(dstName))
	}

	dst, err := NewDataSetType("invalid dataset type name")
	if err == nil {
		t.Error("NewDataSetType should have returned an error")
	}

	if err != ErrInvalidDataSetType {
		t.Error("NewDataSetType returned the wrong error")
	}

	if dst != DataSetType(-1) {
		t.Error("NewDataSetType returned wrong response")
	}
}

func TestMergeDatasets(t *testing.T) {
	d1 := &DataSet{
		Categorization: &DataSet_Categorization{
			Values: []uint32{1},
		},
	}
	d2 := &DataSet{
		Malicious: &DataSet_Malicious{
			Verdict: true,
		},
	}

	if d1.Categorization == nil {
		t.Error("d1.Categorization should not be nil")
	}

	if d2.Malicious == nil {
		t.Error("d2.Malicious should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d1.Malicious != nil {
		t.Error("d1.Malicious should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization == nil {
		t.Error("d3.Categorization should not be nil")
	}

	if d3.Categorization.Values[0] != 1 {
		t.Error("d3.Categorization.Values[0] should equal 1")
	}

	if d3.Malicious == nil {
		t.Error("d3.Malicious should not be nil")
	}

	if !d3.Malicious.Verdict {
		t.Error("d3.Malicious.Verdict should be true")
	}
}

func TestMergeDatasetsOneEmpty(t *testing.T) {
	d1 := &DataSet{}
	d2 := &DataSet{
		Malicious: &DataSet_Malicious{
			Verdict: true,
		},
	}

	if d1.Categorization != nil {
		t.Error("d1.Categorization should be nil")
	}

	if d2.Malicious == nil {
		t.Error("d2.Malicious should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d1.Malicious != nil {
		t.Error("d1.Malicious should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Malicious == nil {
		t.Error("d3.Malicious should not be nil")
	}

	if !d3.Malicious.Verdict {
		t.Error("d3.Malicious.Verdict should be true")
	}
}

func TestMergeDatasetsBothEmpty(t *testing.T) {
	d1 := &DataSet{}
	d2 := &DataSet{}

	if d1.Categorization != nil {
		t.Error("d1.Categorization should be nil")
	}

	if d1.Malicious != nil {
		t.Error("d1.Malicious should be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	if d2.Malicious != nil {
		t.Error("d2.Malicious should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Malicious != nil {
		t.Error("d3.Malicious should be nil")
	}
}

func TestMergeDatasetsOneNil(t *testing.T) {
	var d1 *DataSet
	d2 := &DataSet{
		Malicious: &DataSet_Malicious{
			Verdict: true,
		},
	}

	if d2.Malicious == nil {
		t.Error("d2.Malicious should not be nil")
	}

	if d2.Categorization != nil {
		t.Error("d2.Categorization should be nil")
	}

	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Malicious == nil {
		t.Error("d3.Malicious should not be nil")
	}

	if !d3.Malicious.Verdict {
		t.Error("d3.Malicious.Verdict should be true")
	}
}

func TestMergeDatasetsBothNil(t *testing.T) {
	var d1 *DataSet
	var d2 *DataSet
	d3, err := MergeDatasets(d1, d2)
	if err != nil {
		t.Errorf("MergeDatasets returned an error: %s", err)
	}

	if d3.Categorization != nil {
		t.Error("d3.Categorization should be nil")
	}

	if d3.Malicious != nil {
		t.Error("d3.Malicious should be nil")
	}
}
<|endoftext|>


<commit_before>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package web

import (
	"bytes"
	"fmt"
	"log"
	"mime"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
)

type WebError struct {
	Code int
	Err  string
}

type Context struct {
	Request *http.Request
	RawBody []byte
	Params  map[string]string
	Server  *Server
	User    interface{}
	// False iff 0 bytes of body data have been written so far
	wroteData bool
	http.ResponseWriter
	WebsockConn *websocket.Conn
}

type route struct {
	rex     *regexp.Regexp
	method  string
	handler handlerf
}

type ServerConfig struct {
	StaticDirs   []string
	Addr         string
	Port         int
	CookieSecret string
	RecoverPanic bool
	Cert         string
	Key          string
	ColorOutput  bool
}

type Server struct {
	Config *ServerConfig
	routes []*route
	Logger *log.Logger
	Env    map[string]interface{}
	// Save the listener so it can be closed
	l net.Listener
	// Passed verbatim to every handler on every request
	User interface{}
}

var (
	exeFile string

	preModules  = []func(*Context) error{}
	postModules = []func(*Context, interface{}) (interface{}, error){}

	Config = &ServerConfig{
		RecoverPanic: true,
		Cert:         "",
		Key:          "",
		ColorOutput:  true,
	}

	mainServer = NewServer()
)

func (err WebError) Error() string {
	return err.Err
}

func (ctx *Context) Write(data []byte) (int, error) {
	ctx.wroteData = true
	return ctx.ResponseWriter.Write(data)
}

func (ctx *Context) WriteString(content string) (int, error) {
	return ctx.Write([]byte(content))
}

func (ctx *Context) Abort(status int, body string) {
	ctx.WriteHeader(status)
	ctx.WriteString(body)
}

func (ctx *Context) Redirect(status int, url_ string) {
	ctx.Header().Set("Location", url_)
	ctx.Abort(status, "Redirecting to: "+url_)
}

func (ctx *Context) NotModified() {
	ctx.WriteHeader(304)
}

func (ctx *Context) NotFound(message string) {
	ctx.Abort(404, message)
}

func (ctx *Context) NotAcceptable(message string) {
	ctx.Abort(406, message)
}

func (ctx *Context) Unauthorized(message string) {
	ctx.Abort(401, message)
}

// Sets the content type by extension, as defined in the mime package.
// For example, ctx.ContentType("json") sets the content-type to "application/json"
// if the supplied extension contains a slash (/) it is set as the content-type
// verbatim without passing it to mime.  returns the content type as it was
// set, or an empty string if none was found.
func (ctx *Context) ContentType(ext string) string {
	ctype := ""
	if strings.ContainsRune(ext, '/') {
		ctype = ext
	} else {
		if !strings.HasPrefix(ext, ".") {
			ext = "." + ext
		}
		ctype = mime.TypeByExtension(ext)
	}
	if ctype != "" {
		ctx.Header().Set("Content-Type", ctype)
	}
	return ctype
}

func (ctx *Context) SetHeader(hdr, val string, unique bool) {
	if unique {
		ctx.Header().Set(hdr, val)
	} else {
		ctx.Header().Add(hdr, val)
	}
}

// Default
func defaultStaticDir() string {
	root, _ := path.Split(exeFile)
	return path.Join(root, "static")
}

func init() {
	// find the location of the executable
	arg0 := path.Clean(os.Args[0])
	wd, _ := os.Getwd()
	if strings.HasPrefix(arg0, "/") {
		exeFile = arg0
	} else {
		// TODO For robustness, search each directory in $PATH
		exeFile = path.Join(wd, arg0)
	}

	// Handle different Accept: types
	AddPostModule(MarshalResponse)
	RegisterMimeParser("application/json", JSONparser)
	RegisterMimeParser("application/xml", XMLparser)
	RegisterMimeParser("text/xml", XMLparser)
	RegisterMimeParser("image/jpeg", Binaryparser)

	// Handle different Accept-Encoding: types
	AddPostModule(EncodeResponse)
}

func (s *Server) addRoute(rawrex string, method string, handler interface{}) {
	rex, err := regexp.Compile(rawrex)
	if err != nil {
		s.Logger.Printf("Error in route regex %q: %v", rawrex, err)
		return
	}
	s.routes = append(s.routes, &route{
		rex:     rex,
		method:  method,
		handler: fixHandlerSignature(handler),
	})
}

// Calls function with recover block. The first return value is whatever the
// function returns if it didnt panic. The second is what was passed to panic()
// if it did.
func (s *Server) safelyCall(f func() error) (softerr error, harderr interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if !s.Config.RecoverPanic {
				// go back to panic
				s.Logger.Printf("Panic: %v", err)
				panic(err)
			} else {
				harderr = err
				s.Logger.Println("Handler crashed with error: ", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					s.Logger.Println(file, line)
				}
			}
		}
	}()
	return f(), nil
}

// Determine if this route matches this request purely on the basis of the method
func matchRouteMethods(req *http.Request, route *route) bool {
	if req.Method == route.method {
		return true
	}
	if req.Method == "HEAD" && route.method == "GET" {
		return true
	}
	if req.Header.Get("Upgrade") == "websocket" && route.method == "WEBSOCKET" {
		return true
	}
	return false
}

// If this request matches this route return the group matches from the regular
// expression otherwise return an empty slice. note on success the return value
// includes the entire match as the first element.
func matchRoute(req *http.Request, route *route) []string {
	if !matchRouteMethods(req, route) {
		return nil
	}
	match := route.rex.FindStringSubmatch(req.URL.Path)
	if match == nil || len(match[0]) != len(req.URL.Path) {
		return nil
	}
	return match
}

func findMatchingRoute(req *http.Request, routes []*route) (*route, []string) {
	for _, route := range routes {
		if match := matchRoute(req, route); match != nil {
			return route, match
		}
	}
	return nil, nil
}

// Apply the handler to this context and try to handle errors where possible
func (s *Server) applyHandler(f handlerf, ctx *Context, groups []string) (err error) {
	softerr, harderr := s.safelyCall(func() error {
		return f(ctx, groups...)
	})
	if harderr != nil {
		//there was an error or panic while calling the handler
		ctx.Abort(500, "Server Error")
		return fmt.Errorf("%v", harderr)
	}
	if softerr != nil {
		s.Logger.Printf("Handler returned error: %v", softerr)
		if werr, ok := softerr.(WebError); ok {
			ctx.Abort(werr.Code, werr.Error())
		} else {
			// Non-web errors are not leaked to the outside
			ctx.Abort(500, "Server Error")
			err = softerr
		}
	}
	return
}

// Serve up a static file (or index.html if its a dir) returns whether it was
// found
func (s *Server) tryServeStatic(w http.ResponseWriter, req *http.Request) bool {
	//try to serve static files
	staticDirs := s.Config.StaticDirs
	if len(staticDirs) == 0 {
		staticDirs = []string{defaultStaticDir()}
	}
	for _, staticDir := range staticDirs {
		staticFile := path.Join(staticDir, req.URL.Path)
		if fileExists(staticFile) && (req.Method == "GET" || req.Method == "HEAD") {
			http.ServeFile(w, req, staticFile)
			return true
		}
	}

	// Try to serve index.html || index.htm
	indexFilenames := []string{"index.html", "index.htm"}
	for _, staticDir := range staticDirs {
		for _, indexFilename := range indexFilenames {
			if indexPath := path.Join(path.Join(staticDir, req.URL.Path), indexFilename); fileExists(indexPath) {
				http.ServeFile(w, req, indexPath)
				return true
			}
		}
	}
	return false
}

// Fully clothed request handler
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := &Context{
		Request:        req,
		RawBody:        nil,
		Params:         map[string]string{},
		Server:         s,
		ResponseWriter: w,
		User:           s.User,
		wroteData:      false,
	}

	//log the request
	if s.Config.ColorOutput {
		s.Logger.Printf("\033[32;1m%s %s\033[0m", req.Method, req.URL.Path)
	} else {
		s.Logger.Printf("%s %s", req.Method, req.URL.Path)
	}

	//ignore errors from ParseForm because it's usually harmless.
	req.ParseForm()
	if len(req.Form) > 0 {
		for k, v := range req.Form {
			ctx.Params[k] = v[0]
		}
		if s.Config.ColorOutput {
			s.Logger.Printf("\033[37;1mParams: %v\033[0m\n", ctx.Params)
		} else {
			s.Logger.Printf("Params: %v\n", ctx.Params)
		}

	}

	//set some default headers
	ctx.SetHeader("Server", "web.go", true)
	tm := time.Now().UTC()
	ctx.SetHeader("Date", webTime(tm), true)

	route, match := findMatchingRoute(req, s.routes)
	if route == nil {
		if !s.tryServeStatic(w, req) {
			ctx.NotFound("Page not found")
		}
		return
	}
	//Set the default content-type
	ctx.SetHeader("Content-Type", "text/html; charset=utf-8", true)
	h := route.handler
	if route.method == "WEBSOCKET" {
		// Wrap websocket handler
		h = func(ctx *Context, args ...string) (err error) {
			// yo dawg we heard you like wrapped functions
			websocket.Handler(func(ws *websocket.Conn) {
				ctx.WebsockConn = ws
				err = route.handler(ctx, args...)
			}).ServeHTTP(ctx.ResponseWriter, req)
			return err
		}
	}
	s.applyHandler(h, ctx, match[1:])
	return
}

func NewServer() *Server {
	s := &Server{
		Config: Config,
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		Env:    map[string]interface{}{},
	}
	// Set two commonly used mimetypes that are often not set by default
	// Handy for robots.txt and favicon.ico
	mime.AddExtensionType(".txt", "text/plain; charset=utf-8")
	mime.AddExtensionType(".ico", "image/x-icon")
	return s
}

//Stops the web server
func (s *Server) Close() {
	if s.l != nil {
		s.l.Close()
	}
}

//Stops the web server
func Close() {
	mainServer.Close()
}

func AddPreModule(module func(*Context) error) {
	preModules = append(preModules, module)
}

func AddPostModule(module func(*Context, interface{}) (interface{}, error)) {
	postModules = append(postModules, module)
}

func ResetModules() {
	preModules = []func(*Context) error{}
	postModules = []func(*Context, interface{}) (interface{}, error){}
}

// Runs a single request, used for testing
func AdHoc(c http.ResponseWriter, req *http.Request) {
	mainServer.ServeHTTP(c, req)
}

func (s *Server) SetLogger(logger *log.Logger) {
	s.Logger = logger
}

func SetLogger(logger *log.Logger) {
	mainServer.Logger = logger
}

func webTime(t time.Time) string {
	ftime := t.Format(time.RFC1123)
	if strings.HasSuffix(ftime, "UTC") {
		ftime = ftime[0:len(ftime)-3] + "GMT"
	}
	return ftime
}

func dirExists(dir string) bool {
	d, e := os.Stat(dir)
	switch {
	case e != nil:
		return false
	case !d.IsDir():
		return false
	}

	return true
}

func fileExists(dir string) bool {
	info, err := os.Stat(dir)
	if err != nil {
		return false
	}

	return !info.IsDir()
}

func Urlencode(data map[string]string) string {
	var buf bytes.Buffer
	for k, v := range data {
		buf.WriteString(url.QueryEscape(k))
		buf.WriteByte('=')
		buf.WriteString(url.QueryEscape(v))
		buf.WriteByte('&')
	}
	s := buf.String()
	return s[0 : len(s)-1]
}
<commit_msg>better internal API for static file search<commit_after>// Copyright © 2009--2013 The Web.go Authors
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package web

import (
	"bytes"
	"fmt"
	"log"
	"mime"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/go.net/websocket"
)

type WebError struct {
	Code int
	Err  string
}

type Context struct {
	Request *http.Request
	RawBody []byte
	Params  map[string]string
	Server  *Server
	User    interface{}
	// False iff 0 bytes of body data have been written so far
	wroteData bool
	http.ResponseWriter
	WebsockConn *websocket.Conn
}

type route struct {
	rex     *regexp.Regexp
	method  string
	handler handlerf
}

type ServerConfig struct {
	StaticDirs   []string
	Addr         string
	Port         int
	CookieSecret string
	RecoverPanic bool
	Cert         string
	Key          string
	ColorOutput  bool
}

type Server struct {
	Config *ServerConfig
	routes []*route
	Logger *log.Logger
	Env    map[string]interface{}
	// Save the listener so it can be closed
	l net.Listener
	// Passed verbatim to every handler on every request
	User interface{}
}

var (
	exeFile string

	preModules  = []func(*Context) error{}
	postModules = []func(*Context, interface{}) (interface{}, error){}

	Config = &ServerConfig{
		RecoverPanic: true,
		Cert:         "",
		Key:          "",
		ColorOutput:  true,
	}

	mainServer = NewServer()
)

func (err WebError) Error() string {
	return err.Err
}

func (ctx *Context) Write(data []byte) (int, error) {
	ctx.wroteData = true
	return ctx.ResponseWriter.Write(data)
}

func (ctx *Context) WriteString(content string) (int, error) {
	return ctx.Write([]byte(content))
}

func (ctx *Context) Abort(status int, body string) {
	ctx.WriteHeader(status)
	ctx.WriteString(body)
}

func (ctx *Context) Redirect(status int, url_ string) {
	ctx.Header().Set("Location", url_)
	ctx.Abort(status, "Redirecting to: "+url_)
}

func (ctx *Context) NotModified() {
	ctx.WriteHeader(304)
}

func (ctx *Context) NotFound(message string) {
	ctx.Abort(404, message)
}

func (ctx *Context) NotAcceptable(message string) {
	ctx.Abort(406, message)
}

func (ctx *Context) Unauthorized(message string) {
	ctx.Abort(401, message)
}

// Sets the content type by extension, as defined in the mime package.
// For example, ctx.ContentType("json") sets the content-type to "application/json"
// if the supplied extension contains a slash (/) it is set as the content-type
// verbatim without passing it to mime.  returns the content type as it was
// set, or an empty string if none was found.
func (ctx *Context) ContentType(ext string) string {
	ctype := ""
	if strings.ContainsRune(ext, '/') {
		ctype = ext
	} else {
		if !strings.HasPrefix(ext, ".") {
			ext = "." + ext
		}
		ctype = mime.TypeByExtension(ext)
	}
	if ctype != "" {
		ctx.Header().Set("Content-Type", ctype)
	}
	return ctype
}

func (ctx *Context) SetHeader(hdr, val string, unique bool) {
	if unique {
		ctx.Header().Set(hdr, val)
	} else {
		ctx.Header().Add(hdr, val)
	}
}

// Default
func defaultStaticDir() string {
	root, _ := path.Split(exeFile)
	return path.Join(root, "static")
}

func init() {
	// find the location of the executable
	arg0 := path.Clean(os.Args[0])
	wd, _ := os.Getwd()
	if strings.HasPrefix(arg0, "/") {
		exeFile = arg0
	} else {
		// TODO For robustness, search each directory in $PATH
		exeFile = path.Join(wd, arg0)
	}

	// Handle different Accept: types
	AddPostModule(MarshalResponse)
	RegisterMimeParser("application/json", JSONparser)
	RegisterMimeParser("application/xml", XMLparser)
	RegisterMimeParser("text/xml", XMLparser)
	RegisterMimeParser("image/jpeg", Binaryparser)

	// Handle different Accept-Encoding: types
	AddPostModule(EncodeResponse)
}

func (s *Server) addRoute(rawrex string, method string, handler interface{}) {
	rex, err := regexp.Compile(rawrex)
	if err != nil {
		s.Logger.Printf("Error in route regex %q: %v", rawrex, err)
		return
	}
	s.routes = append(s.routes, &route{
		rex:     rex,
		method:  method,
		handler: fixHandlerSignature(handler),
	})
}

// Calls function with recover block. The first return value is whatever the
// function returns if it didnt panic. The second is what was passed to panic()
// if it did.
func (s *Server) safelyCall(f func() error) (softerr error, harderr interface{}) {
	defer func() {
		if err := recover(); err != nil {
			if !s.Config.RecoverPanic {
				// go back to panic
				s.Logger.Printf("Panic: %v", err)
				panic(err)
			} else {
				harderr = err
				s.Logger.Println("Handler crashed with error: ", err)
				for i := 1; ; i += 1 {
					_, file, line, ok := runtime.Caller(i)
					if !ok {
						break
					}
					s.Logger.Println(file, line)
				}
			}
		}
	}()
	return f(), nil
}

// Determine if this route matches this request purely on the basis of the method
func matchRouteMethods(req *http.Request, route *route) bool {
	if req.Method == route.method {
		return true
	}
	if req.Method == "HEAD" && route.method == "GET" {
		return true
	}
	if req.Header.Get("Upgrade") == "websocket" && route.method == "WEBSOCKET" {
		return true
	}
	return false
}

// If this request matches this route return the group matches from the regular
// expression otherwise return an empty slice. note on success the return value
// includes the entire match as the first element.
func matchRoute(req *http.Request, route *route) []string {
	if !matchRouteMethods(req, route) {
		return nil
	}
	match := route.rex.FindStringSubmatch(req.URL.Path)
	if match == nil || len(match[0]) != len(req.URL.Path) {
		return nil
	}
	return match
}

func findMatchingRoute(req *http.Request, routes []*route) (*route, []string) {
	for _, route := range routes {
		if match := matchRoute(req, route); match != nil {
			return route, match
		}
	}
	return nil, nil
}

// Apply the handler to this context and try to handle errors where possible
func (s *Server) applyHandler(f handlerf, ctx *Context, groups []string) (err error) {
	softerr, harderr := s.safelyCall(func() error {
		return f(ctx, groups...)
	})
	if harderr != nil {
		//there was an error or panic while calling the handler
		ctx.Abort(500, "Server Error")
		return fmt.Errorf("%v", harderr)
	}
	if softerr != nil {
		s.Logger.Printf("Handler returned error: %v", softerr)
		if werr, ok := softerr.(WebError); ok {
			ctx.Abort(werr.Code, werr.Error())
		} else {
			// Non-web errors are not leaked to the outside
			ctx.Abort(500, "Server Error")
			err = softerr
		}
	}
	return
}

// If this request corresponds to a static file return its path
func (s *Server) findFile(req *http.Request) string {
	//try to serve static files
	staticDirs := s.Config.StaticDirs
	if len(staticDirs) == 0 {
		staticDirs = []string{defaultStaticDir()}
	}
	for _, staticDir := range staticDirs {
		staticFile := path.Join(staticDir, req.URL.Path)
		if fileExists(staticFile) && (req.Method == "GET" || req.Method == "HEAD") {
			return staticFile
		}
	}

	// Try to serve index.html || index.htm
	indexFilenames := []string{"index.html", "index.htm"}
	for _, staticDir := range staticDirs {
		for _, indexFilename := range indexFilenames {
			if indexPath := path.Join(path.Join(staticDir, req.URL.Path), indexFilename); fileExists(indexPath) {
				return indexPath
			}
		}
	}
	return ""
}

// Fully clothed request handler
func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx := &Context{
		Request:        req,
		RawBody:        nil,
		Params:         map[string]string{},
		Server:         s,
		ResponseWriter: w,
		User:           s.User,
		wroteData:      false,
	}

	//log the request
	if s.Config.ColorOutput {
		s.Logger.Printf("\033[32;1m%s %s\033[0m", req.Method, req.URL.Path)
	} else {
		s.Logger.Printf("%s %s", req.Method, req.URL.Path)
	}

	//ignore errors from ParseForm because it's usually harmless.
	req.ParseForm()
	if len(req.Form) > 0 {
		for k, v := range req.Form {
			ctx.Params[k] = v[0]
		}
		if s.Config.ColorOutput {
			s.Logger.Printf("\033[37;1mParams: %v\033[0m\n", ctx.Params)
		} else {
			s.Logger.Printf("Params: %v\n", ctx.Params)
		}

	}

	//set some default headers
	ctx.SetHeader("Server", "web.go", true)
	tm := time.Now().UTC()
	ctx.SetHeader("Date", webTime(tm), true)

	route, match := findMatchingRoute(req, s.routes)
	if route == nil {
		if path := s.findFile(req); path != "" {
			http.ServeFile(w, req, path)
		} else {
			ctx.NotFound("Page not found")
		}
		return
	}
	//Set the default content-type
	ctx.SetHeader("Content-Type", "text/html; charset=utf-8", true)
	h := route.handler
	if route.method == "WEBSOCKET" {
		// Wrap websocket handler
		h = func(ctx *Context, args ...string) (err error) {
			// yo dawg we heard you like wrapped functions
			websocket.Handler(func(ws *websocket.Conn) {
				ctx.WebsockConn = ws
				err = route.handler(ctx, args...)
			}).ServeHTTP(ctx.ResponseWriter, req)
			return err
		}
	}
	s.applyHandler(h, ctx, match[1:])
	return
}

func NewServer() *Server {
	s := &Server{
		Config: Config,
		Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
		Env:    map[string]interface{}{},
	}
	// Set two commonly used mimetypes that are often not set by default
	// Handy for robots.txt and favicon.ico
	mime.AddExtensionType(".txt", "text/plain; charset=utf-8")
	mime.AddExtensionType(".ico", "image/x-icon")
	return s
}

//Stops the web server
func (s *Server) Close() {
	if s.l != nil {
		s.l.Close()
	}
}

//Stops the web server
func Close() {
	mainServer.Close()
}

func AddPreModule(module func(*Context) error) {
	preModules = append(preModules, module)
}

func AddPostModule(module func(*Context, interface{}) (interface{}, error)) {
	postModules = append(postModules, module)
}

func ResetModules() {
	preModules = []func(*Context) error{}
	postModules = []func(*Context, interface{}) (interface{}, error){}
}

// Runs a single request, used for testing
func AdHoc(c http.ResponseWriter, req *http.Request) {
	mainServer.ServeHTTP(c, req)
}

func (s *Server) SetLogger(logger *log.Logger) {
	s.Logger = logger
}

func SetLogger(logger *log.Logger) {
	mainServer.Logger = logger
}

func webTime(t time.Time) string {
	ftime := t.Format(time.RFC1123)
	if strings.HasSuffix(ftime, "UTC") {
		ftime = ftime[0:len(ftime)-3] + "GMT"
	}
	return ftime
}

func dirExists(dir string) bool {
	d, e := os.Stat(dir)
	switch {
	case e != nil:
		return false
	case !d.IsDir():
		return false
	}

	return true
}

func fileExists(dir string) bool {
	info, err := os.Stat(dir)
	if err != nil {
		return false
	}

	return !info.IsDir()
}

func Urlencode(data map[string]string) string {
	var buf bytes.Buffer
	for k, v := range data {
		buf.WriteString(url.QueryEscape(k))
		buf.WriteByte('=')
		buf.WriteString(url.QueryEscape(v))
		buf.WriteByte('&')
	}
	s := buf.String()
	return s[0 : len(s)-1]
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 一个模块化的微形web框架。
//  m, err := web.NewModule("m1")
//  m.Get("/", ...).
//    Post("/", ...)
//  // 其它模块的初始化工作...
//  web.Run(&Config{},errhandler) // 开始监听端口
package web

import (
	"net/http"
	"net/http/pprof"
	"path/filepath"
	"strings"

	"github.com/issue9/context"
	"github.com/issue9/mux"
	"github.com/issue9/term/colors"
)

// web包的相关配置内容。
type Config struct {
	HTTPS      bool              `json:"https"`                // 是否启用https
	CertFile   string            `json:"certFile,omitempty"`   // 当https为true时，此值为必须
	KeyFile    string            `json:"keyFile,omitempty"`    // 当https为true时，此值为必须
	Port       string            `json:"port,omitempty"`       // 端口，不指定，默认为80或是443
	ServerName string            `json:"serverName,omitempty"` // 响应头的server变量，为空时，不输出该内容
	Static     map[string]string `json:"static,omitempty"`     // 静态路由映身，键名表示路由路径，键值表示文件目录
	Pprof      string            `json:"pprof,omitempty"`      // 指定pprof地址
	ErrHandler mux.RecoverFunc   `json:"-"`                    // 错误处理
}

// 检测cfg的各项字段是否合法，
func checkConfig(cfg *Config) {
	// Port检测
	if len(cfg.Port) == 0 {
		if cfg.HTTPS {
			cfg.Port = ":443"
		} else {
			cfg.Port = ":80"
		}
	}
	if cfg.Port[0] != ':' {
		cfg.Port = ":" + cfg.Port
	}

	// 确保每个目录都以/结尾
	for k, v := range cfg.Static {
		last := v[len(v)-1]
		if last != filepath.Separator && last != '/' {
			v += string(filepath.Separator)
		}
		cfg.Static[k] = v
	}
}

// 初始化web包的内容。
func Run(cfg *Config) {
	checkConfig(cfg)

	if len(cfg.Static) > 0 {
		group, err := NewModule("static")
		if err != nil {
			panic(err)
		}

		for url, dir := range cfg.Static {
			group.Get(url, http.StripPrefix(url, http.FileServer(http.Dir(dir))))
		}
	}

	listen(cfg)
}

// 开始监听。
// errorHandler 为错误处理函数。
func listen(cfg *Config) {
	var h http.Handler

	if len(cfg.ServerName) > 0 {
		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add("Server", cfg.ServerName)
			context.FreeHandler(serveMux).ServeHTTP(w, r)
		})
	}

	if len(cfg.Pprof) > 0 {
		colors.Println(colors.Stdout, colors.Green, colors.Default, "开启了调试功能，地址为：", cfg.Pprof)

		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if !strings.HasPrefix(r.URL.Path, cfg.Pprof) {
				context.FreeHandler(serveMux).ServeHTTP(w, r)
				return
			}

			path := r.URL.Path[len(cfg.Pprof):]
			switch path {
			case "cmdline":
				pprof.Cmdline(w, r)
			case "profile":
				pprof.Profile(w, r)
			case "symbol":
				pprof.Symbol(w, r)
			case "trace":
				pprof.Trace(w, r)
			default:
				pprof.Index(w, r)
			}
		})
	}

	if h == nil {
		h = context.FreeHandler(serveMux)
	}

	if cfg.HTTPS {
		http.ListenAndServeTLS(cfg.Port, cfg.CertFile, cfg.KeyFile, mux.NewRecovery(h, cfg.ErrHandler))
	} else {
		http.ListenAndServe(cfg.Port, mux.NewRecovery(h, cfg.ErrHandler))
	}
}
<commit_msg>修正多次调用mux.NewRecovery的错误<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 一个模块化的微形web框架。
//  m, err := web.NewModule("m1")
//  m.Get("/", ...).
//    Post("/", ...)
//  // 其它模块的初始化工作...
//  web.Run(&Config{},errhandler) // 开始监听端口
package web

import (
	"net/http"
	"net/http/pprof"
	"path/filepath"
	"strings"

	"github.com/issue9/context"
	"github.com/issue9/mux"
	"github.com/issue9/term/colors"
)

// web包的相关配置内容。
type Config struct {
	HTTPS      bool              `json:"https"`                // 是否启用https
	CertFile   string            `json:"certFile,omitempty"`   // 当https为true时，此值为必须
	KeyFile    string            `json:"keyFile,omitempty"`    // 当https为true时，此值为必须
	Port       string            `json:"port,omitempty"`       // 端口，不指定，默认为80或是443
	ServerName string            `json:"serverName,omitempty"` // 响应头的server变量，为空时，不输出该内容
	Static     map[string]string `json:"static,omitempty"`     // 静态路由映身，键名表示路由路径，键值表示文件目录
	Pprof      string            `json:"pprof,omitempty"`      // 指定pprof地址
	ErrHandler mux.RecoverFunc   `json:"-"`                    // 错误处理
}

// 检测cfg的各项字段是否合法，
func checkConfig(cfg *Config) {
	// Port检测
	if len(cfg.Port) == 0 {
		if cfg.HTTPS {
			cfg.Port = ":443"
		} else {
			cfg.Port = ":80"
		}
	}
	if cfg.Port[0] != ':' {
		cfg.Port = ":" + cfg.Port
	}

	// 确保每个目录都以/结尾
	for k, v := range cfg.Static {
		last := v[len(v)-1]
		if last != filepath.Separator && last != '/' {
			v += string(filepath.Separator)
		}
		cfg.Static[k] = v
	}
}

// 初始化web包的内容。
func Run(cfg *Config) {
	checkConfig(cfg)

	if len(cfg.Static) > 0 {
		group, err := NewModule("static")
		if err != nil {
			panic(err)
		}

		for url, dir := range cfg.Static {
			group.Get(url, http.StripPrefix(url, http.FileServer(http.Dir(dir))))
		}
	}

	listen(cfg)
}

// 开始监听。
// errorHandler 为错误处理函数。
func listen(cfg *Config) {
	var h http.Handler = serveMux

	// 修改服务器名称
	if len(cfg.ServerName) > 0 {
		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add("Server", cfg.ServerName)
			h.ServeHTTP(w, r)
		})
	}

	// 作一些清理和错误处理
	h = mux.NewRecovery(context.FreeHandler(serveMux), cfg.ErrHandler)

	// 在最外层添加调试地址，保证调试内容不会被其它handler干扰。
	ref := h
	if len(cfg.Pprof) > 0 {
		colors.Println(colors.Stdout, colors.Green, colors.Default, "开启了调试功能，地址为：", cfg.Pprof)

		h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if !strings.HasPrefix(r.URL.Path, cfg.Pprof) {
				ref.ServeHTTP(w, r)
				return
			}

			path := r.URL.Path[len(cfg.Pprof):]
			switch path {
			case "cmdline":
				pprof.Cmdline(w, r)
			case "profile":
				pprof.Profile(w, r)
			case "symbol":
				pprof.Symbol(w, r)
			case "trace":
				pprof.Trace(w, r)
			default:
				pprof.Index(w, r)
			}
		})
	}

	if h == nil {
		panic("h==nil")
	}

	if cfg.HTTPS {
		http.ListenAndServeTLS(cfg.Port, cfg.CertFile, cfg.KeyFile, h)
	} else {
		http.ListenAndServe(cfg.Port, h)
	}
}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "log"
  "net"
  "strings"
)

func writeWithErrorLog(c net.Conn, data string) {
  _, err := c.Write([]byte(data))
  if (err != nil) {
    log.Fatal(err)
  }
}

func writeWithTab(c net.Conn, data string) {
  writeWithErrorLog(c, data + "\t")
}

func response(c net.Conn, code string, title string, path string, server string, port string) {
  writeWithErrorLog(c, code)
  writeWithTab(c, title)
  writeWithTab(c, path)
  writeWithTab(c, server)
  writeWithTab(c, port)
  writeWithErrorLog(c, "\r\n")
}

func consumeRequest(c net.Conn, path string, arguments []string) {
  log.Printf(path)
  log.Printf(strings.Join(arguments, "&"))

  writeWithErrorLog(c, "/\r\n")
  response(c, "i", "Tacos are great!", "null", "(FALSE)", "0")
  response(c, "i", "", "null", "(FALSE)", "0")
  response(c, "i", "", "null", "(FALSE)", "0")
  response(c, "i", "I really like them.", "null", "(FALSE)", "0")
  response(c, "i", "You are at: " + path, "null", "(FALSE)", "0")
  writeWithErrorLog(c, ".\r\n")
}

func extractRequest(c net.Conn) (string, []string, error)  {
  buf := make([]byte, 4096)

  n, err := c.Read(buf)
  if (err != nil || n == 0) {
      return "", "", err
  }

  parts := strings.Split(string(buf), "\t")
  return parts[0], parts[1:], nil
}

func handleConnection(c net.Conn) {
  path, arguments, err := extractRequest(c)

  if (err != nil) {
    log.Printf("Hit request error")
    log.Fatal(err)
  } else {
    consumeRequest(c, path, arguments)
  }

  c.Close()
  log.Printf("Connection from %v closed.", c.RemoteAddr())
}

func main() {
  port := os.Getenv("PORT")

  if (len(port) == 0) {
    port = "7070"
  }

  ln, err := net.Listen("tcp", ":" + port)
  log.Printf("Server open on localhost:" + port)

  if err != nil {
    log.Printf("Hit listen error")
    panic(err)
  }

  for {
    conn, err := ln.Accept()

    if err != nil {
      log.Printf("Hit accept error")
      panic(err)
      continue
    }

    log.Printf("After accept error")

    go handleConnection(conn)
  }
}
<commit_msg>Fix compile error<commit_after>package main

import (
  "os"
  "log"
  "net"
  "strings"
)

func writeWithErrorLog(c net.Conn, data string) {
  _, err := c.Write([]byte(data))
  if (err != nil) {
    log.Fatal(err)
  }
}

func writeWithTab(c net.Conn, data string) {
  writeWithErrorLog(c, data + "\t")
}

func response(c net.Conn, code string, title string, path string, server string, port string) {
  writeWithErrorLog(c, code)
  writeWithTab(c, title)
  writeWithTab(c, path)
  writeWithTab(c, server)
  writeWithTab(c, port)
  writeWithErrorLog(c, "\r\n")
}

func consumeRequest(c net.Conn, path string, arguments []string) {
  log.Printf(path)
  log.Printf(strings.Join(arguments, "&"))

  writeWithErrorLog(c, "/\r\n")
  response(c, "i", "Tacos are great!", "null", "(FALSE)", "0")
  response(c, "i", "", "null", "(FALSE)", "0")
  response(c, "i", "", "null", "(FALSE)", "0")
  response(c, "i", "I really like them.", "null", "(FALSE)", "0")
  response(c, "i", "You are at: " + path, "null", "(FALSE)", "0")
  writeWithErrorLog(c, ".\r\n")
}

func extractRequest(c net.Conn) (string, []string, error)  {
  buf := make([]byte, 4096)

  n, err := c.Read(buf)
  if (err != nil || n == 0) {
      return "", nil, err
  }

  parts := strings.Split(string(buf), "\t")
  return parts[0], parts[1:], nil
}

func handleConnection(c net.Conn) {
  path, arguments, err := extractRequest(c)

  if (err != nil) {
    log.Printf("Hit request error")
    log.Fatal(err)
  } else {
    consumeRequest(c, path, arguments)
  }

  c.Close()
  log.Printf("Connection from %v closed.", c.RemoteAddr())
}

func main() {
  port := os.Getenv("PORT")

  if (len(port) == 0) {
    port = "7070"
  }

  ln, err := net.Listen("tcp", ":" + port)
  log.Printf("Server open on localhost:" + port)

  if err != nil {
    log.Printf("Hit listen error")
    panic(err)
  }

  for {
    conn, err := ln.Accept()

    if err != nil {
      log.Printf("Hit accept error")
      panic(err)
      continue
    }

    log.Printf("After accept error")

    go handleConnection(conn)
  }
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image/color"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/ora"
	"golang.org/x/net/websocket"
)

func writeError(w *byteio.StickyWriter, err error) {
	w.WriteUint8(0)
	errStr := []byte(err.Error())
	w.WriteUint16(uint16(len(errStr)))
	w.Write(errStr)
	fmt.Println("error:", err)
}

type paint struct {
	color.Color
	X, Y int32
	Err  error
}

func socketHandler(conn *websocket.Conn) {
	conn.PayloadType = websocket.BinaryFrame
	r := byteio.StickyReader{Reader: &byteio.LittleEndianReader{conn}}
	w := byteio.StickyWriter{Writer: &byteio.LittleEndianWriter{Writer: conn}}
	length := r.ReadInt64()
	if r.Err != nil {
		writeError(&w, r.Err)
		return
	}
	f, err := ioutil.TempFile("", "mineWebGen")
	if err != nil {
		writeError(&w, err)
		return
	}
	defer os.Remove(f.Name())
	defer f.Close()
	n, err := io.Copy(f, io.LimitReader(conn, int64(length)))
	if err != nil {
		writeError(&w, err)
		return
	}
	if n != length {
		writeError(&w, io.EOF)
		return
	}
	f.Seek(0, 0)
	o, err := ora.Open(f, length)
	if err != nil {
		writeError(&w, err)
		return
	}
	if o.Layer("terrain") == nil {
		writeError(&w, layerError{"terrain"})
		return
	}
	if o.Layer("height") == nil {
		writeError(&w, layerError{"height"})
		return
	}
	b := o.Bounds()
	w.WriteUint8(1)
	w.WriteInt32(int32(b.Max.X) >> 4)
	w.WriteInt32(int32(b.Max.Y) >> 4)
	if w.Err != nil {
		writeError(&w, w.Err)
		return
	}
	c := make(chan paint, 1024)
	m := make(chan string, 4)
	e := make(chan error, 1)
	go buildMap(o, c, m, e)
Loop:
	for {
		select {
		case p := <-c:
			w.WriteUint8(1)
			w.WriteInt32(p.X)
			w.WriteInt32(p.Y)
			r, g, b, a := p.RGBA()
			w.WriteUint8(uint8(r >> 8))
			w.WriteUint8(uint8(g >> 8))
			w.WriteUint8(uint8(b >> 8))
			w.WriteUint8(uint8(a >> 8))
		case message := <-m:
			w.WriteUint8(2)
			w.WriteUint16(uint16(len(message)))
			w.Write([]byte(message))
		case err := <-e:
			if err == nil {
				break Loop
			}
			writeError(&w, err)
			return
		}
	}
	w.WriteUint8(255)
}

type layerError struct {
	name string
}

func (l layerError) Error() string {
	return "missing layer: " + l.name
}

var port = flag.Uint("-p", 8080, "server port")

func main() {
	http.Handle("/socket", websocket.Handler(socketHandler))
	http.Handle("/", http.FileServer(dir))
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Println(err)
		return
	}

	c := make(chan os.Signal, 1)
	go func() {
		defer l.Close()
		log.Println("Server Started")
		signal.Notify(c, os.Interrupt)
		defer signal.Stop(c)
		<-c
		close(c)
		log.Println("Closing")
	}()

	err = http.Serve(l, nil)
	select {
	case <-c:
	default:
		close(c)
		log.Println(err)
	}
}
<commit_msg>Run at MaxProc<commit_after>package main

import (
	"flag"
	"fmt"
	"image/color"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"

	"github.com/MJKWoolnough/byteio"
	"github.com/MJKWoolnough/ora"
	"golang.org/x/net/websocket"
)

func writeError(w *byteio.StickyWriter, err error) {
	w.WriteUint8(0)
	errStr := []byte(err.Error())
	w.WriteUint16(uint16(len(errStr)))
	w.Write(errStr)
	fmt.Println("error:", err)
}

type paint struct {
	color.Color
	X, Y int32
	Err  error
}

func socketHandler(conn *websocket.Conn) {
	conn.PayloadType = websocket.BinaryFrame
	r := byteio.StickyReader{Reader: &byteio.LittleEndianReader{conn}}
	w := byteio.StickyWriter{Writer: &byteio.LittleEndianWriter{Writer: conn}}
	length := r.ReadInt64()
	if r.Err != nil {
		writeError(&w, r.Err)
		return
	}
	f, err := ioutil.TempFile("", "mineWebGen")
	if err != nil {
		writeError(&w, err)
		return
	}
	defer os.Remove(f.Name())
	defer f.Close()
	n, err := io.Copy(f, io.LimitReader(conn, int64(length)))
	if err != nil {
		writeError(&w, err)
		return
	}
	if n != length {
		writeError(&w, io.EOF)
		return
	}
	f.Seek(0, 0)
	o, err := ora.Open(f, length)
	if err != nil {
		writeError(&w, err)
		return
	}
	if o.Layer("terrain") == nil {
		writeError(&w, layerError{"terrain"})
		return
	}
	if o.Layer("height") == nil {
		writeError(&w, layerError{"height"})
		return
	}
	b := o.Bounds()
	w.WriteUint8(1)
	w.WriteInt32(int32(b.Max.X) >> 4)
	w.WriteInt32(int32(b.Max.Y) >> 4)
	if w.Err != nil {
		writeError(&w, w.Err)
		return
	}
	c := make(chan paint, 1024)
	m := make(chan string, 4)
	e := make(chan error, 1)
	go buildMap(o, c, m, e)
Loop:
	for {
		select {
		case p := <-c:
			w.WriteUint8(1)
			w.WriteInt32(p.X)
			w.WriteInt32(p.Y)
			r, g, b, a := p.RGBA()
			w.WriteUint8(uint8(r >> 8))
			w.WriteUint8(uint8(g >> 8))
			w.WriteUint8(uint8(b >> 8))
			w.WriteUint8(uint8(a >> 8))
		case message := <-m:
			w.WriteUint8(2)
			w.WriteUint16(uint16(len(message)))
			w.Write([]byte(message))
		case err := <-e:
			if err == nil {
				break Loop
			}
			writeError(&w, err)
			return
		}
	}
	w.WriteUint8(255)
}

type layerError struct {
	name string
}

func (l layerError) Error() string {
	return "missing layer: " + l.name
}

var port = flag.Uint("-p", 8080, "server port")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	http.Handle("/socket", websocket.Handler(socketHandler))
	http.Handle("/", http.FileServer(dir))
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Println(err)
		return
	}

	c := make(chan os.Signal, 1)
	go func() {
		defer l.Close()
		log.Println("Server Started")
		signal.Notify(c, os.Interrupt)
		defer signal.Stop(c)
		<-c
		close(c)
		log.Println("Closing")
	}()

	err = http.Serve(l, nil)
	select {
	case <-c:
	default:
		close(c)
		log.Println(err)
	}
}
<|endoftext|>


<commit_before>package file

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"

	"github.com/docker/docker/pkg/term"
	"github.com/sirupsen/logrus"
)

var (
	re           = regexp.MustCompile("[^a-zA-Z0-9]")
	ErrSkipBuild = errors.New("skip build")
)

type Dapperfile struct {
	File        string
	Mode        string
	docker      string
	env         Context
	Socket      bool
	NoOut       bool
	Args        []string
	From        string
	Quiet       bool
	hostArch    string
	Keep        bool
	NoContext   bool
	MountSuffix string
}

func Lookup(file string) (*Dapperfile, error) {
	if _, err := os.Stat(file); err != nil {
		return nil, err
	}

	d := &Dapperfile{
		File: file,
	}

	return d, d.init()
}

func (d *Dapperfile) init() error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}
	d.docker = docker
	if d.Args, err = d.argsFromEnv(d.File); err != nil {
		return err
	}
	if d.hostArch == "" {
		d.hostArch = d.findHostArch()
	}
	return nil
}

func (d *Dapperfile) argsFromEnv(dockerfile string) ([]string, error) {
	file, err := os.Open(dockerfile)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r := []string{}
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		fields := strings.Fields(line)
		if len(fields) <= 1 {
			continue
		}

		command := fields[0]
		if command != "ARG" {
			continue
		}

		key := strings.Split(fields[1], "=")[0]
		value := os.Getenv(key)

		if key == "DAPPER_HOST_ARCH" && value == "" {
			value = d.findHostArch()
		}

		if key == "DAPPER_HOST_ARCH" {
			d.hostArch = value
		}

		if value != "" {
			r = append(r, fmt.Sprintf("%s=%s", key, value))
		}
	}

	return r, nil
}

func (d *Dapperfile) Run(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running build in %s", tag)
	name, args := d.runArgs(tag, "", commandArgs)
	defer func() {
		if d.Keep {
			logrus.Infof("Keeping build container %s", name)
		} else {
			logrus.Debugf("Deleting temp container %s", name)
			d.execWithOutput("rm", "-fv", name)
		}
	}()

	if err := d.run(args...); err != nil {
		return err
	}

	source := d.env.Source()
	output := d.env.Output()
	if !d.IsBind() && !d.NoOut {
		for _, i := range output {
			p := i
			if !strings.HasPrefix(p, "/") {
				p = path.Join(source, i)
			}
			targetDir := path.Dir(i)
			if err := os.MkdirAll(targetDir, 0755); err != nil {
				return err
			}
			logrus.Infof("docker cp %s %s", p, targetDir)
			if err := d.exec("cp", name+":"+p, targetDir); err != nil {
				logrus.Debugf("Error copying back '%s': %s", i, err)
			}
		}
	}

	return nil
}

func (d *Dapperfile) Shell(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running shell in %s", tag)
	_, args := d.runArgs(tag, d.env.Shell(), nil)
	args = append([]string{"--rm"}, args...)

	return d.runExec(args...)
}

func (d *Dapperfile) runArgs(tag, shell string, commandArgs []string) (string, []string) {
	name := fmt.Sprintf("%s-%s", strings.Split(tag, ":")[0], randString())

	args := []string{"-i", "--name", name}

	if term.IsTerminal(os.Stdout.Fd()) {
		args = append(args, "-t")
	}

	if d.env.Socket() || d.Socket {
		args = append(args, "-v", d.vSocket())
	}

	if d.IsBind() {
		wd, err := os.Getwd()
		if err == nil {
			suffix := ""
			if d.MountSuffix != "" {
				suffix = ":" + d.MountSuffix
			}
			args = append(args, "-v", fmt.Sprintf("%s:%s%s", fmt.Sprintf("%s/%s", wd, d.env.Cp()), d.env.Source(), suffix))
		}
	}

	args = append(args, "-e", fmt.Sprintf("DAPPER_UID=%d", os.Getuid()))
	args = append(args, "-e", fmt.Sprintf("DAPPER_GID=%d", os.Getgid()))

	for _, env := range d.env.Env() {
		args = append(args, "-e", env)
	}

	if shell != "" {
		args = append(args, "--entrypoint", shell)
		args = append(args, "-e", "TERM")
	}

	args = append(args, d.env.RunArgs()...)
	args = append(args, tag)

	if shell != "" && len(commandArgs) == 0 {
		args = append(args, "-")
	} else {
		args = append(args, commandArgs...)
	}

	return name, args
}

func (d *Dapperfile) findHostArch() string {
	output, err := d.execWithOutput("version", "-f", "{{.Server.Arch}}")
	if err != nil {
		return runtime.GOARCH
	}
	return strings.TrimSpace(string(output))
}

func (d *Dapperfile) Build(args []string) error {
	_, err := d.build(args, false)
	return err
}

func (d *Dapperfile) build(args []string, copy bool) (string, error) {
	dapperFile, err := d.dapperFile()
	if err != nil {
		return "", err
	}

	tag := d.tag()
	logrus.Debugf("Building %s using %s", tag, d.File)
	buildArgs := []string{"build"}
	if len(args) == 0 {
		buildArgs = append(buildArgs, "-t", tag)
	}

	if d.Quiet {
		buildArgs = append(buildArgs, "-q")
	}

	for _, v := range d.Args {
		buildArgs = append(buildArgs, "--build-arg", v)
	}

	if d.NoContext {
		buildArgs = append(buildArgs, "-")
		buildArgs = append(buildArgs, args...)
		if err := d.execWithStdin(bytes.NewBuffer(dapperFile), buildArgs...); err != nil {
			return "", err
		}
	} else {
		tempfile, err := d.tempfile(dapperFile)
		if err != nil {
			return "", err
		}
		defer os.Remove(tempfile)

		buildArgs = append(buildArgs, "-f", tempfile)
		if len(args) > 0 {
			buildArgs = append(buildArgs, args...)
		} else {
			buildArgs = append(buildArgs, ".")
		}

		if err := d.exec(buildArgs...); err != nil {
			return "", err
		}
	}

	if !copy {
		return tag, nil
	}

	if err := d.readEnv(tag); err != nil {
		return "", err
	}

	if !d.IsBind() {
		text := fmt.Sprintf("FROM %s\nCOPY %s %s", tag, d.env.Cp(), d.env.Source())
		if err := d.buildWithContent(tag, text); err != nil {
			return "", err
		}
	}

	return tag, nil
}

func (d *Dapperfile) buildWithContent(tag, content string) error {
	tempfile, err := d.tempfile([]byte(content))
	if err != nil {
		return err
	}

	defer func() {
		logrus.Debugf("Deleting tempfile %s", tempfile)
		if err := os.Remove(tempfile); err != nil {
			logrus.Errorf("Failed to delete tempfile %s: %v", tempfile, err)
		}
	}()

	return d.exec("build", "-t", tag, "-f", tempfile, ".")
}

func (d *Dapperfile) readEnv(tag string) error {
	var envList []string

	args := []string{"inspect", "-f", "{{json .Config.Env}}", tag}

	cmd := exec.Command(d.docker, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		logrus.Errorf("Failed to run docker %v: %v", args, err)
		return err
	}

	if err := json.Unmarshal(output, &envList); err != nil {
		return err
	}

	d.env = map[string]string{}

	for _, item := range envList {
		parts := strings.SplitN(item, "=", 2)
		k, v := parts[0], parts[1]
		logrus.Debugf("Reading Env: %s=%s", k, v)
		d.env[k] = v
	}

	logrus.Debugf("Source: %s", d.env.Source())
	logrus.Debugf("Cp: %s", d.env.Cp())
	logrus.Debugf("Socket: %t", d.env.Socket())
	logrus.Debugf("Mode: %s", d.env.Mode(d.Mode))
	logrus.Debugf("Env: %v", d.env.Env())
	logrus.Debugf("Output: %v", d.env.Output())

	return nil
}

func (d *Dapperfile) tag() string {
	cwd, err := os.Getwd()
	if err == nil {
		cwd = filepath.Base(cwd)
	} else {
		cwd = "dapper-unknown"
	}
	// repository name must be lowercase
	cwd = strings.ToLower(cwd)

	output, _ := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
	tag := strings.TrimSpace(string(output))
	if tag == "" {
		tag = randString()
	}
	tag = re.ReplaceAllLiteralString(tag, "-")

	return fmt.Sprintf("%s:%s", cwd, tag)
}

func (d *Dapperfile) run(args ...string) error {
	return d.exec(append([]string{"run"}, args...)...)
}

func (d *Dapperfile) exec(args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) execWithStdin(stdin io.Reader, args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) runExec(args ...string) error {
	logrus.Debugf("Exec %s run %v", d.docker, args)
	return syscall.Exec(d.docker, append([]string{"docker", "run"}, args...), os.Environ())
}

func (d *Dapperfile) execWithOutput(args ...string) ([]byte, error) {
	cmd := exec.Command(d.docker, args...)
	return cmd.CombinedOutput()
}

func (d *Dapperfile) IsBind() bool {
	return d.env.Mode(d.Mode) == "bind"
}

func (d *Dapperfile) dapperFile() ([]byte, error) {
	var input io.Reader

	if d.NoContext {
		input = os.Stdin
	} else {
		f, err := os.Open(d.File)
		if err != nil {
			return nil, err
		}
		input = f
		defer f.Close()
	}

	buffer := &bytes.Buffer{}
	scanner := bufio.NewScanner(input)

	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "FROM ") && len(strings.Fields(line)) == 2 && scanner.Scan() {
			nextLine := scanner.Text()
			if strings.HasPrefix(nextLine, "# FROM") {
				baseImage, ok := toMap(nextLine)[d.hostArch]
				if ok && baseImage == "skip" {
					return nil, ErrSkipBuild
				}
				if ok {
					line = "FROM " + baseImage
				}
			}
			line = line + "\n" + nextLine
		}

		buffer.WriteString(line)
		buffer.WriteString("\n")
	}

	return buffer.Bytes(), scanner.Err()
}
<commit_msg>Switch to mattn/go-isatty for cleaner dependencies<commit_after>package file

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"

	"github.com/mattn/go-isatty"
	"github.com/sirupsen/logrus"
)

var (
	re           = regexp.MustCompile("[^a-zA-Z0-9]")
	ErrSkipBuild = errors.New("skip build")
)

type Dapperfile struct {
	File        string
	Mode        string
	docker      string
	env         Context
	Socket      bool
	NoOut       bool
	Args        []string
	From        string
	Quiet       bool
	hostArch    string
	Keep        bool
	NoContext   bool
	MountSuffix string
}

func Lookup(file string) (*Dapperfile, error) {
	if _, err := os.Stat(file); err != nil {
		return nil, err
	}

	d := &Dapperfile{
		File: file,
	}

	return d, d.init()
}

func (d *Dapperfile) init() error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}
	d.docker = docker
	if d.Args, err = d.argsFromEnv(d.File); err != nil {
		return err
	}
	if d.hostArch == "" {
		d.hostArch = d.findHostArch()
	}
	return nil
}

func (d *Dapperfile) argsFromEnv(dockerfile string) ([]string, error) {
	file, err := os.Open(dockerfile)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r := []string{}
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		fields := strings.Fields(line)
		if len(fields) <= 1 {
			continue
		}

		command := fields[0]
		if command != "ARG" {
			continue
		}

		key := strings.Split(fields[1], "=")[0]
		value := os.Getenv(key)

		if key == "DAPPER_HOST_ARCH" && value == "" {
			value = d.findHostArch()
		}

		if key == "DAPPER_HOST_ARCH" {
			d.hostArch = value
		}

		if value != "" {
			r = append(r, fmt.Sprintf("%s=%s", key, value))
		}
	}

	return r, nil
}

func (d *Dapperfile) Run(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running build in %s", tag)
	name, args := d.runArgs(tag, "", commandArgs)
	defer func() {
		if d.Keep {
			logrus.Infof("Keeping build container %s", name)
		} else {
			logrus.Debugf("Deleting temp container %s", name)
			d.execWithOutput("rm", "-fv", name)
		}
	}()

	if err := d.run(args...); err != nil {
		return err
	}

	source := d.env.Source()
	output := d.env.Output()
	if !d.IsBind() && !d.NoOut {
		for _, i := range output {
			p := i
			if !strings.HasPrefix(p, "/") {
				p = path.Join(source, i)
			}
			targetDir := path.Dir(i)
			if err := os.MkdirAll(targetDir, 0755); err != nil {
				return err
			}
			logrus.Infof("docker cp %s %s", p, targetDir)
			if err := d.exec("cp", name+":"+p, targetDir); err != nil {
				logrus.Debugf("Error copying back '%s': %s", i, err)
			}
		}
	}

	return nil
}

func (d *Dapperfile) Shell(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running shell in %s", tag)
	_, args := d.runArgs(tag, d.env.Shell(), nil)
	args = append([]string{"--rm"}, args...)

	return d.runExec(args...)
}

func (d *Dapperfile) runArgs(tag, shell string, commandArgs []string) (string, []string) {
	name := fmt.Sprintf("%s-%s", strings.Split(tag, ":")[0], randString())

	args := []string{"-i", "--name", name}

	if isatty.IsTerminal(os.Stdout.Fd()) {
		args = append(args, "-t")
	}

	if d.env.Socket() || d.Socket {
		args = append(args, "-v", d.vSocket())
	}

	if d.IsBind() {
		wd, err := os.Getwd()
		if err == nil {
			suffix := ""
			if d.MountSuffix != "" {
				suffix = ":" + d.MountSuffix
			}
			args = append(args, "-v", fmt.Sprintf("%s:%s%s", fmt.Sprintf("%s/%s", wd, d.env.Cp()), d.env.Source(), suffix))
		}
	}

	args = append(args, "-e", fmt.Sprintf("DAPPER_UID=%d", os.Getuid()))
	args = append(args, "-e", fmt.Sprintf("DAPPER_GID=%d", os.Getgid()))

	for _, env := range d.env.Env() {
		args = append(args, "-e", env)
	}

	if shell != "" {
		args = append(args, "--entrypoint", shell)
		args = append(args, "-e", "TERM")
	}

	args = append(args, d.env.RunArgs()...)
	args = append(args, tag)

	if shell != "" && len(commandArgs) == 0 {
		args = append(args, "-")
	} else {
		args = append(args, commandArgs...)
	}

	return name, args
}

func (d *Dapperfile) findHostArch() string {
	output, err := d.execWithOutput("version", "-f", "{{.Server.Arch}}")
	if err != nil {
		return runtime.GOARCH
	}
	return strings.TrimSpace(string(output))
}

func (d *Dapperfile) Build(args []string) error {
	_, err := d.build(args, false)
	return err
}

func (d *Dapperfile) build(args []string, copy bool) (string, error) {
	dapperFile, err := d.dapperFile()
	if err != nil {
		return "", err
	}

	tag := d.tag()
	logrus.Debugf("Building %s using %s", tag, d.File)
	buildArgs := []string{"build"}
	if len(args) == 0 {
		buildArgs = append(buildArgs, "-t", tag)
	}

	if d.Quiet {
		buildArgs = append(buildArgs, "-q")
	}

	for _, v := range d.Args {
		buildArgs = append(buildArgs, "--build-arg", v)
	}

	if d.NoContext {
		buildArgs = append(buildArgs, "-")
		buildArgs = append(buildArgs, args...)
		if err := d.execWithStdin(bytes.NewBuffer(dapperFile), buildArgs...); err != nil {
			return "", err
		}
	} else {
		tempfile, err := d.tempfile(dapperFile)
		if err != nil {
			return "", err
		}
		defer os.Remove(tempfile)

		buildArgs = append(buildArgs, "-f", tempfile)
		if len(args) > 0 {
			buildArgs = append(buildArgs, args...)
		} else {
			buildArgs = append(buildArgs, ".")
		}

		if err := d.exec(buildArgs...); err != nil {
			return "", err
		}
	}

	if !copy {
		return tag, nil
	}

	if err := d.readEnv(tag); err != nil {
		return "", err
	}

	if !d.IsBind() {
		text := fmt.Sprintf("FROM %s\nCOPY %s %s", tag, d.env.Cp(), d.env.Source())
		if err := d.buildWithContent(tag, text); err != nil {
			return "", err
		}
	}

	return tag, nil
}

func (d *Dapperfile) buildWithContent(tag, content string) error {
	tempfile, err := d.tempfile([]byte(content))
	if err != nil {
		return err
	}

	defer func() {
		logrus.Debugf("Deleting tempfile %s", tempfile)
		if err := os.Remove(tempfile); err != nil {
			logrus.Errorf("Failed to delete tempfile %s: %v", tempfile, err)
		}
	}()

	return d.exec("build", "-t", tag, "-f", tempfile, ".")
}

func (d *Dapperfile) readEnv(tag string) error {
	var envList []string

	args := []string{"inspect", "-f", "{{json .Config.Env}}", tag}

	cmd := exec.Command(d.docker, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		logrus.Errorf("Failed to run docker %v: %v", args, err)
		return err
	}

	if err := json.Unmarshal(output, &envList); err != nil {
		return err
	}

	d.env = map[string]string{}

	for _, item := range envList {
		parts := strings.SplitN(item, "=", 2)
		k, v := parts[0], parts[1]
		logrus.Debugf("Reading Env: %s=%s", k, v)
		d.env[k] = v
	}

	logrus.Debugf("Source: %s", d.env.Source())
	logrus.Debugf("Cp: %s", d.env.Cp())
	logrus.Debugf("Socket: %t", d.env.Socket())
	logrus.Debugf("Mode: %s", d.env.Mode(d.Mode))
	logrus.Debugf("Env: %v", d.env.Env())
	logrus.Debugf("Output: %v", d.env.Output())

	return nil
}

func (d *Dapperfile) tag() string {
	cwd, err := os.Getwd()
	if err == nil {
		cwd = filepath.Base(cwd)
	} else {
		cwd = "dapper-unknown"
	}
	// repository name must be lowercase
	cwd = strings.ToLower(cwd)

	output, _ := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
	tag := strings.TrimSpace(string(output))
	if tag == "" {
		tag = randString()
	}
	tag = re.ReplaceAllLiteralString(tag, "-")

	return fmt.Sprintf("%s:%s", cwd, tag)
}

func (d *Dapperfile) run(args ...string) error {
	return d.exec(append([]string{"run"}, args...)...)
}

func (d *Dapperfile) exec(args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) execWithStdin(stdin io.Reader, args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) runExec(args ...string) error {
	logrus.Debugf("Exec %s run %v", d.docker, args)
	return syscall.Exec(d.docker, append([]string{"docker", "run"}, args...), os.Environ())
}

func (d *Dapperfile) execWithOutput(args ...string) ([]byte, error) {
	cmd := exec.Command(d.docker, args...)
	return cmd.CombinedOutput()
}

func (d *Dapperfile) IsBind() bool {
	return d.env.Mode(d.Mode) == "bind"
}

func (d *Dapperfile) dapperFile() ([]byte, error) {
	var input io.Reader

	if d.NoContext {
		input = os.Stdin
	} else {
		f, err := os.Open(d.File)
		if err != nil {
			return nil, err
		}
		input = f
		defer f.Close()
	}

	buffer := &bytes.Buffer{}
	scanner := bufio.NewScanner(input)

	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "FROM ") && len(strings.Fields(line)) == 2 && scanner.Scan() {
			nextLine := scanner.Text()
			if strings.HasPrefix(nextLine, "# FROM") {
				baseImage, ok := toMap(nextLine)[d.hostArch]
				if ok && baseImage == "skip" {
					return nil, ErrSkipBuild
				}
				if ok {
					line = "FROM " + baseImage
				}
			}
			line = line + "\n" + nextLine
		}

		buffer.WriteString(line)
		buffer.WriteString("\n")
	}

	return buffer.Bytes(), scanner.Err()
}
<|endoftext|>